toGeminiPart function
Part
toGeminiPart(
- Part p
)
Implementation
@visibleForTesting
gcl.Part toGeminiPart(Part p) {
final thoughtSignature = p.metadata?['thoughtSignature'] != null
? p.metadata!['thoughtSignature'] as String
: null;
if (p.isReasoning) {
return gcl.Part(
text: p.reasoning,
thought: true,
thoughtSignature: thoughtSignature,
);
}
if (p.isText) {
return gcl.Part(text: p.text, thoughtSignature: thoughtSignature);
}
if (p.isToolRequest) {
return gcl.Part(
functionCall: gcl.FunctionCall(
id: p.toolRequest!.ref ?? '',
name: _toGeminiToolName(p.toolRequest!.name),
args: p.toolRequest!.input is Map
? (p.toolRequest!.input as Map).cast<String, Object?>()
: null,
),
thoughtSignature: thoughtSignature,
);
}
if (p.isToolResponse) {
final tr = p.toolResponse!;
// Multipart tool content (images, media, etc.) becomes function-response
// parts. Gemini's FunctionResponse.parts only supports inline/file data
// (see js-genai `FunctionResponsePart`), so we map media parts to their
// `inlineData`/`fileData` shape and skip parts that cannot be represented
// there (e.g. text) which would otherwise be rejected by the API. The
// structured result still travels in `response.output`.
final contentParts = tr.content
?.map((c) => Part.fromJson(c as Map<String, dynamic>))
.where((part) => part.isMedia)
.map((part) => toGeminiPart(part).toJson())
.toList();
return gcl.Part(
functionResponse: gcl.FunctionResponse.fromJson({
'id': tr.ref ?? '',
'name': _toGeminiToolName(tr.name),
'response': {'output': tr.output},
'parts': ?(contentParts == null || contentParts.isEmpty
? null
: contentParts),
}),
thoughtSignature: thoughtSignature,
);
}
if (p.isMedia) {
final media = p.media;
if (media!.url.startsWith('data:')) {
final uri = Uri.parse(media.url);
if (uri.data != null) {
return gcl.Part.fromJson({
'inlineData': {
'mimeType': media.contentType ?? uri.data!.mimeType,
'data': base64Encode(uri.data!.contentAsBytes()),
},
'thoughtSignature': ?thoughtSignature,
});
}
}
return gcl.Part.fromJson({
'fileData': {'mimeType': media.contentType ?? '', 'fileUri': media.url},
'thoughtSignature': ?thoughtSignature,
});
}
if (p.isCustom && p.custom!['codeExecutionResult'] != null) {
p as CustomPart;
return gcl.Part(
codeExecutionResult: gcl.CodeExecutionResult(
outcome:
(p.custom['codeExecutionResult'] as Map<String, dynamic>)['outcome']
as String?,
output:
(p.custom['codeExecutionResult'] as Map<String, dynamic>)['output']
as String?,
),
thoughtSignature: thoughtSignature,
);
}
if (p.isCustom && p.custom!['executableCode'] != null) {
p as CustomPart;
return gcl.Part(
executableCode: gcl.ExecutableCode(
language:
(p.custom['executableCode'] as Map<String, dynamic>)['language']
as String?,
code:
(p.custom['executableCode'] as Map<String, dynamic>)['code']
as String?,
),
thoughtSignature: thoughtSignature,
);
}
throw UnimplementedError('Unsupported part type: $p');
}