@hamp10/agentforge 0.2.15 → 0.2.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/OllamaAgent.js +52 -88
package/package.json
CHANGED
package/src/OllamaAgent.js
CHANGED
|
@@ -8,112 +8,63 @@ import { fileURLToPath } from 'url';
|
|
|
8
8
|
const execAsync = promisify(exec);
|
|
9
9
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
10
|
|
|
11
|
-
//
|
|
12
|
-
//
|
|
11
|
+
// Minimal tool definitions — one compact JSON per line, embedded in system prompt.
|
|
12
|
+
// Ollama's `tools` API param is broken for qwen3 (github.com/ollama/ollama/issues/14601).
|
|
13
|
+
// Descriptions kept short to fit within qwen3-vl:8b's 4096 token context.
|
|
13
14
|
const TOOL_DEFS = [
|
|
14
15
|
{
|
|
15
16
|
type: 'function',
|
|
16
17
|
function: {
|
|
17
18
|
name: 'bash',
|
|
18
|
-
description: '
|
|
19
|
-
parameters: {
|
|
20
|
-
type: 'object',
|
|
21
|
-
properties: {
|
|
22
|
-
command: { type: 'string', description: 'The shell command to run' }
|
|
23
|
-
},
|
|
24
|
-
required: ['command']
|
|
25
|
-
}
|
|
19
|
+
description: 'Run a shell command. Returns stdout/stderr.',
|
|
20
|
+
parameters: { type: 'object', properties: { command: { type: 'string' } }, required: ['command'] }
|
|
26
21
|
}
|
|
27
22
|
},
|
|
28
23
|
{
|
|
29
24
|
type: 'function',
|
|
30
25
|
function: {
|
|
31
26
|
name: 'read_file',
|
|
32
|
-
description: 'Read
|
|
33
|
-
parameters: {
|
|
34
|
-
type: 'object',
|
|
35
|
-
properties: {
|
|
36
|
-
path: { type: 'string', description: 'Path to the file (absolute or relative to workdir)' }
|
|
37
|
-
},
|
|
38
|
-
required: ['path']
|
|
39
|
-
}
|
|
27
|
+
description: 'Read a file.',
|
|
28
|
+
parameters: { type: 'object', properties: { path: { type: 'string' } }, required: ['path'] }
|
|
40
29
|
}
|
|
41
30
|
},
|
|
42
31
|
{
|
|
43
32
|
type: 'function',
|
|
44
33
|
function: {
|
|
45
34
|
name: 'write_file',
|
|
46
|
-
description: 'Write
|
|
47
|
-
parameters: {
|
|
48
|
-
type: 'object',
|
|
49
|
-
properties: {
|
|
50
|
-
path: { type: 'string', description: 'Path to write (absolute or relative to workdir)' },
|
|
51
|
-
content: { type: 'string', description: 'File content to write' }
|
|
52
|
-
},
|
|
53
|
-
required: ['path', 'content']
|
|
54
|
-
}
|
|
35
|
+
description: 'Write a file.',
|
|
36
|
+
parameters: { type: 'object', properties: { path: { type: 'string' }, content: { type: 'string' } }, required: ['path', 'content'] }
|
|
55
37
|
}
|
|
56
38
|
},
|
|
57
39
|
{
|
|
58
40
|
type: 'function',
|
|
59
41
|
function: {
|
|
60
42
|
name: 'list_directory',
|
|
61
|
-
description: 'List files
|
|
62
|
-
parameters: {
|
|
63
|
-
type: 'object',
|
|
64
|
-
properties: {
|
|
65
|
-
path: { type: 'string', description: 'Directory path (absolute or relative to workdir)' }
|
|
66
|
-
},
|
|
67
|
-
required: ['path']
|
|
68
|
-
}
|
|
43
|
+
description: 'List files in a directory.',
|
|
44
|
+
parameters: { type: 'object', properties: { path: { type: 'string' } }, required: ['path'] }
|
|
69
45
|
}
|
|
70
46
|
},
|
|
71
47
|
{
|
|
72
48
|
type: 'function',
|
|
73
49
|
function: {
|
|
74
50
|
name: 'web_fetch',
|
|
75
|
-
description: 'Fetch
|
|
76
|
-
parameters: {
|
|
77
|
-
type: 'object',
|
|
78
|
-
properties: {
|
|
79
|
-
url: { type: 'string', description: 'URL to fetch' }
|
|
80
|
-
},
|
|
81
|
-
required: ['url']
|
|
82
|
-
}
|
|
51
|
+
description: 'Fetch text content from a URL.',
|
|
52
|
+
parameters: { type: 'object', properties: { url: { type: 'string' } }, required: ['url'] }
|
|
83
53
|
}
|
|
84
54
|
},
|
|
85
55
|
{
|
|
86
56
|
type: 'function',
|
|
87
57
|
function: {
|
|
88
58
|
name: 'take_screenshot',
|
|
89
|
-
description: '
|
|
90
|
-
parameters: {
|
|
91
|
-
type: 'object',
|
|
92
|
-
properties: {
|
|
93
|
-
target: {
|
|
94
|
-
type: 'string',
|
|
95
|
-
enum: ['screen', 'browser'],
|
|
96
|
-
description: 'screen = full screen capture. browser = screenshot of the agent browser (port 9223).'
|
|
97
|
-
},
|
|
98
|
-
url: {
|
|
99
|
-
type: 'string',
|
|
100
|
-
description: 'Optional: navigate the browser to this URL before taking the screenshot.'
|
|
101
|
-
},
|
|
102
|
-
send_to_user: {
|
|
103
|
-
type: 'boolean',
|
|
104
|
-
description: 'If true, send the screenshot to the user\'s chat. Only set this when the user explicitly asked to see a screenshot or visual output.'
|
|
105
|
-
}
|
|
106
|
-
},
|
|
107
|
-
required: ['target']
|
|
108
|
-
}
|
|
59
|
+
description: 'Screenshot the screen. Set send_to_user=true only if user asked to see it.',
|
|
60
|
+
parameters: { type: 'object', properties: { target: { type: 'string', enum: ['screen', 'browser'] }, send_to_user: { type: 'boolean' } }, required: ['target'] }
|
|
109
61
|
}
|
|
110
62
|
}
|
|
111
63
|
];
|
|
112
64
|
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
|
|
116
|
-
const TOOLS_XML = `<tools>\n${TOOL_DEFS.map(t => JSON.stringify(t)).join('\n')}\n</tools>`;
|
|
65
|
+
// Minimal <tools> XML for system prompt — one compact JSON per line, no outer array.
|
|
66
|
+
// Per qwen3 Hermes chat template (tokenizer_config.json).
|
|
67
|
+
const TOOLS_XML = `<tools>\n${TOOL_DEFS.map(t => JSON.stringify(t.function)).join('\n')}\n</tools>`;
|
|
117
68
|
|
|
118
69
|
/**
|
|
119
70
|
* Parse <tool_call>...</tool_call> blocks from streamed content.
|
|
@@ -281,24 +232,34 @@ export class OllamaAgent extends EventEmitter {
|
|
|
281
232
|
// Load conversation history from disk (session persistence)
|
|
282
233
|
const history = this._loadHistory(agentId, workDir, sessionId);
|
|
283
234
|
|
|
284
|
-
//
|
|
285
|
-
//
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
235
|
+
// System prompt uses the exact format from qwen3's Hermes chat template.
|
|
236
|
+
// Tools are embedded as <tools> XML — never passed via the API `tools` param (broken in Ollama).
|
|
237
|
+
const systemPrompt = isQwen3
|
|
238
|
+
? [
|
|
239
|
+
'/no_think',
|
|
240
|
+
`You are a helpful assistant. Working directory: ${workDir}`,
|
|
241
|
+
``,
|
|
242
|
+
`# Tools`,
|
|
243
|
+
``,
|
|
244
|
+
`You may call one or more functions to complete the task.`,
|
|
245
|
+
``,
|
|
246
|
+
`You are provided with function signatures within <tools></tools> XML tags:`,
|
|
247
|
+
TOOLS_XML,
|
|
248
|
+
``,
|
|
249
|
+
`For each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:`,
|
|
250
|
+
`<tool_call>`,
|
|
251
|
+
`{"name": <function-name>, "arguments": <args-json-object>}`,
|
|
252
|
+
`</tool_call>`,
|
|
253
|
+
``,
|
|
254
|
+
`Rules:`,
|
|
255
|
+
`- Call tools to take actions. Do NOT describe what you would do — just do it.`,
|
|
256
|
+
`- For simple conversation (greetings, questions) respond with plain text, no tools.`,
|
|
257
|
+
`- After finishing, write a brief summary.`,
|
|
258
|
+
].join('\n')
|
|
259
|
+
: [
|
|
260
|
+
`You are a helpful AI agent. Working directory: ${workDir}`,
|
|
261
|
+
`Use the provided tools to complete tasks. Don't describe — act.`,
|
|
262
|
+
].join('\n');
|
|
302
263
|
|
|
303
264
|
const messages = [
|
|
304
265
|
{ role: 'system', content: systemPrompt },
|
|
@@ -329,10 +290,13 @@ export class OllamaAgent extends EventEmitter {
|
|
|
329
290
|
model: effectiveModel,
|
|
330
291
|
messages,
|
|
331
292
|
stream: true,
|
|
332
|
-
// qwen3: tools embedded in system prompt — do NOT pass tools param (broken in Ollama)
|
|
333
|
-
// Other models: pass tools normally
|
|
293
|
+
// qwen3: tools embedded in system prompt — do NOT pass tools param (broken in Ollama for qwen3)
|
|
294
|
+
// Other models: pass tools normally
|
|
334
295
|
...(!isQwen3 ? { tools: TOOL_DEFS, tool_choice: 'auto' } : {}),
|
|
335
|
-
|
|
296
|
+
options: {
|
|
297
|
+
num_ctx: 8192, // explicit context — Ollama defaults to 2048 which is too small
|
|
298
|
+
...(isQwen3 ? { think: false } : {}), // CRITICAL: thinking + tools corrupts template
|
|
299
|
+
},
|
|
336
300
|
};
|
|
337
301
|
|
|
338
302
|
response = await fetch(`${this.baseUrl}/v1/chat/completions`, {
|