Skip to content

Commit

Permalink
Join multiple text parts in the text getter (#160)
Browse files Browse the repository at this point in the history
Prepare for cases where the model may reply with both text and a
function call.

When the text getter was introduced the cross-SDK approach was to return
the first text part. Now that multiple parts are more common with
function calling we decided to switch to concatenating the text.

I think this should be non-breaking in practice because current models
don't seem to respond with a text part when it isn't the single part.
  • Loading branch information
natebosch authored May 13, 2024
1 parent a9ebd46 commit 04c56e4
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 7 deletions.
5 changes: 5 additions & 0 deletions pkgs/google_generative_ai/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 0.4.1-wip

- Concatenate multiple `TextPart` into the `text` String in case the model
replies with more than one part.

## 0.4.0

- Add support for parsing Vertex AI specific fields in `CountTokensResponse`.
Expand Down
14 changes: 9 additions & 5 deletions pkgs/google_generative_ai/lib/src/api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,17 @@ final class GenerateContentResponse {
this.usageMetadata,
});

/// The text content of the first part of the first of [candidates], if any.
/// The text content of the text parts of the first of [candidates], if any.
///
/// If the prompt was blocked, or the first candidate was finished for a reason
/// of [FinishReason.recitation] or [FinishReason.safety], accessing this text
/// will throw a [GenerativeAIException].
///
/// If the first candidate's content starts with a text part, this value is
/// that text.
/// If the first candidate's content contains any text parts, this value is
/// the concatenation of the text.
///
/// If there are no candidates, or if the first candidate does not start with
/// a text part, this value is `null`.
/// If there are no candidates, or if the first candidate does not contain any
/// text parts, this value is `null`.
String? get text {
return switch (candidates) {
[] => switch (promptFeedback) {
Expand Down Expand Up @@ -96,8 +96,12 @@ final class GenerateContentResponse {
? ': $finishMessage'
: ''),
),
// Special case for a single TextPart to avoid iterable chain.
[Candidate(content: Content(parts: [TextPart(:final text)])), ...] =>
text,
[Candidate(content: Content(:final parts)), ...]
when parts.any((p) => p is TextPart) =>
parts.whereType<TextPart>().map((p) => p.text).join(''),
[Candidate(), ...] => null,
};
}
Expand Down
2 changes: 1 addition & 1 deletion pkgs/google_generative_ai/lib/src/version.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@
// See the License for the specific language governing permissions and
// limitations under the License.

const packageVersion = '0.4.0';
const packageVersion = '0.4.1-wip';
2 changes: 1 addition & 1 deletion pkgs/google_generative_ai/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: google_generative_ai
# Update `lib/version.dart` when changing version.
version: 0.4.0
version: 0.4.1-wip
description: >-
The Google AI Dart SDK enables developers to use Google's state-of-the-art
generative AI models (like Gemini).
Expand Down
30 changes: 30 additions & 0 deletions pkgs/google_generative_ai/test/response_parsing_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,36 @@ void main() {
),
);
});

test('text getter joins content', () async {
final response = '''
{
"candidates": [
{
"content": {
"parts": [
{
"text": "Initial text"
},
{
"functionCall": {"name": "someFunction", "args": {}}
},
{
"text": " And more text"
}
],
"role": "model"
},
"finishReason": "STOP",
"index": 0
}
]
}
''';
final decoded = jsonDecode(response) as Object;
final generateContentResponse = parseGenerateContentResponse(decoded);
expect(generateContentResponse.text, 'Initial text And more text');
});
});

group('parses and throws error responses', () {
Expand Down

0 comments on commit 04c56e4

Please sign in to comment.