@radaros/core 0.3.5 → 0.3.6

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.
Files changed (60) hide show
  1. package/dist/index.d.ts +1407 -0
  2. package/dist/index.js +5269 -0
  3. package/package.json +6 -2
  4. package/src/a2a/a2a-remote-agent.ts +0 -270
  5. package/src/a2a/types.ts +0 -142
  6. package/src/agent/agent.ts +0 -417
  7. package/src/agent/llm-loop.ts +0 -290
  8. package/src/agent/run-context.ts +0 -35
  9. package/src/agent/types.ts +0 -89
  10. package/src/events/event-bus.ts +0 -45
  11. package/src/events/types.ts +0 -16
  12. package/src/guardrails/types.ts +0 -5
  13. package/src/hooks/types.ts +0 -6
  14. package/src/index.ts +0 -157
  15. package/src/knowledge/knowledge-base.ts +0 -146
  16. package/src/logger/logger.ts +0 -249
  17. package/src/mcp/mcp-client.ts +0 -264
  18. package/src/memory/memory.ts +0 -87
  19. package/src/memory/types.ts +0 -13
  20. package/src/memory/user-memory.ts +0 -211
  21. package/src/models/provider.ts +0 -22
  22. package/src/models/providers/anthropic.ts +0 -360
  23. package/src/models/providers/google.ts +0 -386
  24. package/src/models/providers/ollama.ts +0 -211
  25. package/src/models/providers/openai.ts +0 -345
  26. package/src/models/providers/vertex.ts +0 -427
  27. package/src/models/registry.ts +0 -107
  28. package/src/models/types.ts +0 -124
  29. package/src/session/session-manager.ts +0 -75
  30. package/src/session/types.ts +0 -10
  31. package/src/storage/driver.ts +0 -10
  32. package/src/storage/in-memory.ts +0 -44
  33. package/src/storage/mongodb.ts +0 -70
  34. package/src/storage/postgres.ts +0 -81
  35. package/src/storage/sqlite.ts +0 -81
  36. package/src/team/modes.ts +0 -1
  37. package/src/team/team.ts +0 -323
  38. package/src/team/types.ts +0 -26
  39. package/src/toolkits/base.ts +0 -15
  40. package/src/toolkits/duckduckgo.ts +0 -256
  41. package/src/toolkits/gmail.ts +0 -226
  42. package/src/toolkits/hackernews.ts +0 -121
  43. package/src/toolkits/websearch.ts +0 -158
  44. package/src/toolkits/whatsapp.ts +0 -209
  45. package/src/tools/define-tool.ts +0 -22
  46. package/src/tools/tool-executor.ts +0 -221
  47. package/src/tools/types.ts +0 -36
  48. package/src/utils/retry.ts +0 -56
  49. package/src/vector/base.ts +0 -44
  50. package/src/vector/embeddings/google.ts +0 -64
  51. package/src/vector/embeddings/openai.ts +0 -66
  52. package/src/vector/in-memory.ts +0 -115
  53. package/src/vector/mongodb.ts +0 -241
  54. package/src/vector/pgvector.ts +0 -169
  55. package/src/vector/qdrant.ts +0 -203
  56. package/src/vector/types.ts +0 -55
  57. package/src/workflow/step-runner.ts +0 -303
  58. package/src/workflow/types.ts +0 -55
  59. package/src/workflow/workflow.ts +0 -68
  60. package/tsconfig.json +0 -8
