btca-server 1.0.962 → 2.0.0
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/package.json +3 -3
- package/src/agent/agent.test.ts +31 -24
- package/src/agent/index.ts +8 -2
- package/src/agent/loop.ts +303 -346
- package/src/agent/service.ts +252 -233
- package/src/agent/types.ts +2 -2
- package/src/collections/index.ts +2 -1
- package/src/collections/service.ts +352 -345
- package/src/config/config.test.ts +3 -1
- package/src/config/index.ts +615 -727
- package/src/config/remote.ts +214 -369
- package/src/context/index.ts +6 -12
- package/src/context/transaction.ts +23 -30
- package/src/effect/errors.ts +45 -0
- package/src/effect/layers.ts +26 -0
- package/src/effect/runtime.ts +19 -0
- package/src/effect/services.ts +154 -0
- package/src/index.ts +291 -369
- package/src/metrics/index.ts +46 -46
- package/src/pricing/models-dev.ts +104 -106
- package/src/providers/auth.ts +159 -200
- package/src/providers/index.ts +19 -2
- package/src/providers/model.ts +115 -135
- package/src/providers/openai.ts +3 -3
- package/src/resources/impls/git.ts +123 -146
- package/src/resources/impls/npm.test.ts +16 -5
- package/src/resources/impls/npm.ts +66 -75
- package/src/resources/index.ts +6 -1
- package/src/resources/schema.ts +7 -6
- package/src/resources/service.test.ts +13 -12
- package/src/resources/service.ts +153 -112
- package/src/stream/index.ts +1 -1
- package/src/stream/service.test.ts +5 -5
- package/src/stream/service.ts +282 -293
- package/src/tools/glob.ts +126 -141
- package/src/tools/grep.ts +205 -210
- package/src/tools/index.ts +8 -4
- package/src/tools/list.ts +118 -140
- package/src/tools/read.ts +209 -235
- package/src/tools/virtual-sandbox.ts +91 -83
- package/src/validation/index.ts +18 -22
- package/src/vfs/virtual-fs.test.ts +37 -25
- package/src/vfs/virtual-fs.ts +218 -216
package/src/agent/loop.ts
CHANGED
|
@@ -4,358 +4,315 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { streamText, tool, stepCountIs, type ModelMessage } from 'ai';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { getModel } from '../providers/index.ts';
|
|
8
8
|
import type { ProviderOptions } from '../providers/registry.ts';
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
].
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
9
|
+
import type {
|
|
10
|
+
ReadToolParametersType,
|
|
11
|
+
GrepToolParametersType,
|
|
12
|
+
GlobToolParametersType,
|
|
13
|
+
ListToolParametersType
|
|
14
|
+
} from '../tools/index.ts';
|
|
15
|
+
import {
|
|
16
|
+
ReadToolParameters,
|
|
17
|
+
executeReadTool,
|
|
18
|
+
GrepToolParameters,
|
|
19
|
+
executeGrepTool,
|
|
20
|
+
GlobToolParameters,
|
|
21
|
+
executeGlobTool,
|
|
22
|
+
ListToolParameters,
|
|
23
|
+
executeListTool
|
|
24
|
+
} from '../tools/index.ts';
|
|
25
|
+
|
|
26
|
+
export type AgentEvent =
|
|
27
|
+
| { type: 'text-delta'; text: string }
|
|
28
|
+
| { type: 'reasoning-delta'; text: string }
|
|
29
|
+
| { type: 'tool-call'; toolName: string; input: unknown }
|
|
30
|
+
| { type: 'tool-result'; toolName: string; output: string }
|
|
31
|
+
| {
|
|
32
|
+
type: 'finish';
|
|
33
|
+
finishReason: string;
|
|
34
|
+
usage?: {
|
|
35
|
+
inputTokens?: number;
|
|
36
|
+
outputTokens?: number;
|
|
37
|
+
reasoningTokens?: number;
|
|
38
|
+
totalTokens?: number;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
| { type: 'error'; error: Error };
|
|
42
|
+
|
|
43
|
+
export type AgentLoopOptions = {
|
|
44
|
+
providerId: string;
|
|
45
|
+
modelId: string;
|
|
46
|
+
collectionPath: string;
|
|
47
|
+
vfsId?: string;
|
|
48
|
+
agentInstructions: string;
|
|
49
|
+
question: string;
|
|
50
|
+
maxSteps?: number;
|
|
51
|
+
providerOptions?: Partial<ProviderOptions>;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export type AgentLoopResult = {
|
|
55
|
+
answer: string;
|
|
56
|
+
model: { provider: string; model: string };
|
|
57
|
+
events: AgentEvent[];
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const buildSystemPrompt = (agentInstructions: string): string =>
|
|
61
|
+
[
|
|
62
|
+
'You are btca, an expert documentation search agent.',
|
|
63
|
+
'Your job is to answer questions by searching through the collection of resources.',
|
|
64
|
+
'',
|
|
65
|
+
'You have access to the following tools:',
|
|
66
|
+
'- read: Read file contents with line numbers',
|
|
67
|
+
'- grep: Search file contents using regex patterns',
|
|
68
|
+
'- glob: Find files matching glob patterns',
|
|
69
|
+
'- list: List directory contents',
|
|
70
|
+
'',
|
|
71
|
+
'Guidelines:',
|
|
72
|
+
'- Ground answers in the loaded resources. Do not rely on unstated prior knowledge.',
|
|
73
|
+
'- Search efficiently: start with one focused list/glob pass, then read likely files; only expand search when evidence is insufficient.',
|
|
74
|
+
'- Prefer targeted grep/read over broad repeated scans once candidate files are known.',
|
|
75
|
+
'- Give clear, unambiguous answers. State assumptions, prerequisites, and important version-sensitive caveats.',
|
|
76
|
+
'- For implementation/how-to questions, provide complete step-by-step instructions with commands and code snippets.',
|
|
77
|
+
'- Be concise but thorough in your responses.',
|
|
78
|
+
'- End every answer with a "Sources" section.',
|
|
79
|
+
'- For git resources, source links must be full GitHub blob URLs.',
|
|
80
|
+
'- In "Sources", format git citations as markdown links: "- [repo/relative/path.ext](https://github.com/.../blob/.../repo/relative/path.ext)".',
|
|
81
|
+
'- Do not use raw URLs as link labels.',
|
|
82
|
+
'- Do not repeat a URL in parentheses after a link.',
|
|
83
|
+
'- Do not output sources in "url (url)" format.',
|
|
84
|
+
'- For local resources, cite local file paths (no GitHub URL required).',
|
|
85
|
+
'- If you cannot find the answer, say so clearly',
|
|
86
|
+
'',
|
|
87
|
+
agentInstructions
|
|
88
|
+
].join('\n');
|
|
89
|
+
|
|
90
|
+
const createTools = (basePath: string, vfsId?: string) => ({
|
|
91
|
+
read: tool({
|
|
92
|
+
description: 'Read the contents of a file. Returns the file contents with line numbers.',
|
|
93
|
+
inputSchema: ReadToolParameters,
|
|
94
|
+
execute: async (params: ReadToolParametersType) => {
|
|
95
|
+
const result = await executeReadTool(params, { basePath, vfsId });
|
|
96
|
+
return result.output;
|
|
97
|
+
}
|
|
98
|
+
}),
|
|
99
|
+
|
|
100
|
+
grep: tool({
|
|
101
|
+
description:
|
|
102
|
+
'Search for a regex pattern in file contents. Returns matching lines with file paths and line numbers.',
|
|
103
|
+
inputSchema: GrepToolParameters,
|
|
104
|
+
execute: async (params: GrepToolParametersType) => {
|
|
105
|
+
const result = await executeGrepTool(params, { basePath, vfsId });
|
|
106
|
+
return result.output;
|
|
107
|
+
}
|
|
108
|
+
}),
|
|
109
|
+
|
|
110
|
+
glob: tool({
|
|
111
|
+
description:
|
|
112
|
+
'Find files matching a glob pattern (e.g. "**/*.ts", "src/**/*.js"). Returns a list of matching file paths sorted by modification time.',
|
|
113
|
+
inputSchema: GlobToolParameters,
|
|
114
|
+
execute: async (params: GlobToolParametersType) => {
|
|
115
|
+
const result = await executeGlobTool(params, { basePath, vfsId });
|
|
116
|
+
return result.output;
|
|
117
|
+
}
|
|
118
|
+
}),
|
|
119
|
+
|
|
120
|
+
list: tool({
|
|
121
|
+
description:
|
|
122
|
+
'List the contents of a directory. Returns files and subdirectories with their types.',
|
|
123
|
+
inputSchema: ListToolParameters,
|
|
124
|
+
execute: async (params: ListToolParametersType) => {
|
|
125
|
+
const result = await executeListTool(params, { basePath, vfsId });
|
|
126
|
+
return result.output;
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const getInitialContext = async (collectionPath: string, vfsId?: string) => {
|
|
132
|
+
const result = await executeListTool({ path: '.' }, { basePath: collectionPath, vfsId });
|
|
133
|
+
return `Collection contents:\n${result.output}`;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export const runAgentLoop = async (options: AgentLoopOptions): Promise<AgentLoopResult> => {
|
|
137
|
+
const {
|
|
138
|
+
providerId,
|
|
139
|
+
modelId,
|
|
140
|
+
collectionPath,
|
|
141
|
+
vfsId,
|
|
142
|
+
agentInstructions,
|
|
143
|
+
question,
|
|
144
|
+
maxSteps = 40
|
|
145
|
+
} = options;
|
|
146
|
+
|
|
147
|
+
const systemPrompt = buildSystemPrompt(agentInstructions);
|
|
148
|
+
const sessionId = crypto.randomUUID();
|
|
149
|
+
|
|
150
|
+
const mergedProviderOptions =
|
|
151
|
+
providerId === 'openai'
|
|
152
|
+
? { ...options.providerOptions, instructions: systemPrompt, sessionId }
|
|
153
|
+
: options.providerOptions;
|
|
154
|
+
|
|
155
|
+
const model = await getModel(providerId, modelId, {
|
|
156
|
+
providerOptions: mergedProviderOptions,
|
|
157
|
+
allowMissingAuth: providerId === 'openai-compat'
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const initialContext = await getInitialContext(collectionPath, vfsId);
|
|
161
|
+
const messages: ModelMessage[] = [
|
|
162
|
+
{
|
|
163
|
+
role: 'user',
|
|
164
|
+
content: `${initialContext}\n\nQuestion: ${question}`
|
|
165
|
+
}
|
|
166
|
+
];
|
|
167
|
+
|
|
168
|
+
const tools = createTools(collectionPath, vfsId);
|
|
169
|
+
const events: AgentEvent[] = [];
|
|
170
|
+
let fullText = '';
|
|
171
|
+
|
|
172
|
+
const result = streamText({
|
|
173
|
+
model,
|
|
174
|
+
system: systemPrompt,
|
|
175
|
+
messages,
|
|
176
|
+
tools,
|
|
177
|
+
providerOptions:
|
|
155
178
|
providerId === 'openai'
|
|
156
|
-
? {
|
|
157
|
-
:
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
fullText += part.text;
|
|
201
|
-
events.push({ type: 'text-delta', text: part.text });
|
|
202
|
-
break;
|
|
203
|
-
|
|
204
|
-
case 'reasoning-delta':
|
|
205
|
-
events.push({ type: 'reasoning-delta', text: part.text });
|
|
206
|
-
break;
|
|
207
|
-
|
|
208
|
-
case 'tool-call':
|
|
209
|
-
events.push({
|
|
210
|
-
type: 'tool-call',
|
|
211
|
-
toolName: part.toolName,
|
|
212
|
-
input: part.input
|
|
213
|
-
});
|
|
214
|
-
break;
|
|
215
|
-
|
|
216
|
-
case 'tool-result':
|
|
217
|
-
events.push({
|
|
218
|
-
type: 'tool-result',
|
|
219
|
-
toolName: part.toolName,
|
|
220
|
-
output: typeof part.output === 'string' ? part.output : JSON.stringify(part.output)
|
|
221
|
-
});
|
|
222
|
-
break;
|
|
223
|
-
|
|
224
|
-
case 'finish':
|
|
225
|
-
events.push({
|
|
226
|
-
type: 'finish',
|
|
227
|
-
finishReason: part.finishReason ?? 'unknown',
|
|
228
|
-
usage: {
|
|
229
|
-
inputTokens: part.totalUsage?.inputTokens,
|
|
230
|
-
outputTokens: part.totalUsage?.outputTokens,
|
|
231
|
-
reasoningTokens:
|
|
232
|
-
part.totalUsage?.outputTokenDetails?.reasoningTokens ??
|
|
233
|
-
part.totalUsage?.reasoningTokens,
|
|
234
|
-
totalTokens: part.totalUsage?.totalTokens
|
|
235
|
-
}
|
|
236
|
-
});
|
|
237
|
-
break;
|
|
238
|
-
|
|
239
|
-
case 'error':
|
|
240
|
-
events.push({
|
|
241
|
-
type: 'error',
|
|
242
|
-
error: part.error instanceof Error ? part.error : new Error(String(part.error))
|
|
243
|
-
});
|
|
244
|
-
break;
|
|
245
|
-
}
|
|
179
|
+
? { openai: { instructions: systemPrompt, store: false } }
|
|
180
|
+
: undefined,
|
|
181
|
+
stopWhen: stepCountIs(maxSteps)
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
for await (const part of result.fullStream) {
|
|
185
|
+
switch (part.type) {
|
|
186
|
+
case 'text-delta':
|
|
187
|
+
fullText += part.text;
|
|
188
|
+
events.push({ type: 'text-delta', text: part.text });
|
|
189
|
+
break;
|
|
190
|
+
case 'reasoning-delta':
|
|
191
|
+
events.push({ type: 'reasoning-delta', text: part.text });
|
|
192
|
+
break;
|
|
193
|
+
case 'tool-call':
|
|
194
|
+
events.push({ type: 'tool-call', toolName: part.toolName, input: part.input });
|
|
195
|
+
break;
|
|
196
|
+
case 'tool-result':
|
|
197
|
+
events.push({
|
|
198
|
+
type: 'tool-result',
|
|
199
|
+
toolName: part.toolName,
|
|
200
|
+
output: typeof part.output === 'string' ? part.output : JSON.stringify(part.output)
|
|
201
|
+
});
|
|
202
|
+
break;
|
|
203
|
+
case 'finish':
|
|
204
|
+
events.push({
|
|
205
|
+
type: 'finish',
|
|
206
|
+
finishReason: part.finishReason ?? 'unknown',
|
|
207
|
+
usage: {
|
|
208
|
+
inputTokens: part.totalUsage?.inputTokens,
|
|
209
|
+
outputTokens: part.totalUsage?.outputTokens,
|
|
210
|
+
reasoningTokens:
|
|
211
|
+
part.totalUsage?.outputTokenDetails?.reasoningTokens ??
|
|
212
|
+
part.totalUsage?.reasoningTokens,
|
|
213
|
+
totalTokens: part.totalUsage?.totalTokens
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
break;
|
|
217
|
+
case 'error':
|
|
218
|
+
events.push({
|
|
219
|
+
type: 'error',
|
|
220
|
+
error: part.error instanceof Error ? part.error : new Error(String(part.error))
|
|
221
|
+
});
|
|
222
|
+
break;
|
|
246
223
|
}
|
|
247
|
-
|
|
248
|
-
return {
|
|
249
|
-
answer: fullText.trim(),
|
|
250
|
-
model: { provider: providerId, model: modelId },
|
|
251
|
-
events
|
|
252
|
-
};
|
|
253
224
|
}
|
|
254
225
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
226
|
+
return {
|
|
227
|
+
answer: fullText.trim(),
|
|
228
|
+
model: { provider: providerId, model: modelId },
|
|
229
|
+
events
|
|
230
|
+
};
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
export async function* streamAgentLoop(options: AgentLoopOptions): AsyncGenerator<AgentEvent> {
|
|
234
|
+
const {
|
|
235
|
+
providerId,
|
|
236
|
+
modelId,
|
|
237
|
+
collectionPath,
|
|
238
|
+
vfsId,
|
|
239
|
+
agentInstructions,
|
|
240
|
+
question,
|
|
241
|
+
maxSteps = 40
|
|
242
|
+
} = options;
|
|
243
|
+
|
|
244
|
+
const systemPrompt = buildSystemPrompt(agentInstructions);
|
|
245
|
+
const sessionId = crypto.randomUUID();
|
|
246
|
+
|
|
247
|
+
const mergedProviderOptions =
|
|
248
|
+
providerId === 'openai'
|
|
249
|
+
? { ...options.providerOptions, instructions: systemPrompt, sessionId }
|
|
250
|
+
: options.providerOptions;
|
|
251
|
+
|
|
252
|
+
const model = await getModel(providerId, modelId, {
|
|
253
|
+
providerOptions: mergedProviderOptions,
|
|
254
|
+
allowMissingAuth: providerId === 'openai-compat'
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
const initialContext = await getInitialContext(collectionPath, vfsId);
|
|
258
|
+
const messages: ModelMessage[] = [
|
|
259
|
+
{
|
|
260
|
+
role: 'user',
|
|
261
|
+
content: `${initialContext}\n\nQuestion: ${question}`
|
|
262
|
+
}
|
|
263
|
+
];
|
|
264
|
+
|
|
265
|
+
const tools = createTools(collectionPath, vfsId);
|
|
266
|
+
const result = streamText({
|
|
267
|
+
model,
|
|
268
|
+
system: systemPrompt,
|
|
269
|
+
messages,
|
|
270
|
+
tools,
|
|
271
|
+
providerOptions:
|
|
273
272
|
providerId === 'openai'
|
|
274
|
-
? {
|
|
275
|
-
:
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
case 'reasoning-delta':
|
|
318
|
-
yield { type: 'reasoning-delta', text: part.text };
|
|
319
|
-
break;
|
|
320
|
-
|
|
321
|
-
case 'tool-call':
|
|
322
|
-
yield {
|
|
323
|
-
type: 'tool-call',
|
|
324
|
-
toolName: part.toolName,
|
|
325
|
-
input: part.input
|
|
326
|
-
};
|
|
327
|
-
break;
|
|
328
|
-
|
|
329
|
-
case 'tool-result':
|
|
330
|
-
yield {
|
|
331
|
-
type: 'tool-result',
|
|
332
|
-
toolName: part.toolName,
|
|
333
|
-
output: typeof part.output === 'string' ? part.output : JSON.stringify(part.output)
|
|
334
|
-
};
|
|
335
|
-
break;
|
|
336
|
-
|
|
337
|
-
case 'finish':
|
|
338
|
-
yield {
|
|
339
|
-
type: 'finish',
|
|
340
|
-
finishReason: part.finishReason ?? 'unknown',
|
|
341
|
-
usage: {
|
|
342
|
-
inputTokens: part.totalUsage?.inputTokens,
|
|
343
|
-
outputTokens: part.totalUsage?.outputTokens,
|
|
344
|
-
reasoningTokens:
|
|
345
|
-
part.totalUsage?.outputTokenDetails?.reasoningTokens ??
|
|
346
|
-
part.totalUsage?.reasoningTokens,
|
|
347
|
-
totalTokens: part.totalUsage?.totalTokens
|
|
348
|
-
}
|
|
349
|
-
};
|
|
350
|
-
break;
|
|
351
|
-
|
|
352
|
-
case 'error':
|
|
353
|
-
yield {
|
|
354
|
-
type: 'error',
|
|
355
|
-
error: part.error instanceof Error ? part.error : new Error(String(part.error))
|
|
356
|
-
};
|
|
357
|
-
break;
|
|
358
|
-
}
|
|
273
|
+
? { openai: { instructions: systemPrompt, store: false } }
|
|
274
|
+
: undefined,
|
|
275
|
+
stopWhen: stepCountIs(maxSteps)
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
for await (const part of result.fullStream) {
|
|
279
|
+
switch (part.type) {
|
|
280
|
+
case 'text-delta':
|
|
281
|
+
yield { type: 'text-delta', text: part.text };
|
|
282
|
+
break;
|
|
283
|
+
case 'reasoning-delta':
|
|
284
|
+
yield { type: 'reasoning-delta', text: part.text };
|
|
285
|
+
break;
|
|
286
|
+
case 'tool-call':
|
|
287
|
+
yield { type: 'tool-call', toolName: part.toolName, input: part.input };
|
|
288
|
+
break;
|
|
289
|
+
case 'tool-result':
|
|
290
|
+
yield {
|
|
291
|
+
type: 'tool-result',
|
|
292
|
+
toolName: part.toolName,
|
|
293
|
+
output: typeof part.output === 'string' ? part.output : JSON.stringify(part.output)
|
|
294
|
+
};
|
|
295
|
+
break;
|
|
296
|
+
case 'finish':
|
|
297
|
+
yield {
|
|
298
|
+
type: 'finish',
|
|
299
|
+
finishReason: part.finishReason ?? 'unknown',
|
|
300
|
+
usage: {
|
|
301
|
+
inputTokens: part.totalUsage?.inputTokens,
|
|
302
|
+
outputTokens: part.totalUsage?.outputTokens,
|
|
303
|
+
reasoningTokens:
|
|
304
|
+
part.totalUsage?.outputTokenDetails?.reasoningTokens ??
|
|
305
|
+
part.totalUsage?.reasoningTokens,
|
|
306
|
+
totalTokens: part.totalUsage?.totalTokens
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
break;
|
|
310
|
+
case 'error':
|
|
311
|
+
yield {
|
|
312
|
+
type: 'error',
|
|
313
|
+
error: part.error instanceof Error ? part.error : new Error(String(part.error))
|
|
314
|
+
};
|
|
315
|
+
break;
|
|
359
316
|
}
|
|
360
317
|
}
|
|
361
318
|
}
|