@ainetwork/adk-provider-model-gemini 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -57,10 +57,41 @@ var GeminiModel = class extends import_modules.BaseModel {
57
57
  const userContent = { role: "user", parts: [{ text: query }] };
58
58
  return messages.concat(sessionContent).concat(userContent);
59
59
  }
60
- appendMessages(messages, message) {
60
+ appendAssistantToolCallTurn(messages, turn) {
61
+ const parts = [];
62
+ if (turn.content) {
63
+ parts.push({ text: turn.content });
64
+ }
65
+ for (const tc of turn.toolCalls) {
66
+ let args = {};
67
+ try {
68
+ args = JSON.parse(tc.function.arguments || "{}");
69
+ } catch {
70
+ args = { __raw: tc.function.arguments };
71
+ }
72
+ parts.push({
73
+ functionCall: {
74
+ id: tc.id,
75
+ name: tc.function.name,
76
+ args
77
+ }
78
+ });
79
+ }
80
+ messages.push({ role: "model", parts });
81
+ }
82
+ appendToolResult(messages, result) {
83
+ const response = result.isError ? { error: result.content } : { output: result.content };
61
84
  messages.push({
62
85
  role: "user",
63
- parts: [{ text: message }]
86
+ parts: [
87
+ {
88
+ functionResponse: {
89
+ id: result.toolCallId,
90
+ name: result.toolName,
91
+ response
92
+ }
93
+ }
94
+ ]
64
95
  });
65
96
  }
66
97
  async fetch(messages, options) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../index.ts"],"sourcesContent":["import { BaseModel, ModelFetchOptions } from \"@ainetwork/adk/modules\";\nimport { MessageObject, MessageRole, type ThreadObject } from \"@ainetwork/adk/types/memory\";\nimport type {\n\tLLMStream,\n\tStreamChunk,\n\tToolCallDelta,\n} from \"@ainetwork/adk/types/stream\";\nimport type {\n\tFetchResponse,\n\tToolCall,\n\tConnectorTool,\n} from \"@ainetwork/adk/types/connector\";\nimport {\n\ttype Content,\n\ttype FunctionCall,\n\ttype FunctionDeclaration,\n\tFunctionCallingConfigMode,\n\ttype GenerateContentResponse,\n\tGoogleGenAI,\n\tModel,\n} from \"@google/genai\";\n\nexport class GeminiModel extends BaseModel<Content, FunctionDeclaration> {\n\tprivate client: GoogleGenAI;\n\tprivate modelName: string;\n\n\tconstructor(apiKey: string, modelName: string) {\n\t\tsuper();\n\t\tthis.client = new GoogleGenAI({ apiKey });\n\t\tthis.modelName = modelName;\n\t}\n\n\tprivate getMessageRole(role: MessageRole) {\n\t\tswitch (role) {\n\t\t\tcase MessageRole.USER:\n\t\t\t\treturn \"user\";\n\t\t\tcase MessageRole.MODEL:\n\t\t\tcase MessageRole.SYSTEM:\n\t\t\t\treturn \"model\";\n\t\t\tdefault:\n\t\t\t\treturn \"model\"; /*FIXME*/\n\t\t}\n\t}\n\n\tgenerateMessages(params: {\n\t\tquery: string;\n\t\tthread?: ThreadObject;\n\t\tsystemPrompt?: string;\n\t}): Content[] {\n\t\tconst { query, thread, systemPrompt } = params;\n\t\tconst messages: Content[] = !systemPrompt\n\t\t\t? []\n\t\t\t: [{ role: \"model\", parts: [{ text: systemPrompt.trim() }] }];\n\t\tconst sessionContent: Content[] = !thread\n\t\t\t? []\n\t\t\t: thread.messages.map((message: MessageObject) => {\n\t\t\t\t\t// TODO: check message.content.type\n\t\t\t\t\treturn {\n\t\t\t\t\t\trole: this.getMessageRole(message.role),\n\t\t\t\t\t\tparts: [{ text: message.content.parts[0] as string }],\n\t\t\t\t\t};\n\t\t\t\t});\n\t\tconst userContent: Content = { role: \"user\", parts: [{ text: query }] };\n\t\treturn messages.concat(sessionContent).concat(userContent);\n\t}\n\n\tappendMessages(messages: Content[], message: string): void {\n\t\tmessages.push({\n\t\t\trole: \"user\",\n\t\t\tparts: [{ text: message }],\n\t\t});\n\t}\n\n\tasync fetch(messages: Content[], options?: ModelFetchOptions): Promise<FetchResponse> {\n\t\tconst response = await this.client.models.generateContent({\n\t\t\tmodel: this.modelName,\n\t\t\tcontents: messages,\n\t\t});\n\n\t\treturn { content: response.text };\n\t}\n\n\tasync fetchWithContextMessage(\n\t\tmessages: Content[],\n\t\tfunctions: FunctionDeclaration[],\n\t\toptions?: ModelFetchOptions,\n\t): Promise<FetchResponse> {\n\t\tif (functions.length > 0) {\n\t\t\tconst toolChoiceMode = options?.toolChoice === \"required\"\n\t\t\t\t? FunctionCallingConfigMode.ANY\n\t\t\t\t: FunctionCallingConfigMode.AUTO;\n\t\t\tconst response = await this.client.models.generateContent({\n\t\t\t\tmodel: this.modelName,\n\t\t\t\tcontents: messages,\n\t\t\t\tconfig: {\n\t\t\t\t\ttools: [{ functionDeclarations: functions }],\n\t\t\t\t\ttoolConfig: {\n\t\t\t\t\t\tfunctionCallingConfig: { mode: toolChoiceMode },\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tconst { text, functionCalls } = response;\n\t\t\tconst hasName = (\n\t\t\t\tvalue: FunctionCall,\n\t\t\t): value is FunctionCall & { name: string } => {\n\t\t\t\treturn value.name !== undefined;\n\t\t\t};\n\t\t\tconst toolCalls: ToolCall[] | undefined = functionCalls\n\t\t\t\t?.filter(hasName)\n\t\t\t\t.map((value) => {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tname: value.name,\n\t\t\t\t\t\targuments: value.args,\n\t\t\t\t\t};\n\t\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tcontent: text,\n\t\t\t\ttoolCalls,\n\t\t\t};\n\t\t}\n\t\treturn await this.fetch(messages);\n\t}\n\n\tasync fetchStreamWithContextMessage(\n\t\tmessages: Content[],\n\t\tfunctions: FunctionDeclaration[],\n\t\toptions?: ModelFetchOptions,\n\t): Promise<LLMStream> {\n\t\tconst toolChoiceMode = options?.toolChoice === \"required\"\n\t\t\t? FunctionCallingConfigMode.ANY\n\t\t\t: FunctionCallingConfigMode.AUTO;\n\t\tconst stream = await this.client.models.generateContentStream({\n\t\t\tmodel: this.modelName,\n\t\t\tcontents: messages,\n\t\t\tconfig: {\n\t\t\t\ttools: [{ functionDeclarations: functions }],\n\t\t\t\ttoolConfig: {\n\t\t\t\t\tfunctionCallingConfig: { mode: toolChoiceMode },\n\t\t\t\t},\n\t\t\t},\n\t\t});\n\n\t\treturn this.createGeminiStreamAdapter(stream);\n\t}\n\n\t// NOTE(yoojin): Need to switch API Stream type to LLMStream.\n\tprivate createGeminiStreamAdapter(\n\t\tgeminiStream: AsyncIterable<GenerateContentResponse>,\n\t): LLMStream {\n\t\tconst hasName = (\n\t\t\tvalue: FunctionCall,\n\t\t): value is FunctionCall & { name: string } => {\n\t\t\treturn value.name !== undefined;\n\t\t};\n\n\t\treturn {\n\t\t\tasync *[Symbol.asyncIterator](): AsyncIterator<StreamChunk> {\n\t\t\t\tlet toolCallIndex = 0;\n\t\t\t\tfor await (const geminiChunk of geminiStream) {\n\t\t\t\t\tconst content = geminiChunk.candidates?.[0]?.content;\n\t\t\t\t\tif (!content) continue;\n\n\t\t\t\t\tconst tool_calls: ToolCallDelta[] = [];\n\t\t\t\t\tlet textContent = \"\";\n\n\t\t\t\t\t// Process all parts in the array\n\t\t\t\t\tfor (const part of content.parts || []) {\n\t\t\t\t\t\tif (part.text) {\n\t\t\t\t\t\t\ttextContent += part.text;\n\t\t\t\t\t\t} else if (part.functionCall && hasName(part.functionCall)) {\n\t\t\t\t\t\t\ttool_calls.push({\n\t\t\t\t\t\t\t\tindex: toolCallIndex++,\n\t\t\t\t\t\t\t\tid: part.functionCall.id || `call_${toolCallIndex}`,\n\t\t\t\t\t\t\t\tfunction: {\n\t\t\t\t\t\t\t\t\tname: part.functionCall.name,\n\t\t\t\t\t\t\t\t\targuments: JSON.stringify(part.functionCall.args),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t} as unknown as ToolCallDelta);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Only yield when there's text content\n\t\t\t\t\tif (textContent) {\n\t\t\t\t\t\tyield {\n\t\t\t\t\t\t\tdelta: {\n\t\t\t\t\t\t\t\trole: content.role,\n\t\t\t\t\t\t\t\tcontent: textContent,\n\t\t\t\t\t\t\t\ttool_calls: undefined,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tfinish_reason: geminiChunk.candidates?.[0]?.finishReason as any,\n\t\t\t\t\t\t\tmetadata: { provider: \"gemini\" },\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\t// Only yield when there are tool calls\n\t\t\t\t\tif (tool_calls.length > 0) {\n\t\t\t\t\t\tyield {\n\t\t\t\t\t\t\tdelta: {\n\t\t\t\t\t\t\t\trole: content.role,\n\t\t\t\t\t\t\t\tcontent: undefined,\n\t\t\t\t\t\t\t\ttool_calls,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tfinish_reason: geminiChunk.candidates?.[0]?.finishReason as any,\n\t\t\t\t\t\t\tmetadata: { provider: \"gemini\" },\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t}\n\n\tconvertToolsToFunctions(tools: ConnectorTool[]): FunctionDeclaration[] {\n\t\tconst functions: FunctionDeclaration[] = [];\n\t\tfor (const tool of tools) {\n\t\t\tfunctions.push({\n\t\t\t\tname: tool.toolName,\n\t\t\t\tdescription: tool.description,\n\t\t\t\tparametersJsonSchema: tool.inputSchema,\n\t\t\t});\n\t\t}\n\t\treturn functions;\n\t}\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../index.ts"],"sourcesContent":["import {\n\ttype AssistantToolCallTurn,\n\tBaseModel,\n\ttype ModelFetchOptions,\n\ttype ToolResultMessage,\n} from \"@ainetwork/adk/modules\";\nimport type {\n\tConnectorTool,\n\tFetchResponse,\n\tToolCall,\n} from \"@ainetwork/adk/types/connector\";\nimport {\n\ttype MessageObject,\n\tMessageRole,\n\ttype ThreadObject,\n} from \"@ainetwork/adk/types/memory\";\nimport type {\n\tLLMStream,\n\tStreamChunk,\n\tToolCallDelta,\n} from \"@ainetwork/adk/types/stream\";\nimport {\n\ttype Content,\n\ttype FunctionCall,\n\tFunctionCallingConfigMode,\n\ttype FunctionDeclaration,\n\ttype GenerateContentResponse,\n\tGoogleGenAI,\n\ttype Part,\n} from \"@google/genai\";\n\nexport class GeminiModel extends BaseModel<Content, FunctionDeclaration> {\n\tprivate client: GoogleGenAI;\n\tprivate modelName: string;\n\n\tconstructor(apiKey: string, modelName: string) {\n\t\tsuper();\n\t\tthis.client = new GoogleGenAI({ apiKey });\n\t\tthis.modelName = modelName;\n\t}\n\n\tprivate getMessageRole(role: MessageRole) {\n\t\tswitch (role) {\n\t\t\tcase MessageRole.USER:\n\t\t\t\treturn \"user\";\n\t\t\tcase MessageRole.MODEL:\n\t\t\tcase MessageRole.SYSTEM:\n\t\t\t\treturn \"model\";\n\t\t\tdefault:\n\t\t\t\treturn \"model\"; /*FIXME*/\n\t\t}\n\t}\n\n\tgenerateMessages(params: {\n\t\tquery: string;\n\t\tthread?: ThreadObject;\n\t\tsystemPrompt?: string;\n\t}): Content[] {\n\t\tconst { query, thread, systemPrompt } = params;\n\t\tconst messages: Content[] = !systemPrompt\n\t\t\t? []\n\t\t\t: [{ role: \"model\", parts: [{ text: systemPrompt.trim() }] }];\n\t\tconst sessionContent: Content[] = !thread\n\t\t\t? []\n\t\t\t: thread.messages.map((message: MessageObject) => {\n\t\t\t\t\t// TODO: check message.content.type\n\t\t\t\t\treturn {\n\t\t\t\t\t\trole: this.getMessageRole(message.role),\n\t\t\t\t\t\tparts: [{ text: message.content.parts[0] as string }],\n\t\t\t\t\t};\n\t\t\t\t});\n\t\tconst userContent: Content = { role: \"user\", parts: [{ text: query }] };\n\t\treturn messages.concat(sessionContent).concat(userContent);\n\t}\n\n\tappendAssistantToolCallTurn(\n\t\tmessages: Content[],\n\t\tturn: AssistantToolCallTurn,\n\t): void {\n\t\tconst parts: Part[] = [];\n\t\tif (turn.content) {\n\t\t\tparts.push({ text: turn.content });\n\t\t}\n\t\tfor (const tc of turn.toolCalls) {\n\t\t\tlet args: Record<string, unknown> = {};\n\t\t\ttry {\n\t\t\t\targs = JSON.parse(tc.function.arguments || \"{}\");\n\t\t\t} catch {\n\t\t\t\t// Forward the raw argument string so the model can self-correct.\n\t\t\t\targs = { __raw: tc.function.arguments };\n\t\t\t}\n\t\t\tparts.push({\n\t\t\t\tfunctionCall: {\n\t\t\t\t\tid: tc.id,\n\t\t\t\t\tname: tc.function.name,\n\t\t\t\t\targs,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t\tmessages.push({ role: \"model\", parts });\n\t}\n\n\tappendToolResult(messages: Content[], result: ToolResultMessage): void {\n\t\tconst response: Record<string, unknown> = result.isError\n\t\t\t? { error: result.content }\n\t\t\t: { output: result.content };\n\t\tmessages.push({\n\t\t\trole: \"user\",\n\t\t\tparts: [\n\t\t\t\t{\n\t\t\t\t\tfunctionResponse: {\n\t\t\t\t\t\tid: result.toolCallId,\n\t\t\t\t\t\tname: result.toolName,\n\t\t\t\t\t\tresponse,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t}\n\n\tasync fetch(\n\t\tmessages: Content[],\n\t\toptions?: ModelFetchOptions,\n\t): Promise<FetchResponse> {\n\t\tconst response = await this.client.models.generateContent({\n\t\t\tmodel: this.modelName,\n\t\t\tcontents: messages,\n\t\t});\n\n\t\treturn { content: response.text };\n\t}\n\n\tasync fetchWithContextMessage(\n\t\tmessages: Content[],\n\t\tfunctions: FunctionDeclaration[],\n\t\toptions?: ModelFetchOptions,\n\t): Promise<FetchResponse> {\n\t\tif (functions.length > 0) {\n\t\t\tconst toolChoiceMode =\n\t\t\t\toptions?.toolChoice === \"required\"\n\t\t\t\t\t? FunctionCallingConfigMode.ANY\n\t\t\t\t\t: FunctionCallingConfigMode.AUTO;\n\t\t\tconst response = await this.client.models.generateContent({\n\t\t\t\tmodel: this.modelName,\n\t\t\t\tcontents: messages,\n\t\t\t\tconfig: {\n\t\t\t\t\ttools: [{ functionDeclarations: functions }],\n\t\t\t\t\ttoolConfig: {\n\t\t\t\t\t\tfunctionCallingConfig: { mode: toolChoiceMode },\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tconst { text, functionCalls } = response;\n\t\t\tconst hasName = (\n\t\t\t\tvalue: FunctionCall,\n\t\t\t): value is FunctionCall & { name: string } => {\n\t\t\t\treturn value.name !== undefined;\n\t\t\t};\n\t\t\tconst toolCalls: ToolCall[] | undefined = functionCalls\n\t\t\t\t?.filter(hasName)\n\t\t\t\t.map((value) => {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tname: value.name,\n\t\t\t\t\t\targuments: value.args,\n\t\t\t\t\t};\n\t\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tcontent: text,\n\t\t\t\ttoolCalls,\n\t\t\t};\n\t\t}\n\t\treturn await this.fetch(messages);\n\t}\n\n\tasync fetchStreamWithContextMessage(\n\t\tmessages: Content[],\n\t\tfunctions: FunctionDeclaration[],\n\t\toptions?: ModelFetchOptions,\n\t): Promise<LLMStream> {\n\t\tconst toolChoiceMode =\n\t\t\toptions?.toolChoice === \"required\"\n\t\t\t\t? FunctionCallingConfigMode.ANY\n\t\t\t\t: FunctionCallingConfigMode.AUTO;\n\t\tconst stream = await this.client.models.generateContentStream({\n\t\t\tmodel: this.modelName,\n\t\t\tcontents: messages,\n\t\t\tconfig: {\n\t\t\t\ttools: [{ functionDeclarations: functions }],\n\t\t\t\ttoolConfig: {\n\t\t\t\t\tfunctionCallingConfig: { mode: toolChoiceMode },\n\t\t\t\t},\n\t\t\t},\n\t\t});\n\n\t\treturn this.createGeminiStreamAdapter(stream);\n\t}\n\n\t// NOTE(yoojin): Need to switch API Stream type to LLMStream.\n\tprivate createGeminiStreamAdapter(\n\t\tgeminiStream: AsyncIterable<GenerateContentResponse>,\n\t): LLMStream {\n\t\tconst hasName = (\n\t\t\tvalue: FunctionCall,\n\t\t): value is FunctionCall & { name: string } => {\n\t\t\treturn value.name !== undefined;\n\t\t};\n\n\t\treturn {\n\t\t\tasync *[Symbol.asyncIterator](): AsyncIterator<StreamChunk> {\n\t\t\t\tlet toolCallIndex = 0;\n\t\t\t\tfor await (const geminiChunk of geminiStream) {\n\t\t\t\t\tconst content = geminiChunk.candidates?.[0]?.content;\n\t\t\t\t\tif (!content) continue;\n\n\t\t\t\t\tconst tool_calls: ToolCallDelta[] = [];\n\t\t\t\t\tlet textContent = \"\";\n\n\t\t\t\t\t// Process all parts in the array\n\t\t\t\t\tfor (const part of content.parts || []) {\n\t\t\t\t\t\tif (part.text) {\n\t\t\t\t\t\t\ttextContent += part.text;\n\t\t\t\t\t\t} else if (part.functionCall && hasName(part.functionCall)) {\n\t\t\t\t\t\t\ttool_calls.push({\n\t\t\t\t\t\t\t\tindex: toolCallIndex++,\n\t\t\t\t\t\t\t\tid: part.functionCall.id || `call_${toolCallIndex}`,\n\t\t\t\t\t\t\t\tfunction: {\n\t\t\t\t\t\t\t\t\tname: part.functionCall.name,\n\t\t\t\t\t\t\t\t\targuments: JSON.stringify(part.functionCall.args),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t} as unknown as ToolCallDelta);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Only yield when there's text content\n\t\t\t\t\tif (textContent) {\n\t\t\t\t\t\tyield {\n\t\t\t\t\t\t\tdelta: {\n\t\t\t\t\t\t\t\trole: content.role,\n\t\t\t\t\t\t\t\tcontent: textContent,\n\t\t\t\t\t\t\t\ttool_calls: undefined,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tfinish_reason: geminiChunk.candidates?.[0]?.finishReason as any,\n\t\t\t\t\t\t\tmetadata: { provider: \"gemini\" },\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\t// Only yield when there are tool calls\n\t\t\t\t\tif (tool_calls.length > 0) {\n\t\t\t\t\t\tyield {\n\t\t\t\t\t\t\tdelta: {\n\t\t\t\t\t\t\t\trole: content.role,\n\t\t\t\t\t\t\t\tcontent: undefined,\n\t\t\t\t\t\t\t\ttool_calls,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tfinish_reason: geminiChunk.candidates?.[0]?.finishReason as any,\n\t\t\t\t\t\t\tmetadata: { provider: \"gemini\" },\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t}\n\n\tconvertToolsToFunctions(tools: ConnectorTool[]): FunctionDeclaration[] {\n\t\tconst functions: FunctionDeclaration[] = [];\n\t\tfor (const tool of tools) {\n\t\t\tfunctions.push({\n\t\t\t\tname: tool.toolName,\n\t\t\t\tdescription: tool.description,\n\t\t\t\tparametersJsonSchema: tool.inputSchema,\n\t\t\t});\n\t\t}\n\t\treturn functions;\n\t}\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
- import { BaseModel, ModelFetchOptions } from '@ainetwork/adk/modules';
1
+ import { BaseModel, AssistantToolCallTurn, ToolResultMessage, ModelFetchOptions } from '@ainetwork/adk/modules';
2
+ import { FetchResponse, ConnectorTool } from '@ainetwork/adk/types/connector';
2
3
  import { ThreadObject } from '@ainetwork/adk/types/memory';
3
4
  import { LLMStream } from '@ainetwork/adk/types/stream';
4
- import { FetchResponse, ConnectorTool } from '@ainetwork/adk/types/connector';
5
5
  import { Content, FunctionDeclaration } from '@google/genai';
6
6
 
7
7
  declare class GeminiModel extends BaseModel<Content, FunctionDeclaration> {
@@ -14,7 +14,8 @@ declare class GeminiModel extends BaseModel<Content, FunctionDeclaration> {
14
14
  thread?: ThreadObject;
15
15
  systemPrompt?: string;
16
16
  }): Content[];
17
- appendMessages(messages: Content[], message: string): void;
17
+ appendAssistantToolCallTurn(messages: Content[], turn: AssistantToolCallTurn): void;
18
+ appendToolResult(messages: Content[], result: ToolResultMessage): void;
18
19
  fetch(messages: Content[], options?: ModelFetchOptions): Promise<FetchResponse>;
19
20
  fetchWithContextMessage(messages: Content[], functions: FunctionDeclaration[], options?: ModelFetchOptions): Promise<FetchResponse>;
20
21
  fetchStreamWithContextMessage(messages: Content[], functions: FunctionDeclaration[], options?: ModelFetchOptions): Promise<LLMStream>;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { BaseModel, ModelFetchOptions } from '@ainetwork/adk/modules';
1
+ import { BaseModel, AssistantToolCallTurn, ToolResultMessage, ModelFetchOptions } from '@ainetwork/adk/modules';
2
+ import { FetchResponse, ConnectorTool } from '@ainetwork/adk/types/connector';
2
3
  import { ThreadObject } from '@ainetwork/adk/types/memory';
3
4
  import { LLMStream } from '@ainetwork/adk/types/stream';
4
- import { FetchResponse, ConnectorTool } from '@ainetwork/adk/types/connector';
5
5
  import { Content, FunctionDeclaration } from '@google/genai';
6
6
 
7
7
  declare class GeminiModel extends BaseModel<Content, FunctionDeclaration> {
@@ -14,7 +14,8 @@ declare class GeminiModel extends BaseModel<Content, FunctionDeclaration> {
14
14
  thread?: ThreadObject;
15
15
  systemPrompt?: string;
16
16
  }): Content[];
17
- appendMessages(messages: Content[], message: string): void;
17
+ appendAssistantToolCallTurn(messages: Content[], turn: AssistantToolCallTurn): void;
18
+ appendToolResult(messages: Content[], result: ToolResultMessage): void;
18
19
  fetch(messages: Content[], options?: ModelFetchOptions): Promise<FetchResponse>;
19
20
  fetchWithContextMessage(messages: Content[], functions: FunctionDeclaration[], options?: ModelFetchOptions): Promise<FetchResponse>;
20
21
  fetchStreamWithContextMessage(messages: Content[], functions: FunctionDeclaration[], options?: ModelFetchOptions): Promise<LLMStream>;
package/dist/index.js CHANGED
@@ -1,6 +1,10 @@
1
1
  // index.ts
2
- import { BaseModel } from "@ainetwork/adk/modules";
3
- import { MessageRole } from "@ainetwork/adk/types/memory";
2
+ import {
3
+ BaseModel
4
+ } from "@ainetwork/adk/modules";
5
+ import {
6
+ MessageRole
7
+ } from "@ainetwork/adk/types/memory";
4
8
  import {
5
9
  FunctionCallingConfigMode,
6
10
  GoogleGenAI
@@ -36,10 +40,41 @@ var GeminiModel = class extends BaseModel {
36
40
  const userContent = { role: "user", parts: [{ text: query }] };
37
41
  return messages.concat(sessionContent).concat(userContent);
38
42
  }
39
- appendMessages(messages, message) {
43
+ appendAssistantToolCallTurn(messages, turn) {
44
+ const parts = [];
45
+ if (turn.content) {
46
+ parts.push({ text: turn.content });
47
+ }
48
+ for (const tc of turn.toolCalls) {
49
+ let args = {};
50
+ try {
51
+ args = JSON.parse(tc.function.arguments || "{}");
52
+ } catch {
53
+ args = { __raw: tc.function.arguments };
54
+ }
55
+ parts.push({
56
+ functionCall: {
57
+ id: tc.id,
58
+ name: tc.function.name,
59
+ args
60
+ }
61
+ });
62
+ }
63
+ messages.push({ role: "model", parts });
64
+ }
65
+ appendToolResult(messages, result) {
66
+ const response = result.isError ? { error: result.content } : { output: result.content };
40
67
  messages.push({
41
68
  role: "user",
42
- parts: [{ text: message }]
69
+ parts: [
70
+ {
71
+ functionResponse: {
72
+ id: result.toolCallId,
73
+ name: result.toolName,
74
+ response
75
+ }
76
+ }
77
+ ]
43
78
  });
44
79
  }
45
80
  async fetch(messages, options) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../index.ts"],"sourcesContent":["import { BaseModel, ModelFetchOptions } from \"@ainetwork/adk/modules\";\nimport { MessageObject, MessageRole, type ThreadObject } from \"@ainetwork/adk/types/memory\";\nimport type {\n\tLLMStream,\n\tStreamChunk,\n\tToolCallDelta,\n} from \"@ainetwork/adk/types/stream\";\nimport type {\n\tFetchResponse,\n\tToolCall,\n\tConnectorTool,\n} from \"@ainetwork/adk/types/connector\";\nimport {\n\ttype Content,\n\ttype FunctionCall,\n\ttype FunctionDeclaration,\n\tFunctionCallingConfigMode,\n\ttype GenerateContentResponse,\n\tGoogleGenAI,\n\tModel,\n} from \"@google/genai\";\n\nexport class GeminiModel extends BaseModel<Content, FunctionDeclaration> {\n\tprivate client: GoogleGenAI;\n\tprivate modelName: string;\n\n\tconstructor(apiKey: string, modelName: string) {\n\t\tsuper();\n\t\tthis.client = new GoogleGenAI({ apiKey });\n\t\tthis.modelName = modelName;\n\t}\n\n\tprivate getMessageRole(role: MessageRole) {\n\t\tswitch (role) {\n\t\t\tcase MessageRole.USER:\n\t\t\t\treturn \"user\";\n\t\t\tcase MessageRole.MODEL:\n\t\t\tcase MessageRole.SYSTEM:\n\t\t\t\treturn \"model\";\n\t\t\tdefault:\n\t\t\t\treturn \"model\"; /*FIXME*/\n\t\t}\n\t}\n\n\tgenerateMessages(params: {\n\t\tquery: string;\n\t\tthread?: ThreadObject;\n\t\tsystemPrompt?: string;\n\t}): Content[] {\n\t\tconst { query, thread, systemPrompt } = params;\n\t\tconst messages: Content[] = !systemPrompt\n\t\t\t? []\n\t\t\t: [{ role: \"model\", parts: [{ text: systemPrompt.trim() }] }];\n\t\tconst sessionContent: Content[] = !thread\n\t\t\t? []\n\t\t\t: thread.messages.map((message: MessageObject) => {\n\t\t\t\t\t// TODO: check message.content.type\n\t\t\t\t\treturn {\n\t\t\t\t\t\trole: this.getMessageRole(message.role),\n\t\t\t\t\t\tparts: [{ text: message.content.parts[0] as string }],\n\t\t\t\t\t};\n\t\t\t\t});\n\t\tconst userContent: Content = { role: \"user\", parts: [{ text: query }] };\n\t\treturn messages.concat(sessionContent).concat(userContent);\n\t}\n\n\tappendMessages(messages: Content[], message: string): void {\n\t\tmessages.push({\n\t\t\trole: \"user\",\n\t\t\tparts: [{ text: message }],\n\t\t});\n\t}\n\n\tasync fetch(messages: Content[], options?: ModelFetchOptions): Promise<FetchResponse> {\n\t\tconst response = await this.client.models.generateContent({\n\t\t\tmodel: this.modelName,\n\t\t\tcontents: messages,\n\t\t});\n\n\t\treturn { content: response.text };\n\t}\n\n\tasync fetchWithContextMessage(\n\t\tmessages: Content[],\n\t\tfunctions: FunctionDeclaration[],\n\t\toptions?: ModelFetchOptions,\n\t): Promise<FetchResponse> {\n\t\tif (functions.length > 0) {\n\t\t\tconst toolChoiceMode = options?.toolChoice === \"required\"\n\t\t\t\t? FunctionCallingConfigMode.ANY\n\t\t\t\t: FunctionCallingConfigMode.AUTO;\n\t\t\tconst response = await this.client.models.generateContent({\n\t\t\t\tmodel: this.modelName,\n\t\t\t\tcontents: messages,\n\t\t\t\tconfig: {\n\t\t\t\t\ttools: [{ functionDeclarations: functions }],\n\t\t\t\t\ttoolConfig: {\n\t\t\t\t\t\tfunctionCallingConfig: { mode: toolChoiceMode },\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tconst { text, functionCalls } = response;\n\t\t\tconst hasName = (\n\t\t\t\tvalue: FunctionCall,\n\t\t\t): value is FunctionCall & { name: string } => {\n\t\t\t\treturn value.name !== undefined;\n\t\t\t};\n\t\t\tconst toolCalls: ToolCall[] | undefined = functionCalls\n\t\t\t\t?.filter(hasName)\n\t\t\t\t.map((value) => {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tname: value.name,\n\t\t\t\t\t\targuments: value.args,\n\t\t\t\t\t};\n\t\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tcontent: text,\n\t\t\t\ttoolCalls,\n\t\t\t};\n\t\t}\n\t\treturn await this.fetch(messages);\n\t}\n\n\tasync fetchStreamWithContextMessage(\n\t\tmessages: Content[],\n\t\tfunctions: FunctionDeclaration[],\n\t\toptions?: ModelFetchOptions,\n\t): Promise<LLMStream> {\n\t\tconst toolChoiceMode = options?.toolChoice === \"required\"\n\t\t\t? FunctionCallingConfigMode.ANY\n\t\t\t: FunctionCallingConfigMode.AUTO;\n\t\tconst stream = await this.client.models.generateContentStream({\n\t\t\tmodel: this.modelName,\n\t\t\tcontents: messages,\n\t\t\tconfig: {\n\t\t\t\ttools: [{ functionDeclarations: functions }],\n\t\t\t\ttoolConfig: {\n\t\t\t\t\tfunctionCallingConfig: { mode: toolChoiceMode },\n\t\t\t\t},\n\t\t\t},\n\t\t});\n\n\t\treturn this.createGeminiStreamAdapter(stream);\n\t}\n\n\t// NOTE(yoojin): Need to switch API Stream type to LLMStream.\n\tprivate createGeminiStreamAdapter(\n\t\tgeminiStream: AsyncIterable<GenerateContentResponse>,\n\t): LLMStream {\n\t\tconst hasName = (\n\t\t\tvalue: FunctionCall,\n\t\t): value is FunctionCall & { name: string } => {\n\t\t\treturn value.name !== undefined;\n\t\t};\n\n\t\treturn {\n\t\t\tasync *[Symbol.asyncIterator](): AsyncIterator<StreamChunk> {\n\t\t\t\tlet toolCallIndex = 0;\n\t\t\t\tfor await (const geminiChunk of geminiStream) {\n\t\t\t\t\tconst content = geminiChunk.candidates?.[0]?.content;\n\t\t\t\t\tif (!content) continue;\n\n\t\t\t\t\tconst tool_calls: ToolCallDelta[] = [];\n\t\t\t\t\tlet textContent = \"\";\n\n\t\t\t\t\t// Process all parts in the array\n\t\t\t\t\tfor (const part of content.parts || []) {\n\t\t\t\t\t\tif (part.text) {\n\t\t\t\t\t\t\ttextContent += part.text;\n\t\t\t\t\t\t} else if (part.functionCall && hasName(part.functionCall)) {\n\t\t\t\t\t\t\ttool_calls.push({\n\t\t\t\t\t\t\t\tindex: toolCallIndex++,\n\t\t\t\t\t\t\t\tid: part.functionCall.id || `call_${toolCallIndex}`,\n\t\t\t\t\t\t\t\tfunction: {\n\t\t\t\t\t\t\t\t\tname: part.functionCall.name,\n\t\t\t\t\t\t\t\t\targuments: JSON.stringify(part.functionCall.args),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t} as unknown as ToolCallDelta);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Only yield when there's text content\n\t\t\t\t\tif (textContent) {\n\t\t\t\t\t\tyield {\n\t\t\t\t\t\t\tdelta: {\n\t\t\t\t\t\t\t\trole: content.role,\n\t\t\t\t\t\t\t\tcontent: textContent,\n\t\t\t\t\t\t\t\ttool_calls: undefined,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tfinish_reason: geminiChunk.candidates?.[0]?.finishReason as any,\n\t\t\t\t\t\t\tmetadata: { provider: \"gemini\" },\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\t// Only yield when there are tool calls\n\t\t\t\t\tif (tool_calls.length > 0) {\n\t\t\t\t\t\tyield {\n\t\t\t\t\t\t\tdelta: {\n\t\t\t\t\t\t\t\trole: content.role,\n\t\t\t\t\t\t\t\tcontent: undefined,\n\t\t\t\t\t\t\t\ttool_calls,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tfinish_reason: geminiChunk.candidates?.[0]?.finishReason as any,\n\t\t\t\t\t\t\tmetadata: { provider: \"gemini\" },\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t}\n\n\tconvertToolsToFunctions(tools: ConnectorTool[]): FunctionDeclaration[] {\n\t\tconst functions: FunctionDeclaration[] = [];\n\t\tfor (const tool of tools) {\n\t\t\tfunctions.push({\n\t\t\t\tname: tool.toolName,\n\t\t\t\tdescription: tool.description,\n\t\t\t\tparametersJsonSchema: tool.inputSchema,\n\t\t\t});\n\t\t}\n\t\treturn functions;\n\t}\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../index.ts"],"sourcesContent":["import {\n\ttype AssistantToolCallTurn,\n\tBaseModel,\n\ttype ModelFetchOptions,\n\ttype ToolResultMessage,\n} from \"@ainetwork/adk/modules\";\nimport type {\n\tConnectorTool,\n\tFetchResponse,\n\tToolCall,\n} from \"@ainetwork/adk/types/connector\";\nimport {\n\ttype MessageObject,\n\tMessageRole,\n\ttype ThreadObject,\n} from \"@ainetwork/adk/types/memory\";\nimport type {\n\tLLMStream,\n\tStreamChunk,\n\tToolCallDelta,\n} from \"@ainetwork/adk/types/stream\";\nimport {\n\ttype Content,\n\ttype FunctionCall,\n\tFunctionCallingConfigMode,\n\ttype FunctionDeclaration,\n\ttype GenerateContentResponse,\n\tGoogleGenAI,\n\ttype Part,\n} from \"@google/genai\";\n\nexport class GeminiModel extends BaseModel<Content, FunctionDeclaration> {\n\tprivate client: GoogleGenAI;\n\tprivate modelName: string;\n\n\tconstructor(apiKey: string, modelName: string) {\n\t\tsuper();\n\t\tthis.client = new GoogleGenAI({ apiKey });\n\t\tthis.modelName = modelName;\n\t}\n\n\tprivate getMessageRole(role: MessageRole) {\n\t\tswitch (role) {\n\t\t\tcase MessageRole.USER:\n\t\t\t\treturn \"user\";\n\t\t\tcase MessageRole.MODEL:\n\t\t\tcase MessageRole.SYSTEM:\n\t\t\t\treturn \"model\";\n\t\t\tdefault:\n\t\t\t\treturn \"model\"; /*FIXME*/\n\t\t}\n\t}\n\n\tgenerateMessages(params: {\n\t\tquery: string;\n\t\tthread?: ThreadObject;\n\t\tsystemPrompt?: string;\n\t}): Content[] {\n\t\tconst { query, thread, systemPrompt } = params;\n\t\tconst messages: Content[] = !systemPrompt\n\t\t\t? []\n\t\t\t: [{ role: \"model\", parts: [{ text: systemPrompt.trim() }] }];\n\t\tconst sessionContent: Content[] = !thread\n\t\t\t? []\n\t\t\t: thread.messages.map((message: MessageObject) => {\n\t\t\t\t\t// TODO: check message.content.type\n\t\t\t\t\treturn {\n\t\t\t\t\t\trole: this.getMessageRole(message.role),\n\t\t\t\t\t\tparts: [{ text: message.content.parts[0] as string }],\n\t\t\t\t\t};\n\t\t\t\t});\n\t\tconst userContent: Content = { role: \"user\", parts: [{ text: query }] };\n\t\treturn messages.concat(sessionContent).concat(userContent);\n\t}\n\n\tappendAssistantToolCallTurn(\n\t\tmessages: Content[],\n\t\tturn: AssistantToolCallTurn,\n\t): void {\n\t\tconst parts: Part[] = [];\n\t\tif (turn.content) {\n\t\t\tparts.push({ text: turn.content });\n\t\t}\n\t\tfor (const tc of turn.toolCalls) {\n\t\t\tlet args: Record<string, unknown> = {};\n\t\t\ttry {\n\t\t\t\targs = JSON.parse(tc.function.arguments || \"{}\");\n\t\t\t} catch {\n\t\t\t\t// Forward the raw argument string so the model can self-correct.\n\t\t\t\targs = { __raw: tc.function.arguments };\n\t\t\t}\n\t\t\tparts.push({\n\t\t\t\tfunctionCall: {\n\t\t\t\t\tid: tc.id,\n\t\t\t\t\tname: tc.function.name,\n\t\t\t\t\targs,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t\tmessages.push({ role: \"model\", parts });\n\t}\n\n\tappendToolResult(messages: Content[], result: ToolResultMessage): void {\n\t\tconst response: Record<string, unknown> = result.isError\n\t\t\t? { error: result.content }\n\t\t\t: { output: result.content };\n\t\tmessages.push({\n\t\t\trole: \"user\",\n\t\t\tparts: [\n\t\t\t\t{\n\t\t\t\t\tfunctionResponse: {\n\t\t\t\t\t\tid: result.toolCallId,\n\t\t\t\t\t\tname: result.toolName,\n\t\t\t\t\t\tresponse,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t}\n\n\tasync fetch(\n\t\tmessages: Content[],\n\t\toptions?: ModelFetchOptions,\n\t): Promise<FetchResponse> {\n\t\tconst response = await this.client.models.generateContent({\n\t\t\tmodel: this.modelName,\n\t\t\tcontents: messages,\n\t\t});\n\n\t\treturn { content: response.text };\n\t}\n\n\tasync fetchWithContextMessage(\n\t\tmessages: Content[],\n\t\tfunctions: FunctionDeclaration[],\n\t\toptions?: ModelFetchOptions,\n\t): Promise<FetchResponse> {\n\t\tif (functions.length > 0) {\n\t\t\tconst toolChoiceMode =\n\t\t\t\toptions?.toolChoice === \"required\"\n\t\t\t\t\t? FunctionCallingConfigMode.ANY\n\t\t\t\t\t: FunctionCallingConfigMode.AUTO;\n\t\t\tconst response = await this.client.models.generateContent({\n\t\t\t\tmodel: this.modelName,\n\t\t\t\tcontents: messages,\n\t\t\t\tconfig: {\n\t\t\t\t\ttools: [{ functionDeclarations: functions }],\n\t\t\t\t\ttoolConfig: {\n\t\t\t\t\t\tfunctionCallingConfig: { mode: toolChoiceMode },\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tconst { text, functionCalls } = response;\n\t\t\tconst hasName = (\n\t\t\t\tvalue: FunctionCall,\n\t\t\t): value is FunctionCall & { name: string } => {\n\t\t\t\treturn value.name !== undefined;\n\t\t\t};\n\t\t\tconst toolCalls: ToolCall[] | undefined = functionCalls\n\t\t\t\t?.filter(hasName)\n\t\t\t\t.map((value) => {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tname: value.name,\n\t\t\t\t\t\targuments: value.args,\n\t\t\t\t\t};\n\t\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tcontent: text,\n\t\t\t\ttoolCalls,\n\t\t\t};\n\t\t}\n\t\treturn await this.fetch(messages);\n\t}\n\n\tasync fetchStreamWithContextMessage(\n\t\tmessages: Content[],\n\t\tfunctions: FunctionDeclaration[],\n\t\toptions?: ModelFetchOptions,\n\t): Promise<LLMStream> {\n\t\tconst toolChoiceMode =\n\t\t\toptions?.toolChoice === \"required\"\n\t\t\t\t? FunctionCallingConfigMode.ANY\n\t\t\t\t: FunctionCallingConfigMode.AUTO;\n\t\tconst stream = await this.client.models.generateContentStream({\n\t\t\tmodel: this.modelName,\n\t\t\tcontents: messages,\n\t\t\tconfig: {\n\t\t\t\ttools: [{ functionDeclarations: functions }],\n\t\t\t\ttoolConfig: {\n\t\t\t\t\tfunctionCallingConfig: { mode: toolChoiceMode },\n\t\t\t\t},\n\t\t\t},\n\t\t});\n\n\t\treturn this.createGeminiStreamAdapter(stream);\n\t}\n\n\t// NOTE(yoojin): Need to switch API Stream type to LLMStream.\n\tprivate createGeminiStreamAdapter(\n\t\tgeminiStream: AsyncIterable<GenerateContentResponse>,\n\t): LLMStream {\n\t\tconst hasName = (\n\t\t\tvalue: FunctionCall,\n\t\t): value is FunctionCall & { name: string } => {\n\t\t\treturn value.name !== undefined;\n\t\t};\n\n\t\treturn {\n\t\t\tasync *[Symbol.asyncIterator](): AsyncIterator<StreamChunk> {\n\t\t\t\tlet toolCallIndex = 0;\n\t\t\t\tfor await (const geminiChunk of geminiStream) {\n\t\t\t\t\tconst content = geminiChunk.candidates?.[0]?.content;\n\t\t\t\t\tif (!content) continue;\n\n\t\t\t\t\tconst tool_calls: ToolCallDelta[] = [];\n\t\t\t\t\tlet textContent = \"\";\n\n\t\t\t\t\t// Process all parts in the array\n\t\t\t\t\tfor (const part of content.parts || []) {\n\t\t\t\t\t\tif (part.text) {\n\t\t\t\t\t\t\ttextContent += part.text;\n\t\t\t\t\t\t} else if (part.functionCall && hasName(part.functionCall)) {\n\t\t\t\t\t\t\ttool_calls.push({\n\t\t\t\t\t\t\t\tindex: toolCallIndex++,\n\t\t\t\t\t\t\t\tid: part.functionCall.id || `call_${toolCallIndex}`,\n\t\t\t\t\t\t\t\tfunction: {\n\t\t\t\t\t\t\t\t\tname: part.functionCall.name,\n\t\t\t\t\t\t\t\t\targuments: JSON.stringify(part.functionCall.args),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t} as unknown as ToolCallDelta);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Only yield when there's text content\n\t\t\t\t\tif (textContent) {\n\t\t\t\t\t\tyield {\n\t\t\t\t\t\t\tdelta: {\n\t\t\t\t\t\t\t\trole: content.role,\n\t\t\t\t\t\t\t\tcontent: textContent,\n\t\t\t\t\t\t\t\ttool_calls: undefined,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tfinish_reason: geminiChunk.candidates?.[0]?.finishReason as any,\n\t\t\t\t\t\t\tmetadata: { provider: \"gemini\" },\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\t// Only yield when there are tool calls\n\t\t\t\t\tif (tool_calls.length > 0) {\n\t\t\t\t\t\tyield {\n\t\t\t\t\t\t\tdelta: {\n\t\t\t\t\t\t\t\trole: content.role,\n\t\t\t\t\t\t\t\tcontent: undefined,\n\t\t\t\t\t\t\t\ttool_calls,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tfinish_reason: geminiChunk.candidates?.[0]?.finishReason as any,\n\t\t\t\t\t\t\tmetadata: { provider: \"gemini\" },\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t}\n\n\tconvertToolsToFunctions(tools: ConnectorTool[]): FunctionDeclaration[] {\n\t\tconst functions: FunctionDeclaration[] = [];\n\t\tfor (const tool of tools) {\n\t\t\tfunctions.push({\n\t\t\t\tname: tool.toolName,\n\t\t\t\tdescription: tool.description,\n\t\t\t\tparametersJsonSchema: tool.inputSchema,\n\t\t\t});\n\t\t}\n\t\treturn functions;\n\t}\n}\n"],"mappings":"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","names":[]}
package/index.ts CHANGED
@@ -1,23 +1,32 @@
1
- import { BaseModel, ModelFetchOptions } from "@ainetwork/adk/modules";
2
- import { MessageObject, MessageRole, type ThreadObject } from "@ainetwork/adk/types/memory";
1
+ import {
2
+ type AssistantToolCallTurn,
3
+ BaseModel,
4
+ type ModelFetchOptions,
5
+ type ToolResultMessage,
6
+ } from "@ainetwork/adk/modules";
7
+ import type {
8
+ ConnectorTool,
9
+ FetchResponse,
10
+ ToolCall,
11
+ } from "@ainetwork/adk/types/connector";
12
+ import {
13
+ type MessageObject,
14
+ MessageRole,
15
+ type ThreadObject,
16
+ } from "@ainetwork/adk/types/memory";
3
17
  import type {
4
18
  LLMStream,
5
19
  StreamChunk,
6
20
  ToolCallDelta,
7
21
  } from "@ainetwork/adk/types/stream";
8
- import type {
9
- FetchResponse,
10
- ToolCall,
11
- ConnectorTool,
12
- } from "@ainetwork/adk/types/connector";
13
22
  import {
14
23
  type Content,
15
24
  type FunctionCall,
16
- type FunctionDeclaration,
17
25
  FunctionCallingConfigMode,
26
+ type FunctionDeclaration,
18
27
  type GenerateContentResponse,
19
28
  GoogleGenAI,
20
- Model,
29
+ type Part,
21
30
  } from "@google/genai";
22
31
 
23
32
  export class GeminiModel extends BaseModel<Content, FunctionDeclaration> {
@@ -64,14 +73,55 @@ export class GeminiModel extends BaseModel<Content, FunctionDeclaration> {
64
73
  return messages.concat(sessionContent).concat(userContent);
65
74
  }
66
75
 
67
- appendMessages(messages: Content[], message: string): void {
76
+ appendAssistantToolCallTurn(
77
+ messages: Content[],
78
+ turn: AssistantToolCallTurn,
79
+ ): void {
80
+ const parts: Part[] = [];
81
+ if (turn.content) {
82
+ parts.push({ text: turn.content });
83
+ }
84
+ for (const tc of turn.toolCalls) {
85
+ let args: Record<string, unknown> = {};
86
+ try {
87
+ args = JSON.parse(tc.function.arguments || "{}");
88
+ } catch {
89
+ // Forward the raw argument string so the model can self-correct.
90
+ args = { __raw: tc.function.arguments };
91
+ }
92
+ parts.push({
93
+ functionCall: {
94
+ id: tc.id,
95
+ name: tc.function.name,
96
+ args,
97
+ },
98
+ });
99
+ }
100
+ messages.push({ role: "model", parts });
101
+ }
102
+
103
+ appendToolResult(messages: Content[], result: ToolResultMessage): void {
104
+ const response: Record<string, unknown> = result.isError
105
+ ? { error: result.content }
106
+ : { output: result.content };
68
107
  messages.push({
69
108
  role: "user",
70
- parts: [{ text: message }],
109
+ parts: [
110
+ {
111
+ functionResponse: {
112
+ id: result.toolCallId,
113
+ name: result.toolName,
114
+ response,
115
+ },
116
+ },
117
+ ],
71
118
  });
72
119
  }
73
120
 
74
- async fetch(messages: Content[], options?: ModelFetchOptions): Promise<FetchResponse> {
121
+ async fetch(
122
+ messages: Content[],
123
+ options?: ModelFetchOptions,
124
+ ): Promise<FetchResponse> {
75
125
  const response = await this.client.models.generateContent({
76
126
  model: this.modelName,
77
127
  contents: messages,
@@ -86,9 +136,10 @@ export class GeminiModel extends BaseModel<Content, FunctionDeclaration> {
86
136
  options?: ModelFetchOptions,
87
137
  ): Promise<FetchResponse> {
88
138
  if (functions.length > 0) {
89
- const toolChoiceMode = options?.toolChoice === "required"
90
- ? FunctionCallingConfigMode.ANY
91
- : FunctionCallingConfigMode.AUTO;
139
+ const toolChoiceMode =
140
+ options?.toolChoice === "required"
141
+ ? FunctionCallingConfigMode.ANY
142
+ : FunctionCallingConfigMode.AUTO;
92
143
  const response = await this.client.models.generateContent({
93
144
  model: this.modelName,
94
145
  contents: messages,
@@ -128,9 +179,10 @@ export class GeminiModel extends BaseModel<Content, FunctionDeclaration> {
128
179
  functions: FunctionDeclaration[],
129
180
  options?: ModelFetchOptions,
130
181
  ): Promise<LLMStream> {
131
- const toolChoiceMode = options?.toolChoice === "required"
132
- ? FunctionCallingConfigMode.ANY
133
- : FunctionCallingConfigMode.AUTO;
182
+ const toolChoiceMode =
183
+ options?.toolChoice === "required"
184
+ ? FunctionCallingConfigMode.ANY
185
+ : FunctionCallingConfigMode.AUTO;
134
186
  const stream = await this.client.models.generateContentStream({
135
187
  model: this.modelName,
136
188
  contents: messages,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ainetwork/adk-provider-model-gemini",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "author": "AI Network (https://ainetwork.ai)",
5
5
  "type": "module",
6
6
  "engines": {
@@ -24,7 +24,7 @@
24
24
  "@google/genai": "^1.11.0"
25
25
  },
26
26
  "peerDependencies": {
27
- "@ainetwork/adk": "^0.6.0"
27
+ "@ainetwork/adk": "^0.6.2"
28
28
  },
29
29
  "devDependencies": {
30
30
  "typescript": "^5.0.0"
@@ -33,5 +33,5 @@
33
33
  "publishConfig": {
34
34
  "access": "public"
35
35
  },
36
- "gitHead": "e3e922e5b932ce07902d0ad5b3460a22aae23960"
36
+ "gitHead": "3744b25bd59649146ed0a91e3ad86c1e82b3e3c6"
37
37
  }
@@ -0,0 +1,167 @@
1
+ import type { Content } from "@google/genai";
2
+ import { GeminiModel } from "../index";
3
+
4
+ function makeProvider(): GeminiModel {
5
+ return new GeminiModel("test-key", "gemini-test");
6
+ }
7
+
8
+ describe("GeminiModel tool-call protocol", () => {
9
+ it("appendAssistantToolCallTurn pushes role='model' with functionCall parts", () => {
10
+ const provider = makeProvider();
11
+ const messages: Content[] = [];
12
+
13
+ provider.appendAssistantToolCallTurn(messages, {
14
+ content: "Checking",
15
+ toolCalls: [
16
+ {
17
+ id: "call_1",
18
+ type: "function",
19
+ function: { name: "search", arguments: '{"q":"a"}' },
20
+ },
21
+ ],
22
+ });
23
+
24
+ expect(messages).toHaveLength(1);
25
+ expect(messages[0].role).toBe("model");
26
+ const parts = messages[0].parts ?? [];
27
+ expect(parts[0]).toEqual({ text: "Checking" });
28
+ expect(parts[1]).toEqual({
29
+ functionCall: {
30
+ id: "call_1",
31
+ name: "search",
32
+ args: { q: "a" },
33
+ },
34
+ });
35
+ });
36
+
37
+ it("appendAssistantToolCallTurn omits the text part when content is null", () => {
38
+ const provider = makeProvider();
39
+ const messages: Content[] = [];
40
+
41
+ provider.appendAssistantToolCallTurn(messages, {
42
+ content: null,
43
+ toolCalls: [
44
+ {
45
+ id: "call_1",
46
+ type: "function",
47
+ function: { name: "search", arguments: "{}" },
48
+ },
49
+ ],
50
+ });
51
+
52
+ const parts = messages[0].parts ?? [];
53
+ expect(parts).toHaveLength(1);
54
+ expect(parts[0]).toMatchObject({ functionCall: { name: "search" } });
55
+ });
56
+
57
+ it("appendAssistantToolCallTurn preserves the raw argument string when JSON parsing fails", () => {
58
+ const provider = makeProvider();
59
+ const messages: Content[] = [];
60
+
61
+ provider.appendAssistantToolCallTurn(messages, {
62
+ content: null,
63
+ toolCalls: [
64
+ {
65
+ id: "call_1",
66
+ type: "function",
67
+ function: { name: "search", arguments: "{not json" },
68
+ },
69
+ ],
70
+ });
71
+
72
+ const parts = messages[0].parts ?? [];
73
+ expect(parts[0]).toMatchObject({
74
+ functionCall: { args: { __raw: "{not json" } },
75
+ });
76
+ });
77
+
78
+ it("appendToolResult emits role='user' with functionResponse keyed by id", () => {
79
+ const provider = makeProvider();
80
+ const messages: Content[] = [];
81
+
82
+ provider.appendToolResult(messages, {
83
+ toolCallId: "call_1",
84
+ toolName: "search",
85
+ content: "result body",
86
+ });
87
+
88
+ expect(messages).toEqual([
89
+ {
90
+ role: "user",
91
+ parts: [
92
+ {
93
+ functionResponse: {
94
+ id: "call_1",
95
+ name: "search",
96
+ response: { output: "result body" },
97
+ },
98
+ },
99
+ ],
100
+ },
101
+ ]);
102
+ });
103
+
104
+ it("appendToolResult wraps errors under the 'error' key in the response", () => {
105
+ const provider = makeProvider();
106
+ const messages: Content[] = [];
107
+
108
+ provider.appendToolResult(messages, {
109
+ toolCallId: "call_bad",
110
+ toolName: "search",
111
+ content: "Invalid arguments",
112
+ isError: true,
113
+ });
114
+
115
+ const part = messages[0].parts?.[0];
116
+ expect(part).toMatchObject({
117
+ functionResponse: {
118
+ id: "call_bad",
119
+ response: { error: "Invalid arguments" },
120
+ },
121
+ });
122
+ });
123
+
124
+ it("emits assistant turn and tool result ids that match across the pair", () => {
125
+ const provider = makeProvider();
126
+ const messages: Content[] = [];
127
+
128
+ provider.appendAssistantToolCallTurn(messages, {
129
+ content: null,
130
+ toolCalls: [
131
+ {
132
+ id: "id_a",
133
+ type: "function",
134
+ function: { name: "t1", arguments: "{}" },
135
+ },
136
+ {
137
+ id: "id_b",
138
+ type: "function",
139
+ function: { name: "t2", arguments: "{}" },
140
+ },
141
+ ],
142
+ });
143
+ provider.appendToolResult(messages, {
144
+ toolCallId: "id_a",
145
+ toolName: "t1",
146
+ content: "a",
147
+ });
148
+ provider.appendToolResult(messages, {
149
+ toolCallId: "id_b",
150
+ toolName: "t2",
151
+ content: "b",
152
+ });
153
+
154
+ const assistantIds = (messages[0].parts ?? [])
155
+ .flatMap((p) => (p.functionCall?.id ? [p.functionCall.id] : []))
156
+ .sort();
157
+ const resultIds = messages
158
+ .slice(1)
159
+ .flatMap((m) =>
160
+ (m.parts ?? []).flatMap((p) =>
161
+ p.functionResponse?.id ? [p.functionResponse.id] : [],
162
+ ),
163
+ )
164
+ .sort();
165
+ expect(resultIds).toEqual(assistantIds);
166
+ });
167
+ });