@@ -1,417 +0,0 @@
1
- import { v4 as uuidv4 } from "uuid";
2
- import { EventBus } from "../events/event-bus.js";
3
- import { InMemoryStorage } from "../storage/in-memory.js";
4
- import { SessionManager } from "../session/session-manager.js";
5
- import { ToolExecutor } from "../tools/tool-executor.js";
6
- import { Logger } from "../logger/logger.js";
7
- import { LLMLoop } from "./llm-loop.js";
8
- import { RunContext } from "./run-context.js";
9
- import { getTextContent, type ChatMessage, type MessageContent, type StreamChunk } from "../models/types.js";
10
- import type { Session } from "../session/types.js";
11
- import type {
12
- AgentConfig,
13
- RunOpts,
14
- RunOutput,
15
- } from "./types.js";
16
-
17
- export class Agent {
18
- readonly name: string;
19
- readonly eventBus: EventBus;
20
- readonly instructions?: string | ((ctx: RunContext) => string);
21
-
22
- private config: AgentConfig;
23
- private sessionManager: SessionManager;
24
- private llmLoop: LLMLoop;
25
- private logger: Logger;
26
- private storageInitPromise: Promise<void> | null = null;
27
-
28
- get tools() {
29
- return this.config.tools ?? [];
30
- }
31
-
32
- get modelId(): string {
33
- return this.config.model.modelId;
34
- }
35
-
36
- get providerId(): string {
37
- return this.config.model.providerId;
38
- }
39
-
40
- get hasStructuredOutput(): boolean {
41
- return !!this.config.structuredOutput;
42
- }
43
-
44
- get structuredOutputSchema(): import("zod").ZodSchema | undefined {
45
- return this.config.structuredOutput;
46
- }
47
-
48
- constructor(config: AgentConfig) {
49
- this.config = config;
50
- this.name = config.name;
51
- this.instructions = config.instructions;
52
- this.eventBus = config.eventBus ?? new EventBus();
53
-
54
- const storage = config.storage ?? new InMemoryStorage();
55
- if (typeof (storage as any).initialize === "function") {
56
- this.storageInitPromise = (storage as any).initialize();
57
- }
58
- this.sessionManager = new SessionManager(storage);
59
-
60
- this.logger = new Logger({
61
- level: config.logLevel ?? "silent",
62
- prefix: config.name,
63
- });
64
-
65
- const toolExecutor =
66
- config.tools && config.tools.length > 0
67
- ? new ToolExecutor(config.tools)
68
- : null;
69
-
70
- this.llmLoop = new LLMLoop(config.model, toolExecutor, {
71
- maxToolRoundtrips: config.maxToolRoundtrips ?? 10,
72
- temperature: config.temperature,
73
- structuredOutput: config.structuredOutput,
74
- logger: this.logger,
75
- reasoning: config.reasoning,
76
- retry: config.retry,
77
- });
78
- }
79
-
80
- async run(input: MessageContent, opts?: RunOpts): Promise<RunOutput> {
81
- const startTime = Date.now();
82
- const sessionId = opts?.sessionId ?? this.config.sessionId ?? uuidv4();
83
- const userId = opts?.userId ?? this.config.userId;
84
- const inputText = typeof input === "string" ? input : getTextContent(input);
85
-
86
- if (this.storageInitPromise) await this.storageInitPromise;
87
- const session = await this.sessionManager.getOrCreate(sessionId, userId);
88
-
89
- const ctx = new RunContext({
90
- sessionId,
91
- userId,
92
- metadata: opts?.metadata ?? {},
93
- eventBus: this.eventBus,
94
- sessionState: { ...session.state },
95
- });
96
-
97
- this.logger.agentStart(this.name, inputText);
98
-
99
- this.eventBus.emit("run.start", {
100
- runId: ctx.runId,
101
- agentName: this.name,
102
- input: inputText,
103
- });
104
-
105
- try {
106
- if (this.config.hooks?.beforeRun) {
107
- await this.config.hooks.beforeRun(ctx);
108
- }
109
-
110
- if (this.config.guardrails?.input) {
111
- for (const guardrail of this.config.guardrails.input) {
112
- const result = await guardrail.validate(input, ctx);
113
- if (!result.pass) {
114
- throw new Error(
115
- `Input guardrail "${guardrail.name}" blocked: ${result.reason}`
116
- );
117
- }
118
- }
119
- }
120
-
121
- const messages = await this.buildMessages(input, session, ctx);
122
- const output = await this.llmLoop.run(messages, ctx, opts?.apiKey);
123
-
124
- output.durationMs = Date.now() - startTime;
125
-
126
- if (this.config.guardrails?.output) {
127
- for (const guardrail of this.config.guardrails.output) {
128
- const result = await guardrail.validate(output, ctx);
129
- if (!result.pass) {
130
- throw new Error(
131
- `Output guardrail "${guardrail.name}" blocked: ${result.reason}`
132
- );
133
- }
134
- }
135
- }
136
-
137
- await this.sessionManager.appendMessages(sessionId, [
138
- { role: "user", content: inputText },
139
- { role: "assistant", content: output.text },
140
- ]);
141
- await this.sessionManager.updateState(sessionId, ctx.sessionState);
142
-
143
- if (this.config.memory) {
144
- await this.config.memory.addMessages(sessionId, [
145
- { role: "user", content: inputText },
146
- { role: "assistant", content: output.text },
147
- ]);
148
- }
149
-
150
- if (this.config.userMemory && userId) {
151
- this.config.userMemory
152
- .extractAndStore(
153
- userId,
154
- [
155
- { role: "user", content: inputText },
156
- { role: "assistant", content: output.text },
157
- ],
158
- this.config.model
159
- )
160
- .catch((e: unknown) => this.logger.warn(`UserMemory extraction failed: ${e}`));
161
- }
162
-
163
- if (this.config.hooks?.afterRun) {
164
- await this.config.hooks.afterRun(ctx, output);
165
- }
166
-
167
- if (output.thinking) {
168
- this.logger.thinking(output.thinking);
169
- }
170
- this.logger.agentEnd(this.name, output.text, output.usage, output.durationMs);
171
-
172
- this.eventBus.emit("run.complete", {
173
- runId: ctx.runId,
174
- output,
175
- });
176
-
177
- return output;
178
- } catch (error) {
179
- const err = error instanceof Error ? error : new Error(String(error));
180
-
181
- this.logger.error(`Run failed: ${err.message}`);
182
-
183
- if (this.config.hooks?.onError) {
184
- await this.config.hooks.onError(ctx, err);
185
- }
186
-
187
- this.eventBus.emit("run.error", {
188
- runId: ctx.runId,
189
- error: err,
190
- });
191
-
192
- throw err;
193
- }
194
- }
195
-
196
- async *stream(
197
- input: MessageContent,
198
- opts?: RunOpts
199
- ): AsyncGenerator<StreamChunk> {
200
- const sessionId = opts?.sessionId ?? this.config.sessionId ?? uuidv4();
201
- const userId = opts?.userId ?? this.config.userId;
202
- const inputText = typeof input === "string" ? input : getTextContent(input);
203
-
204
- if (this.storageInitPromise) await this.storageInitPromise;
205
- const session = await this.sessionManager.getOrCreate(sessionId, userId);
206
-
207
- const ctx = new RunContext({
208
- sessionId,
209
- userId,
210
- metadata: opts?.metadata ?? {},
211
- eventBus: this.eventBus,
212
- sessionState: { ...session.state },
213
- });
214
-
215
- this.eventBus.emit("run.start", {
216
- runId: ctx.runId,
217
- agentName: this.name,
218
- input: inputText,
219
- });
220
-
221
- let fullText = "";
222
- let streamOk = false;
223
- let streamUsage: import("../models/types.js").TokenUsage = {
224
- promptTokens: 0,
225
- completionTokens: 0,
226
- totalTokens: 0,
227
- };
228
-
229
- try {
230
- if (this.config.hooks?.beforeRun) {
231
- await this.config.hooks.beforeRun(ctx);
232
- }
233
-
234
- if (this.config.guardrails?.input) {
235
- for (const guardrail of this.config.guardrails.input) {
236
- const result = await guardrail.validate(input, ctx);
237
- if (!result.pass) {
238
- throw new Error(
239
- `Input guardrail "${guardrail.name}" blocked: ${result.reason}`
240
- );
241
- }
242
- }
243
- }
244
-
245
- const messages = await this.buildMessages(input, session, ctx);
246
-
247
- for await (const chunk of this.llmLoop.stream(messages, ctx, opts?.apiKey)) {
248
- if (chunk.type === "text") {
249
- fullText += chunk.text;
250
- } else if (chunk.type === "finish" && chunk.usage) {
251
- streamUsage = {
252
- promptTokens: streamUsage.promptTokens + chunk.usage.promptTokens,
253
- completionTokens: streamUsage.completionTokens + chunk.usage.completionTokens,
254
- totalTokens: streamUsage.totalTokens + chunk.usage.totalTokens,
255
- ...(chunk.usage.reasoningTokens
256
- ? { reasoningTokens: (streamUsage.reasoningTokens ?? 0) + chunk.usage.reasoningTokens }
257
- : {}),
258
- };
259
- }
260
- yield chunk;
261
- }
262
-
263
- streamOk = true;
264
- } catch (error) {
265
- const err = error instanceof Error ? error : new Error(String(error));
266
-
267
- if (this.config.hooks?.onError) {
268
- await this.config.hooks.onError(ctx, err);
269
- }
270
-
271
- this.eventBus.emit("run.error", {
272
- runId: ctx.runId,
273
- error: err,
274
- });
275
-
276
- throw err;
277
- } finally {
278
- if (streamOk) {
279
- await this.sessionManager.appendMessages(sessionId, [
280
- { role: "user", content: inputText },
281
- { role: "assistant", content: fullText },
282
- ]);
283
- await this.sessionManager.updateState(sessionId, ctx.sessionState);
284
-
285
- if (this.config.memory) {
286
- await this.config.memory.addMessages(sessionId, [
287
- { role: "user", content: inputText },
288
- { role: "assistant", content: fullText },
289
- ]);
290
- }
291
-
292
- if (this.config.userMemory && userId) {
293
- this.config.userMemory
294
- .extractAndStore(
295
- userId,
296
- [
297
- { role: "user", content: inputText },
298
- { role: "assistant", content: fullText },
299
- ],
300
- this.config.model
301
- )
302
- .catch((e: unknown) => this.logger.warn(`UserMemory extraction failed: ${e}`));
303
- }
304
-
305
- this.eventBus.emit("run.complete", {
306
- runId: ctx.runId,
307
- output: {
308
- text: fullText,
309
- toolCalls: [],
310
- usage: streamUsage,
311
- },
312
- });
313
- }
314
- }
315
- }
316
-
317
- private async buildMessages(
318
- input: MessageContent,
319
- session: Session,
320
- ctx: RunContext
321
- ): Promise<ChatMessage[]> {
322
- const messages: ChatMessage[] = [];
323
-
324
- let systemContent = "";
325
- if (this.config.instructions) {
326
- systemContent =
327
- typeof this.config.instructions === "function"
328
- ? this.config.instructions(ctx)
329
- : this.config.instructions;
330
- }
331
-
332
- if (this.config.memory) {
333
- const memoryContext = await this.config.memory.getContextString(
334
- session.sessionId
335
- );
336
- if (memoryContext) {
337
- systemContent = systemContent
338
- ? `${systemContent}\n\n${memoryContext}`
339
- : memoryContext;
340
- }
341
- }
342
-
343
- if (this.config.userMemory && ctx.userId) {
344
- const hasRecallTool = (this.config.tools ?? []).some(
345
- (t) => t.name === "recall_user_facts"
346
- );
347
- if (!hasRecallTool) {
348
- const userContext = await this.config.userMemory.getContextString(ctx.userId);
349
- if (userContext) {
350
- systemContent = systemContent
351
- ? `${systemContent}\n\n${userContext}`
352
- : userContext;
353
- }
354
- }
355
- }
356
-
357
- if (systemContent) {
358
- messages.push({ role: "system", content: systemContent });
359
- }
360
-
361
- if (this.config.addHistoryToMessages !== false) {
362
- const limit = this.config.numHistoryRuns
363
- ? this.config.numHistoryRuns * 2
364
- : 20;
365
- let history = session.messages ?? [];
366
- if (limit > 0 && history.length > limit) {
367
- history = history.slice(-limit);
368
- }
369
-
370
- if (this.config.maxContextTokens) {
371
- history = this.trimHistoryByTokens(history, systemContent, input);
372
- }
373
-
374
- if (history.length > 0) {
375
- this.logger.info(`Loaded ${history.length} history messages for session ${session.sessionId}`);
376
- }
377
- messages.push(...history);
378
- }
379
-
380
- messages.push({ role: "user", content: input });
381
-
382
- this.logger.info(`Sending ${messages.length} messages to LLM`);
383
-
384
- return messages;
385
- }
386
-
387
- private estimateTokens(text: string): number {
388
- return Math.ceil(text.length / 3.5);
389
- }
390
-
391
- private trimHistoryByTokens(
392
- history: ChatMessage[],
393
- systemContent: string,
394
- currentInput: MessageContent
395
- ): ChatMessage[] {
396
- const maxTokens = this.config.maxContextTokens!;
397
- const inputText = typeof currentInput === "string" ? currentInput : "(multimodal)";
398
- let reservedTokens = this.estimateTokens(systemContent) + this.estimateTokens(inputText) + 100;
399
-
400
- const available = maxTokens - reservedTokens;
401
- if (available <= 0) return [];
402
-
403
- const result: ChatMessage[] = [];
404
- let used = 0;
405
-
406
- for (let i = history.length - 1; i >= 0; i--) {
407
- const msg = history[i];
408
- const text = typeof msg.content === "string" ? msg.content : "";
409
- const tokens = this.estimateTokens(text);
410
- if (used + tokens > available) break;
411
- used += tokens;
412
- result.unshift(msg);
413
- }
414
-
415
- return result;
416
- }
417
- }
@@ -1,290 +0,0 @@
1
- import { createRequire } from "node:module";
2
- import type { z } from "zod";
3
- import type { ModelProvider } from "../models/provider.js";
4
- import {
5
- getTextContent,
6
- type ChatMessage,
7
- type ModelConfig,
8
- type ReasoningConfig,
9
- type StreamChunk,
10
- type ToolDefinition,
11
- } from "../models/types.js";
12
- import type { ToolExecutor } from "../tools/tool-executor.js";
13
- import type { RunContext } from "./run-context.js";
14
- import type { RunOutput } from "./types.js";
15
- import type { ToolCallResult } from "../tools/types.js";
16
- import type { Logger } from "../logger/logger.js";
17
- import { withRetry, type RetryConfig } from "../utils/retry.js";
18
-
19
- const _require = createRequire(import.meta.url);
20
-
21
- export class LLMLoop {
22
- private provider: ModelProvider;
23
- private toolExecutor: ToolExecutor | null;
24
- private maxToolRoundtrips: number;
25
- private temperature?: number;
26
- private maxTokens?: number;
27
- private structuredOutput?: z.ZodSchema;
28
- private logger?: Logger;
29
- private reasoning?: ReasoningConfig;
30
- private retry?: Partial<RetryConfig>;
31
-
32
- constructor(
33
- provider: ModelProvider,
34
- toolExecutor: ToolExecutor | null,
35
- options: {
36
- maxToolRoundtrips: number;
37
- temperature?: number;
38
- maxTokens?: number;
39
- structuredOutput?: z.ZodSchema;
40
- logger?: Logger;
41
- reasoning?: ReasoningConfig;
42
- retry?: Partial<RetryConfig>;
43
- }
44
- ) {
45
- this.provider = provider;
46
- this.toolExecutor = toolExecutor;
47
- this.maxToolRoundtrips = options.maxToolRoundtrips;
48
- this.temperature = options.temperature;
49
- this.maxTokens = options.maxTokens;
50
- this.structuredOutput = options.structuredOutput;
51
- this.logger = options.logger;
52
- this.reasoning = options.reasoning;
53
- this.retry = options.retry;
54
- }
55
-
56
- async run(messages: ChatMessage[], ctx: RunContext, apiKey?: string): Promise<RunOutput> {
57
- const allToolCalls: ToolCallResult[] = [];
58
- let totalPromptTokens = 0;
59
- let totalCompletionTokens = 0;
60
- let totalReasoningTokens = 0;
61
- let thinkingContent = "";
62
- const currentMessages = [...messages];
63
- const toolDefs = this.toolExecutor?.getToolDefinitions() ?? [];
64
-
65
- for (let roundtrip = 0; roundtrip <= this.maxToolRoundtrips; roundtrip++) {
66
- const modelConfig: ModelConfig & { tools?: ToolDefinition[] } = {};
67
- if (apiKey) modelConfig.apiKey = apiKey;
68
- if (this.temperature !== undefined)
69
- modelConfig.temperature = this.temperature;
70
- if (this.maxTokens !== undefined) modelConfig.maxTokens = this.maxTokens;
71
- if (toolDefs.length > 0) modelConfig.tools = toolDefs;
72
- if (this.reasoning) modelConfig.reasoning = this.reasoning;
73
-
74
- if (this.structuredOutput) {
75
- modelConfig.responseFormat = {
76
- type: "json_schema",
77
- schema: this.zodToJsonSchema(this.structuredOutput),
78
- name: "structured_response",
79
- };
80
- }
81
-
82
- const response = await withRetry(
83
- () => this.provider.generate(currentMessages, modelConfig),
84
- this.retry
85
- );
86
-
87
- totalPromptTokens += response.usage.promptTokens;
88
- totalCompletionTokens += response.usage.completionTokens;
89
- if (response.usage.reasoningTokens) totalReasoningTokens += response.usage.reasoningTokens;
90
-
91
- if ((response as any).thinking) {
92
- thinkingContent += (thinkingContent ? "\n" : "") + (response as any).thinking;
93
- }
94
-
95
- currentMessages.push(response.message);
96
-
97
- if (
98
- response.finishReason !== "tool_calls" ||
99
- !response.message.toolCalls?.length ||
100
- !this.toolExecutor
101
- ) {
102
- const text = getTextContent(response.message.content);
103
-
104
- const output: RunOutput = {
105
- text,
106
- toolCalls: allToolCalls,
107
- usage: {
108
- promptTokens: totalPromptTokens,
109
- completionTokens: totalCompletionTokens,
110
- totalTokens: totalPromptTokens + totalCompletionTokens,
111
- ...(totalReasoningTokens > 0 ? { reasoningTokens: totalReasoningTokens } : {}),
112
- },
113
- };
114
-
115
- if (thinkingContent) output.thinking = thinkingContent;
116
-
117
- if (this.structuredOutput && text) {
118
- try {
119
- const jsonStr = this.extractJson(text);
120
- const parsed = JSON.parse(jsonStr);
121
- output.structured = this.structuredOutput.parse(parsed);
122
- } catch {
123
- // structured parsing failed, raw text is still available
124
- }
125
- }
126
-
127
- return output;
128
- }
129
-
130
- const toolResults = await this.toolExecutor.executeAll(
131
- response.message.toolCalls,
132
- ctx
133
- );
134
-
135
- allToolCalls.push(...toolResults);
136
-
137
- for (const result of toolResults) {
138
- const content =
139
- typeof result.result === "string"
140
- ? result.result
141
- : result.result.content;
142
-
143
- this.logger?.toolCall(result.toolName, {});
144
- this.logger?.toolResult(result.toolName, typeof content === "string" ? content : JSON.stringify(content));
145
-
146
- currentMessages.push({
147
- role: "tool",
148
- content,
149
- toolCallId: result.toolCallId,
150
- name: result.toolName,
151
- });
152
- }
153
- }
154
-
155
- const lastAssistantMsg = currentMessages
156
- .reverse()
157
- .find((m) => m.role === "assistant");
158
-
159
- const text = getTextContent(lastAssistantMsg?.content ?? null);
160
-
161
- return {
162
- text,
163
- toolCalls: allToolCalls,
164
- usage: {
165
- promptTokens: totalPromptTokens,
166
- completionTokens: totalCompletionTokens,
167
- totalTokens: totalPromptTokens + totalCompletionTokens,
168
- ...(totalReasoningTokens > 0 ? { reasoningTokens: totalReasoningTokens } : {}),
169
- },
170
- ...(thinkingContent ? { thinking: thinkingContent } : {}),
171
- };
172
- }
173
-
174
- async *stream(
175
- messages: ChatMessage[],
176
- ctx: RunContext,
177
- apiKey?: string
178
- ): AsyncGenerator<StreamChunk> {
179
- const currentMessages = [...messages];
180
- const toolDefs = this.toolExecutor?.getToolDefinitions() ?? [];
181
-
182
- for (let roundtrip = 0; roundtrip <= this.maxToolRoundtrips; roundtrip++) {
183
- const modelConfig: ModelConfig & { tools?: ToolDefinition[] } = {};
184
- if (apiKey) modelConfig.apiKey = apiKey;
185
- if (this.temperature !== undefined)
186
- modelConfig.temperature = this.temperature;
187
- if (this.maxTokens !== undefined) modelConfig.maxTokens = this.maxTokens;
188
- if (toolDefs.length > 0) modelConfig.tools = toolDefs;
189
- if (this.reasoning) modelConfig.reasoning = this.reasoning;
190
-
191
- let fullText = "";
192
- const pendingToolCalls: Array<{
193
- id: string;
194
- name: string;
195
- args: string;
196
- }> = [];
197
- let finishReason = "stop";
198
-
199
- const streamGen = this.provider.stream(currentMessages, modelConfig);
200
-
201
- for await (const chunk of streamGen) {
202
- yield chunk;
203
-
204
- if (chunk.type === "text") {
205
- fullText += chunk.text;
206
- ctx.eventBus.emit("run.stream.chunk", {
207
- runId: ctx.runId,
208
- chunk: chunk.text,
209
- });
210
- } else if (chunk.type === "tool_call_start") {
211
- pendingToolCalls.push({
212
- id: chunk.toolCall.id,
213
- name: chunk.toolCall.name,
214
- args: "",
215
- });
216
- } else if (chunk.type === "tool_call_delta") {
217
- const tc = pendingToolCalls.find(
218
- (t) => t.id === chunk.toolCallId
219
- );
220
- if (tc) {
221
- tc.args += chunk.argumentsDelta;
222
- }
223
- } else if (chunk.type === "finish") {
224
- finishReason = chunk.finishReason;
225
- }
226
- }
227
-
228
- if (finishReason !== "tool_calls" || pendingToolCalls.length === 0 || !this.toolExecutor) {
229
- return;
230
- }
231
-
232
- const assistantMsg: ChatMessage = {
233
- role: "assistant",
234
- content: fullText || null,
235
- toolCalls: pendingToolCalls.map((tc) => ({
236
- id: tc.id,
237
- name: tc.name,
238
- arguments: JSON.parse(tc.args || "{}"),
239
- })),
240
- };
241
- currentMessages.push(assistantMsg);
242
-
243
- const toolResults = await this.toolExecutor.executeAll(
244
- assistantMsg.toolCalls!,
245
- ctx
246
- );
247
-
248
- for (const result of toolResults) {
249
- const content =
250
- typeof result.result === "string"
251
- ? result.result
252
- : result.result.content;
253
-
254
- currentMessages.push({
255
- role: "tool",
256
- content,
257
- toolCallId: result.toolCallId,
258
- name: result.toolName,
259
- });
260
- }
261
- }
262
- }
263
-
264
- private extractJson(text: string): string {
265
- const fenceMatch = text.match(/```(?:json)?\s*\n?([\s\S]*?)```/);
266
- if (fenceMatch) return fenceMatch[1].trim();
267
-
268
- const braceStart = text.indexOf("{");
269
- const braceEnd = text.lastIndexOf("}");
270
- if (braceStart !== -1 && braceEnd > braceStart) {
271
- return text.slice(braceStart, braceEnd + 1);
272
- }
273
-
274
- return text.trim();
275
- }
276
-
277
- private zodToJsonSchema(schema: z.ZodSchema): Record<string, unknown> {
278
- try {
279
- const { zodToJsonSchema } = _require("zod-to-json-schema");
280
- const result = zodToJsonSchema(schema, {
281
- target: "jsonSchema7",
282
- $refStrategy: "none",
283
- }) as Record<string, unknown>;
284
- delete result["$schema"];
285
- return result;
286
- } catch {
287
- return {};
288
- }
289
- }
290
- }