@@ -67,10 +67,13 @@ public override async IAsyncEnumerable<ChatSegment> ChatStreamed(IReadOnlyList<C
6767 } ;
6868 }
6969
70- Tool tool = ToGoogleAITool ( options ) ?? new Tool ( ) ;
71- if ( SupportsCodeExecution )
70+ Tool ? tool = ToGoogleAIToolCallTool ( options ) ;
71+ if ( tool == null && SupportsCodeExecution )
7272 {
73- tool . CodeExecution = new ( ) ;
73+ tool = new Tool ( )
74+ {
75+ CodeExecution = new ( )
76+ } ;
7477 }
7578
7679 int fcIndex = 0 ;
@@ -80,51 +83,50 @@ public override async IAsyncEnumerable<ChatSegment> ChatStreamed(IReadOnlyList<C
8083 SystemInstruction = OpenAIChatMessageToGoogleContent ( messages . Where ( x => x is SystemChatMessage ) ) switch { [ ] => null , var x => x [ 0 ] } ,
8184 GenerationConfig = gc ,
8285 SafetySettings = _safetySettings ,
83- Tools = [ tool ] ,
86+ Tools = tool == null ? null : [ tool ] ,
8487 } ;
8588 await foreach ( GenerateContentResponse response in _generativeModel . GenerateContentStream ( gcr , new RequestOptions ( null , NetworkTimeout ) , cancellationToken ) )
8689 {
8790 if ( response . Candidates != null && response . Candidates . Count > 0 )
8891 {
89- string ? text = response . Candidates [ 0 ] . Content ? . Text ;
90- InlineData ? image = response . Candidates [ 0 ] . Content ? . Parts [ 0 ] . InlineData ;
91- FinishReason ? finishReason = response . Candidates [ 0 ] . FinishReason ;
92- FunctionCall ? functionCall = response . Candidates [ 0 ] . Content ? . Parts [ 0 ] . FunctionCall ;
93- Dtos . ChatTokenUsage ? usage = GetUsage ( response . UsageMetadata ) ;
94- ExecutableCode ? code = response . Candidates [ 0 ] . Content ? . Parts [ 0 ] . ExecutableCode ;
95- CodeExecutionResult ? result = response . Candidates [ 0 ] . Content ? . Parts [ 0 ] . CodeExecutionResult ;
96-
9792 List < ChatSegmentItem > items = [ ] ;
98- if ( code != null )
93+ foreach ( Part part in response . Candidates [ 0 ] . Content ? . Parts ?? [ ] )
9994 {
100- items . Add ( ChatSegmentItem . FromText ( $ """
101- ```{ code . Language }
102- { code . Code }
95+ if ( part . ExecutableCode != null )
96+ {
97+ items . Add ( ChatSegmentItem . FromText ( $ """
98+ ```{ part . ExecutableCode . Language }
99+ { part . ExecutableCode . Code }
103100 ```
104101
105102 """ ) ) ;
106- }
107- else if ( result != null )
108- {
109- items . Add ( ChatSegmentItem . FromText ( $ """
103+ }
104+ else if ( part . CodeExecutionResult != null )
105+ {
106+ items . Add ( ChatSegmentItem . FromText ( $ """
110107 ```
111- { result . Output }
108+ { part . CodeExecutionResult . Output }
112109 ```
113110
114111 """ ) ) ;
112+ }
113+ else if ( part . Text != null )
114+ {
115+ items . Add ( ChatSegmentItem . FromText ( part . Text ) ) ;
116+ }
117+ if ( part . InlineData != null )
118+ {
119+ items . Add ( ChatSegmentItem . FromBase64Image ( part . InlineData . Data , part . InlineData . MimeType ) ) ;
120+ }
121+ if ( part . FunctionCall != null )
122+ {
123+ items . Add ( ChatSegmentItem . FromToolCall ( fcIndex ++ , part . FunctionCall ) ) ;
124+ }
115125 }
116- else if ( text != null )
117- {
118- items . Add ( ChatSegmentItem . FromText ( text ) ) ;
119- }
120- if ( image != null )
121- {
122- items . Add ( ChatSegmentItem . FromBase64Image ( image . Data , image . MimeType ) ) ;
123- }
124- if ( functionCall != null )
125- {
126- items . Add ( ChatSegmentItem . FromToolCall ( fcIndex ++ , functionCall ) ) ;
127- }
126+
127+ FinishReason ? finishReason = response . Candidates [ 0 ] . FinishReason ;
128+ Dtos . ChatTokenUsage ? usage = GetUsage ( response . UsageMetadata ) ;
129+
128130 yield return new ChatSegment ( )
129131 {
130132 FinishReason = ToChatFinishReason ( finishReason ) ,
@@ -182,24 +184,53 @@ static List<Content> OpenAIChatMessageToGoogleContent(IEnumerable<ChatMessage> c
182184 return [ .. chatMessages
183185 . Select ( msg => msg switch
184186 {
185- SystemChatMessage s => new Content ( "" ) { Role = "system" , Parts = [ .. msg . Content . Select ( OpenAIPartToGooglePart ) ] } ,
186- UserChatMessage u => new Content ( "" ) { Role = "user" , Parts = [ .. msg . Content . Select ( OpenAIPartToGooglePart ) ] } ,
187- AssistantChatMessage a => new Content ( "" ) { Role = "model" , Parts = [ .. msg . Content . Select ( OpenAIPartToGooglePart ) ] } ,
187+ SystemChatMessage s => new Content ( "" ) { Role = Role . System , Parts = [ .. msg . Content . Select ( x => OpenAIPartToGooglePart ( x ) ) ] } ,
188+ UserChatMessage u => new Content ( "" ) { Role = Role . User , Parts = [ .. msg . Content . Select ( x => OpenAIPartToGooglePart ( x ) ) ] } ,
189+ AssistantChatMessage a => new Content ( "" ) { Role = Role . Model , Parts = AssistantMessageToParts ( a ) } ,
190+ ToolChatMessage t => new Content ( "" ) { Role = Role . Function , Parts = [ ToolCallMessageToPart ( t ) ] } ,
188191 _ => throw new NotSupportedException ( $ "Unsupported message type: { msg . GetType ( ) } in { nameof ( GoogleAI2ChatService ) } ") ,
189192 } ) ] ;
190- }
191193
192- static IPart OpenAIPartToGooglePart ( ChatMessageContentPart part )
193- {
194- return part . Kind switch
194+ static IPart ToolCallMessageToPart ( ToolChatMessage message )
195195 {
196- ChatMessageContentPartKind . Text => new TextData ( ) { Text = part . Text } ,
197- ChatMessageContentPartKind . Image => new InlineData ( ) { Data = Convert . ToBase64String ( part . ImageBytes . ToArray ( ) ) , MimeType = part . ImageBytesMediaType } ,
198- _ => throw new NotSupportedException ( $ "Unsupported part kind: { part . Kind } ") ,
199- } ;
196+ return Part . FromFunctionResponse ( message . ToolCallId , new
197+ {
198+ result = string . Join ( "\r \n " , message . Content . Select ( x => x . Text ) )
199+ } ) ;
200+ }
201+
202+ static List < IPart > AssistantMessageToParts ( AssistantChatMessage assistantChatMessage )
203+ {
204+ List < IPart > results = [ ] ;
205+ if ( assistantChatMessage . ToolCalls != null && assistantChatMessage . ToolCalls . Count > 0 )
206+ {
207+ foreach ( ChatToolCall toolCall in assistantChatMessage . ToolCalls )
208+ {
209+ results . Add ( new FunctionCall ( )
210+ {
211+ Id = toolCall . Id ,
212+ Name = toolCall . FunctionName ,
213+ Args = toolCall . FunctionArguments . ToObjectFromJson < JsonObject > ( ) ,
214+ } ) ;
215+ }
216+ }
217+
218+ results . AddRange ( assistantChatMessage . Content . Select ( OpenAIPartToGooglePart ) ) ;
219+ return results ;
220+ }
221+
222+ static IPart OpenAIPartToGooglePart ( ChatMessageContentPart part )
223+ {
224+ return part . Kind switch
225+ {
226+ ChatMessageContentPartKind . Text => new TextData ( ) { Text = part . Text } ,
227+ ChatMessageContentPartKind . Image => new InlineData ( ) { Data = Convert . ToBase64String ( part . ImageBytes . ToArray ( ) ) , MimeType = part . ImageBytesMediaType } ,
228+ _ => throw new NotSupportedException ( $ "Unsupported part kind: { part . Kind } ") ,
229+ } ;
230+ }
200231 }
201232
202- static Tool ? ToGoogleAITool ( ChatCompletionOptions cco )
233+ static Tool ? ToGoogleAIToolCallTool ( ChatCompletionOptions cco )
203234 {
204235 if ( cco . Tools == null || cco . Tools . Count == 0 )
205236 {
0 commit comments