Skip to content

Commit 6746351

Browse files
committed
partial conversion.
1 parent 7838cea commit 6746351

File tree

8 files changed

+306
-240
lines changed

8 files changed

+306
-240
lines changed

packages/agent/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,16 @@
4444
"author": "Ben Houston",
4545
"license": "MIT",
4646
"dependencies": {
47-
"@anthropic-ai/sdk": "^0.37",
47+
"@ai-sdk/anthropic": "^1.1.13",
48+
"@ai-sdk/openai": "^1.2.0",
4849
"@mozilla/readability": "^0.5.0",
4950
"@playwright/test": "^1.50.1",
5051
"@vitest/browser": "^3.0.5",
52+
"ai": "^4.1.50",
5153
"chalk": "^5",
5254
"dotenv": "^16",
5355
"jsdom": "^26.0.0",
56+
"ollama-ai-provider": "^1.2.0",
5457
"playwright": "^1.50.1",
5558
"uuid": "^11",
5659
"zod": "^3",

packages/agent/src/core/tokens.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import Anthropic from '@anthropic-ai/sdk';
1+
//import Anthropic from '@anthropic-ai/sdk';
22

33
import { LogLevel } from '../utils/logger.js';
44

@@ -34,14 +34,15 @@ export class TokenUsage {
3434
return usage;
3535
}
3636

37+
/*
3738
static fromMessage(message: Anthropic.Message) {
3839
const usage = new TokenUsage();
3940
usage.input = message.usage.input_tokens;
4041
usage.cacheWrites = message.usage.cache_creation_input_tokens ?? 0;
4142
usage.cacheReads = message.usage.cache_read_input_tokens ?? 0;
4243
usage.output = message.usage.output_tokens;
4344
return usage;
44-
}
45+
}*/
4546

4647
static sum(usages: TokenUsage[]) {
4748
const usage = new TokenUsage();

packages/agent/src/core/toolAgent.respawn.test.ts

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { anthropic } from '@ai-sdk/anthropic';
12
import { describe, it, expect, vi, beforeEach } from 'vitest';
23

34
import { toolAgent } from '../../src/core/toolAgent.js';
@@ -15,32 +16,6 @@ const toolContext: ToolContext = {
1516
pageFilter: 'simple',
1617
tokenTracker: new TokenTracker(),
1718
};
18-
// Mock Anthropic SDK
19-
vi.mock('@anthropic-ai/sdk', () => {
20-
return {
21-
default: vi.fn().mockImplementation(() => ({
22-
messages: {
23-
create: vi
24-
.fn()
25-
.mockResolvedValueOnce({
26-
content: [
27-
{
28-
type: 'tool_use',
29-
name: 'respawn',
30-
id: 'test-id',
31-
input: { respawnContext: 'new context' },
32-
},
33-
],
34-
usage: { input_tokens: 10, output_tokens: 10 },
35-
})
36-
.mockResolvedValueOnce({
37-
content: [],
38-
usage: { input_tokens: 5, output_tokens: 5 },
39-
}),
40-
},
41-
})),
42-
};
43-
});
4419

4520
describe('toolAgent respawn functionality', () => {
4621
const tools = getTools();
@@ -56,7 +31,7 @@ describe('toolAgent respawn functionality', () => {
5631
tools,
5732
{
5833
maxIterations: 2, // Need at least 2 iterations for respawn + empty response
59-
model: 'test-model',
34+
model: anthropic('claude-3-7-sonnet-20250219'),
6035
maxTokens: 100,
6136
temperature: 0,
6237
getSystemPrompt: () => 'test system prompt',

packages/agent/src/core/toolAgent.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { anthropic } from '@ai-sdk/anthropic';
12
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
23

34
import { MockLogger } from '../utils/mockLogger.js';
@@ -19,7 +20,7 @@ const toolContext: ToolContext = {
1920
// Mock configuration for testing
2021
const testConfig = {
2122
maxIterations: 50,
22-
model: 'claude-3-7-sonnet-latest',
23+
model: anthropic('claude-3-7-sonnet-20250219'),
2324
maxTokens: 4096,
2425
temperature: 0.7,
2526
getSystemPrompt: () => 'Test system prompt',

packages/agent/src/core/toolAgent.ts

Lines changed: 74 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
import { execSync } from 'child_process';
22

3-
import Anthropic from '@anthropic-ai/sdk';
4-
import { ContentBlockParam } from '@anthropic-ai/sdk/resources/messages/messages.js';
3+
import { anthropic } from '@ai-sdk/anthropic';
4+
import {
5+
CoreMessage,
6+
CoreToolMessage,
7+
generateText,
8+
ToolResultPart,
9+
ToolSet,
10+
} from 'ai';
511
import chalk from 'chalk';
612

713
import { getAnthropicApiKeyError } from '../utils/errors.js';
814

915
import { executeToolCall } from './executeToolCall.js';
10-
import { TokenTracker, TokenUsage } from './tokens.js';
16+
import { TokenTracker } from './tokens.js';
1117
import {
1218
Tool,
13-
TextContent,
1419
ToolUseContent,
1520
ToolResultContent,
16-
Message,
1721
ToolContext,
1822
} from './types.js';
1923

@@ -24,7 +28,7 @@ export interface ToolAgentResult {
2428

2529
const CONFIG = {
2630
maxIterations: 200,
27-
model: 'claude-3-7-sonnet-latest',
31+
model: anthropic('claude-3-7-sonnet-20250219'),
2832
maxTokens: 4096,
2933
temperature: 0.7,
3034
getSystemPrompt: () => {
@@ -86,9 +90,9 @@ const CONFIG = {
8690
interface ToolCallResult {
8791
sequenceCompleted: boolean;
8892
completionResult?: string;
89-
toolResults: ToolResultContent[];
93+
toolResults: ToolResultPart[];
9094
}
91-
95+
/*
9296
function processResponse(response: Anthropic.Message) {
9397
const content: (TextContent | ToolUseContent)[] = [];
9498
const toolCalls: ToolUseContent[] = [];
@@ -110,11 +114,12 @@ function processResponse(response: Anthropic.Message) {
110114
111115
return { content, toolCalls };
112116
}
117+
*/
113118

114119
async function executeTools(
115120
toolCalls: ToolUseContent[],
116121
tools: Tool[],
117-
messages: Message[],
122+
messages: CoreMessage[],
118123
context: ToolContext,
119124
): Promise<ToolCallResult & { respawn?: { context: string } }> {
120125
if (toolCalls.length === 0) {
@@ -132,18 +137,19 @@ async function executeTools(
132137
sequenceCompleted: false,
133138
toolResults: [
134139
{
135-
type: 'tool_result',
136-
tool_use_id: respawnCall.id,
137-
content: 'Respawn initiated',
138-
},
140+
type: 'tool-result',
141+
toolCallId: respawnCall.id,
142+
toolName: respawnCall.name,
143+
result: { success: true },
144+
} satisfies ToolResultPart,
139145
],
140146
respawn: {
141147
context: respawnCall.input.respawnContext,
142148
},
143149
};
144150
}
145151

146-
const results = await Promise.all(
152+
const toolResults: ToolResultPart[] = await Promise.all(
147153
toolCalls.map(async (call) => {
148154
let toolResult = '';
149155
try {
@@ -155,35 +161,36 @@ async function executeTools(
155161
toolResult = `Error: Exception thrown during tool execution. Type: ${error.constructor.name}, Message: ${error.message}`;
156162
}
157163
return {
158-
type: 'tool_result' as const,
159-
tool_use_id: call.id,
160-
content: toolResult,
161-
isComplete: call.name === 'sequenceComplete',
162-
};
164+
type: 'tool-result',
165+
toolCallId: call.id,
166+
toolName: call.name,
167+
result: JSON.parse(toolResult) satisfies ToolResultContent,
168+
} satisfies ToolResultPart;
163169
}),
164170
);
165171

166-
const toolResults = results.map(({ type, tool_use_id, content }) => ({
167-
type,
168-
tool_use_id,
169-
content,
170-
}));
171-
172-
const sequenceCompleted = results.some((r) => r.isComplete);
173-
const completionResult = results.find((r) => r.isComplete)?.content;
172+
const sequenceCompletedTool = toolResults.find(
173+
(r) => r.toolName === 'sequenceComplete',
174+
);
175+
const completionResult = sequenceCompletedTool?.result as string;
174176

175177
messages.push({
176-
role: 'user',
178+
role: 'tool',
177179
content: toolResults,
178-
});
180+
} satisfies CoreToolMessage);
179181

180-
if (sequenceCompleted) {
182+
if (sequenceCompletedTool) {
181183
logger.verbose('Sequence completed', { completionResult });
182184
}
183185

184-
return { sequenceCompleted, completionResult, toolResults };
186+
return {
187+
sequenceCompleted: sequenceCompletedTool !== undefined,
188+
completionResult,
189+
toolResults,
190+
};
185191
}
186192

193+
/*
187194
// a function that takes a list of messages and returns a list of messages but with the last message having a cache_control of ephemeral
188195
function addCacheControlToTools<T>(messages: T[]): T[] {
189196
return messages.map((m, i) => ({
@@ -238,7 +245,7 @@ function addCacheControlToMessages(
238245
: m.content,
239246
};
240247
});
241-
}
248+
}*/
242249

243250
export const toolAgent = async (
244251
initialPrompt: string,
@@ -256,8 +263,8 @@ export const toolAgent = async (
256263
const apiKey = process.env.ANTHROPIC_API_KEY;
257264
if (!apiKey) throw new Error(getAnthropicApiKeyError());
258265

259-
const client = new Anthropic({ apiKey });
260-
const messages: Message[] = [
266+
// const client = new Anthropic({ apiKey });
267+
const messages: CoreMessage[] = [
261268
{
262269
role: 'user',
263270
content: [{ type: 'text', text: initialPrompt }],
@@ -278,32 +285,31 @@ export const toolAgent = async (
278285

279286
interactions++;
280287

281-
// Create request parameters
282-
const requestParams: Anthropic.MessageCreateParams = {
283-
model: config.model,
284-
max_tokens: config.maxTokens,
285-
temperature: config.temperature,
286-
messages: addCacheControlToMessages(messages),
287-
system: [
288-
{
289-
type: 'text',
290-
text: systemPrompt,
291-
cache_control: { type: 'ephemeral' },
292-
},
293-
],
294-
tools: addCacheControlToTools(
295-
tools.map((t) => ({
296-
name: t.name,
297-
description: t.description,
298-
input_schema: t.parameters as Anthropic.Tool.InputSchema,
299-
})),
300-
),
301-
tool_choice: { type: 'auto' },
302-
};
288+
const toolSet: ToolSet = {};
289+
tools.forEach((tool) => {
290+
toolSet[tool.name] = {
291+
description: tool.description,
292+
parameters: tool.parameters,
293+
};
294+
});
295+
const { text, reasoning, reasoningDetails, toolCalls, toolResults } =
296+
await generateText({
297+
model: config.model,
298+
temperature: config.temperature,
299+
messages,
300+
system: systemPrompt,
301+
tools: toolSet,
302+
toolChoice: 'auto',
303+
});
303304

304-
const response = await client.messages.create(requestParams);
305+
const localToolCalls: ToolUseContent[] = toolCalls.map((call) => ({
306+
type: 'tool_use',
307+
name: call.toolName,
308+
id: call.toolCallId,
309+
input: call.args,
310+
}));
305311

306-
if (!response.content.length) {
312+
if (!text.length) {
307313
// Instead of treating empty response as completion, remind the agent
308314
logger.verbose('Received empty response from agent, sending reminder');
309315
messages.push({
@@ -319,31 +325,25 @@ export const toolAgent = async (
319325
}
320326

321327
// Track both regular and cached token usage
322-
const tokenUsagePerMessage = TokenUsage.fromMessage(response);
323-
tokenTracker.tokenUsage.add(tokenUsagePerMessage);
328+
//const tokenUsagePerMessage = TokenUsage.fromMessage(response);
329+
//tokenTracker.tokenUsage.add(tokenUsagePerMessage);
324330

325-
const { content, toolCalls } = processResponse(response);
326331
messages.push({
327332
role: 'assistant',
328-
content,
333+
content: [{ type: 'text', text: text }],
329334
});
330335

331-
// Log the assistant's message
332-
const assistantMessage = content
333-
.filter((c) => c.type === 'text')
334-
.map((c) => c.text)
335-
.join('\\n');
336-
if (assistantMessage) {
337-
logger.info(assistantMessage);
336+
if (text) {
337+
logger.info(text);
338338
}
339339

340-
logger.log(
340+
/*logger.log(
341341
tokenTracker.logLevel,
342342
chalk.blue(`[Token Usage/Message] ${tokenUsagePerMessage.toString()}`),
343-
);
343+
);*/
344344

345345
const { sequenceCompleted, completionResult, respawn } = await executeTools(
346-
toolCalls,
346+
localToolCalls,
347347
tools,
348348
messages,
349349
context,
@@ -361,10 +361,8 @@ export const toolAgent = async (
361361
}
362362

363363
if (sequenceCompleted) {
364-
const result = {
365-
result:
366-
completionResult ??
367-
'Sequence explicitly completed with an empty result',
364+
const result: ToolAgentResult = {
365+
result: completionResult ?? 'Sequence explicitly completed',
368366
interactions,
369367
};
370368
logger.log(

packages/agent/src/tools/interaction/subAgent.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { anthropic } from '@ai-sdk/anthropic';
12
import { z } from 'zod';
23
import { zodToJsonSchema } from 'zod-to-json-schema';
34

@@ -47,7 +48,7 @@ type ReturnType = z.infer<typeof returnSchema>;
4748
// Sub-agent specific configuration
4849
const subAgentConfig = {
4950
maxIterations: 50,
50-
model: process.env.AGENT_MODEL || 'claude-3-opus-20240229',
51+
model: anthropic('claude-3-7-sonnet-20250219'),
5152
maxTokens: 4096,
5253
temperature: 0.7,
5354
getSystemPrompt: () => {

0 commit comments

Comments
 (0)