@robota-sdk/agent-provider 3.0.0-beta.64
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/LICENSE +21 -0
- package/dist/browser/index.d.ts +1104 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +7 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/loggers/index.cjs +1 -0
- package/dist/loggers/index.d.ts +151 -0
- package/dist/loggers/index.d.ts.map +1 -0
- package/dist/loggers/index.js +2 -0
- package/dist/loggers/index.js.map +1 -0
- package/dist/node/anthropic/index.cjs +1 -0
- package/dist/node/anthropic/index.d.ts +158 -0
- package/dist/node/anthropic/index.d.ts.map +1 -0
- package/dist/node/anthropic/index.js +1 -0
- package/dist/node/anthropic--1vgLC-e.js +5 -0
- package/dist/node/anthropic--1vgLC-e.js.map +1 -0
- package/dist/node/anthropic-BFQ6DSCP.cjs +4 -0
- package/dist/node/bytedance/index.cjs +1 -0
- package/dist/node/bytedance/index.d.ts +74 -0
- package/dist/node/bytedance/index.d.ts.map +1 -0
- package/dist/node/bytedance/index.js +1 -0
- package/dist/node/bytedance-C_0sF_pJ.js +2 -0
- package/dist/node/bytedance-C_0sF_pJ.js.map +1 -0
- package/dist/node/bytedance-DVPxqEiC.cjs +1 -0
- package/dist/node/chunk-Bmb41Sf3.cjs +1 -0
- package/dist/node/deepseek/index.cjs +1 -0
- package/dist/node/deepseek/index.d.ts +2 -0
- package/dist/node/deepseek/index.js +1 -0
- package/dist/node/deepseek-_8Ixx7rA.js +2 -0
- package/dist/node/deepseek-_8Ixx7rA.js.map +1 -0
- package/dist/node/deepseek-oA2Y6bD0.cjs +1 -0
- package/dist/node/gemini/index.cjs +1 -0
- package/dist/node/gemini/index.d.ts +173 -0
- package/dist/node/gemini/index.d.ts.map +1 -0
- package/dist/node/gemini/index.js +1 -0
- package/dist/node/gemini-Bh2U87MY.js +4 -0
- package/dist/node/gemini-Bh2U87MY.js.map +1 -0
- package/dist/node/gemini-DSaNCxZj.cjs +3 -0
- package/dist/node/gemma/index.cjs +1 -0
- package/dist/node/gemma/index.d.ts +2 -0
- package/dist/node/gemma/index.js +1 -0
- package/dist/node/gemma-Dp_AfCUR.js +2 -0
- package/dist/node/gemma-Dp_AfCUR.js.map +1 -0
- package/dist/node/gemma-G-Pf_PnX.cjs +1 -0
- package/dist/node/google/index.cjs +1 -0
- package/dist/node/google/index.d.ts +14 -0
- package/dist/node/google/index.d.ts.map +1 -0
- package/dist/node/google/index.js +2 -0
- package/dist/node/google/index.js.map +1 -0
- package/dist/node/index-B6PnlDMd.d.ts +82 -0
- package/dist/node/index-B6PnlDMd.d.ts.map +1 -0
- package/dist/node/index-B7UvPJcI.d.ts +315 -0
- package/dist/node/index-B7UvPJcI.d.ts.map +1 -0
- package/dist/node/index-BLPOTNb5.d.ts +98 -0
- package/dist/node/index-BLPOTNb5.d.ts.map +1 -0
- package/dist/node/index-BqixM_XD.d.ts +231 -0
- package/dist/node/index-BqixM_XD.d.ts.map +1 -0
- package/dist/node/index-C3beaqKO.d.ts +231 -0
- package/dist/node/index-C3beaqKO.d.ts.map +1 -0
- package/dist/node/index-Cp2XRh9G.d.ts +82 -0
- package/dist/node/index-Cp2XRh9G.d.ts.map +1 -0
- package/dist/node/index-DSv5xruI.d.ts +98 -0
- package/dist/node/index-DSv5xruI.d.ts.map +1 -0
- package/dist/node/index-w0bV1uaP.d.ts +315 -0
- package/dist/node/index-w0bV1uaP.d.ts.map +1 -0
- package/dist/node/index.cjs +1 -0
- package/dist/node/index.d.ts +8 -0
- package/dist/node/index.js +1 -0
- package/dist/node/openai/index.cjs +1 -0
- package/dist/node/openai/index.d.ts +2 -0
- package/dist/node/openai/index.js +1 -0
- package/dist/node/openai-CRQjg4xF.js +2 -0
- package/dist/node/openai-CRQjg4xF.js.map +1 -0
- package/dist/node/openai-compatible-BYfyY5lb.cjs +1 -0
- package/dist/node/openai-compatible-Dm4Sof9e.js +2 -0
- package/dist/node/openai-compatible-Dm4Sof9e.js.map +1 -0
- package/dist/node/openai-xWC6pY7r.cjs +1 -0
- package/dist/node/qwen/index.cjs +1 -0
- package/dist/node/qwen/index.d.ts +2 -0
- package/dist/node/qwen/index.js +1 -0
- package/dist/node/qwen-ChUZobTL.js +2 -0
- package/dist/node/qwen-ChUZobTL.js.map +1 -0
- package/dist/node/qwen-CjT71vSM.cjs +1 -0
- package/package.json +157 -0
- package/src/anthropic/__tests__/abort-streaming.test.ts +199 -0
- package/src/anthropic/__tests__/model-catalog-refresh.test.ts +92 -0
- package/src/anthropic/__tests__/provider-definition.test.ts +55 -0
- package/src/anthropic/__tests__/provider.test.ts +1357 -0
- package/src/anthropic/__tests__/response-parser.test.ts +326 -0
- package/src/anthropic/index.ts +22 -0
- package/src/anthropic/message-converter.ts +181 -0
- package/src/anthropic/model-catalog-refresh.ts +128 -0
- package/src/anthropic/parsers/response-parser.ts +184 -0
- package/src/anthropic/provider-definition.ts +93 -0
- package/src/anthropic/provider.ts +290 -0
- package/src/anthropic/streaming-handler.ts +204 -0
- package/src/anthropic/types/api-types.ts +158 -0
- package/src/anthropic/types.ts +79 -0
- package/src/bytedance/http-client.test.ts +288 -0
- package/src/bytedance/http-client.ts +163 -0
- package/src/bytedance/index.ts +2 -0
- package/src/bytedance/provider.spec.ts +320 -0
- package/src/bytedance/provider.ts +171 -0
- package/src/bytedance/status-mapper.test.ts +299 -0
- package/src/bytedance/status-mapper.ts +141 -0
- package/src/bytedance/types.ts +68 -0
- package/src/deepseek/defaults.ts +4 -0
- package/src/deepseek/index.ts +22 -0
- package/src/deepseek/model-catalog-refresh.test.ts +57 -0
- package/src/deepseek/model-catalog-refresh.ts +105 -0
- package/src/deepseek/model-catalog.ts +55 -0
- package/src/deepseek/provider-definition.test.ts +109 -0
- package/src/deepseek/provider-definition.ts +132 -0
- package/src/deepseek/provider.test.ts +324 -0
- package/src/deepseek/provider.ts +298 -0
- package/src/deepseek/types.ts +37 -0
- package/src/gemini/execution-helpers.ts +233 -0
- package/src/gemini/genai-transport.test.ts +208 -0
- package/src/gemini/image-operations.test.ts +448 -0
- package/src/gemini/image-operations.ts +261 -0
- package/src/gemini/index.ts +11 -0
- package/src/gemini/message-converter.test.ts +616 -0
- package/src/gemini/message-converter.ts +140 -0
- package/src/gemini/model-catalog-refresh.test.ts +107 -0
- package/src/gemini/model-catalog-refresh.ts +92 -0
- package/src/gemini/provider-definition.test.ts +70 -0
- package/src/gemini/provider-definition.ts +78 -0
- package/src/gemini/provider-extended.test.ts +898 -0
- package/src/gemini/provider.spec.ts +216 -0
- package/src/gemini/provider.ts +279 -0
- package/src/gemini/request-converter.ts +226 -0
- package/src/gemini/tool-schema-converter.ts +78 -0
- package/src/gemini/types/api-types.ts +235 -0
- package/src/gemini/types.ts +121 -0
- package/src/gemma/index.ts +5 -0
- package/src/gemma/message-factory.ts +38 -0
- package/src/gemma/provider-definition.test.ts +43 -0
- package/src/gemma/provider-definition.ts +84 -0
- package/src/gemma/provider-projection.ts +49 -0
- package/src/gemma/provider.test.ts +628 -0
- package/src/gemma/provider.ts +308 -0
- package/src/gemma/pseudo-command-envelope.ts +58 -0
- package/src/gemma/pseudo-tool-call-projector.ts +243 -0
- package/src/gemma/pseudo-tool-call-tag-parser.ts +153 -0
- package/src/gemma/pseudo-tool-call-types.ts +31 -0
- package/src/gemma/reasoning-projector.test.ts +52 -0
- package/src/gemma/reasoning-projector.ts +144 -0
- package/src/gemma/streaming-projection.ts +79 -0
- package/src/gemma/tool-call-argument-parser.ts +126 -0
- package/src/gemma/tool-call-projector.test.ts +227 -0
- package/src/gemma/tool-call-projector.ts +264 -0
- package/src/gemma/types.ts +27 -0
- package/src/google/index.ts +11 -0
- package/src/google/provider-compat.test.ts +19 -0
- package/src/google/provider-definition.ts +6 -0
- package/src/google/provider.ts +10 -0
- package/src/google/types.ts +5 -0
- package/src/index.ts +9 -0
- package/src/openai/adapter.test.ts +494 -0
- package/src/openai/adapter.ts +145 -0
- package/src/openai/chat-completions-chat.ts +189 -0
- package/src/openai/executor-integration.test.ts +206 -0
- package/src/openai/index.ts +21 -0
- package/src/openai/interfaces/payload-logger.ts +48 -0
- package/src/openai/loggers/console-payload-logger.test.ts +173 -0
- package/src/openai/loggers/console-payload-logger.ts +94 -0
- package/src/openai/loggers/console.ts +9 -0
- package/src/openai/loggers/file-payload-logger.test.ts +238 -0
- package/src/openai/loggers/file-payload-logger.ts +112 -0
- package/src/openai/loggers/file.ts +9 -0
- package/src/openai/loggers/index.ts +12 -0
- package/src/openai/loggers/sanitize-openai-log-data.test.ts +89 -0
- package/src/openai/loggers/sanitize-openai-log-data.ts +14 -0
- package/src/openai/message-converter.ts +22 -0
- package/src/openai/model-catalog-refresh.test.ts +92 -0
- package/src/openai/model-catalog-refresh.ts +115 -0
- package/src/openai/openai-request-format.ts +92 -0
- package/src/openai/parsers/response-parser.test.ts +407 -0
- package/src/openai/parsers/response-parser.ts +47 -0
- package/src/openai/provider-definition.test.ts +75 -0
- package/src/openai/provider-definition.ts +132 -0
- package/src/openai/provider.test.ts +1402 -0
- package/src/openai/provider.ts +237 -0
- package/src/openai/responses-chat.ts +258 -0
- package/src/openai/responses-converter.ts +112 -0
- package/src/openai/responses-parser.ts +285 -0
- package/src/openai/responses-stream-utils.ts +45 -0
- package/src/openai/responses-types.ts +195 -0
- package/src/openai/streaming/stream-assembler.ts +3 -0
- package/src/openai/streaming/stream-handler.test.ts +367 -0
- package/src/openai/streaming/stream-handler.ts +119 -0
- package/src/openai/types/api-types.ts +112 -0
- package/src/openai/types.ts +194 -0
- package/src/qwen/defaults.ts +26 -0
- package/src/qwen/index.ts +5 -0
- package/src/qwen/model-catalog-refresh.test.ts +91 -0
- package/src/qwen/model-catalog-refresh.ts +97 -0
- package/src/qwen/provider-capabilities.ts +34 -0
- package/src/qwen/provider-definition.test.ts +139 -0
- package/src/qwen/provider-definition.ts +173 -0
- package/src/qwen/provider-streaming-assembly.ts +40 -0
- package/src/qwen/provider.test.ts +640 -0
- package/src/qwen/provider.ts +293 -0
- package/src/qwen/responses-chat.ts +194 -0
- package/src/qwen/responses-converter.ts +104 -0
- package/src/qwen/responses-parser.ts +299 -0
- package/src/qwen/responses-stream-utils.ts +38 -0
- package/src/qwen/types.ts +228 -0
- package/src/shared/openai-compatible/endpoint-probe.test.ts +52 -0
- package/src/shared/openai-compatible/endpoint-probe.ts +43 -0
- package/src/shared/openai-compatible/index.ts +6 -0
- package/src/shared/openai-compatible/message-converter.test.ts +111 -0
- package/src/shared/openai-compatible/message-converter.ts +84 -0
- package/src/shared/openai-compatible/native-payload-observer.test.ts +43 -0
- package/src/shared/openai-compatible/native-payload-observer.ts +26 -0
- package/src/shared/openai-compatible/response-parser.test.ts +172 -0
- package/src/shared/openai-compatible/response-parser.ts +180 -0
- package/src/shared/openai-compatible/stream-assembler.test.ts +266 -0
- package/src/shared/openai-compatible/stream-assembler.ts +248 -0
- package/src/shared/openai-compatible/types.ts +59 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { GemmaToolCallProjector, projectGemmaToolCallText } from './index';
|
|
3
|
+
|
|
4
|
+
describe('Gemma native tool-call projection', () => {
|
|
5
|
+
it('projects documented LM Studio/Gemma tool-call blocks for declared tools', () => {
|
|
6
|
+
const result = projectGemmaToolCallText(
|
|
7
|
+
'before<|tool_call>call:DeclaredTool{prompt:<|"|>analyze<|"|>,background:true}<tool_call|>after',
|
|
8
|
+
{ toolNames: ['DeclaredTool'] },
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
expect(result.visibleText).toBe('beforeafter');
|
|
12
|
+
expect(result.removedToolCallText).toBe(true);
|
|
13
|
+
expect(result.rawToolCallText).toBe(
|
|
14
|
+
'<|tool_call>call:DeclaredTool{prompt:<|"|>analyze<|"|>,background:true}<tool_call|>',
|
|
15
|
+
);
|
|
16
|
+
expect(result.toolCalls).toEqual([
|
|
17
|
+
{
|
|
18
|
+
id: 'gemma_call_0',
|
|
19
|
+
type: 'function',
|
|
20
|
+
function: {
|
|
21
|
+
name: 'DeclaredTool',
|
|
22
|
+
arguments: '{"prompt":"analyze","background":true}',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
]);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('keeps undeclared tool-call blocks visible and non-executable', () => {
|
|
29
|
+
const raw = '<|tool_call>call:OtherTool{prompt:<|"|>analyze<|"|>}<tool_call|>';
|
|
30
|
+
const result = projectGemmaToolCallText(raw, { toolNames: ['DeclaredTool'] });
|
|
31
|
+
|
|
32
|
+
expect(result.visibleText).toBe(raw);
|
|
33
|
+
expect(result.toolCalls).toEqual([]);
|
|
34
|
+
expect(result.removedToolCallText).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('holds split streamed tool-call blocks until complete', () => {
|
|
38
|
+
const projector = new GemmaToolCallProjector({ toolNames: ['DeclaredTool'] });
|
|
39
|
+
|
|
40
|
+
expect(projector.project('<|tool_call>call:Declar')).toMatchObject({
|
|
41
|
+
visibleText: '',
|
|
42
|
+
toolCalls: [],
|
|
43
|
+
removedToolCallText: false,
|
|
44
|
+
});
|
|
45
|
+
const result = projector.project('edTool{prompt:<|"|>analyze<|"|>}<tool_call|>visible');
|
|
46
|
+
|
|
47
|
+
expect(result.visibleText).toBe('visible');
|
|
48
|
+
expect(result.removedToolCallText).toBe(true);
|
|
49
|
+
expect(result.toolCalls).toEqual([
|
|
50
|
+
{
|
|
51
|
+
id: 'gemma_call_0',
|
|
52
|
+
type: 'function',
|
|
53
|
+
function: {
|
|
54
|
+
name: 'DeclaredTool',
|
|
55
|
+
arguments: '{"prompt":"analyze"}',
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
]);
|
|
59
|
+
expect(projector.flush().visibleText).toBe('');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('does not re-emit raw tool-call metadata after the block was projected', () => {
|
|
63
|
+
const projector = new GemmaToolCallProjector({ toolNames: ['DeclaredTool'] });
|
|
64
|
+
const first = projector.project(
|
|
65
|
+
'<|tool_call>call:DeclaredTool{prompt:<|"|>analyze<|"|>}<tool_call|>',
|
|
66
|
+
);
|
|
67
|
+
const second = projector.project('visible');
|
|
68
|
+
|
|
69
|
+
expect(first.rawToolCallText).toBe(
|
|
70
|
+
'<|tool_call>call:DeclaredTool{prompt:<|"|>analyze<|"|>}<tool_call|>',
|
|
71
|
+
);
|
|
72
|
+
expect(second.visibleText).toBe('visible');
|
|
73
|
+
expect(second.rawToolCallText).toBeUndefined();
|
|
74
|
+
expect(second.removedToolCallText).toBe(false);
|
|
75
|
+
expect(second.toolCalls).toEqual([]);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('preserves malformed complete blocks instead of guessing execution', () => {
|
|
79
|
+
const raw = '<|tool_call>call:DeclaredTool prompt:<|"|>missing braces<|"|><tool_call|>';
|
|
80
|
+
const result = projectGemmaToolCallText(raw, { toolNames: ['DeclaredTool'] });
|
|
81
|
+
|
|
82
|
+
expect(result.visibleText).toBe(raw);
|
|
83
|
+
expect(result.toolCalls).toEqual([]);
|
|
84
|
+
expect(result.removedToolCallText).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('projects XML-like tags whose names match declared tools', () => {
|
|
88
|
+
const result = projectGemmaToolCallText(
|
|
89
|
+
'before<DeclaredTool prompt="analyze backlog" mode="plan" background="true" />after',
|
|
90
|
+
{ toolNames: ['DeclaredTool'] },
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
expect(result.visibleText).toBe('beforeafter');
|
|
94
|
+
expect(result.removedToolCallText).toBe(true);
|
|
95
|
+
expect(result.rawToolCallText).toBe(
|
|
96
|
+
'<DeclaredTool prompt="analyze backlog" mode="plan" background="true" />',
|
|
97
|
+
);
|
|
98
|
+
expect(result.toolCalls).toEqual([
|
|
99
|
+
{
|
|
100
|
+
id: 'gemma_call_0',
|
|
101
|
+
type: 'function',
|
|
102
|
+
function: {
|
|
103
|
+
name: 'DeclaredTool',
|
|
104
|
+
arguments: '{"prompt":"analyze backlog","mode":"plan","background":true}',
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
]);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('strips XML wrapper tags while projecting nested declared tool tags', () => {
|
|
111
|
+
const result = projectGemmaToolCallText(
|
|
112
|
+
[
|
|
113
|
+
'<tool-launch-sequence>internal planning text</tool-launch-sequence>',
|
|
114
|
+
'<tool-call-1>',
|
|
115
|
+
'<DeclaredTool prompt="implementation analysis" mode="implementation" />',
|
|
116
|
+
'</tool-call-1>',
|
|
117
|
+
'visible',
|
|
118
|
+
].join(''),
|
|
119
|
+
{ toolNames: ['DeclaredTool'] },
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
expect(result.visibleText).toBe('visible');
|
|
123
|
+
expect(result.rawToolCallText).toContain(
|
|
124
|
+
'<DeclaredTool prompt="implementation analysis" mode="implementation" />',
|
|
125
|
+
);
|
|
126
|
+
expect(result.toolCalls).toEqual([
|
|
127
|
+
{
|
|
128
|
+
id: 'gemma_call_0',
|
|
129
|
+
type: 'function',
|
|
130
|
+
function: {
|
|
131
|
+
name: 'DeclaredTool',
|
|
132
|
+
arguments: '{"prompt":"implementation analysis","mode":"implementation"}',
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
]);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('projects JSON command envelopes for declared tools', () => {
|
|
139
|
+
const result = projectGemmaToolCallText(
|
|
140
|
+
[
|
|
141
|
+
'<tool-call-1>',
|
|
142
|
+
'{"command":"DeclaredTool","args":{"prompt":"analyze implementation","mode":"worker","background":true}}',
|
|
143
|
+
'</tool-call-1>',
|
|
144
|
+
].join(''),
|
|
145
|
+
{ toolNames: ['DeclaredTool'] },
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
expect(result.visibleText).toBe('');
|
|
149
|
+
expect(result.toolCalls).toEqual([
|
|
150
|
+
{
|
|
151
|
+
id: 'gemma_call_0',
|
|
152
|
+
type: 'function',
|
|
153
|
+
function: {
|
|
154
|
+
name: 'DeclaredTool',
|
|
155
|
+
arguments: '{"prompt":"analyze implementation","mode":"worker","background":true}',
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
]);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('does not infer tool calls from wrapper text or command-like DSL', () => {
|
|
162
|
+
const result = projectGemmaToolCallText(
|
|
163
|
+
[
|
|
164
|
+
'<tool-launch>',
|
|
165
|
+
'parallel',
|
|
166
|
+
' worker=DeclaredTool:"Analyze implementation details."',
|
|
167
|
+
' reviewer=DeclaredTool:"Analyze architecture boundaries."',
|
|
168
|
+
'</tool-launch>',
|
|
169
|
+
].join('\n'),
|
|
170
|
+
{ toolNames: ['DeclaredTool'] },
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
expect(result.visibleText).toBe('');
|
|
174
|
+
expect(result.toolCalls).toEqual([]);
|
|
175
|
+
expect(result.removedToolCallText).toBe(true);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('does not infer slash-command text inside XML wrappers as tool calls', () => {
|
|
179
|
+
const result = projectGemmaToolCallText(
|
|
180
|
+
[
|
|
181
|
+
'<tool-launch>',
|
|
182
|
+
'/tool prompt:"Analyze concurrency." mode:"worker"',
|
|
183
|
+
'/tool prompt:"Analyze UI flow." mode:"reviewer" background:true',
|
|
184
|
+
'</tool-launch>',
|
|
185
|
+
].join('\n'),
|
|
186
|
+
{ toolNames: ['DeclaredTool'] },
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
expect(result.visibleText).toBe('');
|
|
190
|
+
expect(result.toolCalls).toEqual([]);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('strips generic XML wrapper tags without provider-specific tag names', () => {
|
|
194
|
+
const result = projectGemmaToolCallText(
|
|
195
|
+
'<execution-step>The task will be analyzed.</execution-step>visible',
|
|
196
|
+
{ toolNames: ['DeclaredTool'] },
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
expect(result.visibleText).toBe('visible');
|
|
200
|
+
expect(result.toolCalls).toEqual([]);
|
|
201
|
+
expect(result.removedToolCallText).toBe(true);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('holds split streamed XML-like declared tool tags until complete', () => {
|
|
205
|
+
const projector = new GemmaToolCallProjector({ toolNames: ['DeclaredTool'] });
|
|
206
|
+
|
|
207
|
+
expect(projector.project('<DeclaredTool prompt="analyze')).toMatchObject({
|
|
208
|
+
visibleText: '',
|
|
209
|
+
toolCalls: [],
|
|
210
|
+
removedToolCallText: false,
|
|
211
|
+
});
|
|
212
|
+
const result = projector.project(' backlog" mode="plan" />done');
|
|
213
|
+
|
|
214
|
+
expect(result.visibleText).toBe('done');
|
|
215
|
+
expect(result.toolCalls).toEqual([
|
|
216
|
+
{
|
|
217
|
+
id: 'gemma_call_0',
|
|
218
|
+
type: 'function',
|
|
219
|
+
function: {
|
|
220
|
+
name: 'DeclaredTool',
|
|
221
|
+
arguments: '{"prompt":"analyze backlog","mode":"plan"}',
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
]);
|
|
225
|
+
expect(projector.flush().visibleText).toBe('');
|
|
226
|
+
});
|
|
227
|
+
});
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import type { IToolCall, IToolSchema } from '@robota-sdk/agent-core';
|
|
2
|
+
import type {
|
|
3
|
+
IOpenAICompatibleToolCallTextProjection,
|
|
4
|
+
IOpenAICompatibleToolCallTextProjector,
|
|
5
|
+
} from '../shared/openai-compatible/index.js';
|
|
6
|
+
import { GemmaArgumentParser } from './tool-call-argument-parser';
|
|
7
|
+
import { projectGemmaPseudoToolCallText } from './pseudo-tool-call-projector';
|
|
8
|
+
|
|
9
|
+
const TOOL_CALL_START = '<|tool_call>';
|
|
10
|
+
const TOOL_CALL_END = '<tool_call|>';
|
|
11
|
+
const CALL_PREFIX = 'call:';
|
|
12
|
+
const DEFAULT_CALL_ID_PREFIX = 'gemma_call';
|
|
13
|
+
|
|
14
|
+
export interface IGemmaToolCallProjectorOptions {
|
|
15
|
+
toolNames: readonly string[];
|
|
16
|
+
callIdPrefix?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface IGemmaToolCallProjection extends IOpenAICompatibleToolCallTextProjection {
|
|
20
|
+
rawText: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface IProjectionOptions {
|
|
24
|
+
final: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface IProjectionState {
|
|
28
|
+
visibleParts: string[];
|
|
29
|
+
toolCalls: IToolCall[];
|
|
30
|
+
rawToolCallTextParts: string[];
|
|
31
|
+
removedToolCallText: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function createGemmaToolCallProjector(
|
|
35
|
+
tools: readonly IToolSchema[] | undefined,
|
|
36
|
+
): GemmaToolCallProjector | undefined {
|
|
37
|
+
if (!tools || tools.length === 0) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
return new GemmaToolCallProjector({ toolNames: tools.map((tool) => tool.name) });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function projectGemmaToolCallText(
|
|
44
|
+
rawText: string,
|
|
45
|
+
options: IGemmaToolCallProjectorOptions,
|
|
46
|
+
): IGemmaToolCallProjection {
|
|
47
|
+
const result = projectText(rawText, options, { final: true });
|
|
48
|
+
return toPublicProjection(rawText, result);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export class GemmaToolCallProjector implements IOpenAICompatibleToolCallTextProjector {
|
|
52
|
+
private buffer = '';
|
|
53
|
+
private emittedVisibleText = '';
|
|
54
|
+
private emittedRawToolCallText = '';
|
|
55
|
+
private readonly emittedToolCallIds = new Set<string>();
|
|
56
|
+
|
|
57
|
+
constructor(private readonly options: IGemmaToolCallProjectorOptions) {}
|
|
58
|
+
|
|
59
|
+
project(delta: string): IOpenAICompatibleToolCallTextProjection {
|
|
60
|
+
if (delta.length === 0) {
|
|
61
|
+
return emptyProjection();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
this.buffer += delta;
|
|
65
|
+
return this.projectVisibleText({ final: false });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
flush(): IOpenAICompatibleToolCallTextProjection {
|
|
69
|
+
return this.projectVisibleText({ final: true });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private projectVisibleText(options: IProjectionOptions): IOpenAICompatibleToolCallTextProjection {
|
|
73
|
+
const result = projectText(this.buffer, this.options, options);
|
|
74
|
+
const nextVisibleText = result.visibleParts.join('');
|
|
75
|
+
const visibleText = nextVisibleText.slice(this.emittedVisibleText.length);
|
|
76
|
+
const nextRawToolCallText = result.rawToolCallTextParts.join('');
|
|
77
|
+
const rawToolCallText = nextRawToolCallText.slice(this.emittedRawToolCallText.length);
|
|
78
|
+
this.emittedVisibleText = nextVisibleText;
|
|
79
|
+
this.emittedRawToolCallText = nextRawToolCallText;
|
|
80
|
+
|
|
81
|
+
const toolCalls = result.toolCalls.filter((toolCall) => {
|
|
82
|
+
if (this.emittedToolCallIds.has(toolCall.id)) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
this.emittedToolCallIds.add(toolCall.id);
|
|
86
|
+
return true;
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
visibleText,
|
|
91
|
+
toolCalls,
|
|
92
|
+
removedToolCallText: rawToolCallText.length > 0,
|
|
93
|
+
...(rawToolCallText.length > 0 && {
|
|
94
|
+
rawToolCallText,
|
|
95
|
+
}),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function projectText(
|
|
101
|
+
rawText: string,
|
|
102
|
+
options: IGemmaToolCallProjectorOptions,
|
|
103
|
+
projectionOptions: IProjectionOptions,
|
|
104
|
+
): IProjectionState {
|
|
105
|
+
const nativeProjection = projectNativeToolCallText(rawText, options, projectionOptions);
|
|
106
|
+
const pseudoProjection = projectGemmaPseudoToolCallText(
|
|
107
|
+
nativeProjection.visibleParts.join(''),
|
|
108
|
+
{
|
|
109
|
+
toolNames: options.toolNames,
|
|
110
|
+
callIdPrefix: options.callIdPrefix ?? DEFAULT_CALL_ID_PREFIX,
|
|
111
|
+
startCallIndex: nativeProjection.toolCalls.length,
|
|
112
|
+
},
|
|
113
|
+
projectionOptions,
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
visibleParts: [pseudoProjection.visibleText],
|
|
118
|
+
toolCalls: [...nativeProjection.toolCalls, ...pseudoProjection.toolCalls],
|
|
119
|
+
rawToolCallTextParts: [
|
|
120
|
+
...nativeProjection.rawToolCallTextParts,
|
|
121
|
+
...pseudoProjection.rawToolCallTextParts,
|
|
122
|
+
],
|
|
123
|
+
removedToolCallText:
|
|
124
|
+
nativeProjection.removedToolCallText || pseudoProjection.removedToolCallText,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function projectNativeToolCallText(
|
|
129
|
+
rawText: string,
|
|
130
|
+
options: IGemmaToolCallProjectorOptions,
|
|
131
|
+
projectionOptions: IProjectionOptions,
|
|
132
|
+
): IProjectionState {
|
|
133
|
+
const state = createProjectionState();
|
|
134
|
+
const toolNames = new Set(options.toolNames);
|
|
135
|
+
let cursor = 0;
|
|
136
|
+
let projectedCallIndex = 0;
|
|
137
|
+
|
|
138
|
+
while (cursor < rawText.length) {
|
|
139
|
+
const nextMarker = rawText.indexOf(TOOL_CALL_START, cursor);
|
|
140
|
+
if (nextMarker === -1) {
|
|
141
|
+
appendVisibleTail(state, rawText.slice(cursor), projectionOptions);
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
appendVisibleTail(state, rawText.slice(cursor, nextMarker), projectionOptions);
|
|
146
|
+
const afterStart = nextMarker + TOOL_CALL_START.length;
|
|
147
|
+
const markerEnd = rawText.indexOf(TOOL_CALL_END, afterStart);
|
|
148
|
+
if (markerEnd === -1) {
|
|
149
|
+
if (projectionOptions.final) {
|
|
150
|
+
state.visibleParts.push(rawText.slice(nextMarker));
|
|
151
|
+
}
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const rawBlock = rawText.slice(nextMarker, markerEnd + TOOL_CALL_END.length);
|
|
156
|
+
const blockText = rawText.slice(afterStart, markerEnd);
|
|
157
|
+
const toolCall = parseToolCallBlock(blockText, toolNames, options, projectedCallIndex);
|
|
158
|
+
if (!toolCall) {
|
|
159
|
+
state.visibleParts.push(rawBlock);
|
|
160
|
+
} else {
|
|
161
|
+
state.toolCalls.push(toolCall);
|
|
162
|
+
state.rawToolCallTextParts.push(rawBlock);
|
|
163
|
+
state.removedToolCallText = true;
|
|
164
|
+
projectedCallIndex += 1;
|
|
165
|
+
}
|
|
166
|
+
cursor = markerEnd + TOOL_CALL_END.length;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return state;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function parseToolCallBlock(
|
|
173
|
+
blockText: string,
|
|
174
|
+
toolNames: ReadonlySet<string>,
|
|
175
|
+
options: IGemmaToolCallProjectorOptions,
|
|
176
|
+
projectedCallIndex: number,
|
|
177
|
+
): IToolCall | undefined {
|
|
178
|
+
const trimmed = blockText.trim();
|
|
179
|
+
if (!trimmed.startsWith(CALL_PREFIX)) {
|
|
180
|
+
return undefined;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const callText = trimmed.slice(CALL_PREFIX.length).trimStart();
|
|
184
|
+
const argsStart = callText.indexOf('{');
|
|
185
|
+
if (argsStart <= 0) {
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const toolName = callText.slice(0, argsStart).trim();
|
|
190
|
+
if (!toolNames.has(toolName)) {
|
|
191
|
+
return undefined;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const args = new GemmaArgumentParser(callText.slice(argsStart)).parse();
|
|
195
|
+
if (!args) {
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
id: `${options.callIdPrefix ?? DEFAULT_CALL_ID_PREFIX}_${projectedCallIndex}`,
|
|
201
|
+
type: 'function',
|
|
202
|
+
function: {
|
|
203
|
+
name: toolName,
|
|
204
|
+
arguments: JSON.stringify(args),
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function createProjectionState(): IProjectionState {
|
|
210
|
+
return {
|
|
211
|
+
visibleParts: [],
|
|
212
|
+
toolCalls: [],
|
|
213
|
+
rawToolCallTextParts: [],
|
|
214
|
+
removedToolCallText: false,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function appendVisibleTail(
|
|
219
|
+
state: IProjectionState,
|
|
220
|
+
tail: string,
|
|
221
|
+
options: IProjectionOptions,
|
|
222
|
+
): void {
|
|
223
|
+
if (tail.length === 0) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (options.final) {
|
|
228
|
+
state.visibleParts.push(tail);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const heldLength = longestStartMarkerPrefixSuffixLength(tail);
|
|
233
|
+
state.visibleParts.push(tail.slice(0, tail.length - heldLength));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function longestStartMarkerPrefixSuffixLength(text: string): number {
|
|
237
|
+
const maxLength = Math.min(text.length, TOOL_CALL_START.length - 1);
|
|
238
|
+
for (let length = maxLength; length > 0; length -= 1) {
|
|
239
|
+
if (TOOL_CALL_START.startsWith(text.slice(text.length - length))) {
|
|
240
|
+
return length;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return 0;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function toPublicProjection(rawText: string, state: IProjectionState): IGemmaToolCallProjection {
|
|
247
|
+
return {
|
|
248
|
+
rawText,
|
|
249
|
+
visibleText: state.visibleParts.join(''),
|
|
250
|
+
toolCalls: state.toolCalls,
|
|
251
|
+
removedToolCallText: state.removedToolCallText,
|
|
252
|
+
...(state.rawToolCallTextParts.length > 0 && {
|
|
253
|
+
rawToolCallText: state.rawToolCallTextParts.join(''),
|
|
254
|
+
}),
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function emptyProjection(): IOpenAICompatibleToolCallTextProjection {
|
|
259
|
+
return {
|
|
260
|
+
visibleText: '',
|
|
261
|
+
toolCalls: [],
|
|
262
|
+
removedToolCallText: false,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type OpenAI from 'openai';
|
|
2
|
+
import type { IExecutor, ILogger, TProviderOptionValueBase } from '@robota-sdk/agent-core';
|
|
3
|
+
|
|
4
|
+
export type TGemmaProviderOptionValue =
|
|
5
|
+
| string
|
|
6
|
+
| number
|
|
7
|
+
| boolean
|
|
8
|
+
| undefined
|
|
9
|
+
| null
|
|
10
|
+
| OpenAI
|
|
11
|
+
| ILogger
|
|
12
|
+
| IExecutor
|
|
13
|
+
| TProviderOptionValueBase
|
|
14
|
+
| TGemmaProviderOptionValue[]
|
|
15
|
+
| { [key: string]: TGemmaProviderOptionValue };
|
|
16
|
+
|
|
17
|
+
export interface IGemmaProviderOptions {
|
|
18
|
+
[key: string]: TGemmaProviderOptionValue;
|
|
19
|
+
|
|
20
|
+
apiKey?: string;
|
|
21
|
+
baseURL?: string;
|
|
22
|
+
timeout?: number;
|
|
23
|
+
defaultModel?: string;
|
|
24
|
+
client?: OpenAI;
|
|
25
|
+
executor?: IExecutor;
|
|
26
|
+
logger?: ILogger;
|
|
27
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @robota-sdk/agent-provider-google compatibility package.
|
|
3
|
+
*
|
|
4
|
+
* New code should import from @robota-sdk/agent-provider/gemini.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { GeminiProvider } from '../gemini/index.js';
|
|
8
|
+
export type { IGeminiProviderOptions, TGeminiProviderOptionValue } from '../gemini/index.js';
|
|
9
|
+
export * from './provider.js';
|
|
10
|
+
export * from './provider-definition.js';
|
|
11
|
+
export * from './types.js';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { GeminiProvider, createGeminiProviderDefinition } from '../gemini/index.js';
|
|
3
|
+
import { GoogleProvider } from './provider.js';
|
|
4
|
+
|
|
5
|
+
describe('Google provider compatibility package', () => {
|
|
6
|
+
it('keeps GoogleProvider as a compatibility subclass of GeminiProvider', () => {
|
|
7
|
+
const provider = new GoogleProvider({ apiKey: 'test-key' });
|
|
8
|
+
|
|
9
|
+
expect(provider).toBeInstanceOf(GeminiProvider);
|
|
10
|
+
expect(provider.name).toBe('google');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('re-exports the canonical Gemini provider definition metadata', () => {
|
|
14
|
+
const definition = createGeminiProviderDefinition();
|
|
15
|
+
|
|
16
|
+
expect(definition.type).toBe('gemini');
|
|
17
|
+
expect(definition.aliases).toContain('google');
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { GeminiProvider } from '../gemini/index.js';
|
|
2
|
+
import type { IGoogleProviderOptions } from './types.js';
|
|
3
|
+
|
|
4
|
+
export class GoogleProvider extends GeminiProvider {
|
|
5
|
+
override readonly name = 'google';
|
|
6
|
+
|
|
7
|
+
constructor(options: IGoogleProviderOptions) {
|
|
8
|
+
super(options);
|
|
9
|
+
}
|
|
10
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './anthropic/index.js';
|
|
2
|
+
export * from './openai/index.js';
|
|
3
|
+
export * from './deepseek/index.js';
|
|
4
|
+
export * from './gemini/index.js';
|
|
5
|
+
export * from './gemma/index.js';
|
|
6
|
+
export * from './bytedance/index.js';
|
|
7
|
+
export * from './qwen/index.js';
|
|
8
|
+
// google/ omitted: deprecated compatibility alias for gemini, available via sub-path only
|
|
9
|
+
// import { GoogleProvider } from '@robota-sdk/agent-provider/google';
|