@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.
- package/LICENSE +73 -0
- package/README.md +207 -0
- package/bin/hdAsk.js +103 -0
- package/bin/hdClear.js +13 -0
- package/bin/hdCode.js +110 -0
- package/bin/hdConnect.js +230 -0
- package/bin/hdInspect.js +28 -0
- package/bin/hdNpm.js +114 -0
- package/bin/hdPrompt.js +108 -0
- package/examples/claude-test.js +89 -0
- package/examples/claude.js +143 -0
- package/examples/gpt.js +127 -0
- package/examples/gpt_code.js +125 -0
- package/examples/gpt_note_keeping.js +117 -0
- package/examples/grok.js +119 -0
- package/examples/grok_code.js +114 -0
- package/examples/grok_note_keeping.js +111 -0
- package/lib/API/anthropic.com/text.js +402 -0
- package/lib/API/brave.com/search.js +239 -0
- package/lib/API/openai.com/README.md +1 -0
- package/lib/API/openai.com/reponses/MESSAGES.md +69 -0
- package/lib/API/openai.com/reponses/text.js +416 -0
- package/lib/API/x.ai/text.js +415 -0
- package/lib/AgentClient.js +197 -0
- package/lib/AgentManager.js +144 -0
- package/lib/AgentServer.js +336 -0
- package/lib/Cli.js +256 -0
- package/lib/Prompt.js +728 -0
- package/lib/Session.js +231 -0
- package/lib/ToolSet.js +186 -0
- package/lib/fafs.js +93 -0
- package/lib/genericToolset.js +170 -0
- package/lib/index.js +34 -0
- package/lib/promptHelpers.js +132 -0
- package/lib/testToolset.js +42 -0
- package/module.md +189 -0
- package/package.json +49 -0
- package/types/API/anthropic.com/text.d.ts +207 -0
- package/types/API/brave.com/search.d.ts +156 -0
- package/types/API/openai.com/reponses/text.d.ts +225 -0
- package/types/API/x.ai/text.d.ts +286 -0
- package/types/AgentClient.d.ts +70 -0
- package/types/AgentManager.d.ts +112 -0
- package/types/AgentServer.d.ts +38 -0
- package/types/Cli.d.ts +52 -0
- package/types/Prompt.d.ts +298 -0
- package/types/Session.d.ts +31 -0
- package/types/ToolSet.d.ts +95 -0
- package/types/fafs.d.ts +47 -0
- package/types/genericToolset.d.ts +3 -0
- package/types/index.d.ts +23 -0
- package/types/promptHelpers.d.ts +1 -0
- 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
|
+
}
|