Download src/agent.js from ProCreations/minicpm5-2b-webgpu: direct link, hf CLI and curl.
- Browser
- Download file 5.56 kB
-
https://huggingface.co/spaces/ProCreations/minicpm5-2b-webgpu/resolve/main/src/agent.js
- Command line
-
hf download hf://spaces/ProCreations/minicpm5-2b-webgpu/src/agent.js
-
curl -L -o agent.js https://huggingface.co/spaces/ProCreations/minicpm5-2b-webgpu/resolve/main/src/agent.js
5.56 kB
| import { TOOLS, SYSTEM, parseXMLCalls } from "./core.js"; | |
| import { normalizedCall, argumentExcerpt } from "./prompt.js"; | |
| export const MAX_TOOL_ROUNDS = 6; | |
| export function recoveryBudget(current, context) { | |
| return Math.min(context - 512, Math.max(8192, current * 2)); | |
| } | |
| export function toolFailure(call, error, { finish, budget, context }) { | |
| const truncated = finish === "length"; | |
| return { | |
| success: false, | |
| error: truncated | |
| ? "The tool call was cut off before its arguments were complete." | |
| : error.message, | |
| attempted_arguments: argumentExcerpt(call.function.arguments), | |
| instruction: truncated | |
| ? `Call ${call.function.name} again with a complete, self-contained result. You now have up to ${recoveryBudget(budget, context)} output tokens. Keep the implementation concise enough to finish.` | |
| : `Correct the arguments and call ${call.function.name} again. Do not claim the file exists until the tool succeeds.`, | |
| }; | |
| } | |
| // One shared loop drives the UI and regression tests, including error feedback. | |
| export async function runAgent({ | |
| chat, | |
| history, | |
| settings, | |
| signal, | |
| createArtifact, | |
| onEvent = () => {}, | |
| }) { | |
| let current = { ...settings }; | |
| let recovering = false; | |
| for (let round = 0; round < MAX_TOOL_ROUNDS; round++) { | |
| signal?.throwIfAborted(); | |
| const message = { content: "", reasoning: "", artifacts: [] }, | |
| calls = new Map(); | |
| let finish = null, | |
| timings = null, | |
| tokens = 0; | |
| await onEvent({ type: "start", round }); | |
| const messages = [ | |
| { | |
| role: "system", | |
| content: | |
| SYSTEM + | |
| (settings.system ? "\n\nUser preferences:\n" + settings.system : ""), | |
| }, | |
| ...history, | |
| ]; | |
| await chat( | |
| messages, | |
| current, | |
| settings.tools ? TOOLS : [], | |
| signal, | |
| async (chunk) => { | |
| const delta = chunk.choices?.[0]?.delta || {}; | |
| message.content += delta.content || ""; | |
| message.reasoning += delta.reasoning_content || ""; | |
| for (const part of delta.tool_calls || []) { | |
| const index = part.index ?? 0; | |
| let call = calls.get(index); | |
| if (!call) { | |
| call = { | |
| id: part.id || crypto.randomUUID(), | |
| type: "function", | |
| function: { name: "", arguments: "" }, | |
| }; | |
| calls.set(index, call); | |
| } | |
| if (part.id) call.id = part.id; | |
| call.function.name += part.function?.name || ""; | |
| call.function.arguments += part.function?.arguments || ""; | |
| } | |
| finish = chunk.choices?.[0]?.finish_reason || finish; | |
| timings = chunk.timings || timings; | |
| tokens = chunk.usage?.completion_tokens || tokens; | |
| await onEvent({ type: "chunk", chunk }); | |
| }, | |
| ); | |
| const toolCalls = calls.size | |
| ? [...calls.values()] | |
| : parseXMLCalls(message.content); | |
| if (!toolCalls.length && finish === "length" && settings.tools) { | |
| const match = message.content.match(/<function\s+name=["']([^"']+)["']/); | |
| if (match) | |
| toolCalls.push({ | |
| id: crypto.randomUUID(), | |
| type: "function", | |
| function: { | |
| name: match[1], | |
| arguments: message.content.slice( | |
| message.content.indexOf("<function"), | |
| ), | |
| }, | |
| }); | |
| } | |
| await onEvent({ type: "response", finish, timings, tokens }); | |
| if (toolCalls.length && settings.tools) { | |
| const visible = message.content | |
| .replace(/<function\b[\s\S]*$/, "") | |
| .replace(/<think>[\s\S]*?<\/think>/g, "") | |
| .trim(); | |
| history.push({ | |
| role: "assistant", | |
| content: visible, | |
| tool_calls: toolCalls.map(normalizedCall), | |
| }); | |
| let failed = false; | |
| for (const call of toolCalls) { | |
| signal?.throwIfAborted(); | |
| await onEvent({ type: "tool-start", name: call.function.name }); | |
| try { | |
| const args = JSON.parse(call.function.arguments); | |
| const artifact = await createArtifact(call.function.name, args); | |
| history.push({ | |
| role: "tool", | |
| tool_call_id: call.id, | |
| content: JSON.stringify({ | |
| success: true, | |
| filename: artifact.filename, | |
| artifact_id: artifact.id, | |
| preview: | |
| "Available in the chat. The user can preview and download this file.", | |
| }), | |
| }); | |
| await onEvent({ | |
| type: "tool-success", | |
| artifact, | |
| recovered: recovering, | |
| }); | |
| } catch (error) { | |
| failed = true; | |
| history.push({ | |
| role: "tool", | |
| tool_call_id: call.id, | |
| content: JSON.stringify( | |
| toolFailure(call, error, { | |
| finish, | |
| budget: current.maxTokens, | |
| context: settings.context, | |
| }), | |
| ), | |
| }); | |
| await onEvent({ | |
| type: "tool-error", | |
| error: error.message, | |
| retrying: round + 1 < MAX_TOOL_ROUNDS, | |
| }); | |
| } | |
| } | |
| await onEvent({ type: "tools-complete", content: visible }); | |
| if (failed) { | |
| recovering = true; | |
| current = { | |
| ...current, | |
| maxTokens: recoveryBudget(current.maxTokens, settings.context), | |
| thinking: false, | |
| }; | |
| } | |
| continue; | |
| } | |
| history.push({ role: "assistant", content: message.content }); | |
| await onEvent({ type: "done", finish }); | |
| return; | |
| } | |
| await onEvent({ type: "limit" }); | |
| } | |