Skip to content

Commit a5d0b02

Browse files
committed
added validation of JSON and parameter structure.
1 parent b947f35 commit a5d0b02

File tree

12 files changed

+65
-113
lines changed

12 files changed

+65
-113
lines changed

packages/agent/src/core/executeToolCall.ts

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,60 @@ export const executeToolCall = async (
2828
logger,
2929
};
3030

31+
let parsedJson: any;
32+
try {
33+
parsedJson = JSON.parse(toolCall.content);
34+
} catch (err) {
35+
if (err instanceof Error) {
36+
logger.error(err.message);
37+
return JSON.stringify({
38+
error: true,
39+
message: 'Invalid JSON for tool call: ' + err.message,
40+
stack: err.stack,
41+
});
42+
} else {
43+
logger.error(err);
44+
return JSON.stringify({
45+
error: true,
46+
message: 'Invalid JSON for tool call: ' + err,
47+
});
48+
}
49+
}
50+
51+
// validate JSON schema for input
52+
let validatedJson: any;
53+
try {
54+
validatedJson = tool.parameters.parse(parsedJson);
55+
} catch (err) {
56+
if (err instanceof Error) {
57+
logger.error(err.message);
58+
return JSON.stringify({
59+
error: true,
60+
message: 'Invalid format for tool call: ' + err.message,
61+
stack: err.stack,
62+
});
63+
} else {
64+
logger.error(err);
65+
return JSON.stringify({
66+
error: true,
67+
message: 'Invalid format for tool call: ' + err,
68+
});
69+
}
70+
}
71+
3172
// for each parameter log it and its name
3273
if (tool.logParameters) {
33-
tool.logParameters(toolCall.input, toolContext);
74+
tool.logParameters(validatedJson, toolContext);
3475
} else {
3576
logger.info('Parameters:');
36-
Object.entries(toolCall.input).forEach(([name, value]) => {
77+
Object.entries(validatedJson).forEach(([name, value]) => {
3778
logger.info(` - ${name}: ${JSON.stringify(value).substring(0, 60)}`);
3879
});
3980
}
4081

41-
// TODO: validate JSON schema for input
42-
let output;
82+
let output: any;
4383
try {
44-
output = await tool.execute(toolCall.input, toolContext);
84+
output = await tool.execute(validatedJson, toolContext);
4585
} catch (err) {
4686
if (err instanceof Error) {
4787
logger.error(err.message);

packages/agent/src/core/llm/core.ts

Lines changed: 1 addition & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/**
22
* Core LLM abstraction for generating text
33
*/
4+
45
import { LLMProvider } from './provider.js';
56
import {
67
AssistantMessage,
@@ -9,7 +10,6 @@ import {
910
LLMResponse,
1011
Message,
1112
SystemMessage,
12-
ToolCall,
1313
ToolResultMessage,
1414
ToolUseMessage,
1515
UserMessage,
@@ -35,47 +35,6 @@ export async function generateText(
3535
return provider.generateText(options);
3636
}
3737

38-
/**
39-
* Format tool calls for consistent usage across providers
40-
*
41-
* @param rawToolCalls Tool calls from provider
42-
* @returns Normalized tool calls
43-
*/
44-
export function normalizeToolCalls(rawToolCalls: any[]): ToolCall[] {
45-
if (
46-
!rawToolCalls ||
47-
!Array.isArray(rawToolCalls) ||
48-
rawToolCalls.length === 0
49-
) {
50-
return [];
51-
}
52-
53-
return rawToolCalls.map((call) => {
54-
// Handle different provider formats
55-
if (typeof call.arguments === 'string') {
56-
// Already in correct format
57-
return {
58-
id:
59-
call.id ||
60-
`tool-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,
61-
name: call.name || call.function?.name,
62-
arguments: call.arguments,
63-
};
64-
} else if (typeof call.arguments === 'object') {
65-
// Convert object to JSON string
66-
return {
67-
id:
68-
call.id ||
69-
`tool-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,
70-
name: call.name || call.function?.name,
71-
arguments: JSON.stringify(call.arguments),
72-
};
73-
} else {
74-
throw new Error(`Unsupported tool call format: ${JSON.stringify(call)}`);
75-
}
76-
});
77-
}
78-
7938
/**
8039
* Format function definitions for provider compatibility
8140
*

packages/agent/src/core/llm/examples.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ async function _openaiExample() {
6666
const toolCall = response.toolCalls[0];
6767
if (toolCall) {
6868
console.log(`Tool called: ${toolCall.name}`);
69-
console.log(`Arguments: ${toolCall.arguments}`);
69+
console.log(`Arguments: ${toolCall.content}`);
7070

7171
// Example of adding a tool result
7272
const toolResult: Message = {

packages/agent/src/core/llm/providers/anthropic.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -85,18 +85,8 @@ export class AnthropicProvider implements LLMProvider {
8585
(requestOptions as any).tools = tools;
8686
}
8787

88-
console.log(
89-
'Input Messages for Anthropic:',
90-
JSON.stringify(requestOptions.messages, null, 2),
91-
);
92-
9388
const response = await this.client.messages.create(requestOptions);
9489

95-
console.log(
96-
'Response from Anthropic:',
97-
JSON.stringify(response.content, null, 2),
98-
);
99-
10090
// Extract content and tool calls
10191
const content =
10292
response.content.find((c) => c.type === 'text')?.text || '';
@@ -112,7 +102,7 @@ export class AnthropicProvider implements LLMProvider {
112102
toolUse.id ||
113103
`tool-${Math.random().toString(36).substring(2, 11)}`,
114104
name: toolUse.name,
115-
arguments: JSON.stringify(toolUse.input),
105+
content: JSON.stringify(toolUse.input),
116106
};
117107
});
118108

packages/agent/src/core/llm/providers/openai.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
/**
22
* OpenAI provider implementation
33
*/
4-
import { normalizeToolCalls } from '../core.js';
54
import { LLMProvider } from '../provider.js';
65
import {
76
GenerateOptions,
@@ -100,7 +99,7 @@ export class OpenAIProvider implements LLMProvider {
10099

101100
return {
102101
text: content,
103-
toolCalls: normalizeToolCalls(toolCalls),
102+
toolCalls: toolCalls,
104103
};
105104
} catch (error) {
106105
throw new Error(`Error calling OpenAI API: ${(error as Error).message}`);

packages/agent/src/core/llm/types.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
* Core message types for LLM interactions
33
*/
44

5+
import { ToolCall } from '../types';
6+
57
/**
68
* Base message type with role and content
79
*/
@@ -68,15 +70,6 @@ export interface FunctionDefinition {
6870
parameters: Record<string, any>; // JSON Schema object
6971
}
7072

71-
/**
72-
* Tool call made by the model
73-
*/
74-
export interface ToolCall {
75-
id: string;
76-
name: string;
77-
arguments: string; // JSON string of arguments
78-
}
79-
8073
/**
8174
* Response from LLM with text and/or tool calls
8275
*/

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ describe('toolAgent', () => {
7575
{
7676
id: '1',
7777
name: 'mockTool',
78-
input: { input: 'test' },
78+
content: JSON.stringify({ input: 'test' }),
7979
},
8080
[mockTool],
8181
toolContext,
@@ -90,7 +90,7 @@ describe('toolAgent', () => {
9090
{
9191
id: '1',
9292
name: 'nonexistentTool',
93-
input: {},
93+
content: JSON.stringify({}),
9494
},
9595
[mockTool],
9696
toolContext,
@@ -103,7 +103,7 @@ describe('toolAgent', () => {
103103
{
104104
id: '1',
105105
name: 'errorTool',
106-
input: {},
106+
content: JSON.stringify({}),
107107
},
108108
[errorTool],
109109
toolContext,

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

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,4 @@
1-
import { Message, ToolCall } from '../llm/types.js';
2-
3-
/**
4-
* Formats tool calls from the LLM into the ToolUseContent format
5-
*/
6-
export function formatToolCalls(toolCalls: ToolCall[]): any[] {
7-
return toolCalls.map((call) => ({
8-
type: 'tool_use',
9-
name: call.name,
10-
id: call.id,
11-
input: call.arguments,
12-
}));
13-
}
14-
15-
/**
16-
* Creates tool call parts for the assistant message
17-
* This is for backward compatibility with existing code
18-
*/
19-
export function createToolCallParts(toolCalls: any[]): any[] {
20-
return toolCalls.map((toolCall) => ({
21-
type: 'tool-call',
22-
toolCallId: toolCall.id,
23-
toolName: toolCall.name,
24-
args: toolCall.arguments,
25-
}));
26-
}
1+
import { Message } from '../llm/types.js';
272

283
/**
294
* Helper function to add a tool result to messages

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

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { zodToJsonSchema } from 'zod-to-json-schema';
33
import { Message, ToolUseMessage, generateText } from '../llm/index.js';
44

55
import { DEFAULT_CONFIG } from './config.js';
6-
import { formatToolCalls } from './messageUtils.js';
76
import { logTokenUsage } from './tokenTracking.js';
87
import { executeTools } from './toolExecutor.js';
98
import { Tool, ToolAgentResult, ToolContext } from './types.js';
@@ -78,9 +77,6 @@ export const toolAgent = async (
7877

7978
const { text, toolCalls } = await generateText(provider, generateOptions);
8079

81-
// Format tool calls to our expected format
82-
const localToolCalls = formatToolCalls(toolCalls);
83-
8480
if (!text.length && toolCalls.length === 0) {
8581
// Only consider it empty if there's no text AND no tool calls
8682
logger.verbose(
@@ -112,14 +108,14 @@ export const toolAgent = async (
112108
role: 'tool_use',
113109
name: toolCall.name,
114110
id: toolCall.id,
115-
content: toolCall.arguments,
111+
content: toolCall.content,
116112
}) satisfies ToolUseMessage,
117113
),
118114
);
119115

120116
// Execute the tools and get results
121117
const { sequenceCompleted, completionResult, respawn } =
122-
await executeTools(localToolCalls, tools, messages, context);
118+
await executeTools(toolCalls, tools, messages, context);
123119

124120
if (respawn) {
125121
logger.info('Respawning agent with new context');

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { executeToolCall } from '../executeToolCall.js';
22
import { Message } from '../llm/types.js';
33
import { TokenTracker } from '../tokens.js';
4-
import { ToolUseContent } from '../types.js';
4+
import { ToolCall } from '../types.js';
55

66
import { addToolResultToMessages } from './messageUtils.js';
77
import { Tool, ToolCallResult, ToolContext } from './types.js';
@@ -19,7 +19,7 @@ const safeParse = (value: string) => {
1919
* Executes a list of tool calls and returns the results
2020
*/
2121
export async function executeTools(
22-
toolCalls: ToolUseContent[],
22+
toolCalls: ToolCall[],
2323
tools: Tool[],
2424
messages: Message[],
2525
context: ToolContext,
@@ -48,7 +48,7 @@ export async function executeTools(
4848
},
4949
],
5050
respawn: {
51-
context: respawnCall.input.respawnContext,
51+
context: JSON.parse(respawnCall.content).respawnContext,
5252
},
5353
};
5454
}

0 commit comments

Comments
 (0)