@j-o-r/hello-dave 0.0.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.
Files changed (53) hide show
  1. package/LICENSE +73 -0
  2. package/README.md +207 -0
  3. package/bin/hdAsk.js +103 -0
  4. package/bin/hdClear.js +13 -0
  5. package/bin/hdCode.js +110 -0
  6. package/bin/hdConnect.js +230 -0
  7. package/bin/hdInspect.js +28 -0
  8. package/bin/hdNpm.js +114 -0
  9. package/bin/hdPrompt.js +108 -0
  10. package/examples/claude-test.js +89 -0
  11. package/examples/claude.js +143 -0
  12. package/examples/gpt.js +127 -0
  13. package/examples/gpt_code.js +125 -0
  14. package/examples/gpt_note_keeping.js +117 -0
  15. package/examples/grok.js +119 -0
  16. package/examples/grok_code.js +114 -0
  17. package/examples/grok_note_keeping.js +111 -0
  18. package/lib/API/anthropic.com/text.js +402 -0
  19. package/lib/API/brave.com/search.js +239 -0
  20. package/lib/API/openai.com/README.md +1 -0
  21. package/lib/API/openai.com/reponses/MESSAGES.md +69 -0
  22. package/lib/API/openai.com/reponses/text.js +416 -0
  23. package/lib/API/x.ai/text.js +415 -0
  24. package/lib/AgentClient.js +197 -0
  25. package/lib/AgentManager.js +144 -0
  26. package/lib/AgentServer.js +336 -0
  27. package/lib/Cli.js +256 -0
  28. package/lib/Prompt.js +728 -0
  29. package/lib/Session.js +231 -0
  30. package/lib/ToolSet.js +186 -0
  31. package/lib/fafs.js +93 -0
  32. package/lib/genericToolset.js +170 -0
  33. package/lib/index.js +34 -0
  34. package/lib/promptHelpers.js +132 -0
  35. package/lib/testToolset.js +42 -0
  36. package/module.md +189 -0
  37. package/package.json +49 -0
  38. package/types/API/anthropic.com/text.d.ts +207 -0
  39. package/types/API/brave.com/search.d.ts +156 -0
  40. package/types/API/openai.com/reponses/text.d.ts +225 -0
  41. package/types/API/x.ai/text.d.ts +286 -0
  42. package/types/AgentClient.d.ts +70 -0
  43. package/types/AgentManager.d.ts +112 -0
  44. package/types/AgentServer.d.ts +38 -0
  45. package/types/Cli.d.ts +52 -0
  46. package/types/Prompt.d.ts +298 -0
  47. package/types/Session.d.ts +31 -0
  48. package/types/ToolSet.d.ts +95 -0
  49. package/types/fafs.d.ts +47 -0
  50. package/types/genericToolset.d.ts +3 -0
  51. package/types/index.d.ts +23 -0
  52. package/types/promptHelpers.d.ts +1 -0
  53. package/types/testToolset.d.ts +3 -0
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env node
2
+ import { fileURLToPath } from 'url';
3
+ import path from 'path';
4
+ import AgentManager from '../lib/AgentManager.js';
5
+ import { parseArgs, readIn } from '@j-o-r/sh';
6
+ import genTools from '../lib/genericToolset.js';
7
+ import { systemInfo } from '../lib/fafs.js';
8
+
9
+ const name = path.basename(fileURLToPath(import.meta.url), path.extname(fileURLToPath(import.meta.url)));
10
+ const api = 'grok';
11
+
12
+ const input = await readIn();
13
+ const args = parseArgs();
14
+ const help = args['help'] || false;
15
+ const connect = args['connect'] ? args['connect'] : undefined;
16
+ const serve = args['serve'] ? parseInt(args['serve']) : undefined;
17
+
18
+ /** @type {import('lib/API/x.ai/text.js').XOptions} */
19
+ const options = {}
20
+ // Set properties only if provided via command line (except model which has default)
21
+ if (args['model'] || true) { // model gets default value
22
+ // @ts-ignore
23
+ options.model = args['model'] || 'grok-code-fast-1';
24
+ }
25
+ if (args['temperature']) {
26
+ options.temperature = parseFloat(args['temperature']);
27
+ }
28
+ if (args['tokens']) {
29
+ options.max_completion_tokens = parseInt(args['tokens']);
30
+ }
31
+ if (args['top_p']) {
32
+ options.top_p = parseFloat(args['top_p']);
33
+ }
34
+ options.search_parameters = { mode: 'auto' }
35
+ const reasoning = args['reasoning'] ? args['reasoning'] : null;
36
+ if (reasoning) {
37
+ // @ts-ignore
38
+ options.reasoning_effort = reasoning
39
+ }
40
+
41
+ const toolsetMode = 'auto';
42
+ const contextWindow = args['context'] ? parseInt(args['context']) : 250000;
43
+
44
+ // Copy from the generic toolset
45
+ function printHelp() {
46
+ console.log(`
47
+ '${name} --help' You are looking at it.
48
+ '
49
+ OPTIONS:
50
+ --tokens [number]: max generated tokens
51
+ --context [number] : truncate message history to context-windows size default 130000
52
+ --temperature [float] : -2 / +2
53
+ --model [grok-4|grok-3|grok-3-mini|grok-3-mini-fast|grok-code-fast-1]
54
+ --top_p [float]: number > 0, 0.1 means no top_p
55
+ --reasoning [low|high]
56
+ --tools [javascript,bash] comma seperated list
57
+ e.g.
58
+ grok.js --model grok-3-mini --tokens 4000 --context 10000
59
+
60
+ `);
61
+ process.exit()
62
+ }
63
+
64
+ if (help) {
65
+ printHelp();
66
+ }
67
+
68
+ const sys = await systemInfo();
69
+ const prompt = `
70
+ You are a coding assistant specializing in Bash and JavaScript (ESM/ESNext), with support for other languages. Assist by executing, reading, creating, querying, explaining, or helping with code. Use tools like 'execute_bash_script' for safe Bash execution. For writing and reading files, stay in the current working folder. Provide clear, step-by-step responses, examples, and ensure safety compliance.
71
+ Update your 'memory' frequently.
72
+ ---env
73
+ ${sys}
74
+ ---
75
+ `.trim();
76
+ const agent = new AgentManager({ name });
77
+ agent.setup({
78
+ prompt,
79
+ api,
80
+ options,
81
+ toolsetMode,
82
+ contextWindow
83
+ });
84
+ const toolset = agent.getToolset();
85
+ if (toolset) {
86
+ const addTools = (args['tools']) ? args['tools'].split(',') : ['bash'];
87
+ if (addTools.includes('javascript')) {
88
+ agent.addGenericToolcall('javascript_interpreter');
89
+ }
90
+ if (addTools.includes('bash')) {
91
+ agent.addGenericToolcall('execute_bash_script');
92
+ }
93
+ }
94
+ const cliIntro = `
95
+ ${name} ${options.model}.
96
+ - search ${options.search_parameters.mode}
97
+ - context: ${contextWindow}
98
+ `.trim();
99
+ const description = `
100
+ Gateway to a specialized coding assistant for Bash, JavaScript (ESM/ESNext), and other languages. Handles execution, reading, creation, querying, explaining, and code help, with safety and folder restrictions.
101
+ `.trim();
102
+
103
+ if (input === '' && serve) {
104
+ agent.enableServer('code', description, serve);
105
+ }
106
+ if (input !== '') {
107
+ // Direct input output
108
+ const res = await agent.directCall(input);
109
+ console.log(res);
110
+ } else if (connect) {
111
+ agent.attach('code', description, connect)
112
+ } else {
113
+ agent.startCli(cliIntro);
114
+ }
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env node
2
+ import { fileURLToPath } from 'url';
3
+ import path from 'path';
4
+ import AgentManager from '../lib/AgentManager.js';
5
+ import { systemInfo } from '../lib/fafs.js';
6
+ import { parseArgs, readIn } from '@j-o-r/sh';
7
+ import toolsPool from '../lib/genericToolset.js';
8
+
9
+ const name = path.basename(fileURLToPath(import.meta.url), path.extname(fileURLToPath(import.meta.url)));
10
+ const api = 'grok'; // Using GPT API for general AI assistance, adjust if needed
11
+
12
+ const input = await readIn();
13
+ const args = parseArgs();
14
+ const help = args['help'] || false;
15
+ const connect = args['connect'] ? args['connect'] : undefined;
16
+ const serve = args['serve'] ? parseInt(args['serve']) : undefined;
17
+
18
+ /** @type {import('../lib/API/x.ai/text.js').XOptions} */ // Assuming GPT uses OpenAI API
19
+ const options = {}
20
+ // Set properties only if provided via command line
21
+ if (args['model'] || true) {
22
+ // @ts-ignore
23
+ options.model = args['model'] || 'grok-4';
24
+ }
25
+ if (args['temperature']) {
26
+ options.temperature = parseFloat(args['temperature']);
27
+ }
28
+ if (args['tokens']) {
29
+ options.max_completion_tokens = parseInt(args['tokens']);
30
+ }
31
+ if (args['top_p']) {
32
+ options.top_p = parseFloat(args['top_p']);
33
+ }
34
+
35
+ options.search_parameters = {mode:'auto'}
36
+
37
+ const reasoning = args['reasoning'] ? args['reasoning'] : null;
38
+ if (reasoning) {
39
+ // @ts-ignore
40
+ options.reasoning_effort = reasoning
41
+ }
42
+
43
+ const toolsetMode = 'auto';
44
+ const contextWindow = args['context'] ? parseInt(args['context']) : 250000;
45
+
46
+ // Copy from the generic toolset
47
+ function printHelp() {
48
+ console.log(`
49
+ '${name} --help' You are looking at it.
50
+
51
+ OPTIONS:
52
+ --tokens [number]: max generated tokens
53
+ --context [number] : truncate message history to context-windows size default 250000
54
+ --temperature [float] : -2 / +2
55
+ --model [gpt-4o|gpt-4o-mini|etc] // Adjust based on available models
56
+ --top_p [float]: number > 0, 0.1 means no top_p
57
+ --reasoning [low|high]
58
+ --tools [javascript,bash] comma separated list
59
+ e.g.
60
+ note_keeping.js --model gpt-4o-mini --tokens 4000 --context 10000
61
+
62
+ `);
63
+ process.exit()
64
+ }
65
+
66
+ if (help) {
67
+ printHelp();
68
+ }
69
+ const sys = await systemInfo();
70
+ const prompt = `
71
+ You are an AI note-keeping assistant for Markdown notes in extended context. Use/create 'notes' folder in cwd. Search via tools like grep. Create/update/delete .md files safely. Goals: track progress, manage todos, capture requirements, document processes, store links/bookmarks/remarks. Respond concisely, step-by-step, no unnecessary follow-ups.
72
+ ---env
73
+ ${sys}
74
+ ---
75
+ `.trim();
76
+ const agent = new AgentManager({ name });
77
+ agent.setup({
78
+ prompt,
79
+ api,
80
+ options,
81
+ toolsetMode,
82
+ contextWindow
83
+ });
84
+ const toolset = agent.getToolset();
85
+
86
+ if (toolset) {
87
+ let tool = toolsPool.get('execute_bash_script');
88
+ if (tool) {
89
+ toolset.add('execute_bash_script', tool.description, tool.parameters, tool.method);
90
+ }
91
+ }
92
+ const cliIntro = `
93
+ ${options.model}.
94
+ - search ${options.search_parameters.mode}
95
+ `.trim();
96
+ const description = `
97
+ AI-assisted note-keeping tool for managing Markdown notes, including search, creation, updates, and deletion. Supports progress tracking, todos, requirements, docs, links, bookmarks, and remarks.
98
+ `.trim();
99
+
100
+ if (input === '' && serve) {
101
+ agent.enableServer('memory', description, serve);
102
+ }
103
+ if (input !== '') {
104
+ // Direct input output
105
+ const res = await agent.directCall(input);
106
+ console.log(res);
107
+ } else if(connect) {
108
+ agent.attach('memory', description, connect)
109
+ } else {
110
+ agent.startCli(cliIntro);
111
+ }
@@ -0,0 +1,402 @@
1
+ import { GLOBAL } from '../../fafs.js';
2
+ import { request as doRequest } from '@j-o-r/apiserver';
3
+ import { sleep } from '@j-o-r/sh';
4
+
5
+ /**
6
+ * @typedef {import('../../Prompt.js').default} Prompt
7
+ * @typedef {import('../../ToolSet.js').default} ToolSet
8
+ */
9
+ /**
10
+ * Search-tool configuration options.
11
+ *
12
+ * @typedef {Object} SearchOptions
13
+ * @property {'web_search_20250305'} type Engine identifier (constant).
14
+ * @property {'web_search'} name Human-readable tool name.
15
+ *
16
+ * @property {number} [max_uses] Maximum searches the caller may perform in a single request.
17
+ * @property {string[]} [allowed_domains] Domains that are explicitly allowed in the results.
18
+ * @property {string[]} [blocked_domains] Domains that must never appear in the results.
19
+ * @property {UserLocation}[user_location] Hints for localizing search results.
20
+ */
21
+ /**
22
+ * Geographic context supplied to the search tool.
23
+ *
24
+ * @typedef {Object} UserLocation
25
+ * @property {'approximate'|'precise'} type How exact the location is.
26
+ * @property {string} city City name (e.g., "San Francisco").
27
+ * @property {string} region First-level region or state (e.g., "California").
28
+ * @property {string} country ISO-3166-1 alpha-2 country code (e.g., "US").
29
+ * @property {string} timezone IANA time-zone identifier (e.g., "America/Los_Angeles").
30
+ */
31
+
32
+ /**
33
+ * @typedef {Object} ANTHContent
34
+ * @property {string} type - The type of content.
35
+ * @property {string} text - The text content.
36
+ */
37
+ /**
38
+ * @typedef {object} ANTHOptions
39
+ *
40
+ * @property {string} [model] - What model to use
41
+ * @property {string} [system] - System prompt
42
+ * @property {ANTHContent[]} [messages] - Prompt / conversation
43
+ * @property {Array<ANTHTool|SearchOptions>} [tools] - Prompt / conversation
44
+ * @property {ANTHToolChoice} [tool_choice] - ..
45
+ * @property {object} [metadata] - ..
46
+ * @property {number} [temperature] - What sampling temperature to use, between 0 and 2.
47
+ * @property {number} [max_tokens] - The maximum number of tokens allowed for the generated answer.
48
+ * @property {number} [top_p] - Number between > 0 0.1 is NO top_p
49
+ * @property {number} [top_k] - Number between > 0 < 2048
50
+ * @property {SearchOptions} [search] - embedded search
51
+ * @property {boolean} [stream] - Chunk/stream request default null
52
+ * @property {Object} [thinking] - Reasoning
53
+ * @property {'enabled'} [thinking.type] -
54
+ * @property {number} [thinking.budget_tokens] -
55
+ */
56
+ /**
57
+ * @typedef {object} ANTHRequest
58
+ * @property {ANTHOptions} body
59
+ * @property {Headers} headers
60
+ * @property {string} url -
61
+ */
62
+ /**
63
+ * @typedef {Object} ANTHTool
64
+ * @property {string} name - The name of the tool.
65
+ * @property {string} description - A brief description of the tool's functionality.
66
+ * @property {Object} input_schema - The JSON schema defining the input parameters for the tool.
67
+ */
68
+ /**
69
+ * Represents a tool usage event.
70
+ * @typedef {Object} ANTHToolUse
71
+ * @property {string} type - The type of the tool usage event, in this case "tool_use".
72
+ * @property {string} id - The unique identifier for the tool usage event.
73
+ * @property {string} name - The name of the tool used.
74
+ * @property {Object} input - The input data for the tool usage.
75
+ */
76
+ /**
77
+ * @typedef {Object} ANTHToolChoice
78
+ * @property {string} type -
79
+ */
80
+
81
+ const API_URL = 'https://api.anthropic.com/v1/messages';
82
+
83
+ /** @type {ANTHOptions} */
84
+ const defaultSettings = {
85
+ model: 'claude-sonnet-4-0',
86
+ system: 'Be precise and concise.',
87
+ max_tokens: 50000,
88
+ stream: false
89
+ };
90
+
91
+ /**
92
+ * Get the default headers
93
+ * @returns {Headers}
94
+ */
95
+ const getHeaders = () => {
96
+ if (!process.env['ANTHKEY']) {
97
+ throw new Error('Missing ANTHKEY! (Anthropic key) export ANTHKEY=<Anthropic_KEY>')
98
+ }
99
+
100
+ const KEY = process.env['ANTHKEY'];
101
+ return {
102
+ // @ts-ignore
103
+ 'content-type': 'application/json',
104
+ 'anthropic-version': '2023-06-01',
105
+ // https://docs.anthropic.com/en/docs/tool-use
106
+ 'anthropic-beta': 'tools-2024-05-16',
107
+ 'x-api-key': `${KEY}`
108
+ }
109
+ }
110
+ /**
111
+ * Convert a toolset to something anthropic understands
112
+ * @param {ToolSet} toolset
113
+ * @returns {ANTHTool[]}
114
+ */
115
+ const generateAnthToolCalls = (toolset) => {
116
+ const list = toolset.list();
117
+ // @ts-ignore
118
+ const result = [];
119
+ list.forEach((item) => {
120
+ result.push({
121
+ name: item.name,
122
+ description: item.description,
123
+ input_schema: item.parameters
124
+ })
125
+ });
126
+ // @ts-ignore
127
+ return result;
128
+ }
129
+
130
+ /*
131
+ When `thinking` is enabled, a final `assistant` message must start with a thinking block
132
+ (preceeding the lastmost set of `tool_use` and `tool_result` blocks).
133
+ We recommend you include thinking blocks from previous turns.
134
+ To avoid this requirement, disable `thinking`.
135
+ Please consult our documentation at https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking"
136
+ */
137
+ /**
138
+ * Convert messages to Anthropic requirements
139
+ * @param {Prompt} prompt
140
+ * @returns {Array<ANTHContent|ANTHToolUse>}
141
+ */
142
+ const generateAnthMessages = (prompt) => {
143
+ const messages = prompt.messages;
144
+ // @ts-ignore
145
+ let result = [];
146
+ messages.forEach((msg) => {
147
+ delete msg.sticky;
148
+ delete msg.ts;
149
+ if (
150
+ ['assistant', 'user'].includes(msg.role)
151
+ ) {
152
+ const new_content = msg.content.filter((item) => ['text', 'image_url', 'function_request'].includes(item.type));
153
+ if (new_content.length > 0) {
154
+ let i = 0, len = new_content.length;
155
+ for (; i < len; i++) {
156
+ let mes = new_content[i];
157
+ // @ts-ignore
158
+ if (mes.type === 'image_url') {
159
+ /// @ts-ignore
160
+ let test = transformToImageObject(mes);
161
+ if (test) {
162
+ /// @ts-ignore
163
+ new_content[i] = test;
164
+ } else {
165
+ // @ts-ignore
166
+ new_content[i] = undefined;
167
+ }
168
+ // @ts-ignore
169
+ } else if (mes.type === 'function_request') {
170
+ new_content[i] = {
171
+ type: 'tool_use',
172
+ // @ts-ignore
173
+ id: mes.function_request.id,
174
+ // @ts-ignore
175
+ name: mes.function_request.name,
176
+ // @ts-ignore
177
+ input: JSON.parse(mes.function_request.parameters)
178
+ }
179
+ }
180
+ }
181
+ msg.content = new_content.filter(element => element !== undefined);
182
+ result.push(msg);
183
+ };
184
+ } else if (['tool'].includes(msg.role)) {
185
+ const fcontent = msg.content.filter((item) => item.type === 'function_response');
186
+ msg.content = [];
187
+ msg.role = 'user';
188
+ fcontent.forEach((fr) => {
189
+ msg.content.push({
190
+ type: 'tool_result',
191
+ // @ts-ignore
192
+ tool_use_id: fr.function_response.id,
193
+ // @ts-ignore
194
+ content: fr.function_response.response
195
+ })
196
+ });
197
+ if (msg.content.length > 0) {
198
+ result.push(msg);
199
+ }
200
+ } else if (['reasoning'].includes(msg.role)) {
201
+
202
+ }
203
+ });
204
+ // @ts-ignore
205
+ return result;
206
+ }
207
+ /**
208
+ * Create an anthropic request
209
+ * @param {Prompt} prompt
210
+ * @param {ToolSet} [tools]
211
+ * @param {ANTHOptions} [opts] overwrite default request settings
212
+ * @param {Headers|object} [hdrs] - optional headers to pass
213
+ * @returns {ANTHRequest}
214
+ * @throws {Error}
215
+ */
216
+ const generateRequest = (prompt, tools, opts = {}, hdrs = {}) => {
217
+ /** @type {ANTHOptions} */
218
+ // @ts-ignore
219
+ const body = { ...defaultSettings, ...opts };
220
+ const headers = { ...getHeaders(), ...hdrs };
221
+ if (body.thinking) {
222
+ // Thinking isn’t compatible with temperature or top_k modifications as well as forced tool use.
223
+ delete body.temperature;
224
+ delete body.top_k
225
+ if (typeof (body.top_p) === 'number' && body.top_p < 0.95) {
226
+ body.top_p = 0.95
227
+ }
228
+ }
229
+ // Sanity check
230
+ // basePromptCheck(prompt);
231
+ // @ts-ignore
232
+ body.messages = generateAnthMessages(prompt) || [];
233
+ // @ts-ignore
234
+ if (body.messages.length == 0) {
235
+ throw new Error('No messages found');
236
+ }
237
+ body.tools = (tools) ? generateAnthToolCalls(tools) : [];
238
+ body.tool_choice = { type: 'auto' };
239
+
240
+ if (body.search) {
241
+ if (!body.tools) {
242
+ body.tools = [];
243
+ body.tool_choice = { type: 'auto' };
244
+ }
245
+ body.tools.push(body.search)
246
+ }
247
+ delete body.search;
248
+
249
+ if (body.tools.length === 0) {
250
+ delete body.tools;
251
+ delete body.tool_choice;
252
+ }
253
+ // For now we delete the meta data
254
+ delete body.metadata;
255
+ return { body, headers, url: API_URL };
256
+ };
257
+
258
+
259
+ const parseMessages = (response, prompt) => {
260
+ /** @type {import('../../Session.js').FunctionRequest[]} */
261
+ const function_requests = [];
262
+ const messages = response.content;
263
+ // if (response.citations && response.citations.length > 0) {
264
+ // const cits = response.citations.map((item, index) => `${index + 1}. ${item}`).join('\n');
265
+ // prompt.add('log', cits);
266
+ // }
267
+ let i = 0;
268
+ const len = messages.length;
269
+ for (; i < len; i++) {
270
+ const msg = messages[i];
271
+ if (msg.type === 'thinking') {
272
+ prompt.add('reasoning', msg.thinking)
273
+ }
274
+ // Normal assistant message
275
+ if (msg.type === 'text') {
276
+ prompt.add('assistant', msg.text)
277
+ }
278
+ // Function request
279
+ if (msg.type === 'tool_use') {
280
+ /** @type {import('../../Session').FunctionRequest} */
281
+ const fr = {
282
+ type: 'function_request',
283
+ function_request: {
284
+ name: msg.name,
285
+ id: msg.id,
286
+ call_id: msg.id,
287
+ parameters: JSON.stringify(msg.input)
288
+ }
289
+ }
290
+ function_requests.push(fr)
291
+ }
292
+ }
293
+ if (function_requests.length > 0) {
294
+ prompt.addMultiModal('assistant', function_requests);
295
+ }
296
+ }
297
+
298
+ /**
299
+ * Process an openai response
300
+ * @param {number} duration - the time is MS before and after the request
301
+ * @param {import('lib/request.js').FetchResponse} res
302
+ * @param {Prompt} prompt
303
+ * @param {ToolSet} [toolset]
304
+ * @returns {Promise<void>}
305
+ * @throws {Error}
306
+ */
307
+ const parseResponse = async (duration, prompt, res, toolset) => {
308
+ if (res.status !== 200) {
309
+ new Error(`${res.status}: res.response`)
310
+ }
311
+ const record = prompt.templateRecord();
312
+ record.id = res.response.id;
313
+ record.model = res.response.model;
314
+ record.id = res.response.id;
315
+ record.environment = 'anthropic';
316
+ // @ts-ignore
317
+ record.isoDate = new Date(res.headers.get('date')).toISOString();
318
+ record.tokensIn = res.response.usage.input_tokens;
319
+ record.tokensOut = res.response.usage.output_tokens;
320
+ record.duration = duration;
321
+ prompt.addRecord(record);
322
+ parseMessages(res.response, prompt);
323
+ if (toolset) {
324
+ // Inspect for function_request and execute if so
325
+ await toolset.execute(prompt);
326
+ }
327
+ // if (response.stop_reason === 'tool_use') {
328
+ // // @ts-ignore
329
+ // await executeAnthToolCalls(tools, prompt, response.content);
330
+ // return headers;
331
+ // }
332
+
333
+ // if (response.content.length > 0) {
334
+ // // @ts-ignore
335
+ // const msg = anthMessagesToPromptContent(response.content);
336
+ // prompt.addMultiModal('assistant', msg);
337
+ // return headers;
338
+ // }
339
+ // throw new Error('No valid response found');
340
+ }
341
+
342
+ /**
343
+ * Do a request
344
+ * @param {Prompt} prompt
345
+ * @param {ToolSet|null} toolset
346
+ * @param {ANTHOptions} [options]:
347
+ * @param {number} [counter] - leave empty this counts the number of requests when doing recursive request calls
348
+ * @return {Promise<import('../../Session.js').Message>}
349
+ */
350
+ async function request(prompt, toolset = null, options, counter = 0) {
351
+ // Max number of recurusive calls
352
+ const counterMax = GLOBAL.max_recursive_requests;
353
+ const start = new Date().getTime();
354
+ // Generate the request
355
+ const { url, headers, body } = generateRequest(prompt, toolset, options);
356
+ prompt.emit(prompt.EVENTS.http_request, {url, counter, body});
357
+ const res = await doRequest(url, 'POST', headers, body);
358
+ prompt.emit(prompt.EVENTS.http_response, res);
359
+ if (res.status !== 200) {
360
+ throw new Error(`${res.status}: ${JSON.stringify(res.response, null, ' ')}`)
361
+ }
362
+ counter = 1 + counter;
363
+ const stop_reason = res.response.stop_reason;
364
+ if (stop_reason === 'pause_turn') {
365
+ await sleep('4s');
366
+ // We need to REPOST, nothing has been done yet
367
+ return request(prompt, toolset, options, counter)
368
+ } else if (stop_reason === 'refusal') {
369
+ throw new Error('Request is refused');
370
+ } else if (stop_reason === 'max_tokens') {
371
+ // Response was truncated
372
+ prompt.add('log', 'Response is truncated: max_tokens');
373
+ }
374
+ const duration = new Date().getTime() - start;
375
+ await parseResponse(duration, prompt, res, toolset);
376
+ if (counter >= counterMax) {
377
+ // This should be handled better
378
+ // Can't we just stop? instead of an error
379
+ // Or should we inspect the last message of a session restore
380
+ throw new Error('Max number of recursive calls reached');
381
+ }
382
+ const lastMessage = prompt.getLastMessage();
383
+ if (!lastMessage) throw new Error('No message found');
384
+ // A last message is no indication
385
+ // https://docs.anthropic.com/en/api/handling-stop-reasons
386
+ // This is tool_use
387
+ if (lastMessage.role === 'tool') {
388
+ // need to do another roundtrip to include the funtion_responses
389
+ // Claude is quiet stricted in token p/m
390
+ // Delay with 4 secs
391
+ // We delay with 60 seconds to get rid of the rate limit
392
+ await sleep('60s');
393
+ return request(prompt, toolset, options, counter)
394
+ } else {
395
+ return lastMessage
396
+ }
397
+ }
398
+ export {
399
+ generateRequest,
400
+ parseResponse,
401
+ request
402
+ }