Skip to content

Commit be8f480

Browse files
[AI] Add support for thinkingSummaries and thoughtSignatures (#7272)
Added support for returning [thought summaries](https://ai.google.dev/gemini-api/docs/thinking#summaries) when using thinking-compatible models. Additionally, it adds support for automatic handling of `thoughtSignatures`. --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
1 parent e11e900 commit be8f480

File tree

8 files changed

+286
-52
lines changed

8 files changed

+286
-52
lines changed

firebase-ai/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
# Unreleased
2+
* [feature] Added support for returning thought summaries, which are synthesized
3+
versions of a model's internal reasoning process.
24
* [fixed] Fixed an issue causing the accessor methods in `GenerateContentResponse` to throw an exception
35
when the response contained no candidates.
46
* [changed] Added better description for requests which fail due to the Gemini API not being

firebase-ai/api.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,8 @@ package com.google.firebase.ai.type {
217217
ctor public CodeExecutionResultPart(String outcome, String output);
218218
method public String getOutcome();
219219
method public String getOutput();
220+
method public boolean isThought();
221+
property public boolean isThought;
220222
property public final String outcome;
221223
property public final String output;
222224
}
@@ -292,14 +294,18 @@ package com.google.firebase.ai.type {
292294
ctor public ExecutableCodePart(String language, String code);
293295
method public String getCode();
294296
method public String getLanguage();
297+
method public boolean isThought();
295298
property public final String code;
299+
property public boolean isThought;
296300
property public final String language;
297301
}
298302

299303
public final class FileDataPart implements com.google.firebase.ai.type.Part {
300304
ctor public FileDataPart(String uri, String mimeType);
301305
method public String getMimeType();
302306
method public String getUri();
307+
method public boolean isThought();
308+
property public boolean isThought;
303309
property public final String mimeType;
304310
property public final String uri;
305311
}
@@ -334,8 +340,10 @@ package com.google.firebase.ai.type {
334340
method public java.util.Map<java.lang.String,kotlinx.serialization.json.JsonElement> getArgs();
335341
method public String? getId();
336342
method public String getName();
343+
method public boolean isThought();
337344
property public final java.util.Map<java.lang.String,kotlinx.serialization.json.JsonElement> args;
338345
property public final String? id;
346+
property public boolean isThought;
339347
property public final String name;
340348
}
341349

@@ -364,7 +372,9 @@ package com.google.firebase.ai.type {
364372
method public String? getId();
365373
method public String getName();
366374
method public kotlinx.serialization.json.JsonObject getResponse();
375+
method public boolean isThought();
367376
property public final String? id;
377+
property public boolean isThought;
368378
property public final String name;
369379
property public final kotlinx.serialization.json.JsonObject response;
370380
}
@@ -376,12 +386,14 @@ package com.google.firebase.ai.type {
376386
method public java.util.List<com.google.firebase.ai.type.InlineDataPart> getInlineDataParts();
377387
method public com.google.firebase.ai.type.PromptFeedback? getPromptFeedback();
378388
method public String? getText();
389+
method public String? getThoughtSummary();
379390
method public com.google.firebase.ai.type.UsageMetadata? getUsageMetadata();
380391
property public final java.util.List<com.google.firebase.ai.type.Candidate> candidates;
381392
property public final java.util.List<com.google.firebase.ai.type.FunctionCallPart> functionCalls;
382393
property public final java.util.List<com.google.firebase.ai.type.InlineDataPart> inlineDataParts;
383394
property public final com.google.firebase.ai.type.PromptFeedback? promptFeedback;
384395
property public final String? text;
396+
property public final String? thoughtSummary;
385397
property public final com.google.firebase.ai.type.UsageMetadata? usageMetadata;
386398
}
387399

@@ -552,7 +564,9 @@ package com.google.firebase.ai.type {
552564
public final class ImagePart implements com.google.firebase.ai.type.Part {
553565
ctor public ImagePart(android.graphics.Bitmap image);
554566
method public android.graphics.Bitmap getImage();
567+
method public boolean isThought();
555568
property public final android.graphics.Bitmap image;
569+
property public boolean isThought;
556570
}
557571

558572
@com.google.firebase.ai.type.PublicPreviewAPI public final class ImagenAspectRatio {
@@ -777,7 +791,9 @@ package com.google.firebase.ai.type {
777791
ctor public InlineDataPart(byte[] inlineData, String mimeType);
778792
method public byte[] getInlineData();
779793
method public String getMimeType();
794+
method public boolean isThought();
780795
property public final byte[] inlineData;
796+
property public boolean isThought;
781797
property public final String mimeType;
782798
}
783799

@@ -886,6 +902,8 @@ package com.google.firebase.ai.type {
886902
}
887903

888904
public interface Part {
905+
method public boolean isThought();
906+
property public abstract boolean isThought;
889907
}
890908

891909
public final class PartKt {
@@ -1146,6 +1164,8 @@ package com.google.firebase.ai.type {
11461164
public final class TextPart implements com.google.firebase.ai.type.Part {
11471165
ctor public TextPart(String text);
11481166
method public String getText();
1167+
method public boolean isThought();
1168+
property public boolean isThought;
11491169
property public final String text;
11501170
}
11511171

@@ -1155,6 +1175,7 @@ package com.google.firebase.ai.type {
11551175
public static final class ThinkingConfig.Builder {
11561176
ctor public ThinkingConfig.Builder();
11571177
method public com.google.firebase.ai.type.ThinkingConfig build();
1178+
method public com.google.firebase.ai.type.ThinkingConfig.Builder setIncludeThoughts(boolean includeThoughts);
11581179
method public com.google.firebase.ai.type.ThinkingConfig.Builder setThinkingBudget(int thinkingBudget);
11591180
}
11601181

firebase-ai/src/main/kotlin/com/google/firebase/ai/type/GenerateContentResponse.kt

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,42 +34,66 @@ public class GenerateContentResponse(
3434
/**
3535
* Convenience field representing all the text parts in the response as a single string.
3636
*
37-
* The value is null if the response contains no [candidates].
37+
* The value is null if the response contains no valid text [candidates].
38+
*
39+
* Any part that's marked as a thought will be ignored. Learn more about
40+
* [thinking](https://firebase.google.com/docs/ai-logic/thinking?api=dev).
3841
*/
3942
public val text: String? by lazy {
40-
candidates.firstOrNull()?.content?.parts?.filterIsInstance<TextPart>()?.joinToString(" ") {
41-
it.text
42-
}
43+
val parts = candidates.firstOrNull()?.nonThoughtParts()?.filterIsInstance<TextPart>()
44+
if (parts.isNullOrEmpty()) return@lazy null
45+
parts.joinToString(" ") { it.text }
4346
}
4447

4548
/**
4649
* Convenience field to list all the [FunctionCallPart]s in the response.
4750
*
4851
* The value is an empty list if the response contains no [candidates].
52+
*
53+
* Any part that's marked as a thought will be ignored. Learn more about
54+
* [thinking](https://firebase.google.com/docs/ai-logic/thinking?api=dev).
4955
*/
5056
public val functionCalls: List<FunctionCallPart> by lazy {
51-
candidates.firstOrNull()?.content?.parts?.filterIsInstance<FunctionCallPart>().orEmpty()
57+
candidates.firstOrNull()?.nonThoughtParts()?.filterIsInstance<FunctionCallPart>().orEmpty()
58+
}
59+
60+
/**
61+
* Convenience field representing all the text parts in the response that are marked as thoughts
62+
* as a single string, if they exists.
63+
*
64+
* Learn more about [thinking](https://firebase.google.com/docs/ai-logic/thinking?api=dev).
65+
*/
66+
public val thoughtSummary: String? by lazy {
67+
candidates.firstOrNull()?.thoughtParts()?.filterIsInstance<TextPart>()?.joinToString(" ") {
68+
it.text
69+
}
5270
}
5371

5472
/**
55-
* Convenience field representing all the [InlineDataPart]s in the first candidate, if they exist.
73+
* Convenience field representing all the [InlineDataPart]s in the first candidate.
5674
*
5775
* This also includes any [ImagePart], but they will be represented as [InlineDataPart] instead.
5876
*
5977
* The value is an empty list if the response contains no [candidates].
78+
*
79+
* Any part that's marked as a thought will be ignored. Learn more about
80+
* [thinking](https://firebase.google.com/docs/ai-logic/thinking?api=dev).
6081
*/
6182
public val inlineDataParts: List<InlineDataPart> by lazy {
6283
candidates
6384
.firstOrNull()
64-
?.content
65-
?.parts
85+
?.nonThoughtParts()
6686
?.let { parts ->
6787
parts.filterIsInstance<ImagePart>().map { it.toInlineDataPart() } +
6888
parts.filterIsInstance<InlineDataPart>()
6989
}
7090
.orEmpty()
7191
}
7292

93+
private fun Candidate.thoughtParts(): List<Part> = content.parts.filter { it.isThought }
94+
95+
private fun Candidate.nonThoughtParts(): List<Part> = content.parts.filter { !it.isThought }
96+
7397
@Serializable
7498
internal data class Internal(
7599
val candidates: List<Candidate.Internal>? = null,

0 commit comments

Comments
 (0)