Skip to content

Commit 9b63cd6

Browse files
authored
[Firebase AI] Add thought summary and signature support (#9192)
1 parent 02280d7 commit 9b63cd6

17 files changed

+372
-79
lines changed

.changeset/brave-llamas-impress.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@firebase/ai': minor
3+
'firebase': minor
4+
---
5+
6+
Add `thoughtSummary()` convenience method to `EnhancedGenerateContentResponse`.

common/api-review/ai.api.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,10 +223,10 @@ export { Date_2 as Date }
223223

224224
// @public
225225
export interface EnhancedGenerateContentResponse extends GenerateContentResponse {
226-
// (undocumented)
227226
functionCalls: () => FunctionCall[] | undefined;
228227
inlineDataParts: () => InlineDataPart[] | undefined;
229228
text: () => string;
229+
thoughtSummary: () => string | undefined;
230230
}
231231

232232
// @public
@@ -264,6 +264,10 @@ export interface FileDataPart {
264264
inlineData?: never;
265265
// (undocumented)
266266
text?: never;
267+
// (undocumented)
268+
thought?: boolean;
269+
// @internal (undocumented)
270+
thoughtSignature?: never;
267271
}
268272

269273
// @public
@@ -318,6 +322,10 @@ export interface FunctionCallPart {
318322
inlineData?: never;
319323
// (undocumented)
320324
text?: never;
325+
// (undocumented)
326+
thought?: boolean;
327+
// @internal (undocumented)
328+
thoughtSignature?: never;
321329
}
322330

323331
// @public
@@ -350,6 +358,10 @@ export interface FunctionResponsePart {
350358
inlineData?: never;
351359
// (undocumented)
352360
text?: never;
361+
// (undocumented)
362+
thought?: boolean;
363+
// @internal (undocumented)
364+
thoughtSignature?: never;
353365
}
354366

355367
// @public
@@ -732,6 +744,10 @@ export interface InlineDataPart {
732744
inlineData: GenerativeContentBlob;
733745
// (undocumented)
734746
text?: never;
747+
// (undocumented)
748+
thought?: boolean;
749+
// @internal (undocumented)
750+
thoughtSignature?: never;
735751
videoMetadata?: VideoMetadata;
736752
}
737753

@@ -1063,10 +1079,15 @@ export interface TextPart {
10631079
inlineData?: never;
10641080
// (undocumented)
10651081
text: string;
1082+
// (undocumented)
1083+
thought?: boolean;
1084+
// @internal (undocumented)
1085+
thoughtSignature?: string;
10661086
}
10671087

10681088
// @public
10691089
export interface ThinkingConfig {
1090+
includeThoughts?: boolean;
10701091
thinkingBudget?: number;
10711092
}
10721093

docs-devsite/ai.enhancedgeneratecontentresponse.md

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,15 @@ export interface EnhancedGenerateContentResponse extends GenerateContentResponse
2323
2424
| Property | Type | Description |
2525
| --- | --- | --- |
26-
| [functionCalls](./ai.enhancedgeneratecontentresponse.md#enhancedgeneratecontentresponsefunctioncalls) | () =&gt; [FunctionCall](./ai.functioncall.md#functioncall_interface)<!-- -->\[\] \| undefined | |
27-
| [inlineDataParts](./ai.enhancedgeneratecontentresponse.md#enhancedgeneratecontentresponseinlinedataparts) | () =&gt; [InlineDataPart](./ai.inlinedatapart.md#inlinedatapart_interface)<!-- -->\[\] \| undefined | Aggregates and returns all [InlineDataPart](./ai.inlinedatapart.md#inlinedatapart_interface)<!-- -->s from the [GenerateContentResponse](./ai.generatecontentresponse.md#generatecontentresponse_interface)<!-- -->'s first candidate. |
26+
| [functionCalls](./ai.enhancedgeneratecontentresponse.md#enhancedgeneratecontentresponsefunctioncalls) | () =&gt; [FunctionCall](./ai.functioncall.md#functioncall_interface)<!-- -->\[\] \| undefined | Aggregates and returns every [FunctionCall](./ai.functioncall.md#functioncall_interface) from the first candidate of [GenerateContentResponse](./ai.generatecontentresponse.md#generatecontentresponse_interface)<!-- -->. |
27+
| [inlineDataParts](./ai.enhancedgeneratecontentresponse.md#enhancedgeneratecontentresponseinlinedataparts) | () =&gt; [InlineDataPart](./ai.inlinedatapart.md#inlinedatapart_interface)<!-- -->\[\] \| undefined | Aggregates and returns every [InlineDataPart](./ai.inlinedatapart.md#inlinedatapart_interface) from the first candidate of [GenerateContentResponse](./ai.generatecontentresponse.md#generatecontentresponse_interface)<!-- -->. |
2828
| [text](./ai.enhancedgeneratecontentresponse.md#enhancedgeneratecontentresponsetext) | () =&gt; string | Returns the text string from the response, if available. Throws if the prompt or candidate was blocked. |
29+
| [thoughtSummary](./ai.enhancedgeneratecontentresponse.md#enhancedgeneratecontentresponsethoughtsummary) | () =&gt; string \| undefined | Aggregates and returns every [TextPart](./ai.textpart.md#textpart_interface) with their <code>thought</code> property set to <code>true</code> from the first candidate of [GenerateContentResponse](./ai.generatecontentresponse.md#generatecontentresponse_interface)<!-- -->. |
2930
3031
## EnhancedGenerateContentResponse.functionCalls
3132
33+
Aggregates and returns every [FunctionCall](./ai.functioncall.md#functioncall_interface) from the first candidate of [GenerateContentResponse](./ai.generatecontentresponse.md#generatecontentresponse_interface)<!-- -->.
34+
3235
<b>Signature:</b>
3336
3437
```typescript
@@ -37,7 +40,7 @@ functionCalls: () => FunctionCall[] | undefined;
3740
3841
## EnhancedGenerateContentResponse.inlineDataParts
3942
40-
Aggregates and returns all [InlineDataPart](./ai.inlinedatapart.md#inlinedatapart_interface)<!-- -->s from the [GenerateContentResponse](./ai.generatecontentresponse.md#generatecontentresponse_interface)<!-- -->'s first candidate.
43+
Aggregates and returns every [InlineDataPart](./ai.inlinedatapart.md#inlinedatapart_interface) from the first candidate of [GenerateContentResponse](./ai.generatecontentresponse.md#generatecontentresponse_interface)<!-- -->.
4144
4245
<b>Signature:</b>
4346
@@ -54,3 +57,17 @@ Returns the text string from the response, if available. Throws if the prompt or
5457
```typescript
5558
text: () => string;
5659
```
60+
61+
## EnhancedGenerateContentResponse.thoughtSummary
62+
63+
Aggregates and returns every [TextPart](./ai.textpart.md#textpart_interface) with their `thought` property set to `true` from the first candidate of [GenerateContentResponse](./ai.generatecontentresponse.md#generatecontentresponse_interface)<!-- -->.
64+
65+
Thought summaries provide a brief overview of the model's internal thinking process, offering insight into how it arrived at the final answer. This can be useful for debugging, understanding the model's reasoning, and verifying its accuracy.
66+
67+
Thoughts will only be included if [ThinkingConfig.includeThoughts](./ai.thinkingconfig.md#thinkingconfigincludethoughts) is set to `true`<!-- -->.
68+
69+
<b>Signature:</b>
70+
71+
```typescript
72+
thoughtSummary: () => string | undefined;
73+
```

docs-devsite/ai.filedatapart.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export interface FileDataPart
2727
| [functionResponse](./ai.filedatapart.md#filedatapartfunctionresponse) | never | |
2828
| [inlineData](./ai.filedatapart.md#filedatapartinlinedata) | never | |
2929
| [text](./ai.filedatapart.md#filedataparttext) | never | |
30+
| [thought](./ai.filedatapart.md#filedatapartthought) | boolean | |
3031

3132
## FileDataPart.fileData
3233

@@ -67,3 +68,11 @@ inlineData?: never;
6768
```typescript
6869
text?: never;
6970
```
71+
72+
## FileDataPart.thought
73+
74+
<b>Signature:</b>
75+
76+
```typescript
77+
thought?: boolean;
78+
```

docs-devsite/ai.functioncallpart.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export interface FunctionCallPart
2626
| [functionResponse](./ai.functioncallpart.md#functioncallpartfunctionresponse) | never | |
2727
| [inlineData](./ai.functioncallpart.md#functioncallpartinlinedata) | never | |
2828
| [text](./ai.functioncallpart.md#functioncallparttext) | never | |
29+
| [thought](./ai.functioncallpart.md#functioncallpartthought) | boolean | |
2930

3031
## FunctionCallPart.functionCall
3132

@@ -58,3 +59,11 @@ inlineData?: never;
5859
```typescript
5960
text?: never;
6061
```
62+
63+
## FunctionCallPart.thought
64+
65+
<b>Signature:</b>
66+
67+
```typescript
68+
thought?: boolean;
69+
```

docs-devsite/ai.functionresponsepart.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export interface FunctionResponsePart
2626
| [functionResponse](./ai.functionresponsepart.md#functionresponsepartfunctionresponse) | [FunctionResponse](./ai.functionresponse.md#functionresponse_interface) | |
2727
| [inlineData](./ai.functionresponsepart.md#functionresponsepartinlinedata) | never | |
2828
| [text](./ai.functionresponsepart.md#functionresponseparttext) | never | |
29+
| [thought](./ai.functionresponsepart.md#functionresponsepartthought) | boolean | |
2930

3031
## FunctionResponsePart.functionCall
3132

@@ -58,3 +59,11 @@ inlineData?: never;
5859
```typescript
5960
text?: never;
6061
```
62+
63+
## FunctionResponsePart.thought
64+
65+
<b>Signature:</b>
66+
67+
```typescript
68+
thought?: boolean;
69+
```

docs-devsite/ai.inlinedatapart.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export interface InlineDataPart
2626
| [functionResponse](./ai.inlinedatapart.md#inlinedatapartfunctionresponse) | never | |
2727
| [inlineData](./ai.inlinedatapart.md#inlinedatapartinlinedata) | [GenerativeContentBlob](./ai.generativecontentblob.md#generativecontentblob_interface) | |
2828
| [text](./ai.inlinedatapart.md#inlinedataparttext) | never | |
29+
| [thought](./ai.inlinedatapart.md#inlinedatapartthought) | boolean | |
2930
| [videoMetadata](./ai.inlinedatapart.md#inlinedatapartvideometadata) | [VideoMetadata](./ai.videometadata.md#videometadata_interface) | Applicable if <code>inlineData</code> is a video. |
3031

3132
## InlineDataPart.functionCall
@@ -60,6 +61,14 @@ inlineData: GenerativeContentBlob;
6061
text?: never;
6162
```
6263

64+
## InlineDataPart.thought
65+
66+
<b>Signature:</b>
67+
68+
```typescript
69+
thought?: boolean;
70+
```
71+
6372
## InlineDataPart.videoMetadata
6473

6574
Applicable if `inlineData` is a video.

docs-devsite/ai.textpart.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export interface TextPart
2626
| [functionResponse](./ai.textpart.md#textpartfunctionresponse) | never | |
2727
| [inlineData](./ai.textpart.md#textpartinlinedata) | never | |
2828
| [text](./ai.textpart.md#textparttext) | string | |
29+
| [thought](./ai.textpart.md#textpartthought) | boolean | |
2930

3031
## TextPart.functionCall
3132

@@ -58,3 +59,11 @@ inlineData?: never;
5859
```typescript
5960
text: string;
6061
```
62+
63+
## TextPart.thought
64+
65+
<b>Signature:</b>
66+
67+
```typescript
68+
thought?: boolean;
69+
```

docs-devsite/ai.thinkingconfig.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,21 @@ export interface ThinkingConfig
2424

2525
| Property | Type | Description |
2626
| --- | --- | --- |
27+
| [includeThoughts](./ai.thinkingconfig.md#thinkingconfigincludethoughts) | boolean | Whether to include "thought summaries" in the model's response. |
2728
| [thinkingBudget](./ai.thinkingconfig.md#thinkingconfigthinkingbudget) | number | The thinking budget, in tokens.<!-- -->This parameter sets an upper limit on the number of tokens the model can use for its internal "thinking" process. A higher budget may result in higher quality responses for complex tasks but can also increase latency and cost.<!-- -->If you don't specify a budget, the model will determine the appropriate amount of thinking based on the complexity of the prompt.<!-- -->An error will be thrown if you set a thinking budget for a model that does not support this feature or if the specified budget is not within the model's supported range. |
2829

30+
## ThinkingConfig.includeThoughts
31+
32+
Whether to include "thought summaries" in the model's response.
33+
34+
Thought summaries provide a brief overview of the model's internal thinking process, offering insight into how it arrived at the final answer. This can be useful for debugging, understanding the model's reasoning, and verifying its accuracy.
35+
36+
<b>Signature:</b>
37+
38+
```typescript
39+
includeThoughts?: boolean;
40+
```
41+
2942
## ThinkingConfig.thinkingBudget
3043

3144
The thinking budget, in tokens.

packages/ai/src/methods/chat-session-helpers.test.ts

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ import { FirebaseError } from '@firebase/util';
2222

2323
describe('chat-session-helpers', () => {
2424
describe('validateChatHistory', () => {
25-
const TCS: Array<{ history: Content[]; isValid: boolean }> = [
25+
const TCS: Array<{
26+
history: Content[];
27+
isValid: boolean;
28+
errorShouldInclude?: string;
29+
}> = [
2630
{
2731
history: [{ role: 'user', parts: [{ text: 'hi' }] }],
2832
isValid: true
@@ -99,19 +103,23 @@ describe('chat-session-helpers', () => {
99103
{
100104
//@ts-expect-error
101105
history: [{ role: 'user', parts: '' }],
106+
errorShouldInclude: `array of Parts`,
102107
isValid: false
103108
},
104109
{
105110
//@ts-expect-error
106111
history: [{ role: 'user' }],
112+
errorShouldInclude: `array of Parts`,
107113
isValid: false
108114
},
109115
{
110116
history: [{ role: 'user', parts: [] }],
117+
errorShouldInclude: `at least one part`,
111118
isValid: false
112119
},
113120
{
114121
history: [{ role: 'model', parts: [{ text: 'hi' }] }],
122+
errorShouldInclude: `model`,
115123
isValid: false
116124
},
117125
{
@@ -125,13 +133,15 @@ describe('chat-session-helpers', () => {
125133
]
126134
}
127135
],
136+
errorShouldInclude: `function`,
128137
isValid: false
129138
},
130139
{
131140
history: [
132141
{ role: 'user', parts: [{ text: 'hi' }] },
133142
{ role: 'user', parts: [{ text: 'hi' }] }
134143
],
144+
errorShouldInclude: `can't follow 'user'`,
135145
isValid: false
136146
},
137147
{
@@ -140,6 +150,45 @@ describe('chat-session-helpers', () => {
140150
{ role: 'model', parts: [{ text: 'hi' }] },
141151
{ role: 'model', parts: [{ text: 'hi' }] }
142152
],
153+
errorShouldInclude: `can't follow 'model'`,
154+
isValid: false
155+
},
156+
{
157+
history: [
158+
{ role: 'user', parts: [{ text: 'hi' }] },
159+
{
160+
role: 'model',
161+
parts: [
162+
{ text: 'hi' },
163+
{
164+
text: 'thought about hi',
165+
thought: true,
166+
thoughtSignature: 'thought signature'
167+
}
168+
]
169+
}
170+
],
171+
isValid: true
172+
},
173+
{
174+
history: [
175+
{
176+
role: 'user',
177+
parts: [{ text: 'hi', thought: true, thoughtSignature: 'sig' }]
178+
},
179+
{
180+
role: 'model',
181+
parts: [
182+
{ text: 'hi' },
183+
{
184+
text: 'thought about hi',
185+
thought: true,
186+
thoughtSignature: 'thought signature'
187+
}
188+
]
189+
}
190+
],
191+
errorShouldInclude: 'thought',
143192
isValid: false
144193
}
145194
];
@@ -149,7 +198,14 @@ describe('chat-session-helpers', () => {
149198
if (tc.isValid) {
150199
expect(fn).to.not.throw();
151200
} else {
152-
expect(fn).to.throw(FirebaseError);
201+
try {
202+
fn();
203+
} catch (e) {
204+
expect(e).to.be.instanceOf(FirebaseError);
205+
if (e instanceof FirebaseError && tc.errorShouldInclude) {
206+
expect(e.message).to.include(tc.errorShouldInclude);
207+
}
208+
}
153209
}
154210
});
155211
});

0 commit comments

Comments
 (0)