@kkeve/suite 0.0.1 → 0.0.2
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/README.md +53 -0
- package/dist/bin/keve.d.ts +13 -0
- package/dist/bin/keve.js +164 -0
- package/dist/commands/auth-update.d.ts +16 -0
- package/dist/commands/auth-update.js +88 -0
- package/dist/commands/clean.d.ts +10 -0
- package/dist/commands/clean.js +78 -0
- package/dist/commands/config.d.ts +17 -0
- package/dist/commands/config.js +99 -0
- package/dist/commands/init.d.ts +15 -0
- package/dist/commands/init.js +292 -0
- package/dist/commands/report.d.ts +12 -0
- package/dist/commands/report.js +159 -0
- package/dist/commands/run.d.ts +29 -0
- package/dist/commands/run.js +417 -0
- package/dist/commands/test.d.ts +12 -0
- package/dist/commands/test.js +102 -0
- package/dist/commands/vitest.d.ts +14 -0
- package/dist/commands/vitest.js +166 -0
- package/dist/config.d.ts +109 -0
- package/dist/config.js +291 -0
- package/dist/core/action-log-writer.d.ts +47 -0
- package/dist/{tools/write-file.js → core/action-log-writer.js} +42 -26
- package/dist/core/auth.d.ts +17 -0
- package/dist/core/auth.js +224 -0
- package/dist/core/cdp-resolver.d.ts +5 -0
- package/dist/core/cdp-resolver.js +93 -0
- package/dist/core/generateReport/report.d.ts +19 -0
- package/dist/core/generateReport/report.js +91 -0
- package/dist/core/generateReport/reportData.d.ts +215 -0
- package/dist/core/generateReport/reportData.js +735 -0
- package/dist/core/global-setup.d.ts +6 -0
- package/dist/core/global-setup.js +113 -0
- package/dist/core/keve-aspect.d.ts +54 -0
- package/dist/core/keve-aspect.js +52 -0
- package/dist/core/keve-decorators.d.ts +51 -0
- package/dist/core/keve-decorators.js +95 -0
- package/dist/core/keve-registry.d.ts +38 -0
- package/dist/core/keve-registry.js +25 -0
- package/dist/core/keve-report.d.ts +27 -0
- package/dist/core/keve-report.js +357 -0
- package/dist/core/keve-test.d.ts +36 -0
- package/dist/core/keve-test.js +364 -0
- package/dist/core/learned-actions.d.ts +24 -0
- package/dist/core/learned-actions.js +65 -0
- package/dist/core/pw-config.d.ts +19 -0
- package/dist/core/pw-config.js +180 -0
- package/dist/index.d.ts +8 -23
- package/dist/index.js +36 -43
- package/dist/page-agent/agent.d.ts +129 -0
- package/dist/page-agent/agent.js +585 -0
- package/dist/page-agent/hooks.d.ts +13 -0
- package/dist/page-agent/hooks.js +218 -0
- package/dist/page-agent/tools.d.ts +29 -0
- package/dist/page-agent/tools.js +297 -0
- package/doc/README.md +124 -0
- package/doc/agents/keve-dev-test-agent.md +140 -0
- package/doc/agents/keve-test-agent.md +178 -0
- package/doc/asserts//346/265/213/350/257/225/345/220/204/351/230/266/346/256/265/350/257/264/346/230/216.png +0 -0
- package/doc/keve-agent-demo.html +370 -0
- package/doc/skills/keve-bddcase-gen/SKILL.md +332 -0
- package/doc/skills/keve-bddcase-gen/references/test-case-rules.md +91 -0
- package/doc/skills/keve-bddcase-gen/references/test-cases-example.md +103 -0
- package/doc/skills/keve-component-gen/SKILL.md +255 -0
- package/doc/skills/keve-component-gen/references/component-mock-strategy.md +74 -0
- package/doc/skills/keve-component-gen/references/component-model-template.md +98 -0
- package/doc/skills/keve-component-gen/references/component-test-template.md +100 -0
- package/doc/skills/keve-component-gen/references/vue-test-utils-guide.md +91 -0
- package/doc/skills/keve-e2e-gen/SKILL.md +179 -0
- package/doc/skills/keve-e2e-gen/references/code-analysis-guide.md +208 -0
- package/doc/skills/keve-e2e-gen/references/decisions.md +36 -0
- package/doc/skills/keve-e2e-gen/references/pom-generation-guide.md +140 -0
- package/doc/skills/keve-e2e-gen/references/script-generation-guide.md +236 -0
- package/doc/skills/keve-e2e-gen/references/test-auditor-guide.md +189 -0
- package/doc/skills/keve-e2e-gen/scripts/extract-components.sh +85 -0
- package/doc/skills/keve-e2e-gen/scripts/inject-testids.sh +95 -0
- package/doc/skills/keve-static-analysis/SKILL.md +362 -0
- package/doc/skills/keve-static-analysis/references/codegraph-analysis-guide.md +108 -0
- package/doc/skills/keve-static-analysis/references/git-impact-analysis.md +81 -0
- package/doc/skills/keve-static-analysis/references/lint-fix-guide.md +69 -0
- package/doc/skills/keve-static-analysis/references/runtime-risk-patterns.md +92 -0
- package/doc/skills/keve-static-analysis/references/security-scan-guide.md +32 -0
- package/doc/skills/keve-static-analysis/references/ts-error-patterns.md +98 -0
- package/doc/skills/keve-static-analysis/references/vue-security-guide.md +72 -0
- package/doc/skills/keve-test-eval/SKILL.md +78 -0
- package/doc/skills/keve-test-eval/references/common-fix-guide.md +166 -0
- package/doc/skills/keve-test-eval/references/confidence-data-guide.md +78 -0
- package/doc/skills/keve-test-eval/references/fix-loop-guide.md +67 -0
- package/doc/skills/keve-unit-gen/SKILL.md +203 -0
- package/doc/skills/keve-unit-gen/references/boundary-conditions.md +78 -0
- package/doc/skills/keve-unit-gen/references/unit-mock-strategy.md +71 -0
- package/doc/skills/keve-unit-gen/references/unit-test-template.md +105 -0
- package/package.json +47 -25
- package/dist/agent/factory.d.ts +0 -25
- package/dist/agent/factory.js +0 -99
- package/dist/agent/index.d.ts +0 -3
- package/dist/agent/index.js +0 -7
- package/dist/agent/loader.d.ts +0 -22
- package/dist/agent/loader.js +0 -117
- package/dist/agent/types.d.ts +0 -9
- package/dist/agent/types.js +0 -2
- package/dist/agent-loop/complete-task.d.ts +0 -28
- package/dist/agent-loop/complete-task.js +0 -70
- package/dist/agent-loop/index.d.ts +0 -16
- package/dist/agent-loop/index.js +0 -26
- package/dist/agent-loop/loop.d.ts +0 -42
- package/dist/agent-loop/loop.js +0 -282
- package/dist/agent-loop/runtime.d.ts +0 -40
- package/dist/agent-loop/runtime.js +0 -163
- package/dist/agent-loop/types.d.ts +0 -161
- package/dist/agent-loop/types.js +0 -105
- package/dist/context/index.d.ts +0 -8
- package/dist/context/index.js +0 -29
- package/dist/context/tokenCalculation.d.ts +0 -55
- package/dist/context/tokenCalculation.js +0 -95
- package/dist/context/truncation.d.ts +0 -67
- package/dist/context/truncation.js +0 -142
- package/dist/context/types.d.ts +0 -7
- package/dist/context/types.js +0 -27
- package/dist/llm/OpenAIClient.d.ts +0 -31
- package/dist/llm/OpenAIClient.js +0 -455
- package/dist/llm/constants.d.ts +0 -2
- package/dist/llm/constants.js +0 -6
- package/dist/llm/errors.d.ts +0 -25
- package/dist/llm/errors.js +0 -51
- package/dist/llm/index.d.ts +0 -49
- package/dist/llm/index.js +0 -158
- package/dist/llm/types.d.ts +0 -124
- package/dist/llm/types.js +0 -2
- package/dist/llm/utils.d.ts +0 -29
- package/dist/llm/utils.js +0 -284
- package/dist/skill/frontmatter-parser.d.ts +0 -50
- package/dist/skill/frontmatter-parser.js +0 -152
- package/dist/skill/index.d.ts +0 -12
- package/dist/skill/index.js +0 -17
- package/dist/skill/loader.d.ts +0 -96
- package/dist/skill/loader.js +0 -256
- package/dist/tools/ask-user.d.ts +0 -8
- package/dist/tools/ask-user.js +0 -47
- package/dist/tools/bash.d.ts +0 -47
- package/dist/tools/bash.js +0 -213
- package/dist/tools/edit-file.d.ts +0 -9
- package/dist/tools/edit-file.js +0 -76
- package/dist/tools/glob.d.ts +0 -8
- package/dist/tools/glob.js +0 -101
- package/dist/tools/grep.d.ts +0 -10
- package/dist/tools/grep.js +0 -76
- package/dist/tools/index.d.ts +0 -24
- package/dist/tools/index.js +0 -47
- package/dist/tools/read-files.d.ts +0 -8
- package/dist/tools/read-files.js +0 -81
- package/dist/tools/write-file.d.ts +0 -8
package/dist/llm/OpenAIClient.js
DELETED
|
@@ -1,455 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.OpenAIClient = void 0;
|
|
37
|
-
/**
|
|
38
|
-
* OpenAI Client implementation
|
|
39
|
-
|
|
40
|
-
不需要工具 → LLM.chat() → 直接调 API,不带 tools
|
|
41
|
-
需要工具循环 → LLM.chatWithTools() → 调 OpenAIClient.chat(),tool_choice: auto
|
|
42
|
-
需要强制工具 → LLM.invoke() → 调 OpenAIClient.invoke(),tool_choice: required
|
|
43
|
-
|
|
44
|
-
*/
|
|
45
|
-
const z = __importStar(require("zod/v4"));
|
|
46
|
-
const errors_1 = require("./errors");
|
|
47
|
-
const utils_1 = require("./utils");
|
|
48
|
-
/**
|
|
49
|
-
* Client for OpenAI compatible APIs
|
|
50
|
-
*/
|
|
51
|
-
class OpenAIClient {
|
|
52
|
-
config;
|
|
53
|
-
fetch;
|
|
54
|
-
constructor(config) {
|
|
55
|
-
this.config = config;
|
|
56
|
-
this.fetch = config.customFetch;
|
|
57
|
-
}
|
|
58
|
-
async invoke(messages, tools, abortSignal, options) {
|
|
59
|
-
// 1. Convert tools to OpenAI format
|
|
60
|
-
// If a Tool has customJsonSchema, use it directly (detailed schema for LLM)
|
|
61
|
-
// Otherwise, convert from zod inputSchema via zodToOpenAITool
|
|
62
|
-
const openaiTools = Object.entries(tools).map(([name, t]) => {
|
|
63
|
-
if (t.customJsonSchema) {
|
|
64
|
-
return {
|
|
65
|
-
type: 'function',
|
|
66
|
-
function: {
|
|
67
|
-
name,
|
|
68
|
-
description: t.description,
|
|
69
|
-
parameters: t.customJsonSchema,
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
return (0, utils_1.zodToOpenAITool)(name, t);
|
|
74
|
-
});
|
|
75
|
-
// Build request body
|
|
76
|
-
let toolChoice = 'required';
|
|
77
|
-
if (options?.toolChoiceName && !this.config.disableNamedToolChoice) {
|
|
78
|
-
toolChoice = { type: 'function', function: { name: options.toolChoiceName } };
|
|
79
|
-
}
|
|
80
|
-
else if (this.config.disableNamedToolChoice) {
|
|
81
|
-
toolChoice = undefined;
|
|
82
|
-
}
|
|
83
|
-
const requestBody = {
|
|
84
|
-
model: this.config.model,
|
|
85
|
-
temperature: this.config.temperature,
|
|
86
|
-
messages,
|
|
87
|
-
tools: openaiTools,
|
|
88
|
-
parallel_tool_calls: false,
|
|
89
|
-
...(toolChoice !== undefined && { tool_choice: toolChoice }),
|
|
90
|
-
};
|
|
91
|
-
(0, utils_1.modelPatch)(requestBody);
|
|
92
|
-
// 2. Call API
|
|
93
|
-
let response;
|
|
94
|
-
try {
|
|
95
|
-
response = await this.fetch(`${this.config.baseURL}/chat/completions`, {
|
|
96
|
-
method: 'POST',
|
|
97
|
-
headers: {
|
|
98
|
-
'Content-Type': 'application/json',
|
|
99
|
-
...(this.config.apiKey && { Authorization: `Bearer ${this.config.apiKey}` }),
|
|
100
|
-
},
|
|
101
|
-
body: JSON.stringify(requestBody),
|
|
102
|
-
signal: abortSignal,
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
catch (error) {
|
|
106
|
-
const isAbortError = error?.name === 'AbortError';
|
|
107
|
-
const errorMessage = isAbortError ? 'Network request aborted' : 'Network request failed';
|
|
108
|
-
if (!isAbortError)
|
|
109
|
-
console.error(error);
|
|
110
|
-
throw new errors_1.InvokeError(errors_1.InvokeErrorType.NETWORK_ERROR, errorMessage, error);
|
|
111
|
-
}
|
|
112
|
-
// 3. Handle HTTP errors
|
|
113
|
-
if (!response.ok) {
|
|
114
|
-
const errorData = await response.json().catch();
|
|
115
|
-
const errorMessage = errorData.error?.message || response.statusText;
|
|
116
|
-
if (response.status === 401 || response.status === 403) {
|
|
117
|
-
throw new errors_1.InvokeError(errors_1.InvokeErrorType.AUTH_ERROR, `Authentication failed: ${errorMessage}`, errorData);
|
|
118
|
-
}
|
|
119
|
-
if (response.status === 429) {
|
|
120
|
-
throw new errors_1.InvokeError(errors_1.InvokeErrorType.RATE_LIMIT, `Rate limit exceeded: ${errorMessage}`, errorData);
|
|
121
|
-
}
|
|
122
|
-
if (response.status >= 500) {
|
|
123
|
-
throw new errors_1.InvokeError(errors_1.InvokeErrorType.SERVER_ERROR, `Server error: ${errorMessage}`, errorData);
|
|
124
|
-
}
|
|
125
|
-
throw new errors_1.InvokeError(errors_1.InvokeErrorType.UNKNOWN, `HTTP ${response.status}: ${errorMessage}`, errorData);
|
|
126
|
-
}
|
|
127
|
-
// 4. Parse and validate response
|
|
128
|
-
const data = await response.json();
|
|
129
|
-
const choice = data.choices?.[0];
|
|
130
|
-
if (!choice) {
|
|
131
|
-
throw new errors_1.InvokeError(errors_1.InvokeErrorType.UNKNOWN, 'No choices in response', data);
|
|
132
|
-
}
|
|
133
|
-
// Check finish_reason
|
|
134
|
-
switch (choice.finish_reason) {
|
|
135
|
-
case 'tool_calls':
|
|
136
|
-
case 'function_call':
|
|
137
|
-
case 'stop':
|
|
138
|
-
break;
|
|
139
|
-
case 'length':
|
|
140
|
-
throw new errors_1.InvokeError(errors_1.InvokeErrorType.CONTEXT_LENGTH, 'Response truncated: max tokens reached', undefined, data);
|
|
141
|
-
case 'content_filter':
|
|
142
|
-
throw new errors_1.InvokeError(errors_1.InvokeErrorType.CONTENT_FILTER, 'Content filtered by safety system', undefined, data);
|
|
143
|
-
default:
|
|
144
|
-
throw new errors_1.InvokeError(errors_1.InvokeErrorType.UNKNOWN, `Unexpected finish_reason: ${choice.finish_reason}`, undefined, data);
|
|
145
|
-
}
|
|
146
|
-
// Apply normalizeResponse if provided
|
|
147
|
-
const normalizedData = options?.normalizeResponse ? options.normalizeResponse(data) : data;
|
|
148
|
-
const normalizedChoice = normalizedData.choices?.[0];
|
|
149
|
-
// Get tool name from response
|
|
150
|
-
const toolCallName = normalizedChoice?.message?.tool_calls?.[0]?.function?.name;
|
|
151
|
-
if (!toolCallName) {
|
|
152
|
-
// When LLM returns no valid tool_call (finish_reason: 'stop', empty tool_calls, etc.),
|
|
153
|
-
// treat it as a graceful fallback rather than throwing an error.
|
|
154
|
-
const textContent = normalizedChoice?.message?.content;
|
|
155
|
-
// Try to find a suitable tool for synthetic call:
|
|
156
|
-
// 1. Prefer "done" or "AgentOutput" (main loop tools)
|
|
157
|
-
// 2. Fall back to the first available tool (sub-tool calls like VerifyResult)
|
|
158
|
-
const doneTool = tools['done'] || tools['AgentOutput'] || Object.values(tools)[0];
|
|
159
|
-
if (doneTool) {
|
|
160
|
-
// For "done"/"AgentOutput", wrap as { text, success }
|
|
161
|
-
// For other tools, try to parse textContent as JSON; fallback to minimal valid input
|
|
162
|
-
let syntheticArgs;
|
|
163
|
-
const toolName = doneTool === tools['done'] ? 'done'
|
|
164
|
-
: doneTool === tools['AgentOutput'] ? 'AgentOutput'
|
|
165
|
-
: Object.keys(tools)[0];
|
|
166
|
-
if (toolName === 'done' || toolName === 'AgentOutput') {
|
|
167
|
-
syntheticArgs = { text: textContent || '', success: true };
|
|
168
|
-
}
|
|
169
|
-
else {
|
|
170
|
-
// Try to extract JSON from the text content (LLM sometimes outputs JSON in text)
|
|
171
|
-
if (textContent) {
|
|
172
|
-
const jsonMatch = textContent.match(/\{[\s\S]*\}/);
|
|
173
|
-
if (jsonMatch) {
|
|
174
|
-
try {
|
|
175
|
-
syntheticArgs = JSON.parse(jsonMatch[0]);
|
|
176
|
-
}
|
|
177
|
-
catch {
|
|
178
|
-
syntheticArgs = {};
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
else {
|
|
182
|
-
syntheticArgs = {};
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
else {
|
|
186
|
-
syntheticArgs = {};
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
const validation = doneTool.inputSchema.safeParse(syntheticArgs);
|
|
190
|
-
const toolInput = validation.success ? validation.data : syntheticArgs;
|
|
191
|
-
let toolResult;
|
|
192
|
-
try {
|
|
193
|
-
toolResult = validation.success ? await doneTool.execute(toolInput) : toolInput;
|
|
194
|
-
}
|
|
195
|
-
catch {
|
|
196
|
-
toolResult = toolInput;
|
|
197
|
-
}
|
|
198
|
-
return {
|
|
199
|
-
toolCall: {
|
|
200
|
-
name: toolName,
|
|
201
|
-
args: toolInput,
|
|
202
|
-
},
|
|
203
|
-
toolResult,
|
|
204
|
-
usage: {
|
|
205
|
-
promptTokens: data.usage?.prompt_tokens ?? 0,
|
|
206
|
-
completionTokens: data.usage?.completion_tokens ?? 0,
|
|
207
|
-
totalTokens: data.usage?.total_tokens ?? 0,
|
|
208
|
-
cachedTokens: data.usage?.prompt_tokens_details?.cached_tokens,
|
|
209
|
-
reasoningTokens: data.usage?.completion_tokens_details?.reasoning_tokens,
|
|
210
|
-
},
|
|
211
|
-
rawResponse: data,
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
throw new errors_1.InvokeError(errors_1.InvokeErrorType.NO_TOOL_CALL, 'No tool call found in response', undefined, data);
|
|
215
|
-
}
|
|
216
|
-
const tool = tools[toolCallName];
|
|
217
|
-
if (!tool) {
|
|
218
|
-
throw new errors_1.InvokeError(errors_1.InvokeErrorType.UNKNOWN, `Tool "${toolCallName}" not found in tools`, undefined, data);
|
|
219
|
-
}
|
|
220
|
-
// Extract and parse tool arguments
|
|
221
|
-
const argString = normalizedChoice.message?.tool_calls?.[0]?.function?.arguments;
|
|
222
|
-
if (!argString) {
|
|
223
|
-
throw new errors_1.InvokeError(errors_1.InvokeErrorType.INVALID_TOOL_ARGS, 'No tool call arguments found', undefined, data);
|
|
224
|
-
}
|
|
225
|
-
let parsedArgs;
|
|
226
|
-
try {
|
|
227
|
-
parsedArgs = JSON.parse(argString);
|
|
228
|
-
}
|
|
229
|
-
catch (jsonError) {
|
|
230
|
-
// LLM sometimes generates slightly malformed JSON (e.g. trailing characters,
|
|
231
|
-
// mismatched quotes, or extra text after the closing brace).
|
|
232
|
-
// Attempt to recover by extracting the first valid JSON object from the string.
|
|
233
|
-
const recovered = this.tryRecoverJSON(argString);
|
|
234
|
-
if (recovered !== undefined) {
|
|
235
|
-
parsedArgs = recovered;
|
|
236
|
-
}
|
|
237
|
-
else {
|
|
238
|
-
throw new errors_1.InvokeError(errors_1.InvokeErrorType.INVALID_TOOL_ARGS, 'Failed to parse tool arguments as JSON', jsonError, data);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
// Validate with schema
|
|
242
|
-
const validation = tool.inputSchema.safeParse(parsedArgs);
|
|
243
|
-
if (!validation.success) {
|
|
244
|
-
console.error(z.prettifyError(validation.error));
|
|
245
|
-
throw new errors_1.InvokeError(errors_1.InvokeErrorType.INVALID_TOOL_ARGS, 'Tool arguments validation failed', validation.error, data);
|
|
246
|
-
}
|
|
247
|
-
const toolInput = validation.data;
|
|
248
|
-
// 5. Execute tool
|
|
249
|
-
let toolResult;
|
|
250
|
-
try {
|
|
251
|
-
toolResult = await tool.execute(toolInput);
|
|
252
|
-
}
|
|
253
|
-
catch (e) {
|
|
254
|
-
throw new errors_1.InvokeError(errors_1.InvokeErrorType.TOOL_EXECUTION_ERROR, `Tool execution failed: ${e.message}`, e, data);
|
|
255
|
-
}
|
|
256
|
-
// Return result
|
|
257
|
-
return {
|
|
258
|
-
toolCall: {
|
|
259
|
-
name: toolCallName,
|
|
260
|
-
args: toolInput,
|
|
261
|
-
},
|
|
262
|
-
toolResult,
|
|
263
|
-
usage: {
|
|
264
|
-
promptTokens: data.usage?.prompt_tokens ?? 0,
|
|
265
|
-
completionTokens: data.usage?.completion_tokens ?? 0,
|
|
266
|
-
totalTokens: data.usage?.total_tokens ?? 0,
|
|
267
|
-
cachedTokens: data.usage?.prompt_tokens_details?.cached_tokens,
|
|
268
|
-
reasoningTokens: data.usage?.completion_tokens_details?.reasoning_tokens,
|
|
269
|
-
},
|
|
270
|
-
rawResponse: data,
|
|
271
|
-
rawRequest: requestBody,
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* Attempt to recover a valid JSON object from a malformed LLM response.
|
|
276
|
-
* Handles common LLM JSON errors:
|
|
277
|
-
* 1. Extra text after closing brace (e.g. "...}{extra")
|
|
278
|
-
* 2. Trailing commas inside objects
|
|
279
|
-
* 3. Unbalanced braces — find the deepest matching pair
|
|
280
|
-
* 4. Mixed quote styles (single quotes instead of double quotes)
|
|
281
|
-
*/
|
|
282
|
-
tryRecoverJSON(argString) {
|
|
283
|
-
// Strategy 1: Find the first complete JSON object using brace matching
|
|
284
|
-
const firstBrace = argString.indexOf('{');
|
|
285
|
-
if (firstBrace === -1)
|
|
286
|
-
return undefined;
|
|
287
|
-
let depth = 0;
|
|
288
|
-
let inString = false;
|
|
289
|
-
let escapeNext = false;
|
|
290
|
-
for (let i = firstBrace; i < argString.length; i++) {
|
|
291
|
-
const ch = argString[i];
|
|
292
|
-
if (escapeNext) {
|
|
293
|
-
escapeNext = false;
|
|
294
|
-
continue;
|
|
295
|
-
}
|
|
296
|
-
if (ch === '\\') {
|
|
297
|
-
escapeNext = true;
|
|
298
|
-
continue;
|
|
299
|
-
}
|
|
300
|
-
if (ch === '"' && !escapeNext) {
|
|
301
|
-
inString = !inString;
|
|
302
|
-
continue;
|
|
303
|
-
}
|
|
304
|
-
if (inString)
|
|
305
|
-
continue;
|
|
306
|
-
if (ch === '{')
|
|
307
|
-
depth++;
|
|
308
|
-
if (ch === '}') {
|
|
309
|
-
depth--;
|
|
310
|
-
if (depth === 0) {
|
|
311
|
-
// Extract the matched JSON object
|
|
312
|
-
const candidate = argString.substring(firstBrace, i + 1);
|
|
313
|
-
try {
|
|
314
|
-
return JSON.parse(candidate);
|
|
315
|
-
}
|
|
316
|
-
catch {
|
|
317
|
-
// Strategy 2: Fix common issues
|
|
318
|
-
let fixed = candidate;
|
|
319
|
-
// Remove trailing commas before } or ]
|
|
320
|
-
fixed = fixed.replace(/,\s*([}\]])/g, '$1');
|
|
321
|
-
// Replace single-quoted property names with double quotes
|
|
322
|
-
fixed = fixed.replace(/'([^']+)'\s*:/g, '"$1":');
|
|
323
|
-
// Replace single-quoted string values with double quotes (careful with nested)
|
|
324
|
-
fixed = fixed.replace(/:\s*'([^']*)'/g, ': "$1"');
|
|
325
|
-
try {
|
|
326
|
-
return JSON.parse(fixed);
|
|
327
|
-
}
|
|
328
|
-
catch {
|
|
329
|
-
continue; // try deeper nesting
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
return undefined;
|
|
336
|
-
}
|
|
337
|
-
/**
|
|
338
|
-
* Call the LLM and return the full assistant message without executing any tools.
|
|
339
|
-
* Used by the agent loop to get the LLM's response and process tool_calls separately.
|
|
340
|
-
*
|
|
341
|
-
* This method:
|
|
342
|
-
* 1. Sends messages + tools to the OpenAI-compatible API
|
|
343
|
-
* 2. Returns the full assistant Message (with content + tool_calls)
|
|
344
|
-
* 3. Does NOT execute any tools or validate tool args
|
|
345
|
-
*
|
|
346
|
-
* The agent loop handles tool execution via its own ToolRegistry.
|
|
347
|
-
*/
|
|
348
|
-
async chat(messages, tools, abortSignal, options) {
|
|
349
|
-
// Convert tools to OpenAI format (same as invoke)
|
|
350
|
-
const openaiTools = Object.entries(tools).map(([name, t]) => {
|
|
351
|
-
if (t.customJsonSchema) {
|
|
352
|
-
return {
|
|
353
|
-
type: 'function',
|
|
354
|
-
function: {
|
|
355
|
-
name,
|
|
356
|
-
description: t.description,
|
|
357
|
-
parameters: t.customJsonSchema,
|
|
358
|
-
},
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
return (0, utils_1.zodToOpenAITool)(name, t);
|
|
362
|
-
});
|
|
363
|
-
// Build request body
|
|
364
|
-
let toolChoice = 'required';
|
|
365
|
-
if (options?.toolChoiceName && !this.config.disableNamedToolChoice) {
|
|
366
|
-
toolChoice = { type: 'function', function: { name: options.toolChoiceName } };
|
|
367
|
-
}
|
|
368
|
-
else if (this.config.disableNamedToolChoice) {
|
|
369
|
-
toolChoice = undefined;
|
|
370
|
-
}
|
|
371
|
-
// For agent loop, we want the LLM to have freedom to respond
|
|
372
|
-
// with text or tool_calls. Use 'auto' instead of 'required'.
|
|
373
|
-
if (!options?.toolChoiceName) {
|
|
374
|
-
toolChoice = 'auto';
|
|
375
|
-
}
|
|
376
|
-
const requestBody = {
|
|
377
|
-
model: this.config.model,
|
|
378
|
-
temperature: this.config.temperature,
|
|
379
|
-
messages,
|
|
380
|
-
tools: openaiTools,
|
|
381
|
-
parallel_tool_calls: false,
|
|
382
|
-
...(toolChoice !== undefined && { tool_choice: toolChoice }),
|
|
383
|
-
};
|
|
384
|
-
(0, utils_1.modelPatch)(requestBody);
|
|
385
|
-
// Call API
|
|
386
|
-
let response;
|
|
387
|
-
try {
|
|
388
|
-
response = await this.fetch(`${this.config.baseURL}/chat/completions`, {
|
|
389
|
-
method: 'POST',
|
|
390
|
-
headers: {
|
|
391
|
-
'Content-Type': 'application/json',
|
|
392
|
-
...(this.config.apiKey && { Authorization: `Bearer ${this.config.apiKey}` }),
|
|
393
|
-
},
|
|
394
|
-
body: JSON.stringify(requestBody),
|
|
395
|
-
signal: abortSignal,
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
catch (error) {
|
|
399
|
-
const isAbortError = error?.name === 'AbortError';
|
|
400
|
-
const errorMessage = isAbortError ? 'Network request aborted' : 'Network request failed';
|
|
401
|
-
if (!isAbortError)
|
|
402
|
-
console.error(error);
|
|
403
|
-
throw new errors_1.InvokeError(errors_1.InvokeErrorType.NETWORK_ERROR, errorMessage, error);
|
|
404
|
-
}
|
|
405
|
-
// Handle HTTP errors (same as invoke)
|
|
406
|
-
if (!response.ok) {
|
|
407
|
-
const errorData = await response.json().catch();
|
|
408
|
-
const errorMessage = errorData.error?.message || response.statusText;
|
|
409
|
-
if (response.status === 401 || response.status === 403) {
|
|
410
|
-
throw new errors_1.InvokeError(errors_1.InvokeErrorType.AUTH_ERROR, `Authentication failed: ${errorMessage}`, errorData);
|
|
411
|
-
}
|
|
412
|
-
if (response.status === 429) {
|
|
413
|
-
throw new errors_1.InvokeError(errors_1.InvokeErrorType.RATE_LIMIT, `Rate limit exceeded: ${errorMessage}`, errorData);
|
|
414
|
-
}
|
|
415
|
-
if (response.status >= 500) {
|
|
416
|
-
throw new errors_1.InvokeError(errors_1.InvokeErrorType.SERVER_ERROR, `Server error: ${errorMessage}`, errorData);
|
|
417
|
-
}
|
|
418
|
-
throw new errors_1.InvokeError(errors_1.InvokeErrorType.UNKNOWN, `HTTP ${response.status}: ${errorMessage}`, errorData);
|
|
419
|
-
}
|
|
420
|
-
// Parse response and extract full assistant message
|
|
421
|
-
const data = await response.json();
|
|
422
|
-
const choice = data.choices?.[0];
|
|
423
|
-
if (!choice) {
|
|
424
|
-
throw new errors_1.InvokeError(errors_1.InvokeErrorType.UNKNOWN, 'No choices in response', data);
|
|
425
|
-
}
|
|
426
|
-
// Build the assistant Message from the response
|
|
427
|
-
const assistantMessage = {
|
|
428
|
-
role: 'assistant',
|
|
429
|
-
content: choice.message?.content ?? null,
|
|
430
|
-
};
|
|
431
|
-
// Include tool_calls if present
|
|
432
|
-
if (choice.message?.tool_calls?.length) {
|
|
433
|
-
assistantMessage.tool_calls = choice.message.tool_calls.map((tc) => ({
|
|
434
|
-
id: tc.id,
|
|
435
|
-
type: 'function',
|
|
436
|
-
function: {
|
|
437
|
-
name: tc.function.name,
|
|
438
|
-
arguments: tc.function.arguments,
|
|
439
|
-
},
|
|
440
|
-
}));
|
|
441
|
-
}
|
|
442
|
-
return {
|
|
443
|
-
message: assistantMessage,
|
|
444
|
-
usage: {
|
|
445
|
-
promptTokens: data.usage?.prompt_tokens ?? 0,
|
|
446
|
-
completionTokens: data.usage?.completion_tokens ?? 0,
|
|
447
|
-
totalTokens: data.usage?.total_tokens ?? 0,
|
|
448
|
-
cachedTokens: data.usage?.prompt_tokens_details?.cached_tokens,
|
|
449
|
-
reasoningTokens: data.usage?.completion_tokens_details?.reasoning_tokens,
|
|
450
|
-
},
|
|
451
|
-
rawResponse: data,
|
|
452
|
-
};
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
exports.OpenAIClient = OpenAIClient;
|
package/dist/llm/constants.d.ts
DELETED
package/dist/llm/constants.js
DELETED
package/dist/llm/errors.d.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Error types and error handling for LLM invocations
|
|
3
|
-
*/
|
|
4
|
-
export declare const InvokeErrorType: {
|
|
5
|
-
readonly NETWORK_ERROR: "network_error";
|
|
6
|
-
readonly RATE_LIMIT: "rate_limit";
|
|
7
|
-
readonly SERVER_ERROR: "server_error";
|
|
8
|
-
readonly NO_TOOL_CALL: "no_tool_call";
|
|
9
|
-
readonly INVALID_TOOL_ARGS: "invalid_tool_args";
|
|
10
|
-
readonly TOOL_EXECUTION_ERROR: "tool_execution_error";
|
|
11
|
-
readonly UNKNOWN: "unknown";
|
|
12
|
-
readonly AUTH_ERROR: "auth_error";
|
|
13
|
-
readonly CONTEXT_LENGTH: "context_length";
|
|
14
|
-
readonly CONTENT_FILTER: "content_filter";
|
|
15
|
-
};
|
|
16
|
-
export type InvokeErrorType = (typeof InvokeErrorType)[keyof typeof InvokeErrorType];
|
|
17
|
-
export declare class InvokeError extends Error {
|
|
18
|
-
type: InvokeErrorType;
|
|
19
|
-
retryable: boolean;
|
|
20
|
-
statusCode?: number;
|
|
21
|
-
rawError?: unknown;
|
|
22
|
-
rawResponse?: unknown;
|
|
23
|
-
constructor(type: InvokeErrorType, message: string, rawError?: unknown, rawResponse?: unknown);
|
|
24
|
-
private isRetryable;
|
|
25
|
-
}
|
package/dist/llm/errors.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Error types and error handling for LLM invocations
|
|
4
|
-
*/
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.InvokeError = exports.InvokeErrorType = void 0;
|
|
7
|
-
exports.InvokeErrorType = {
|
|
8
|
-
// Retryable
|
|
9
|
-
NETWORK_ERROR: 'network_error',
|
|
10
|
-
RATE_LIMIT: 'rate_limit',
|
|
11
|
-
SERVER_ERROR: 'server_error',
|
|
12
|
-
NO_TOOL_CALL: 'no_tool_call',
|
|
13
|
-
INVALID_TOOL_ARGS: 'invalid_tool_args',
|
|
14
|
-
TOOL_EXECUTION_ERROR: 'tool_execution_error',
|
|
15
|
-
UNKNOWN: 'unknown',
|
|
16
|
-
// Non-retryable
|
|
17
|
-
AUTH_ERROR: 'auth_error',
|
|
18
|
-
CONTEXT_LENGTH: 'context_length',
|
|
19
|
-
CONTENT_FILTER: 'content_filter',
|
|
20
|
-
};
|
|
21
|
-
class InvokeError extends Error {
|
|
22
|
-
type;
|
|
23
|
-
retryable;
|
|
24
|
-
statusCode;
|
|
25
|
-
rawError;
|
|
26
|
-
rawResponse;
|
|
27
|
-
constructor(type, message, rawError, rawResponse) {
|
|
28
|
-
super(message);
|
|
29
|
-
this.name = 'InvokeError';
|
|
30
|
-
this.type = type;
|
|
31
|
-
this.retryable = this.isRetryable(type, rawError);
|
|
32
|
-
this.rawError = rawError;
|
|
33
|
-
this.rawResponse = rawResponse;
|
|
34
|
-
}
|
|
35
|
-
isRetryable(type, rawError) {
|
|
36
|
-
const isAbortError = rawError?.name === 'AbortError';
|
|
37
|
-
if (isAbortError)
|
|
38
|
-
return false;
|
|
39
|
-
const retryableTypes = [
|
|
40
|
-
exports.InvokeErrorType.NETWORK_ERROR,
|
|
41
|
-
exports.InvokeErrorType.RATE_LIMIT,
|
|
42
|
-
exports.InvokeErrorType.SERVER_ERROR,
|
|
43
|
-
exports.InvokeErrorType.NO_TOOL_CALL,
|
|
44
|
-
exports.InvokeErrorType.INVALID_TOOL_ARGS,
|
|
45
|
-
exports.InvokeErrorType.TOOL_EXECUTION_ERROR,
|
|
46
|
-
exports.InvokeErrorType.UNKNOWN,
|
|
47
|
-
];
|
|
48
|
-
return retryableTypes.includes(type);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
exports.InvokeError = InvokeError;
|
package/dist/llm/index.d.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LLM client with MacroTool support
|
|
3
|
-
*
|
|
4
|
-
* Key features:
|
|
5
|
-
* - Direct fetch to OpenAI API (no LangChain dependency)
|
|
6
|
-
* - Zod schema for tool definitions
|
|
7
|
-
* - toolChoiceName support (force specific tool call)
|
|
8
|
-
* - parallel_tool_calls: false (one tool per step)
|
|
9
|
-
* - Retry with error classification
|
|
10
|
-
* - Model-specific patches (Claude, Qwen, GPT, etc.)
|
|
11
|
-
*/
|
|
12
|
-
import { InvokeError, InvokeErrorType } from './errors';
|
|
13
|
-
import type { ChatResult, ContentItem, InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool } from './types';
|
|
14
|
-
import { parseXMLExtractionResponse, extractXMLTag } from './utils';
|
|
15
|
-
export { InvokeError, InvokeErrorType };
|
|
16
|
-
export { parseXMLExtractionResponse, extractXMLTag };
|
|
17
|
-
export type { ChatResult, ContentItem, InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool };
|
|
18
|
-
export declare function parseLLMConfig(config: LLMConfig): Required<LLMConfig>;
|
|
19
|
-
export declare class LLM extends EventTarget {
|
|
20
|
-
config: Required<LLMConfig>;
|
|
21
|
-
client: LLMClient;
|
|
22
|
-
constructor(config: LLMConfig);
|
|
23
|
-
/**
|
|
24
|
-
* Call LLM API once, invoke tool call once, return the result.
|
|
25
|
-
* This is the core method used by gui-agent's MacroTool loop.
|
|
26
|
-
*/
|
|
27
|
-
invoke(messages: Message[], tools: Record<string, Tool>, abortSignal?: AbortSignal, options?: InvokeOptions): Promise<InvokeResult>;
|
|
28
|
-
/**
|
|
29
|
-
* Simple chat call — no tools, just text generation.
|
|
30
|
-
* Used for validation and other non-tool-call LLM tasks.
|
|
31
|
-
*/
|
|
32
|
-
chat(messages: Message[], abortSignal?: AbortSignal): Promise<{
|
|
33
|
-
content: string;
|
|
34
|
-
usage: {
|
|
35
|
-
prompt_tokens: number;
|
|
36
|
-
completion_tokens: number;
|
|
37
|
-
total_tokens: number;
|
|
38
|
-
};
|
|
39
|
-
}>;
|
|
40
|
-
/**
|
|
41
|
-
* Agent loop chat — call LLM with tools and return the full assistant message.
|
|
42
|
-
* Unlike invoke(), this does NOT execute tools. The agent loop handles tool
|
|
43
|
-
* execution via its own ToolRegistry after getting the response.
|
|
44
|
-
*
|
|
45
|
-
* Uses tool_choice: 'auto' by default (LLM can choose text or tool_calls).
|
|
46
|
-
* Includes retry logic with exponential backoff.
|
|
47
|
-
*/
|
|
48
|
-
chatWithTools(messages: Message[], tools: Record<string, Tool>, abortSignal?: AbortSignal, options?: InvokeOptions): Promise<ChatResult>;
|
|
49
|
-
}
|