@samrahimi/smol-js 0.4.0 → 0.6.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/dist/cli.js +3159 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.mts +459 -111
- package/dist/index.d.ts +459 -111
- package/dist/index.js +1444 -140
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1433 -140
- package/dist/index.mjs.map +1 -1
- package/package.json +14 -4
package/dist/index.mjs
CHANGED
|
@@ -8,31 +8,43 @@ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
|
8
8
|
})(LogLevel || {});
|
|
9
9
|
|
|
10
10
|
// src/memory/AgentMemory.ts
|
|
11
|
+
function estimateTokens(text) {
|
|
12
|
+
return Math.ceil(text.length / 4);
|
|
13
|
+
}
|
|
14
|
+
function estimateMessagesTokens(messages) {
|
|
15
|
+
let total = 0;
|
|
16
|
+
for (const msg of messages) {
|
|
17
|
+
total += estimateTokens(msg.content ?? "");
|
|
18
|
+
if (msg.toolCalls) {
|
|
19
|
+
total += estimateTokens(JSON.stringify(msg.toolCalls));
|
|
20
|
+
}
|
|
21
|
+
total += 4;
|
|
22
|
+
}
|
|
23
|
+
return total;
|
|
24
|
+
}
|
|
11
25
|
var AgentMemory = class {
|
|
12
|
-
/**
|
|
13
|
-
* System prompt step (always first)
|
|
14
|
-
*/
|
|
26
|
+
/** System prompt step (always first) */
|
|
15
27
|
systemPrompt;
|
|
16
|
-
/**
|
|
17
|
-
* All execution steps
|
|
18
|
-
*/
|
|
28
|
+
/** All execution steps */
|
|
19
29
|
steps = [];
|
|
20
|
-
|
|
30
|
+
maxContextLength;
|
|
31
|
+
memoryStrategy;
|
|
32
|
+
model;
|
|
33
|
+
constructor(systemPrompt, config) {
|
|
21
34
|
this.systemPrompt = {
|
|
22
35
|
type: "system",
|
|
23
36
|
content: systemPrompt,
|
|
24
37
|
timestamp: Date.now()
|
|
25
38
|
};
|
|
39
|
+
this.maxContextLength = config?.maxContextLength ?? 1e5;
|
|
40
|
+
this.memoryStrategy = config?.memoryStrategy ?? "truncate";
|
|
41
|
+
this.model = config?.model;
|
|
26
42
|
}
|
|
27
|
-
/**
|
|
28
|
-
* Reset memory, keeping only the system prompt.
|
|
29
|
-
*/
|
|
43
|
+
/** Reset memory, keeping only the system prompt */
|
|
30
44
|
reset() {
|
|
31
45
|
this.steps = [];
|
|
32
46
|
}
|
|
33
|
-
/**
|
|
34
|
-
* Add a task step.
|
|
35
|
-
*/
|
|
47
|
+
/** Add a task step */
|
|
36
48
|
addTask(task) {
|
|
37
49
|
const step = {
|
|
38
50
|
type: "task",
|
|
@@ -42,25 +54,19 @@ var AgentMemory = class {
|
|
|
42
54
|
this.steps.push(step);
|
|
43
55
|
return step;
|
|
44
56
|
}
|
|
45
|
-
/**
|
|
46
|
-
* Create a new action step.
|
|
47
|
-
*/
|
|
57
|
+
/** Create a new action step */
|
|
48
58
|
createActionStep(stepNumber) {
|
|
49
59
|
const step = {
|
|
50
60
|
type: "action",
|
|
51
61
|
stepNumber,
|
|
52
|
-
timing: {
|
|
53
|
-
startTime: Date.now()
|
|
54
|
-
},
|
|
62
|
+
timing: { startTime: Date.now() },
|
|
55
63
|
modelInputMessages: [],
|
|
56
64
|
timestamp: Date.now()
|
|
57
65
|
};
|
|
58
66
|
this.steps.push(step);
|
|
59
67
|
return step;
|
|
60
68
|
}
|
|
61
|
-
/**
|
|
62
|
-
* Add a final answer step.
|
|
63
|
-
*/
|
|
69
|
+
/** Add a final answer step */
|
|
64
70
|
addFinalAnswer(answer) {
|
|
65
71
|
const step = {
|
|
66
72
|
type: "final",
|
|
@@ -70,20 +76,17 @@ var AgentMemory = class {
|
|
|
70
76
|
this.steps.push(step);
|
|
71
77
|
return step;
|
|
72
78
|
}
|
|
73
|
-
/**
|
|
74
|
-
* Get the last step.
|
|
75
|
-
*/
|
|
79
|
+
/** Get the last step */
|
|
76
80
|
getLastStep() {
|
|
77
81
|
return this.steps[this.steps.length - 1];
|
|
78
82
|
}
|
|
79
|
-
/**
|
|
80
|
-
* Get all action steps.
|
|
81
|
-
*/
|
|
83
|
+
/** Get all action steps */
|
|
82
84
|
getActionSteps() {
|
|
83
85
|
return this.steps.filter((s) => s.type === "action");
|
|
84
86
|
}
|
|
85
87
|
/**
|
|
86
88
|
* Convert memory to messages for LLM context.
|
|
89
|
+
* Handles both CodeAgent (observation-based) and ToolUseAgent (tool_call-based) patterns.
|
|
87
90
|
*/
|
|
88
91
|
toMessages() {
|
|
89
92
|
const messages = [];
|
|
@@ -100,23 +103,40 @@ var AgentMemory = class {
|
|
|
100
103
|
});
|
|
101
104
|
break;
|
|
102
105
|
case "action":
|
|
103
|
-
if (step.
|
|
106
|
+
if (step.toolCalls && step.toolCalls.length > 0) {
|
|
104
107
|
messages.push({
|
|
105
108
|
role: "assistant",
|
|
106
|
-
content: step.modelOutputMessage
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
if (step.observation) {
|
|
110
|
-
messages.push({
|
|
111
|
-
role: "user",
|
|
112
|
-
content: step.observation
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
if (step.error) {
|
|
116
|
-
messages.push({
|
|
117
|
-
role: "user",
|
|
118
|
-
content: `Error: ${step.error.message}`
|
|
109
|
+
content: step.modelOutputMessage?.content ?? null,
|
|
110
|
+
toolCalls: step.toolCalls
|
|
119
111
|
});
|
|
112
|
+
if (step.toolResults) {
|
|
113
|
+
for (const result of step.toolResults) {
|
|
114
|
+
messages.push({
|
|
115
|
+
role: "tool",
|
|
116
|
+
content: result.error ? `Error: ${result.error}` : typeof result.result === "string" ? result.result : JSON.stringify(result.result, null, 2),
|
|
117
|
+
toolCallId: result.toolCallId
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
} else {
|
|
122
|
+
if (step.modelOutputMessage) {
|
|
123
|
+
messages.push({
|
|
124
|
+
role: "assistant",
|
|
125
|
+
content: step.modelOutputMessage.content
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
if (step.observation) {
|
|
129
|
+
messages.push({
|
|
130
|
+
role: "user",
|
|
131
|
+
content: step.observation
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
if (step.error && !step.observation) {
|
|
135
|
+
messages.push({
|
|
136
|
+
role: "user",
|
|
137
|
+
content: `Error: ${step.error.message}`
|
|
138
|
+
});
|
|
139
|
+
}
|
|
120
140
|
}
|
|
121
141
|
break;
|
|
122
142
|
case "final":
|
|
@@ -126,8 +146,90 @@ var AgentMemory = class {
|
|
|
126
146
|
return messages;
|
|
127
147
|
}
|
|
128
148
|
/**
|
|
129
|
-
*
|
|
149
|
+
* Manage context length - truncate or compact if exceeded.
|
|
150
|
+
*/
|
|
151
|
+
async manageContext() {
|
|
152
|
+
const messages = this.toMessages();
|
|
153
|
+
const tokenCount = estimateMessagesTokens(messages);
|
|
154
|
+
if (tokenCount <= this.maxContextLength) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (this.memoryStrategy === "truncate") {
|
|
158
|
+
this.truncateOlderMessages();
|
|
159
|
+
} else if (this.memoryStrategy === "compact") {
|
|
160
|
+
await this.compactMessages();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Truncate older action steps to fit within context.
|
|
165
|
+
*/
|
|
166
|
+
truncateOlderMessages() {
|
|
167
|
+
const actionSteps = this.getActionSteps();
|
|
168
|
+
if (actionSteps.length <= 2) return;
|
|
169
|
+
const targetTokens = this.maxContextLength * 0.75;
|
|
170
|
+
let currentTokens = estimateMessagesTokens(this.toMessages());
|
|
171
|
+
while (currentTokens > targetTokens && this.steps.length > 2) {
|
|
172
|
+
const idx = this.steps.findIndex((s) => s.type === "action");
|
|
173
|
+
if (idx === -1) break;
|
|
174
|
+
this.steps.splice(idx, 1);
|
|
175
|
+
currentTokens = estimateMessagesTokens(this.toMessages());
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Compact older messages into a summary.
|
|
130
180
|
*/
|
|
181
|
+
async compactMessages() {
|
|
182
|
+
if (!this.model) {
|
|
183
|
+
this.truncateOlderMessages();
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const actionSteps = this.getActionSteps();
|
|
187
|
+
if (actionSteps.length <= 2) return;
|
|
188
|
+
const stepsToSummarize = actionSteps.slice(0, -2);
|
|
189
|
+
const summaryContent = stepsToSummarize.map((step) => {
|
|
190
|
+
const parts = [];
|
|
191
|
+
if (step.modelOutputMessage?.content) {
|
|
192
|
+
parts.push(`Action: ${step.modelOutputMessage.content.slice(0, 200)}`);
|
|
193
|
+
}
|
|
194
|
+
if (step.observation) {
|
|
195
|
+
parts.push(`Observation: ${step.observation.slice(0, 200)}`);
|
|
196
|
+
}
|
|
197
|
+
if (step.toolResults) {
|
|
198
|
+
for (const r of step.toolResults) {
|
|
199
|
+
const resultStr = typeof r.result === "string" ? r.result : JSON.stringify(r.result);
|
|
200
|
+
parts.push(`Tool ${r.toolName}: ${resultStr.slice(0, 200)}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return parts.join("\n");
|
|
204
|
+
}).join("\n---\n");
|
|
205
|
+
try {
|
|
206
|
+
const summaryResponse = await this.model.generate([
|
|
207
|
+
{
|
|
208
|
+
role: "system",
|
|
209
|
+
content: "Summarize the following agent execution history concisely, preserving key findings and results. Be brief but complete."
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
role: "user",
|
|
213
|
+
content: summaryContent
|
|
214
|
+
}
|
|
215
|
+
]);
|
|
216
|
+
const recentSteps = this.steps.filter(
|
|
217
|
+
(s) => s.type === "task" || s.type === "final" || s.type === "action" && actionSteps.indexOf(s) >= actionSteps.length - 2
|
|
218
|
+
);
|
|
219
|
+
this.steps = [
|
|
220
|
+
{
|
|
221
|
+
type: "task",
|
|
222
|
+
task: `[Context Summary from previous steps]
|
|
223
|
+
${summaryResponse.content}`,
|
|
224
|
+
timestamp: Date.now()
|
|
225
|
+
},
|
|
226
|
+
...recentSteps
|
|
227
|
+
];
|
|
228
|
+
} catch {
|
|
229
|
+
this.truncateOlderMessages();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
/** Get total token usage across all steps */
|
|
131
233
|
getTotalTokenUsage() {
|
|
132
234
|
let inputTokens = 0;
|
|
133
235
|
let outputTokens = 0;
|
|
@@ -143,9 +245,11 @@ var AgentMemory = class {
|
|
|
143
245
|
totalTokens: inputTokens + outputTokens
|
|
144
246
|
};
|
|
145
247
|
}
|
|
146
|
-
/**
|
|
147
|
-
|
|
148
|
-
|
|
248
|
+
/** Get current estimated token count */
|
|
249
|
+
getEstimatedTokenCount() {
|
|
250
|
+
return estimateMessagesTokens(this.toMessages());
|
|
251
|
+
}
|
|
252
|
+
/** Get a summary of the memory for logging */
|
|
149
253
|
getSummary() {
|
|
150
254
|
const actionSteps = this.getActionSteps();
|
|
151
255
|
const lines = [
|
|
@@ -159,9 +263,7 @@ var AgentMemory = class {
|
|
|
159
263
|
}
|
|
160
264
|
return lines.join("\n");
|
|
161
265
|
}
|
|
162
|
-
/**
|
|
163
|
-
* Serialize memory to JSON.
|
|
164
|
-
*/
|
|
266
|
+
/** Serialize memory to JSON */
|
|
165
267
|
toJSON() {
|
|
166
268
|
return {
|
|
167
269
|
systemPrompt: this.systemPrompt,
|
|
@@ -450,35 +552,24 @@ ${"\u2550".repeat(60)}
|
|
|
450
552
|
};
|
|
451
553
|
|
|
452
554
|
// src/agents/Agent.ts
|
|
555
|
+
var DEFAULT_MAX_CONTEXT_LENGTH = 1e5;
|
|
453
556
|
var Agent = class {
|
|
454
|
-
/**
|
|
455
|
-
* The LLM model for generation
|
|
456
|
-
*/
|
|
557
|
+
/** The LLM model for generation */
|
|
457
558
|
model;
|
|
458
|
-
/**
|
|
459
|
-
* Available tools mapped by name
|
|
460
|
-
*/
|
|
559
|
+
/** Available tools mapped by name */
|
|
461
560
|
tools = /* @__PURE__ */ new Map();
|
|
462
|
-
/**
|
|
463
|
-
* Agent memory tracking all steps
|
|
464
|
-
*/
|
|
561
|
+
/** Agent memory tracking all steps */
|
|
465
562
|
memory;
|
|
466
|
-
/**
|
|
467
|
-
* Logger for formatted output
|
|
468
|
-
*/
|
|
563
|
+
/** Logger for formatted output */
|
|
469
564
|
logger;
|
|
470
|
-
/**
|
|
471
|
-
* Configuration options
|
|
472
|
-
*/
|
|
565
|
+
/** Configuration options */
|
|
473
566
|
config;
|
|
474
|
-
/**
|
|
475
|
-
* Current step number
|
|
476
|
-
*/
|
|
567
|
+
/** Current step number */
|
|
477
568
|
currentStep = 0;
|
|
478
|
-
/**
|
|
479
|
-
* Whether the agent is currently running
|
|
480
|
-
*/
|
|
569
|
+
/** Whether the agent is currently running */
|
|
481
570
|
isRunning = false;
|
|
571
|
+
/** Whether the agent has been initialized at least once */
|
|
572
|
+
initialized = false;
|
|
482
573
|
constructor(config) {
|
|
483
574
|
this.model = config.model;
|
|
484
575
|
this.logger = new AgentLogger(config.verboseLevel ?? 1 /* INFO */);
|
|
@@ -487,7 +578,14 @@ var Agent = class {
|
|
|
487
578
|
codeExecutionDelay: config.codeExecutionDelay ?? 5e3,
|
|
488
579
|
customInstructions: config.customInstructions ?? "",
|
|
489
580
|
verboseLevel: config.verboseLevel ?? 1 /* INFO */,
|
|
490
|
-
streamOutputs: config.streamOutputs ?? true
|
|
581
|
+
streamOutputs: config.streamOutputs ?? true,
|
|
582
|
+
persistent: config.persistent ?? false,
|
|
583
|
+
maxContextLength: config.maxContextLength ?? DEFAULT_MAX_CONTEXT_LENGTH,
|
|
584
|
+
memoryStrategy: config.memoryStrategy ?? "truncate",
|
|
585
|
+
maxTokens: config.maxTokens,
|
|
586
|
+
temperature: config.temperature,
|
|
587
|
+
name: config.name ?? "Agent",
|
|
588
|
+
onEvent: config.onEvent
|
|
491
589
|
};
|
|
492
590
|
if (config.tools) {
|
|
493
591
|
for (const tool of config.tools) {
|
|
@@ -497,27 +595,31 @@ var Agent = class {
|
|
|
497
595
|
}
|
|
498
596
|
/**
|
|
499
597
|
* Run the agent on a task.
|
|
500
|
-
*
|
|
501
|
-
* @param task - The task description
|
|
502
|
-
* @param reset - Whether to reset memory before running
|
|
503
|
-
* @returns The final result
|
|
504
598
|
*/
|
|
505
599
|
async run(task, reset = true) {
|
|
506
600
|
const startTime = Date.now();
|
|
507
|
-
|
|
601
|
+
const shouldReset = !this.config.persistent ? reset || !this.memory : !this.initialized;
|
|
602
|
+
if (shouldReset) {
|
|
508
603
|
const systemPrompt = this.initializeSystemPrompt();
|
|
509
|
-
this.memory = new AgentMemory(systemPrompt
|
|
604
|
+
this.memory = new AgentMemory(systemPrompt, {
|
|
605
|
+
maxContextLength: this.config.maxContextLength,
|
|
606
|
+
memoryStrategy: this.config.memoryStrategy,
|
|
607
|
+
model: this.model
|
|
608
|
+
});
|
|
510
609
|
this.currentStep = 0;
|
|
610
|
+
this.initialized = true;
|
|
511
611
|
}
|
|
512
612
|
this.memory.addTask(task);
|
|
513
613
|
this.isRunning = true;
|
|
514
|
-
this.
|
|
614
|
+
this.emitEvent("agent_start", { task, name: this.config.name });
|
|
615
|
+
this.logger.header(`Starting ${this.config.name}: ${task.slice(0, 80)}${task.length > 80 ? "..." : ""}`);
|
|
515
616
|
let finalOutput = null;
|
|
516
617
|
let isFinalAnswer = false;
|
|
517
618
|
try {
|
|
518
619
|
while (this.currentStep < this.config.maxSteps && this.isRunning) {
|
|
519
620
|
this.currentStep++;
|
|
520
621
|
this.logger.stepProgress(this.currentStep, this.config.maxSteps);
|
|
622
|
+
this.emitEvent("agent_step", { step: this.currentStep, maxSteps: this.config.maxSteps });
|
|
521
623
|
const memoryStep = this.memory.createActionStep(this.currentStep);
|
|
522
624
|
try {
|
|
523
625
|
const actionOutput = await this.executeStep(memoryStep);
|
|
@@ -536,7 +638,9 @@ var Agent = class {
|
|
|
536
638
|
memoryStep.timing.endTime = Date.now();
|
|
537
639
|
memoryStep.timing.duration = memoryStep.timing.endTime - memoryStep.timing.startTime;
|
|
538
640
|
this.logger.error("Step execution failed", error);
|
|
641
|
+
this.emitEvent("agent_error", { error: error.message, step: this.currentStep });
|
|
539
642
|
}
|
|
643
|
+
await this.memory.manageContext();
|
|
540
644
|
}
|
|
541
645
|
if (!isFinalAnswer && this.currentStep >= this.config.maxSteps) {
|
|
542
646
|
this.logger.warn(`Max steps (${this.config.maxSteps}) reached without final answer`);
|
|
@@ -548,13 +652,10 @@ var Agent = class {
|
|
|
548
652
|
const duration = Date.now() - startTime;
|
|
549
653
|
const tokenUsage = this.memory.getTotalTokenUsage();
|
|
550
654
|
this.memory.addFinalAnswer(finalOutput);
|
|
655
|
+
this.emitEvent("agent_end", { output: finalOutput, duration, tokenUsage });
|
|
551
656
|
this.logger.info(`
|
|
552
|
-
|
|
553
|
-
this.logger.info(
|
|
554
|
-
const logPath = this.logger.getLogPath();
|
|
555
|
-
if (logPath) {
|
|
556
|
-
this.logger.info(`\u{1F4C1} Log file: ${logPath}`);
|
|
557
|
-
}
|
|
657
|
+
Total time: ${(duration / 1e3).toFixed(2)}s`);
|
|
658
|
+
this.logger.info(`Total tokens: ${tokenUsage.totalTokens}`);
|
|
558
659
|
return {
|
|
559
660
|
output: finalOutput,
|
|
560
661
|
steps: this.memory.steps,
|
|
@@ -570,49 +671,48 @@ var Agent = class {
|
|
|
570
671
|
const messages = this.memory.toMessages();
|
|
571
672
|
messages.push({
|
|
572
673
|
role: "user",
|
|
573
|
-
content: `You have reached the maximum number of steps. Based on your work so far, provide the best answer you can for the original task: "${task}"
|
|
574
|
-
|
|
575
|
-
|
|
674
|
+
content: `You have reached the maximum number of steps. Based on your work so far, provide the best answer you can for the original task: "${task}". Summarize what you accomplished and provide a final answer.`
|
|
675
|
+
});
|
|
676
|
+
const response = await this.model.generate(messages, {
|
|
677
|
+
maxTokens: this.config.maxTokens,
|
|
678
|
+
temperature: this.config.temperature
|
|
576
679
|
});
|
|
577
|
-
const response = await this.model.generate(messages);
|
|
578
680
|
return response.content;
|
|
579
681
|
}
|
|
580
|
-
/**
|
|
581
|
-
|
|
582
|
-
|
|
682
|
+
/** Emit an orchestration event */
|
|
683
|
+
emitEvent(type, data) {
|
|
684
|
+
if (this.config.onEvent) {
|
|
685
|
+
this.config.onEvent({ type, data });
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
/** Stop the agent */
|
|
583
689
|
stop() {
|
|
584
690
|
this.isRunning = false;
|
|
585
691
|
this.logger.info("Agent stopped by user");
|
|
586
692
|
}
|
|
587
|
-
/**
|
|
588
|
-
* Get the current memory.
|
|
589
|
-
*/
|
|
693
|
+
/** Get the current memory */
|
|
590
694
|
getMemory() {
|
|
591
695
|
return this.memory;
|
|
592
696
|
}
|
|
593
|
-
/**
|
|
594
|
-
* Get registered tools.
|
|
595
|
-
*/
|
|
697
|
+
/** Get registered tools */
|
|
596
698
|
getTools() {
|
|
597
699
|
return this.tools;
|
|
598
700
|
}
|
|
599
|
-
/**
|
|
600
|
-
* Add a tool to the agent.
|
|
601
|
-
*/
|
|
701
|
+
/** Add a tool to the agent */
|
|
602
702
|
addTool(tool) {
|
|
603
703
|
this.tools.set(tool.name, tool);
|
|
604
704
|
}
|
|
605
|
-
/**
|
|
606
|
-
* Remove a tool from the agent.
|
|
607
|
-
*/
|
|
705
|
+
/** Remove a tool from the agent */
|
|
608
706
|
removeTool(name) {
|
|
609
707
|
return this.tools.delete(name);
|
|
610
708
|
}
|
|
611
|
-
/**
|
|
612
|
-
|
|
613
|
-
|
|
709
|
+
/** Get agent name */
|
|
710
|
+
getName() {
|
|
711
|
+
return this.config.name;
|
|
712
|
+
}
|
|
713
|
+
/** Sleep for a specified duration */
|
|
614
714
|
sleep(ms) {
|
|
615
|
-
return new Promise((
|
|
715
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
616
716
|
}
|
|
617
717
|
};
|
|
618
718
|
|
|
@@ -1096,9 +1196,6 @@ var Tool = class {
|
|
|
1096
1196
|
}
|
|
1097
1197
|
providedKeys.delete(key);
|
|
1098
1198
|
}
|
|
1099
|
-
if (providedKeys.size > 0) {
|
|
1100
|
-
console.warn(`Unknown arguments provided to ${this.name}: ${[...providedKeys].join(", ")}`);
|
|
1101
|
-
}
|
|
1102
1199
|
}
|
|
1103
1200
|
/**
|
|
1104
1201
|
* Check if a value matches the expected type.
|
|
@@ -1141,6 +1238,62 @@ ${argsDoc}
|
|
|
1141
1238
|
async function ${this.name}(${argsSignature}): Promise<${this.typeToJsType(this.outputType)}> { ... }
|
|
1142
1239
|
`.trim();
|
|
1143
1240
|
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Generate an OpenAI-compatible tool definition for function calling.
|
|
1243
|
+
*/
|
|
1244
|
+
toOpenAITool() {
|
|
1245
|
+
const properties = {};
|
|
1246
|
+
const required = [];
|
|
1247
|
+
for (const [key, input] of Object.entries(this.inputs)) {
|
|
1248
|
+
const prop = {
|
|
1249
|
+
type: this.typeToJsonSchemaType(input.type),
|
|
1250
|
+
description: input.description
|
|
1251
|
+
};
|
|
1252
|
+
if (input.enum) {
|
|
1253
|
+
prop.enum = input.enum;
|
|
1254
|
+
}
|
|
1255
|
+
if (input.default !== void 0) {
|
|
1256
|
+
prop.default = input.default;
|
|
1257
|
+
}
|
|
1258
|
+
properties[key] = prop;
|
|
1259
|
+
if (input.required !== false) {
|
|
1260
|
+
required.push(key);
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
return {
|
|
1264
|
+
type: "function",
|
|
1265
|
+
function: {
|
|
1266
|
+
name: this.name,
|
|
1267
|
+
description: this.description,
|
|
1268
|
+
parameters: {
|
|
1269
|
+
type: "object",
|
|
1270
|
+
properties,
|
|
1271
|
+
...required.length > 0 && { required }
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
};
|
|
1275
|
+
}
|
|
1276
|
+
/**
|
|
1277
|
+
* Convert tool input type to JSON Schema type.
|
|
1278
|
+
*/
|
|
1279
|
+
typeToJsonSchemaType(type) {
|
|
1280
|
+
switch (type) {
|
|
1281
|
+
case "string":
|
|
1282
|
+
return "string";
|
|
1283
|
+
case "number":
|
|
1284
|
+
return "number";
|
|
1285
|
+
case "boolean":
|
|
1286
|
+
return "boolean";
|
|
1287
|
+
case "array":
|
|
1288
|
+
return "array";
|
|
1289
|
+
case "object":
|
|
1290
|
+
return "object";
|
|
1291
|
+
case "any":
|
|
1292
|
+
return "string";
|
|
1293
|
+
default:
|
|
1294
|
+
return "string";
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1144
1297
|
/**
|
|
1145
1298
|
* Convert tool input type to JS/TS type string.
|
|
1146
1299
|
*/
|
|
@@ -1228,12 +1381,12 @@ var UserInputTool = class extends Tool {
|
|
|
1228
1381
|
input: process.stdin,
|
|
1229
1382
|
output: process.stdout
|
|
1230
1383
|
});
|
|
1231
|
-
return new Promise((
|
|
1384
|
+
return new Promise((resolve5) => {
|
|
1232
1385
|
rl.question(`
|
|
1233
1386
|
[Agent asks]: ${question}
|
|
1234
1387
|
Your response: `, (answer) => {
|
|
1235
1388
|
rl.close();
|
|
1236
|
-
|
|
1389
|
+
resolve5(answer);
|
|
1237
1390
|
});
|
|
1238
1391
|
});
|
|
1239
1392
|
}
|
|
@@ -1498,7 +1651,9 @@ ${result.error.message}`;
|
|
|
1498
1651
|
this.logger.subheader("Agent thinking...");
|
|
1499
1652
|
let fullContent = "";
|
|
1500
1653
|
const generator = this.model.generateStream(messages, {
|
|
1501
|
-
stopSequences: ["Observation:", "Observation:\n"]
|
|
1654
|
+
stopSequences: ["Observation:", "Observation:\n"],
|
|
1655
|
+
maxTokens: this.config.maxTokens,
|
|
1656
|
+
temperature: this.config.temperature
|
|
1502
1657
|
});
|
|
1503
1658
|
for await (const chunk of generator) {
|
|
1504
1659
|
this.logger.streamChar(chunk);
|
|
@@ -1512,7 +1667,9 @@ ${result.error.message}`;
|
|
|
1512
1667
|
} else {
|
|
1513
1668
|
this.logger.subheader("Agent thinking...");
|
|
1514
1669
|
return this.model.generate(messages, {
|
|
1515
|
-
stopSequences: ["Observation:", "Observation:\n"]
|
|
1670
|
+
stopSequences: ["Observation:", "Observation:\n"],
|
|
1671
|
+
maxTokens: this.config.maxTokens,
|
|
1672
|
+
temperature: this.config.temperature
|
|
1516
1673
|
});
|
|
1517
1674
|
}
|
|
1518
1675
|
}
|
|
@@ -1568,6 +1725,263 @@ ${parts.join("\n\n")}`;
|
|
|
1568
1725
|
}
|
|
1569
1726
|
};
|
|
1570
1727
|
|
|
1728
|
+
// src/prompts/toolUseAgent.ts
|
|
1729
|
+
function generateToolUseSystemPrompt(variables) {
|
|
1730
|
+
const { tools, customInstructions, hasSubAgents, hasFileTools } = variables;
|
|
1731
|
+
let contentGuidelines = "";
|
|
1732
|
+
if (hasFileTools) {
|
|
1733
|
+
contentGuidelines += `
|
|
1734
|
+
## Content Output Guidelines
|
|
1735
|
+
|
|
1736
|
+
When you produce long-form content (reports, articles, analyses, or any output longer than a few paragraphs):
|
|
1737
|
+
1. **Save to file**: Use \`write_file\` to save the full content to a descriptively-named file (e.g., \`research_report.md\`, \`analysis_results.md\`).
|
|
1738
|
+
2. **Return summary + filename**: In your \`final_answer\`, provide a brief summary of what you produced AND the filename where the full content is saved. Example: "Completed the research report covering X, Y, Z. Full report saved to: research_report.md"
|
|
1739
|
+
|
|
1740
|
+
This ensures managing agents can access your full output via \`read_file\` without it being truncated in message passing.
|
|
1741
|
+
`;
|
|
1742
|
+
}
|
|
1743
|
+
if (hasSubAgents) {
|
|
1744
|
+
contentGuidelines += `
|
|
1745
|
+
## Working with Sub-Agents
|
|
1746
|
+
|
|
1747
|
+
When you delegate tasks to sub-agents:
|
|
1748
|
+
- Sub-agents return a **summary and filename** rather than the full content of long-form outputs.
|
|
1749
|
+
- To access the full content a sub-agent produced, use \`read_file\` with the filename they provide.
|
|
1750
|
+
- **Do NOT re-invoke a sub-agent to retrieve content it already created.** Instead, read the file directly.
|
|
1751
|
+
- When composing your own final output from sub-agent results, read their files as needed and synthesize.
|
|
1752
|
+
`;
|
|
1753
|
+
}
|
|
1754
|
+
if (hasSubAgents && hasFileTools) {
|
|
1755
|
+
contentGuidelines += `
|
|
1756
|
+
## When You Are Both a Manager and a Sub-Agent
|
|
1757
|
+
|
|
1758
|
+
If you manage sub-agents AND are yourself delegated tasks by a parent agent:
|
|
1759
|
+
- Follow the sub-agent content guidelines: save your own long-form output to a file, return summary + filename.
|
|
1760
|
+
- Follow the manager guidelines: read sub-agent output files rather than re-calling them.
|
|
1761
|
+
`;
|
|
1762
|
+
}
|
|
1763
|
+
return `You are an expert assistant and problem-solving agent. You solve tasks by reasoning step by step and using the tools available to you.
|
|
1764
|
+
|
|
1765
|
+
## How You Work
|
|
1766
|
+
|
|
1767
|
+
You follow a ReAct (Reasoning + Acting) framework:
|
|
1768
|
+
1. **Think**: Analyze the current situation and decide what to do next
|
|
1769
|
+
2. **Act**: Call one or more tools to perform actions
|
|
1770
|
+
3. **Observe**: Review the results of your tool calls
|
|
1771
|
+
4. Repeat until you have the final answer
|
|
1772
|
+
|
|
1773
|
+
## Available Tools
|
|
1774
|
+
|
|
1775
|
+
${tools}
|
|
1776
|
+
|
|
1777
|
+
## Rules
|
|
1778
|
+
|
|
1779
|
+
1. **Always use final_answer**: When you have the complete answer, call the \`final_answer\` tool to return it. This is mandatory - you must always end by calling final_answer.
|
|
1780
|
+
|
|
1781
|
+
2. **Think before acting**: Provide your reasoning in the content of your response before making tool calls. This helps track your thought process.
|
|
1782
|
+
|
|
1783
|
+
3. **One logical action per step**: Focus on one logical action per step. You may call multiple tools in a single step if they are independent, but avoid chaining dependent operations without reviewing intermediate results.
|
|
1784
|
+
|
|
1785
|
+
4. **Handle errors gracefully**: If a tool call fails, analyze the error and try a different approach.
|
|
1786
|
+
|
|
1787
|
+
5. **Be concise**: Keep your reasoning brief and focused. Don't over-explain.
|
|
1788
|
+
|
|
1789
|
+
6. **Use the right tool**: Choose the most appropriate tool for each action. Don't try to accomplish something a tool can do through other means.
|
|
1790
|
+
${contentGuidelines}
|
|
1791
|
+
${customInstructions ? `## Additional Instructions
|
|
1792
|
+
|
|
1793
|
+
${customInstructions}` : ""}
|
|
1794
|
+
|
|
1795
|
+
Now, let's solve the task step by step. Think carefully about what tools to use and in what order.`;
|
|
1796
|
+
}
|
|
1797
|
+
function formatToolDescriptions(tools) {
|
|
1798
|
+
return tools.map((tool) => {
|
|
1799
|
+
const params = Object.entries(tool.inputs).map(([name, input]) => {
|
|
1800
|
+
const req = input.required !== false ? " (required)" : " (optional)";
|
|
1801
|
+
return ` - ${name}${req}: ${input.description}`;
|
|
1802
|
+
}).join("\n");
|
|
1803
|
+
return `### ${tool.name}
|
|
1804
|
+
${tool.description}
|
|
1805
|
+
Parameters:
|
|
1806
|
+
${params}`;
|
|
1807
|
+
}).join("\n\n");
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
// src/agents/ToolUseAgent.ts
|
|
1811
|
+
var ToolUseAgent = class extends Agent {
|
|
1812
|
+
parallelToolCalls;
|
|
1813
|
+
constructor(config) {
|
|
1814
|
+
super(config);
|
|
1815
|
+
this.parallelToolCalls = config.parallelToolCalls ?? true;
|
|
1816
|
+
if (!this.tools.has("final_answer")) {
|
|
1817
|
+
this.tools.set("final_answer", new FinalAnswerTool());
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
/**
|
|
1821
|
+
* Initialize the system prompt with tool descriptions.
|
|
1822
|
+
*/
|
|
1823
|
+
initializeSystemPrompt() {
|
|
1824
|
+
const toolList = Array.from(this.tools.values());
|
|
1825
|
+
const toolDescriptions = formatToolDescriptions(
|
|
1826
|
+
toolList.map((t) => ({
|
|
1827
|
+
name: t.name,
|
|
1828
|
+
description: t.description,
|
|
1829
|
+
inputs: t.inputs
|
|
1830
|
+
}))
|
|
1831
|
+
);
|
|
1832
|
+
const toolNames = new Set(Array.from(this.tools.keys()));
|
|
1833
|
+
const hasSubAgents = toolList.some((t) => t.constructor.name === "AgentTool");
|
|
1834
|
+
const hasFileTools = toolNames.has("read_file") || toolNames.has("write_file") || toolNames.has("read") || toolNames.has("write");
|
|
1835
|
+
return generateToolUseSystemPrompt({
|
|
1836
|
+
tools: toolDescriptions,
|
|
1837
|
+
customInstructions: this.config.customInstructions,
|
|
1838
|
+
hasSubAgents,
|
|
1839
|
+
hasFileTools
|
|
1840
|
+
});
|
|
1841
|
+
}
|
|
1842
|
+
/**
|
|
1843
|
+
* Execute a single step: send messages with tool definitions, process tool calls.
|
|
1844
|
+
*/
|
|
1845
|
+
async executeStep(memoryStep) {
|
|
1846
|
+
const messages = this.memory.toMessages();
|
|
1847
|
+
memoryStep.modelInputMessages = [...messages];
|
|
1848
|
+
const actionSteps = this.memory.getActionSteps();
|
|
1849
|
+
const prevStep = actionSteps.length >= 2 ? actionSteps[actionSteps.length - 2] : void 0;
|
|
1850
|
+
if (prevStep?.error) {
|
|
1851
|
+
messages.push({
|
|
1852
|
+
role: "user",
|
|
1853
|
+
content: `Your previous action encountered an error: ${prevStep.error.message}
|
|
1854
|
+
Please try a different approach.`
|
|
1855
|
+
});
|
|
1856
|
+
}
|
|
1857
|
+
const toolDefinitions = Array.from(this.tools.values()).map((t) => t.toOpenAITool());
|
|
1858
|
+
this.logger.subheader("Agent thinking...");
|
|
1859
|
+
const response = await this.model.generate(messages, {
|
|
1860
|
+
toolDefinitions,
|
|
1861
|
+
maxTokens: this.config.maxTokens,
|
|
1862
|
+
temperature: this.config.temperature
|
|
1863
|
+
});
|
|
1864
|
+
memoryStep.modelOutputMessage = response;
|
|
1865
|
+
memoryStep.tokenUsage = response.tokenUsage;
|
|
1866
|
+
if (response.content && response.content.trim()) {
|
|
1867
|
+
this.logger.reasoning(response.content.trim());
|
|
1868
|
+
}
|
|
1869
|
+
if (!response.toolCalls || response.toolCalls.length === 0) {
|
|
1870
|
+
this.logger.warn("No tool calls in response. Prompting model to use tools.");
|
|
1871
|
+
memoryStep.observation = "You must use tools to complete the task. Please call the appropriate tool(s). When you have the final answer, call the final_answer tool.";
|
|
1872
|
+
return { output: null, isFinalAnswer: false };
|
|
1873
|
+
}
|
|
1874
|
+
memoryStep.toolCalls = response.toolCalls;
|
|
1875
|
+
const toolResults = await this.processToolCalls(response.toolCalls);
|
|
1876
|
+
memoryStep.toolResults = toolResults;
|
|
1877
|
+
for (const result of toolResults) {
|
|
1878
|
+
if (result.toolName === "final_answer") {
|
|
1879
|
+
return { output: result.result, isFinalAnswer: true };
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
for (const result of toolResults) {
|
|
1883
|
+
if (result.error) {
|
|
1884
|
+
this.logger.error(`Tool ${result.toolName} failed: ${result.error}`);
|
|
1885
|
+
this.emitEvent("agent_error", { tool: result.toolName, error: result.error });
|
|
1886
|
+
} else {
|
|
1887
|
+
const resultStr = typeof result.result === "string" ? result.result : JSON.stringify(result.result, null, 2);
|
|
1888
|
+
this.logger.output(`[${result.toolName}]: ${resultStr.slice(0, 500)}${resultStr.length > 500 ? "..." : ""}`);
|
|
1889
|
+
this.emitEvent("agent_observation", { tool: result.toolName, result: resultStr.slice(0, 500) });
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
return { output: null, isFinalAnswer: false };
|
|
1893
|
+
}
|
|
1894
|
+
/**
|
|
1895
|
+
* Process tool calls from the model response.
|
|
1896
|
+
*/
|
|
1897
|
+
async processToolCalls(toolCalls) {
|
|
1898
|
+
const results = [];
|
|
1899
|
+
const executeTool = async (tc) => {
|
|
1900
|
+
const toolName = tc.function.name;
|
|
1901
|
+
const tool = this.tools.get(toolName);
|
|
1902
|
+
if (!tool) {
|
|
1903
|
+
return {
|
|
1904
|
+
toolCallId: tc.id,
|
|
1905
|
+
toolName,
|
|
1906
|
+
result: null,
|
|
1907
|
+
error: `Unknown tool: ${toolName}. Available tools: ${Array.from(this.tools.keys()).join(", ")}`
|
|
1908
|
+
};
|
|
1909
|
+
}
|
|
1910
|
+
let args;
|
|
1911
|
+
try {
|
|
1912
|
+
args = typeof tc.function.arguments === "string" ? JSON.parse(tc.function.arguments) : tc.function.arguments;
|
|
1913
|
+
} catch {
|
|
1914
|
+
return {
|
|
1915
|
+
toolCallId: tc.id,
|
|
1916
|
+
toolName,
|
|
1917
|
+
result: null,
|
|
1918
|
+
error: `Failed to parse tool arguments: ${tc.function.arguments}`
|
|
1919
|
+
};
|
|
1920
|
+
}
|
|
1921
|
+
this.logger.info(` Calling tool: ${toolName}(${JSON.stringify(args).slice(0, 100)}...)`);
|
|
1922
|
+
this.emitEvent("agent_tool_call", { tool: toolName, args });
|
|
1923
|
+
try {
|
|
1924
|
+
const result = await tool.call(args);
|
|
1925
|
+
return {
|
|
1926
|
+
toolCallId: tc.id,
|
|
1927
|
+
toolName,
|
|
1928
|
+
result
|
|
1929
|
+
};
|
|
1930
|
+
} catch (error) {
|
|
1931
|
+
return {
|
|
1932
|
+
toolCallId: tc.id,
|
|
1933
|
+
toolName,
|
|
1934
|
+
result: null,
|
|
1935
|
+
error: `Tool execution error: ${error.message}`
|
|
1936
|
+
};
|
|
1937
|
+
}
|
|
1938
|
+
};
|
|
1939
|
+
if (this.parallelToolCalls) {
|
|
1940
|
+
const promises = toolCalls.map((tc) => executeTool(tc));
|
|
1941
|
+
const resolvedResults = await Promise.all(promises);
|
|
1942
|
+
results.push(...resolvedResults);
|
|
1943
|
+
} else {
|
|
1944
|
+
for (const tc of toolCalls) {
|
|
1945
|
+
results.push(await executeTool(tc));
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
return results;
|
|
1949
|
+
}
|
|
1950
|
+
/**
|
|
1951
|
+
* Override provideFinalAnswer to use tool calling format.
|
|
1952
|
+
*/
|
|
1953
|
+
async provideFinalAnswer(task) {
|
|
1954
|
+
this.logger.subheader("Generating final answer from accumulated context");
|
|
1955
|
+
const messages = this.memory.toMessages();
|
|
1956
|
+
messages.push({
|
|
1957
|
+
role: "user",
|
|
1958
|
+
content: `You have reached the maximum number of steps. Based on your work so far, provide the best answer for the task: "${task}". Call the final_answer tool with your response.`
|
|
1959
|
+
});
|
|
1960
|
+
const toolDefinitions = [new FinalAnswerTool().toOpenAITool()];
|
|
1961
|
+
const response = await this.model.generate(messages, {
|
|
1962
|
+
toolDefinitions,
|
|
1963
|
+
maxTokens: this.config.maxTokens,
|
|
1964
|
+
temperature: this.config.temperature
|
|
1965
|
+
});
|
|
1966
|
+
if (response.toolCalls && response.toolCalls.length > 0) {
|
|
1967
|
+
const tc = response.toolCalls[0];
|
|
1968
|
+
try {
|
|
1969
|
+
const args = typeof tc.function.arguments === "string" ? JSON.parse(tc.function.arguments) : tc.function.arguments;
|
|
1970
|
+
return args.answer;
|
|
1971
|
+
} catch {
|
|
1972
|
+
return response.content;
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
return response.content;
|
|
1976
|
+
}
|
|
1977
|
+
/**
|
|
1978
|
+
* Add a tool, which can also be an Agent instance (auto-wraps with AgentTool).
|
|
1979
|
+
*/
|
|
1980
|
+
addTool(tool) {
|
|
1981
|
+
super.addTool(tool);
|
|
1982
|
+
}
|
|
1983
|
+
};
|
|
1984
|
+
|
|
1571
1985
|
// src/models/Model.ts
|
|
1572
1986
|
var Model = class {
|
|
1573
1987
|
/**
|
|
@@ -1597,25 +2011,18 @@ var Model = class {
|
|
|
1597
2011
|
|
|
1598
2012
|
// src/models/OpenAIModel.ts
|
|
1599
2013
|
import OpenAI from "openai";
|
|
1600
|
-
var
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
maxTokens: 65e3,
|
|
1604
|
-
temperature: 1,
|
|
1605
|
-
timeout: 12e4
|
|
1606
|
-
};
|
|
2014
|
+
var DEFAULT_MODEL_ID = "anthropic/claude-sonnet-4.5";
|
|
2015
|
+
var DEFAULT_BASE_URL = "https://openrouter.ai/api/v1";
|
|
2016
|
+
var DEFAULT_TIMEOUT = 12e4;
|
|
1607
2017
|
var OpenAIModel = class extends Model {
|
|
1608
2018
|
modelId;
|
|
1609
2019
|
client;
|
|
1610
2020
|
config;
|
|
1611
2021
|
constructor(config = {}) {
|
|
1612
2022
|
super();
|
|
1613
|
-
this.config =
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
};
|
|
1617
|
-
this.modelId = this.config.modelId ?? DEFAULT_CONFIG.modelId;
|
|
1618
|
-
const apiKey = this.config.apiKey ?? process.env.OPENAI_API_KEY ?? process.env.OPENROUTER_API_KEY;
|
|
2023
|
+
this.config = config;
|
|
2024
|
+
this.modelId = config.modelId ?? DEFAULT_MODEL_ID;
|
|
2025
|
+
const apiKey = config.apiKey ?? process.env.OPENAI_API_KEY ?? process.env.OPENROUTER_API_KEY;
|
|
1619
2026
|
if (!apiKey) {
|
|
1620
2027
|
throw new Error(
|
|
1621
2028
|
"API key is required. Set OPENAI_API_KEY or OPENROUTER_API_KEY environment variable, or pass apiKey in config."
|
|
@@ -1623,22 +2030,40 @@ var OpenAIModel = class extends Model {
|
|
|
1623
2030
|
}
|
|
1624
2031
|
this.client = new OpenAI({
|
|
1625
2032
|
apiKey,
|
|
1626
|
-
baseURL:
|
|
1627
|
-
timeout:
|
|
1628
|
-
defaultHeaders:
|
|
2033
|
+
baseURL: config.baseUrl ?? DEFAULT_BASE_URL,
|
|
2034
|
+
timeout: config.timeout ?? DEFAULT_TIMEOUT,
|
|
2035
|
+
defaultHeaders: config.defaultHeaders
|
|
1629
2036
|
});
|
|
1630
2037
|
}
|
|
1631
2038
|
/**
|
|
1632
|
-
* Generate a response from the model.
|
|
2039
|
+
* Generate a response from the model (supports tool calling).
|
|
1633
2040
|
*/
|
|
1634
2041
|
async generate(messages, options = {}) {
|
|
1635
2042
|
const formattedMessages = this.formatMessages(messages);
|
|
2043
|
+
const requestParams = {
|
|
2044
|
+
model: this.modelId,
|
|
2045
|
+
messages: formattedMessages
|
|
2046
|
+
};
|
|
2047
|
+
const maxTokens = options.maxTokens ?? this.config.maxTokens;
|
|
2048
|
+
if (maxTokens !== void 0) {
|
|
2049
|
+
requestParams.max_tokens = maxTokens;
|
|
2050
|
+
}
|
|
2051
|
+
const temperature = options.temperature ?? this.config.temperature;
|
|
2052
|
+
if (temperature !== void 0) {
|
|
2053
|
+
requestParams.temperature = temperature;
|
|
2054
|
+
}
|
|
2055
|
+
if (options.stopSequences) {
|
|
2056
|
+
requestParams.stop = options.stopSequences;
|
|
2057
|
+
}
|
|
2058
|
+
if (options.toolDefinitions && options.toolDefinitions.length > 0) {
|
|
2059
|
+
requestParams.tools = options.toolDefinitions;
|
|
2060
|
+
} else if (options.tools && options.tools.length > 0) {
|
|
2061
|
+
requestParams.tools = options.tools.map((t) => t.toOpenAITool());
|
|
2062
|
+
}
|
|
1636
2063
|
const response = await this.client.chat.completions.create({
|
|
2064
|
+
...requestParams,
|
|
1637
2065
|
model: this.modelId,
|
|
1638
|
-
messages: formattedMessages
|
|
1639
|
-
max_tokens: options.maxTokens ?? this.config.maxTokens,
|
|
1640
|
-
temperature: options.temperature ?? this.config.temperature,
|
|
1641
|
-
...options.stopSequences && { stop: options.stopSequences }
|
|
2066
|
+
messages: formattedMessages
|
|
1642
2067
|
});
|
|
1643
2068
|
const choice = response.choices[0];
|
|
1644
2069
|
const message = choice?.message;
|
|
@@ -1647,12 +2072,21 @@ var OpenAIModel = class extends Model {
|
|
|
1647
2072
|
}
|
|
1648
2073
|
const tokenUsage = response.usage ? {
|
|
1649
2074
|
inputTokens: response.usage.prompt_tokens,
|
|
1650
|
-
outputTokens: response.usage.completion_tokens,
|
|
2075
|
+
outputTokens: response.usage.completion_tokens ?? 0,
|
|
1651
2076
|
totalTokens: response.usage.total_tokens
|
|
1652
2077
|
} : void 0;
|
|
2078
|
+
const toolCalls = message.tool_calls?.map((tc) => ({
|
|
2079
|
+
id: tc.id,
|
|
2080
|
+
type: "function",
|
|
2081
|
+
function: {
|
|
2082
|
+
name: tc.function.name,
|
|
2083
|
+
arguments: tc.function.arguments
|
|
2084
|
+
}
|
|
2085
|
+
}));
|
|
1653
2086
|
return {
|
|
1654
2087
|
role: "assistant",
|
|
1655
2088
|
content: message.content ?? "",
|
|
2089
|
+
toolCalls,
|
|
1656
2090
|
tokenUsage
|
|
1657
2091
|
};
|
|
1658
2092
|
}
|
|
@@ -1661,12 +2095,26 @@ var OpenAIModel = class extends Model {
|
|
|
1661
2095
|
*/
|
|
1662
2096
|
async *generateStream(messages, options = {}) {
|
|
1663
2097
|
const formattedMessages = this.formatMessages(messages);
|
|
2098
|
+
const requestParams = {
|
|
2099
|
+
model: this.modelId,
|
|
2100
|
+
messages: formattedMessages,
|
|
2101
|
+
stream: true
|
|
2102
|
+
};
|
|
2103
|
+
const maxTokens = options.maxTokens ?? this.config.maxTokens;
|
|
2104
|
+
if (maxTokens !== void 0) {
|
|
2105
|
+
requestParams.max_tokens = maxTokens;
|
|
2106
|
+
}
|
|
2107
|
+
const temperature = options.temperature ?? this.config.temperature;
|
|
2108
|
+
if (temperature !== void 0) {
|
|
2109
|
+
requestParams.temperature = temperature;
|
|
2110
|
+
}
|
|
2111
|
+
if (options.stopSequences) {
|
|
2112
|
+
requestParams.stop = options.stopSequences;
|
|
2113
|
+
}
|
|
1664
2114
|
const stream = await this.client.chat.completions.create({
|
|
2115
|
+
...requestParams,
|
|
1665
2116
|
model: this.modelId,
|
|
1666
2117
|
messages: formattedMessages,
|
|
1667
|
-
max_tokens: options.maxTokens ?? this.config.maxTokens,
|
|
1668
|
-
temperature: options.temperature ?? this.config.temperature,
|
|
1669
|
-
...options.stopSequences && { stop: options.stopSequences },
|
|
1670
2118
|
stream: true
|
|
1671
2119
|
});
|
|
1672
2120
|
let fullContent = "";
|
|
@@ -1683,16 +2131,37 @@ var OpenAIModel = class extends Model {
|
|
|
1683
2131
|
};
|
|
1684
2132
|
}
|
|
1685
2133
|
/**
|
|
1686
|
-
* Format messages for the OpenAI API.
|
|
2134
|
+
* Format messages for the OpenAI API, including tool call/response messages.
|
|
1687
2135
|
*/
|
|
1688
2136
|
formatMessages(messages) {
|
|
1689
2137
|
return messages.map((msg) => {
|
|
2138
|
+
if (msg.role === "tool" && msg.toolCallId) {
|
|
2139
|
+
return {
|
|
2140
|
+
role: "tool",
|
|
2141
|
+
content: msg.content ?? "",
|
|
2142
|
+
tool_call_id: msg.toolCallId
|
|
2143
|
+
};
|
|
2144
|
+
}
|
|
1690
2145
|
if (msg.role === "tool") {
|
|
1691
2146
|
return {
|
|
1692
2147
|
role: "user",
|
|
1693
2148
|
content: msg.content ?? ""
|
|
1694
2149
|
};
|
|
1695
2150
|
}
|
|
2151
|
+
if (msg.role === "assistant" && msg.toolCalls && msg.toolCalls.length > 0) {
|
|
2152
|
+
return {
|
|
2153
|
+
role: "assistant",
|
|
2154
|
+
content: msg.content || null,
|
|
2155
|
+
tool_calls: msg.toolCalls.map((tc) => ({
|
|
2156
|
+
id: tc.id,
|
|
2157
|
+
type: "function",
|
|
2158
|
+
function: {
|
|
2159
|
+
name: tc.function.name,
|
|
2160
|
+
arguments: typeof tc.function.arguments === "string" ? tc.function.arguments : JSON.stringify(tc.function.arguments)
|
|
2161
|
+
}
|
|
2162
|
+
}))
|
|
2163
|
+
};
|
|
2164
|
+
}
|
|
1696
2165
|
return {
|
|
1697
2166
|
role: msg.role,
|
|
1698
2167
|
content: msg.content ?? ""
|
|
@@ -1775,24 +2244,848 @@ async function ${this.name}(task: string): Promise<string> { ... }
|
|
|
1775
2244
|
function agentAsTool(agent, options) {
|
|
1776
2245
|
return new AgentTool({ agent, ...options });
|
|
1777
2246
|
}
|
|
2247
|
+
|
|
2248
|
+
// src/tools/ReadFileTool.ts
|
|
2249
|
+
import * as fs3 from "fs";
|
|
2250
|
+
import * as path3 from "path";
|
|
2251
|
+
var ReadFileTool = class extends Tool {
|
|
2252
|
+
name = "read_file";
|
|
2253
|
+
description = "Read the contents of a file at the specified path. Returns the file content as a string.";
|
|
2254
|
+
inputs = {
|
|
2255
|
+
path: {
|
|
2256
|
+
type: "string",
|
|
2257
|
+
description: "The file path to read (absolute or relative to working directory)",
|
|
2258
|
+
required: true
|
|
2259
|
+
},
|
|
2260
|
+
encoding: {
|
|
2261
|
+
type: "string",
|
|
2262
|
+
description: "File encoding (default: utf-8)",
|
|
2263
|
+
required: false,
|
|
2264
|
+
default: "utf-8"
|
|
2265
|
+
}
|
|
2266
|
+
};
|
|
2267
|
+
outputType = "string";
|
|
2268
|
+
workingDirectory;
|
|
2269
|
+
constructor(config) {
|
|
2270
|
+
super();
|
|
2271
|
+
this.workingDirectory = config?.workingDirectory ?? process.cwd();
|
|
2272
|
+
}
|
|
2273
|
+
async execute(args) {
|
|
2274
|
+
const filePath = args.path;
|
|
2275
|
+
const encoding = args.encoding ?? "utf-8";
|
|
2276
|
+
const resolvedPath = path3.isAbsolute(filePath) ? filePath : path3.resolve(this.workingDirectory, filePath);
|
|
2277
|
+
if (!fs3.existsSync(resolvedPath)) {
|
|
2278
|
+
throw new Error(`File not found: ${resolvedPath}`);
|
|
2279
|
+
}
|
|
2280
|
+
const stat = fs3.statSync(resolvedPath);
|
|
2281
|
+
if (stat.isDirectory()) {
|
|
2282
|
+
throw new Error(`Path is a directory, not a file: ${resolvedPath}`);
|
|
2283
|
+
}
|
|
2284
|
+
const content = fs3.readFileSync(resolvedPath, encoding);
|
|
2285
|
+
const maxLength = 1e5;
|
|
2286
|
+
if (content.length > maxLength) {
|
|
2287
|
+
return content.slice(0, maxLength) + `
|
|
2288
|
+
|
|
2289
|
+
[... truncated, file is ${content.length} characters total]`;
|
|
2290
|
+
}
|
|
2291
|
+
return content;
|
|
2292
|
+
}
|
|
2293
|
+
};
|
|
2294
|
+
|
|
2295
|
+
// src/tools/WriteFileTool.ts
|
|
2296
|
+
import * as fs4 from "fs";
|
|
2297
|
+
import * as path4 from "path";
|
|
2298
|
+
var WriteFileTool = class extends Tool {
|
|
2299
|
+
name = "write_file";
|
|
2300
|
+
description = "Write content to a file at the specified path. Creates the file if it does not exist, and creates parent directories as needed. Overwrites existing content by default.";
|
|
2301
|
+
inputs = {
|
|
2302
|
+
path: {
|
|
2303
|
+
type: "string",
|
|
2304
|
+
description: "The file path to write to (absolute or relative to working directory)",
|
|
2305
|
+
required: true
|
|
2306
|
+
},
|
|
2307
|
+
content: {
|
|
2308
|
+
type: "string",
|
|
2309
|
+
description: "The content to write to the file",
|
|
2310
|
+
required: true
|
|
2311
|
+
},
|
|
2312
|
+
append: {
|
|
2313
|
+
type: "boolean",
|
|
2314
|
+
description: "If true, append to the file instead of overwriting (default: false)",
|
|
2315
|
+
required: false,
|
|
2316
|
+
default: false
|
|
2317
|
+
}
|
|
2318
|
+
};
|
|
2319
|
+
outputType = "string";
|
|
2320
|
+
workingDirectory;
|
|
2321
|
+
constructor(config) {
|
|
2322
|
+
super();
|
|
2323
|
+
this.workingDirectory = config?.workingDirectory ?? process.cwd();
|
|
2324
|
+
}
|
|
2325
|
+
async execute(args) {
|
|
2326
|
+
const filePath = args.path;
|
|
2327
|
+
const content = args.content;
|
|
2328
|
+
const append = args.append ?? false;
|
|
2329
|
+
const resolvedPath = path4.isAbsolute(filePath) ? filePath : path4.resolve(this.workingDirectory, filePath);
|
|
2330
|
+
const dir = path4.dirname(resolvedPath);
|
|
2331
|
+
if (!fs4.existsSync(dir)) {
|
|
2332
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
2333
|
+
}
|
|
2334
|
+
if (append) {
|
|
2335
|
+
fs4.appendFileSync(resolvedPath, content, "utf-8");
|
|
2336
|
+
return `Successfully appended ${content.length} characters to ${resolvedPath}`;
|
|
2337
|
+
} else {
|
|
2338
|
+
fs4.writeFileSync(resolvedPath, content, "utf-8");
|
|
2339
|
+
return `Successfully wrote ${content.length} characters to ${resolvedPath}`;
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
};
|
|
2343
|
+
|
|
2344
|
+
// src/tools/CurlTool.ts
|
|
2345
|
+
var CurlTool = class extends Tool {
|
|
2346
|
+
name = "curl";
|
|
2347
|
+
description = "Make HTTP requests to any URL. Supports GET and POST methods with custom headers and body. Returns the response body as text.";
|
|
2348
|
+
inputs = {
|
|
2349
|
+
url: {
|
|
2350
|
+
type: "string",
|
|
2351
|
+
description: "The URL to request",
|
|
2352
|
+
required: true
|
|
2353
|
+
},
|
|
2354
|
+
method: {
|
|
2355
|
+
type: "string",
|
|
2356
|
+
description: "HTTP method: GET or POST (default: GET)",
|
|
2357
|
+
required: false,
|
|
2358
|
+
default: "GET",
|
|
2359
|
+
enum: ["GET", "POST"]
|
|
2360
|
+
},
|
|
2361
|
+
headers: {
|
|
2362
|
+
type: "object",
|
|
2363
|
+
description: 'Optional HTTP headers as key-value pairs (e.g., {"Content-Type": "application/json"})',
|
|
2364
|
+
required: false
|
|
2365
|
+
},
|
|
2366
|
+
body: {
|
|
2367
|
+
type: "string",
|
|
2368
|
+
description: "Request body for POST requests (typically JSON string)",
|
|
2369
|
+
required: false
|
|
2370
|
+
}
|
|
2371
|
+
};
|
|
2372
|
+
outputType = "string";
|
|
2373
|
+
timeout;
|
|
2374
|
+
constructor(config) {
|
|
2375
|
+
super();
|
|
2376
|
+
this.timeout = config?.timeout ?? 3e4;
|
|
2377
|
+
}
|
|
2378
|
+
async execute(args) {
|
|
2379
|
+
const url = args.url;
|
|
2380
|
+
const method = (args.method ?? "GET").toUpperCase();
|
|
2381
|
+
const headers = args.headers ?? {};
|
|
2382
|
+
const body = args.body;
|
|
2383
|
+
const controller = new AbortController();
|
|
2384
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
2385
|
+
try {
|
|
2386
|
+
const fetchOptions = {
|
|
2387
|
+
method,
|
|
2388
|
+
headers,
|
|
2389
|
+
signal: controller.signal
|
|
2390
|
+
};
|
|
2391
|
+
if (method === "POST" && body) {
|
|
2392
|
+
fetchOptions.body = body;
|
|
2393
|
+
if (!headers["Content-Type"] && !headers["content-type"]) {
|
|
2394
|
+
fetchOptions.headers["Content-Type"] = "application/json";
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
const response = await fetch(url, fetchOptions);
|
|
2398
|
+
const responseText = await response.text();
|
|
2399
|
+
const statusLine = `HTTP ${response.status} ${response.statusText}`;
|
|
2400
|
+
const maxLength = 5e4;
|
|
2401
|
+
const truncatedBody = responseText.length > maxLength ? responseText.slice(0, maxLength) + `
|
|
2402
|
+
|
|
2403
|
+
[... truncated, response is ${responseText.length} characters total]` : responseText;
|
|
2404
|
+
return `${statusLine}
|
|
2405
|
+
|
|
2406
|
+
${truncatedBody}`;
|
|
2407
|
+
} catch (error) {
|
|
2408
|
+
if (error.name === "AbortError") {
|
|
2409
|
+
throw new Error(`Request timed out after ${this.timeout}ms: ${url}`);
|
|
2410
|
+
}
|
|
2411
|
+
throw new Error(`HTTP request failed: ${error.message}`);
|
|
2412
|
+
} finally {
|
|
2413
|
+
clearTimeout(timeoutId);
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
};
|
|
2417
|
+
|
|
2418
|
+
// src/tools/ExaSearchTool.ts
|
|
2419
|
+
var ExaSearchTool = class extends Tool {
|
|
2420
|
+
name = "exa_search";
|
|
2421
|
+
description = "Search the web using Exa.ai semantic search. Returns relevant web pages with titles, URLs, and optionally content snippets. Use this for finding information, research, and fact-checking.";
|
|
2422
|
+
inputs = {
|
|
2423
|
+
query: {
|
|
2424
|
+
type: "string",
|
|
2425
|
+
description: "The search query. Be specific and descriptive for best results.",
|
|
2426
|
+
required: true
|
|
2427
|
+
},
|
|
2428
|
+
numResults: {
|
|
2429
|
+
type: "number",
|
|
2430
|
+
description: "Number of results to return (default: 10, max: 30)",
|
|
2431
|
+
required: false,
|
|
2432
|
+
default: 10
|
|
2433
|
+
},
|
|
2434
|
+
type: {
|
|
2435
|
+
type: "string",
|
|
2436
|
+
description: 'Search type: "auto" (default), "neural" (embeddings-based), or "keyword"',
|
|
2437
|
+
required: false,
|
|
2438
|
+
default: "auto",
|
|
2439
|
+
enum: ["auto", "neural", "keyword"]
|
|
2440
|
+
},
|
|
2441
|
+
category: {
|
|
2442
|
+
type: "string",
|
|
2443
|
+
description: 'Optional category filter: "research paper", "news", "pdf", "github", "tweet", "company", "blog"',
|
|
2444
|
+
required: false
|
|
2445
|
+
},
|
|
2446
|
+
includeDomains: {
|
|
2447
|
+
type: "array",
|
|
2448
|
+
description: 'Only include results from these domains (e.g., ["arxiv.org", "github.com"])',
|
|
2449
|
+
required: false
|
|
2450
|
+
},
|
|
2451
|
+
excludeDomains: {
|
|
2452
|
+
type: "array",
|
|
2453
|
+
description: "Exclude results from these domains",
|
|
2454
|
+
required: false
|
|
2455
|
+
},
|
|
2456
|
+
startPublishedDate: {
|
|
2457
|
+
type: "string",
|
|
2458
|
+
description: 'Filter results published after this ISO 8601 date (e.g., "2024-01-01")',
|
|
2459
|
+
required: false
|
|
2460
|
+
}
|
|
2461
|
+
};
|
|
2462
|
+
outputType = "string";
|
|
2463
|
+
apiKey;
|
|
2464
|
+
constructor(config) {
|
|
2465
|
+
super();
|
|
2466
|
+
this.apiKey = config?.apiKey ?? process.env.EXA_API_KEY ?? "";
|
|
2467
|
+
}
|
|
2468
|
+
async setup() {
|
|
2469
|
+
if (!this.apiKey) {
|
|
2470
|
+
throw new Error("EXA_API_KEY is required. Set it as an environment variable or pass it in the config.");
|
|
2471
|
+
}
|
|
2472
|
+
this.isSetup = true;
|
|
2473
|
+
}
|
|
2474
|
+
async execute(args) {
|
|
2475
|
+
const query = args.query;
|
|
2476
|
+
const numResults = Math.min(args.numResults ?? 10, 30);
|
|
2477
|
+
const type = args.type ?? "auto";
|
|
2478
|
+
const requestBody = {
|
|
2479
|
+
query,
|
|
2480
|
+
numResults,
|
|
2481
|
+
type,
|
|
2482
|
+
text: { maxCharacters: 1e3 }
|
|
2483
|
+
};
|
|
2484
|
+
if (args.category) requestBody.category = args.category;
|
|
2485
|
+
if (args.includeDomains) requestBody.includeDomains = args.includeDomains;
|
|
2486
|
+
if (args.excludeDomains) requestBody.excludeDomains = args.excludeDomains;
|
|
2487
|
+
if (args.startPublishedDate) requestBody.startPublishedDate = args.startPublishedDate;
|
|
2488
|
+
const response = await fetch("https://api.exa.ai/search", {
|
|
2489
|
+
method: "POST",
|
|
2490
|
+
headers: {
|
|
2491
|
+
"x-api-key": this.apiKey,
|
|
2492
|
+
"Content-Type": "application/json"
|
|
2493
|
+
},
|
|
2494
|
+
body: JSON.stringify(requestBody)
|
|
2495
|
+
});
|
|
2496
|
+
if (!response.ok) {
|
|
2497
|
+
const errorText = await response.text();
|
|
2498
|
+
throw new Error(`Exa search failed (${response.status}): ${errorText}`);
|
|
2499
|
+
}
|
|
2500
|
+
const data = await response.json();
|
|
2501
|
+
if (!data.results || data.results.length === 0) {
|
|
2502
|
+
return "No results found for the query.";
|
|
2503
|
+
}
|
|
2504
|
+
const formattedResults = data.results.map((result, i) => {
|
|
2505
|
+
const parts = [`[${i + 1}] ${result.title ?? "Untitled"}`];
|
|
2506
|
+
parts.push(` URL: ${result.url}`);
|
|
2507
|
+
if (result.publishedDate) parts.push(` Date: ${result.publishedDate}`);
|
|
2508
|
+
if (result.author) parts.push(` Author: ${result.author}`);
|
|
2509
|
+
if (result.text) {
|
|
2510
|
+
const snippet = result.text.slice(0, 300).trim();
|
|
2511
|
+
parts.push(` Snippet: ${snippet}${result.text.length > 300 ? "..." : ""}`);
|
|
2512
|
+
}
|
|
2513
|
+
return parts.join("\n");
|
|
2514
|
+
}).join("\n\n");
|
|
2515
|
+
return `Search results for "${query}" (${data.results.length} results):
|
|
2516
|
+
|
|
2517
|
+
${formattedResults}`;
|
|
2518
|
+
}
|
|
2519
|
+
};
|
|
2520
|
+
|
|
2521
|
+
// src/tools/ExaGetContentsTool.ts
|
|
2522
|
+
var ExaGetContentsTool = class extends Tool {
|
|
2523
|
+
name = "exa_get_contents";
|
|
2524
|
+
description = "Get the full text content of one or more web pages using Exa.ai. Returns cleaned, readable text extracted from the HTML. Use this to read articles, documentation, or any web page content.";
|
|
2525
|
+
inputs = {
|
|
2526
|
+
urls: {
|
|
2527
|
+
type: "array",
|
|
2528
|
+
description: "Array of URLs to fetch content from (max 10)",
|
|
2529
|
+
required: true
|
|
2530
|
+
},
|
|
2531
|
+
maxCharacters: {
|
|
2532
|
+
type: "number",
|
|
2533
|
+
description: "Maximum characters of content to return per page (default: 10000)",
|
|
2534
|
+
required: false,
|
|
2535
|
+
default: 1e4
|
|
2536
|
+
},
|
|
2537
|
+
livecrawl: {
|
|
2538
|
+
type: "string",
|
|
2539
|
+
description: 'Crawl strategy: "fallback" (use cache, fetch live if unavailable), "always" (always fetch live), "never" (cache only). Default: "fallback"',
|
|
2540
|
+
required: false,
|
|
2541
|
+
default: "fallback",
|
|
2542
|
+
enum: ["fallback", "always", "never"]
|
|
2543
|
+
}
|
|
2544
|
+
};
|
|
2545
|
+
outputType = "string";
|
|
2546
|
+
apiKey;
|
|
2547
|
+
constructor(config) {
|
|
2548
|
+
super();
|
|
2549
|
+
this.apiKey = config?.apiKey ?? process.env.EXA_API_KEY ?? "";
|
|
2550
|
+
}
|
|
2551
|
+
async setup() {
|
|
2552
|
+
if (!this.apiKey) {
|
|
2553
|
+
throw new Error("EXA_API_KEY is required. Set it as an environment variable or pass it in the config.");
|
|
2554
|
+
}
|
|
2555
|
+
this.isSetup = true;
|
|
2556
|
+
}
|
|
2557
|
+
async execute(args) {
|
|
2558
|
+
const urls = args.urls.slice(0, 10);
|
|
2559
|
+
const maxCharacters = args.maxCharacters ?? 1e4;
|
|
2560
|
+
const livecrawl = args.livecrawl ?? "fallback";
|
|
2561
|
+
const requestBody = {
|
|
2562
|
+
urls,
|
|
2563
|
+
text: { maxCharacters },
|
|
2564
|
+
livecrawl
|
|
2565
|
+
};
|
|
2566
|
+
const response = await fetch("https://api.exa.ai/contents", {
|
|
2567
|
+
method: "POST",
|
|
2568
|
+
headers: {
|
|
2569
|
+
"x-api-key": this.apiKey,
|
|
2570
|
+
"Content-Type": "application/json"
|
|
2571
|
+
},
|
|
2572
|
+
body: JSON.stringify(requestBody)
|
|
2573
|
+
});
|
|
2574
|
+
if (!response.ok) {
|
|
2575
|
+
const errorText = await response.text();
|
|
2576
|
+
throw new Error(`Exa get contents failed (${response.status}): ${errorText}`);
|
|
2577
|
+
}
|
|
2578
|
+
const data = await response.json();
|
|
2579
|
+
if (!data.results || data.results.length === 0) {
|
|
2580
|
+
return "No content could be retrieved from the provided URLs.";
|
|
2581
|
+
}
|
|
2582
|
+
const formattedResults = data.results.map((result) => {
|
|
2583
|
+
const parts = [`## ${result.title ?? result.url}`];
|
|
2584
|
+
parts.push(`URL: ${result.url}`);
|
|
2585
|
+
if (result.author) parts.push(`Author: ${result.author}`);
|
|
2586
|
+
if (result.publishedDate) parts.push(`Date: ${result.publishedDate}`);
|
|
2587
|
+
parts.push("");
|
|
2588
|
+
if (result.text) {
|
|
2589
|
+
parts.push(result.text);
|
|
2590
|
+
} else {
|
|
2591
|
+
parts.push("[No text content available]");
|
|
2592
|
+
}
|
|
2593
|
+
return parts.join("\n");
|
|
2594
|
+
}).join("\n\n---\n\n");
|
|
2595
|
+
return formattedResults;
|
|
2596
|
+
}
|
|
2597
|
+
};
|
|
2598
|
+
|
|
2599
|
+
// src/tools/ExaResearchTool.ts
|
|
2600
|
+
var ExaResearchTool = class extends Tool {
|
|
2601
|
+
name = "exa_research";
|
|
2602
|
+
description = "Perform deep research on a single topic using Exa.ai. Searches for relevant sources, retrieves their content, and finds similar pages for comprehensive coverage. Returns a structured research summary with sources. Use this for thorough research on any topic.";
|
|
2603
|
+
inputs = {
|
|
2604
|
+
topic: {
|
|
2605
|
+
type: "string",
|
|
2606
|
+
description: "The research topic or question to investigate",
|
|
2607
|
+
required: true
|
|
2608
|
+
},
|
|
2609
|
+
numSources: {
|
|
2610
|
+
type: "number",
|
|
2611
|
+
description: "Number of primary sources to retrieve (default: 5, max: 10)",
|
|
2612
|
+
required: false,
|
|
2613
|
+
default: 5
|
|
2614
|
+
},
|
|
2615
|
+
category: {
|
|
2616
|
+
type: "string",
|
|
2617
|
+
description: 'Optional category: "research paper", "news", "blog", "company"',
|
|
2618
|
+
required: false
|
|
2619
|
+
},
|
|
2620
|
+
includeDomains: {
|
|
2621
|
+
type: "array",
|
|
2622
|
+
description: "Only include results from these domains",
|
|
2623
|
+
required: false
|
|
2624
|
+
},
|
|
2625
|
+
startPublishedDate: {
|
|
2626
|
+
type: "string",
|
|
2627
|
+
description: "Only include results published after this date (ISO 8601)",
|
|
2628
|
+
required: false
|
|
2629
|
+
}
|
|
2630
|
+
};
|
|
2631
|
+
outputType = "string";
|
|
2632
|
+
apiKey;
|
|
2633
|
+
constructor(config) {
|
|
2634
|
+
super();
|
|
2635
|
+
this.apiKey = config?.apiKey ?? process.env.EXA_API_KEY ?? "";
|
|
2636
|
+
}
|
|
2637
|
+
async setup() {
|
|
2638
|
+
if (!this.apiKey) {
|
|
2639
|
+
throw new Error("EXA_API_KEY is required. Set it as an environment variable or pass it in the config.");
|
|
2640
|
+
}
|
|
2641
|
+
this.isSetup = true;
|
|
2642
|
+
}
|
|
2643
|
+
async execute(args) {
|
|
2644
|
+
const topic = args.topic;
|
|
2645
|
+
const numSources = Math.min(args.numSources ?? 5, 10);
|
|
2646
|
+
const searchBody = {
|
|
2647
|
+
query: topic,
|
|
2648
|
+
numResults: numSources,
|
|
2649
|
+
type: "auto",
|
|
2650
|
+
text: { maxCharacters: 3e3 }
|
|
2651
|
+
};
|
|
2652
|
+
if (args.category) searchBody.category = args.category;
|
|
2653
|
+
if (args.includeDomains) searchBody.includeDomains = args.includeDomains;
|
|
2654
|
+
if (args.startPublishedDate) searchBody.startPublishedDate = args.startPublishedDate;
|
|
2655
|
+
const searchResponse = await fetch("https://api.exa.ai/search", {
|
|
2656
|
+
method: "POST",
|
|
2657
|
+
headers: {
|
|
2658
|
+
"x-api-key": this.apiKey,
|
|
2659
|
+
"Content-Type": "application/json"
|
|
2660
|
+
},
|
|
2661
|
+
body: JSON.stringify(searchBody)
|
|
2662
|
+
});
|
|
2663
|
+
if (!searchResponse.ok) {
|
|
2664
|
+
const errorText = await searchResponse.text();
|
|
2665
|
+
throw new Error(`Exa research search failed (${searchResponse.status}): ${errorText}`);
|
|
2666
|
+
}
|
|
2667
|
+
const searchData = await searchResponse.json();
|
|
2668
|
+
if (!searchData.results || searchData.results.length === 0) {
|
|
2669
|
+
return `No research sources found for topic: "${topic}"`;
|
|
2670
|
+
}
|
|
2671
|
+
let similarResults = [];
|
|
2672
|
+
if (searchData.results.length > 0) {
|
|
2673
|
+
try {
|
|
2674
|
+
const similarBody = {
|
|
2675
|
+
url: searchData.results[0].url,
|
|
2676
|
+
numResults: 3,
|
|
2677
|
+
text: { maxCharacters: 2e3 }
|
|
2678
|
+
};
|
|
2679
|
+
const similarResponse = await fetch("https://api.exa.ai/findSimilar", {
|
|
2680
|
+
method: "POST",
|
|
2681
|
+
headers: {
|
|
2682
|
+
"x-api-key": this.apiKey,
|
|
2683
|
+
"Content-Type": "application/json"
|
|
2684
|
+
},
|
|
2685
|
+
body: JSON.stringify(similarBody)
|
|
2686
|
+
});
|
|
2687
|
+
if (similarResponse.ok) {
|
|
2688
|
+
const similarData = await similarResponse.json();
|
|
2689
|
+
similarResults = similarData.results ?? [];
|
|
2690
|
+
}
|
|
2691
|
+
} catch {
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
const allSources = [...searchData.results, ...similarResults];
|
|
2695
|
+
const seenUrls = /* @__PURE__ */ new Set();
|
|
2696
|
+
const uniqueSources = allSources.filter((s) => {
|
|
2697
|
+
if (seenUrls.has(s.url)) return false;
|
|
2698
|
+
seenUrls.add(s.url);
|
|
2699
|
+
return true;
|
|
2700
|
+
});
|
|
2701
|
+
const sections = [];
|
|
2702
|
+
sections.push(`# Research: ${topic}
|
|
2703
|
+
`);
|
|
2704
|
+
sections.push(`Found ${uniqueSources.length} sources.
|
|
2705
|
+
`);
|
|
2706
|
+
sections.push("## Key Sources\n");
|
|
2707
|
+
for (let i = 0; i < uniqueSources.length; i++) {
|
|
2708
|
+
const source = uniqueSources[i];
|
|
2709
|
+
sections.push(`### ${i + 1}. ${source.title ?? "Untitled"}`);
|
|
2710
|
+
sections.push(`URL: ${source.url}`);
|
|
2711
|
+
if ("publishedDate" in source && source.publishedDate) {
|
|
2712
|
+
sections.push(`Date: ${source.publishedDate}`);
|
|
2713
|
+
}
|
|
2714
|
+
if ("author" in source && source.author) {
|
|
2715
|
+
sections.push(`Author: ${source.author}`);
|
|
2716
|
+
}
|
|
2717
|
+
if (source.text) {
|
|
2718
|
+
sections.push(`
|
|
2719
|
+
Content:
|
|
2720
|
+
${source.text.slice(0, 2e3)}`);
|
|
2721
|
+
}
|
|
2722
|
+
sections.push("");
|
|
2723
|
+
}
|
|
2724
|
+
sections.push("## Source URLs\n");
|
|
2725
|
+
uniqueSources.forEach((s, i) => {
|
|
2726
|
+
sections.push(`${i + 1}. ${s.url}`);
|
|
2727
|
+
});
|
|
2728
|
+
return sections.join("\n");
|
|
2729
|
+
}
|
|
2730
|
+
};
|
|
2731
|
+
|
|
2732
|
+
// src/orchestrator/YAMLLoader.ts
|
|
2733
|
+
import * as fs5 from "fs";
|
|
2734
|
+
import * as path5 from "path";
|
|
2735
|
+
import YAML from "yaml";
|
|
2736
|
+
var TOOL_REGISTRY = {
|
|
2737
|
+
read_file: ReadFileTool,
|
|
2738
|
+
write_file: WriteFileTool,
|
|
2739
|
+
curl: CurlTool,
|
|
2740
|
+
exa_search: ExaSearchTool,
|
|
2741
|
+
exa_get_contents: ExaGetContentsTool,
|
|
2742
|
+
exa_research: ExaResearchTool,
|
|
2743
|
+
final_answer: FinalAnswerTool
|
|
2744
|
+
};
|
|
2745
|
+
var YAMLLoader = class {
|
|
2746
|
+
customTools = /* @__PURE__ */ new Map();
|
|
2747
|
+
/**
|
|
2748
|
+
* Register a custom tool type for use in YAML definitions.
|
|
2749
|
+
*/
|
|
2750
|
+
registerToolType(typeName, toolClass) {
|
|
2751
|
+
this.customTools.set(typeName, toolClass);
|
|
2752
|
+
}
|
|
2753
|
+
/**
|
|
2754
|
+
* Load a workflow from a YAML file path.
|
|
2755
|
+
*/
|
|
2756
|
+
loadFromFile(filePath) {
|
|
2757
|
+
const absolutePath = path5.isAbsolute(filePath) ? filePath : path5.resolve(process.cwd(), filePath);
|
|
2758
|
+
if (!fs5.existsSync(absolutePath)) {
|
|
2759
|
+
throw new Error(`Workflow file not found: ${absolutePath}`);
|
|
2760
|
+
}
|
|
2761
|
+
const content = fs5.readFileSync(absolutePath, "utf-8");
|
|
2762
|
+
return this.loadFromString(content);
|
|
2763
|
+
}
|
|
2764
|
+
/**
|
|
2765
|
+
* Load a workflow from a YAML string.
|
|
2766
|
+
*/
|
|
2767
|
+
loadFromString(yamlContent) {
|
|
2768
|
+
const definition = YAML.parse(yamlContent);
|
|
2769
|
+
return this.buildWorkflow(definition);
|
|
2770
|
+
}
|
|
2771
|
+
/**
|
|
2772
|
+
* Build a runnable workflow from a parsed definition.
|
|
2773
|
+
*/
|
|
2774
|
+
buildWorkflow(definition) {
|
|
2775
|
+
if (!definition.name) {
|
|
2776
|
+
throw new Error("Workflow must have a name");
|
|
2777
|
+
}
|
|
2778
|
+
if (!definition.entrypoint) {
|
|
2779
|
+
throw new Error("Workflow must have an entrypoint agent");
|
|
2780
|
+
}
|
|
2781
|
+
if (!definition.agents) {
|
|
2782
|
+
throw new Error("Workflow must define at least one agent");
|
|
2783
|
+
}
|
|
2784
|
+
const tools = /* @__PURE__ */ new Map();
|
|
2785
|
+
if (definition.tools) {
|
|
2786
|
+
for (const [name, toolDef] of Object.entries(definition.tools)) {
|
|
2787
|
+
const tool = this.buildTool(name, toolDef.type, toolDef.config);
|
|
2788
|
+
tools.set(name, tool);
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2791
|
+
const agents = /* @__PURE__ */ new Map();
|
|
2792
|
+
const agentDefs = definition.agents;
|
|
2793
|
+
const resolved = /* @__PURE__ */ new Set();
|
|
2794
|
+
const maxIterations = Object.keys(agentDefs).length * 2;
|
|
2795
|
+
let iterations = 0;
|
|
2796
|
+
while (resolved.size < Object.keys(agentDefs).length && iterations < maxIterations) {
|
|
2797
|
+
iterations++;
|
|
2798
|
+
for (const [agentName, agentDef] of Object.entries(agentDefs)) {
|
|
2799
|
+
if (resolved.has(agentName)) continue;
|
|
2800
|
+
const agentDeps = agentDef.agents ?? [];
|
|
2801
|
+
const allDepsResolved = agentDeps.every((dep) => resolved.has(dep));
|
|
2802
|
+
if (allDepsResolved) {
|
|
2803
|
+
const agent = this.buildAgent(
|
|
2804
|
+
agentName,
|
|
2805
|
+
agentDef,
|
|
2806
|
+
definition.model,
|
|
2807
|
+
tools,
|
|
2808
|
+
agents,
|
|
2809
|
+
definition.globalMaxContextLength
|
|
2810
|
+
);
|
|
2811
|
+
agents.set(agentName, agent);
|
|
2812
|
+
resolved.add(agentName);
|
|
2813
|
+
}
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
if (resolved.size < Object.keys(agentDefs).length) {
|
|
2817
|
+
const unresolved = Object.keys(agentDefs).filter((n) => !resolved.has(n));
|
|
2818
|
+
throw new Error(`Circular or unresolvable agent dependencies: ${unresolved.join(", ")}`);
|
|
2819
|
+
}
|
|
2820
|
+
const entrypointAgent = agents.get(definition.entrypoint);
|
|
2821
|
+
if (!entrypointAgent) {
|
|
2822
|
+
throw new Error(`Entrypoint agent "${definition.entrypoint}" not found in agents`);
|
|
2823
|
+
}
|
|
2824
|
+
return {
|
|
2825
|
+
name: definition.name,
|
|
2826
|
+
description: definition.description,
|
|
2827
|
+
entrypointAgent,
|
|
2828
|
+
agents,
|
|
2829
|
+
tools
|
|
2830
|
+
};
|
|
2831
|
+
}
|
|
2832
|
+
/**
|
|
2833
|
+
* Build a tool instance from a type name and config.
|
|
2834
|
+
*/
|
|
2835
|
+
buildTool(name, type, config) {
|
|
2836
|
+
const ToolClass = TOOL_REGISTRY[type] ?? this.customTools.get(type);
|
|
2837
|
+
if (!ToolClass) {
|
|
2838
|
+
throw new Error(`Unknown tool type: ${type}. Available types: ${[...Object.keys(TOOL_REGISTRY), ...this.customTools.keys()].join(", ")}`);
|
|
2839
|
+
}
|
|
2840
|
+
const tool = new ToolClass(config);
|
|
2841
|
+
if (name !== type && name !== tool.name) {
|
|
2842
|
+
Object.defineProperty(tool, "name", { value: name, writable: false });
|
|
2843
|
+
}
|
|
2844
|
+
return tool;
|
|
2845
|
+
}
|
|
2846
|
+
/**
|
|
2847
|
+
* Build an agent instance from a YAML definition.
|
|
2848
|
+
*/
|
|
2849
|
+
buildAgent(name, definition, globalModel, availableTools, resolvedAgents, globalMaxContextLength) {
|
|
2850
|
+
const modelConfig = definition.model ?? globalModel;
|
|
2851
|
+
const model = new OpenAIModel({
|
|
2852
|
+
modelId: modelConfig?.modelId,
|
|
2853
|
+
apiKey: modelConfig?.apiKey,
|
|
2854
|
+
baseUrl: modelConfig?.baseUrl,
|
|
2855
|
+
maxTokens: definition.maxTokens ?? modelConfig?.maxTokens,
|
|
2856
|
+
temperature: definition.temperature ?? modelConfig?.temperature,
|
|
2857
|
+
timeout: modelConfig?.timeout
|
|
2858
|
+
});
|
|
2859
|
+
const agentTools = [];
|
|
2860
|
+
if (definition.tools && availableTools) {
|
|
2861
|
+
for (const toolName of definition.tools) {
|
|
2862
|
+
const tool = availableTools.get(toolName);
|
|
2863
|
+
if (tool) {
|
|
2864
|
+
agentTools.push(tool);
|
|
2865
|
+
} else {
|
|
2866
|
+
const ToolClass = TOOL_REGISTRY[toolName] ?? this.customTools.get(toolName);
|
|
2867
|
+
if (ToolClass) {
|
|
2868
|
+
agentTools.push(new ToolClass());
|
|
2869
|
+
} else {
|
|
2870
|
+
throw new Error(`Tool "${toolName}" not found for agent "${name}"`);
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
if (definition.agents && resolvedAgents) {
|
|
2876
|
+
for (const subAgentName of definition.agents) {
|
|
2877
|
+
const subAgent = resolvedAgents.get(subAgentName);
|
|
2878
|
+
if (!subAgent) {
|
|
2879
|
+
throw new Error(`Sub-agent "${subAgentName}" not found for agent "${name}"`);
|
|
2880
|
+
}
|
|
2881
|
+
agentTools.push(new AgentTool({
|
|
2882
|
+
agent: subAgent,
|
|
2883
|
+
name: subAgentName,
|
|
2884
|
+
description: definition.description ? `Sub-agent: ${subAgentName}` : `Delegate tasks to the ${subAgentName} agent`
|
|
2885
|
+
}));
|
|
2886
|
+
}
|
|
2887
|
+
}
|
|
2888
|
+
const maxContextLength = definition.maxContextLength ?? globalMaxContextLength;
|
|
2889
|
+
if (definition.type === "CodeAgent") {
|
|
2890
|
+
return new CodeAgent({
|
|
2891
|
+
model,
|
|
2892
|
+
tools: agentTools,
|
|
2893
|
+
maxSteps: definition.maxSteps,
|
|
2894
|
+
customInstructions: definition.customInstructions,
|
|
2895
|
+
persistent: definition.persistent,
|
|
2896
|
+
maxContextLength,
|
|
2897
|
+
memoryStrategy: definition.memoryStrategy,
|
|
2898
|
+
maxTokens: definition.maxTokens,
|
|
2899
|
+
temperature: definition.temperature,
|
|
2900
|
+
name
|
|
2901
|
+
});
|
|
2902
|
+
} else {
|
|
2903
|
+
return new ToolUseAgent({
|
|
2904
|
+
model,
|
|
2905
|
+
tools: agentTools,
|
|
2906
|
+
maxSteps: definition.maxSteps,
|
|
2907
|
+
customInstructions: definition.customInstructions,
|
|
2908
|
+
persistent: definition.persistent,
|
|
2909
|
+
maxContextLength,
|
|
2910
|
+
memoryStrategy: definition.memoryStrategy,
|
|
2911
|
+
maxTokens: definition.maxTokens,
|
|
2912
|
+
temperature: definition.temperature,
|
|
2913
|
+
name
|
|
2914
|
+
});
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
};
|
|
2918
|
+
|
|
2919
|
+
// src/orchestrator/Orchestrator.ts
|
|
2920
|
+
import chalk2 from "chalk";
|
|
2921
|
+
var Orchestrator = class {
|
|
2922
|
+
loader;
|
|
2923
|
+
config;
|
|
2924
|
+
activeAgents = /* @__PURE__ */ new Map();
|
|
2925
|
+
eventLog = [];
|
|
2926
|
+
constructor(config = {}) {
|
|
2927
|
+
this.loader = new YAMLLoader();
|
|
2928
|
+
this.config = {
|
|
2929
|
+
verbose: config.verbose ?? true,
|
|
2930
|
+
onEvent: config.onEvent
|
|
2931
|
+
};
|
|
2932
|
+
}
|
|
2933
|
+
/**
|
|
2934
|
+
* Load a workflow from a YAML file.
|
|
2935
|
+
*/
|
|
2936
|
+
loadWorkflow(filePath) {
|
|
2937
|
+
const workflow = this.loader.loadFromFile(filePath);
|
|
2938
|
+
this.displayWorkflowInfo(workflow);
|
|
2939
|
+
return workflow;
|
|
2940
|
+
}
|
|
2941
|
+
/**
|
|
2942
|
+
* Load a workflow from YAML string.
|
|
2943
|
+
*/
|
|
2944
|
+
loadWorkflowFromString(yamlContent) {
|
|
2945
|
+
const workflow = this.loader.loadFromString(yamlContent);
|
|
2946
|
+
this.displayWorkflowInfo(workflow);
|
|
2947
|
+
return workflow;
|
|
2948
|
+
}
|
|
2949
|
+
/**
|
|
2950
|
+
* Run a loaded workflow with a task.
|
|
2951
|
+
*/
|
|
2952
|
+
async runWorkflow(workflow, task) {
|
|
2953
|
+
this.displayRunStart(workflow.name, task);
|
|
2954
|
+
this.instrumentAgent(workflow.entrypointAgent, workflow.entrypointAgent.getName(), 0);
|
|
2955
|
+
for (const [name, agent] of workflow.agents) {
|
|
2956
|
+
if (agent !== workflow.entrypointAgent) {
|
|
2957
|
+
this.instrumentAgent(agent, name, 1);
|
|
2958
|
+
}
|
|
2959
|
+
}
|
|
2960
|
+
try {
|
|
2961
|
+
const result = await workflow.entrypointAgent.run(task);
|
|
2962
|
+
this.displayRunEnd(result);
|
|
2963
|
+
return result;
|
|
2964
|
+
} catch (error) {
|
|
2965
|
+
this.displayError(error);
|
|
2966
|
+
throw error;
|
|
2967
|
+
}
|
|
2968
|
+
}
|
|
2969
|
+
/**
|
|
2970
|
+
* Run a standalone agent with a task.
|
|
2971
|
+
*/
|
|
2972
|
+
async runAgent(agent, task) {
|
|
2973
|
+
this.instrumentAgent(agent, agent.getName(), 0);
|
|
2974
|
+
const result = await agent.run(task);
|
|
2975
|
+
return result;
|
|
2976
|
+
}
|
|
2977
|
+
/**
|
|
2978
|
+
* Instrument an agent with orchestrator event tracking.
|
|
2979
|
+
*/
|
|
2980
|
+
instrumentAgent(agent, name, depth) {
|
|
2981
|
+
this.activeAgents.set(name, { agent, depth });
|
|
2982
|
+
}
|
|
2983
|
+
/**
|
|
2984
|
+
* Display workflow info at startup.
|
|
2985
|
+
*/
|
|
2986
|
+
displayWorkflowInfo(workflow) {
|
|
2987
|
+
if (!this.config.verbose) return;
|
|
2988
|
+
const line = "\u2550".repeat(70);
|
|
2989
|
+
console.log(chalk2.cyan(line));
|
|
2990
|
+
console.log(chalk2.cyan.bold(` Workflow: ${workflow.name}`));
|
|
2991
|
+
if (workflow.description) {
|
|
2992
|
+
console.log(chalk2.cyan(` ${workflow.description}`));
|
|
2993
|
+
}
|
|
2994
|
+
console.log(chalk2.cyan(` Agents: ${Array.from(workflow.agents.keys()).join(", ")}`));
|
|
2995
|
+
console.log(chalk2.cyan(` Tools: ${Array.from(workflow.tools.keys()).join(", ") || "(none defined at workflow level)"}`));
|
|
2996
|
+
console.log(chalk2.cyan(` Entrypoint: ${workflow.entrypointAgent.getName()}`));
|
|
2997
|
+
console.log(chalk2.cyan(line));
|
|
2998
|
+
console.log();
|
|
2999
|
+
}
|
|
3000
|
+
/**
|
|
3001
|
+
* Display run start info.
|
|
3002
|
+
*/
|
|
3003
|
+
displayRunStart(workflowName, task) {
|
|
3004
|
+
if (!this.config.verbose) return;
|
|
3005
|
+
console.log(chalk2.green.bold(`
|
|
3006
|
+
\u25B6 Running workflow "${workflowName}"`));
|
|
3007
|
+
console.log(chalk2.green(` Task: ${task}`));
|
|
3008
|
+
console.log(chalk2.gray("\u2500".repeat(70)));
|
|
3009
|
+
}
|
|
3010
|
+
/**
|
|
3011
|
+
* Display run completion info.
|
|
3012
|
+
*/
|
|
3013
|
+
displayRunEnd(result) {
|
|
3014
|
+
if (!this.config.verbose) return;
|
|
3015
|
+
console.log(chalk2.gray("\n" + "\u2500".repeat(70)));
|
|
3016
|
+
console.log(chalk2.green.bold(`
|
|
3017
|
+
\u2705 Workflow complete`));
|
|
3018
|
+
console.log(chalk2.green(` Duration: ${(result.duration / 1e3).toFixed(2)}s`));
|
|
3019
|
+
console.log(chalk2.green(` Tokens: ${result.tokenUsage.totalTokens}`));
|
|
3020
|
+
console.log(chalk2.green(` Steps: ${result.steps.length}`));
|
|
3021
|
+
const outputStr = typeof result.output === "string" ? result.output : JSON.stringify(result.output, null, 2);
|
|
3022
|
+
console.log(chalk2.magenta.bold("\n Final Output:"));
|
|
3023
|
+
const indentedOutput = outputStr.split("\n").map((line) => ` ${line}`).join("\n");
|
|
3024
|
+
console.log(chalk2.magenta(indentedOutput));
|
|
3025
|
+
console.log();
|
|
3026
|
+
}
|
|
3027
|
+
/**
|
|
3028
|
+
* Display an error.
|
|
3029
|
+
*/
|
|
3030
|
+
displayError(error) {
|
|
3031
|
+
if (!this.config.verbose) return;
|
|
3032
|
+
console.error(chalk2.red.bold(`
|
|
3033
|
+
\u274C Workflow failed: ${error.message}`));
|
|
3034
|
+
if (error.stack) {
|
|
3035
|
+
console.error(chalk2.red.dim(error.stack));
|
|
3036
|
+
}
|
|
3037
|
+
}
|
|
3038
|
+
/**
|
|
3039
|
+
* Log an orchestration event.
|
|
3040
|
+
*/
|
|
3041
|
+
logEvent(event) {
|
|
3042
|
+
this.eventLog.push(event);
|
|
3043
|
+
if (this.config.onEvent) {
|
|
3044
|
+
this.config.onEvent(event);
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
3047
|
+
/**
|
|
3048
|
+
* Get the event log.
|
|
3049
|
+
*/
|
|
3050
|
+
getEventLog() {
|
|
3051
|
+
return [...this.eventLog];
|
|
3052
|
+
}
|
|
3053
|
+
/**
|
|
3054
|
+
* Get the YAML loader for registering custom tools.
|
|
3055
|
+
*/
|
|
3056
|
+
getLoader() {
|
|
3057
|
+
return this.loader;
|
|
3058
|
+
}
|
|
3059
|
+
};
|
|
1778
3060
|
export {
|
|
1779
3061
|
Agent,
|
|
1780
3062
|
AgentLogger,
|
|
1781
3063
|
AgentMemory,
|
|
1782
3064
|
AgentTool,
|
|
1783
3065
|
CodeAgent,
|
|
3066
|
+
CurlTool,
|
|
3067
|
+
ExaGetContentsTool,
|
|
3068
|
+
ExaResearchTool,
|
|
3069
|
+
ExaSearchTool,
|
|
1784
3070
|
FINAL_ANSWER_PROMPT,
|
|
1785
3071
|
FinalAnswerTool,
|
|
1786
3072
|
LocalExecutor,
|
|
1787
3073
|
LogLevel,
|
|
1788
3074
|
Model,
|
|
1789
3075
|
OpenAIModel,
|
|
3076
|
+
Orchestrator,
|
|
3077
|
+
ReadFileTool,
|
|
1790
3078
|
Tool,
|
|
3079
|
+
ToolUseAgent,
|
|
1791
3080
|
UserInputTool,
|
|
3081
|
+
WriteFileTool,
|
|
3082
|
+
YAMLLoader,
|
|
1792
3083
|
agentAsTool,
|
|
1793
3084
|
createTool,
|
|
1794
3085
|
finalAnswerTool,
|
|
3086
|
+
formatToolDescriptions,
|
|
1795
3087
|
generateSystemPrompt,
|
|
3088
|
+
generateToolUseSystemPrompt,
|
|
1796
3089
|
getErrorRecoveryPrompt
|
|
1797
3090
|
};
|
|
1798
3091
|
//# sourceMappingURL=index.mjs.map
|