UNPKG

@botpress/adk-cli

Version:

Command-line interface for the Botpress Agent Development Kit (ADK)

700 lines (698 loc) 21.4 kB
// @bun import { runAsyncFunction } from "./chunk-qvn2q0t4.js"; import { Context, ErrorExecutionResult, PartialExecutionResult, Snapshot, SuccessExecutionResult, parseExit, ulid } from "./chunk-vay209b5.js"; import { Uk } from "./chunk-3xrpxgq4.js"; import { Cognitive } from "./chunk-rfm3jr1m.js"; import"./chunk-w346ejn9.js"; import"./chunk-knvm2anf.js"; import"./chunk-65h5trb5.js"; import"./chunk-s2akeqpw.js"; import"./chunk-6771vrjp.js"; import { require_ms } from "./chunk-g8mm42v1.js"; import"./chunk-50hzjdck.js"; import { AssignmentError, CodeExecutionError, CognitiveError, InvalidCodeError, LoopExceededError, Signals, SnapshotSignal, ThinkSignal, VMSignal } from "./chunk-nn2jb0x0.js"; import"./chunk-v8xvth6j.js"; import { truncateWrappedContent } from "./chunk-kkk13rcb.js"; import { cleanStackTrace } from "./chunk-ytpp1kam.js"; import { init, stripInvalidIdentifiers } from "./chunk-na956zz3.js"; import"./chunk-kb6q8m3w.js"; import"./chunk-0q6b88ns.js"; import"./chunk-f4bw8q7c.js"; import"./chunk-0v8vgrns.js"; import { clamp_default, exports_exports, isEqual_default, isPlainObject_default, omit_default } from "./chunk-54qt5g7m.js"; import { __toESM } from "./chunk-dhs2bg35.js"; // ../../node_modules/.bun/llmz@0.0.79+b49d396f5ed96e7f/node_modules/llmz/dist/llmz-F227HVGB.js var import_ms = __toESM(require_ms(), 1); function createJoinedAbortController(signals) { const controller = new AbortController; const validSignals = signals.filter((signal) => signal != null); if (validSignals.length === 0) { return controller; } for (const signal of validSignals) { if (signal.aborted) { controller.abort(signal.reason); return controller; } } const abortListeners = []; for (const signal of validSignals) { const listener = () => { controller.abort(signal.reason); cleanup(); }; signal.addEventListener("abort", listener); abortListeners.push(() => signal.removeEventListener("abort", listener)); } const cleanup = () => { abortListeners.forEach((removeListener) => removeListener()); }; controller.signal.addEventListener("abort", cleanup, { once: true }); return controller; } var getErrorMessage = (err) => err instanceof Error ? err.message : JSON.stringify(err); var SLOW_TOOL_WARNING = import_ms.default("15s"); var RESPONSE_LENGTH_BUFFER = { MIN_TOKENS: 1000, MAX_TOKENS: 16000, PERCENTAGE: 0.1 }; var getModelOutputLimit = (inputLength) => clamp_default(RESPONSE_LENGTH_BUFFER.PERCENTAGE * inputLength, RESPONSE_LENGTH_BUFFER.MIN_TOKENS, RESPONSE_LENGTH_BUFFER.MAX_TOKENS); var executeContext = async (props) => { var _a, _b; await init(); const result = await _executeContext(props); try { (_b = (_a = result.context.chat) == null ? undefined : _a.onExecutionDone) == null || _b.call(_a, result); } catch {} return result; }; var _executeContext = async (props) => { var _a, _b; const controller = createJoinedAbortController([props.signal]); const { onIterationStart, onIterationEnd, onTrace, onExit, onBeforeExecution, onAfterTool, onBeforeTool } = props; const client = props.client ?? new Uk; const cognitive = Cognitive.isCognitiveClient(client) ? client : new Cognitive({ client, __experimental_beta: true }); const cleanups = []; const ctx = new Context({ chat: props.chat, instructions: props.instructions, objects: props.objects, tools: props.tools, loop: (_a = props.options) == null ? undefined : _a.loop, timeout: (_b = props.options) == null ? undefined : _b.timeout, exits: props.exits, snapshot: props.snapshot, model: props.model, temperature: props.temperature, reasoningEffort: props.reasoningEffort }); try { while (true) { if (ctx.iterations.length >= ctx.loop) { return new ErrorExecutionResult(ctx, new LoopExceededError); } const iteration = await ctx.nextIteration(); try { await executeOnIterationStartHook({ iteration, ctx, onIterationStart, controller, onIterationEnd }); } catch (err) { if (err instanceof ThinkSignal) { continue; } } if (controller.signal.aborted) { iteration.end({ type: "aborted", aborted: { reason: controller.signal.reason ?? "The operation was aborted" } }); return new ErrorExecutionResult(ctx, controller.signal.reason ?? "The operation was aborted"); } cleanups.push(iteration.traces.onPush((traces) => { for (const trace of traces) { onTrace == null || onTrace({ trace, iteration: ctx.iterations.length }); } })); try { await executeIteration({ iteration, ctx, cognitive, controller, onExit, onBeforeExecution, onAfterTool, onBeforeTool }); } catch (err) { if (err instanceof CognitiveError) { return new ErrorExecutionResult(ctx, err); } iteration.end({ type: "execution_error", execution_error: { message: "An unexpected error occurred: " + getErrorMessage(err), stack: cleanStackTrace(err.stack ?? "No stack trace available") } }); } try { await (onIterationEnd == null ? undefined : onIterationEnd(iteration, controller)); } catch (err) { console.error(err); } if (iteration.status.type === "exit_success") { const exitName = iteration.status.exit_success.exit_name; return new SuccessExecutionResult(ctx, { exit: iteration.exits.find((x) => x.name === exitName), result: iteration.status.exit_success.return_value }); } if (iteration.status.type === "callback_requested") { return new PartialExecutionResult(ctx, iteration.status.callback_requested.signal, Snapshot.fromSignal(iteration.status.callback_requested.signal)); } if (iteration.status.type === "thinking_requested" || iteration.status.type === "exit_error" || iteration.status.type === "execution_error" || iteration.status.type === "invalid_code_error") { continue; } return new ErrorExecutionResult(ctx, iteration.error ?? `Unknown error. Status: ${iteration.status.type}`); } } catch (error) { return new ErrorExecutionResult(ctx, error ?? "Unknown error"); } finally { for (const cleanup of cleanups) { try { cleanup(); } catch {} } } }; var executeIteration = async ({ iteration, ctx, cognitive, controller, onExit, onBeforeExecution, onBeforeTool, onAfterTool }) => { var _a, _b, _c, _d; let startedAt = Date.now(); const traces = iteration.traces; const modelRef = Array.isArray(iteration.model) ? iteration.model[0] : iteration.model; const model = await cognitive.getModelDetails(modelRef).catch((thrown) => { throw new CognitiveError(`Failed to fetch model details for model "${modelRef}": ${getErrorMessage(thrown)}`); }); const modelLimit = Math.max(model.input.maxTokens, 8000); const responseLengthBuffer = getModelOutputLimit(modelLimit); const messages = truncateWrappedContent({ messages: iteration.messages, tokenLimit: modelLimit - responseLengthBuffer, throwOnFailure: true }).filter((x) => typeof x.content !== "string" || x.content.trim().length > 0); iteration.messages = messages; traces.push({ type: "llm_call_started", started_at: startedAt, ended_at: startedAt, model: model.ref }); const output = await cognitive.generateContent({ signal: controller.signal, systemPrompt: (_a = messages.find((x) => x.role === "system")) == null ? undefined : _a.content, model: iteration.model, temperature: iteration.temperature, responseFormat: "text", reasoningEffort: iteration.reasoningEffort, messages: messages.filter((x) => x.role !== "system"), stopSequences: ctx.version.getStopTokens() }).catch((thrown) => { throw new CognitiveError(`LLM generation failed: ${getErrorMessage(thrown)}`); }); const out = typeof ((_c = (_b = output.output.choices) == null ? undefined : _b[0]) == null ? undefined : _c.content) === "string" ? output.output.choices[0].content : null; if (!out) { throw new CognitiveError("LLM did not return any text output"); } const assistantResponse = ctx.version.parseAssistantResponse(out); iteration.code = assistantResponse.code.trim(); if (typeof onBeforeExecution === "function") { try { const hookRes = await onBeforeExecution(iteration, controller); if (typeof (hookRes == null ? undefined : hookRes.code) === "string" && hookRes.code.trim().length > 0) { iteration.code = hookRes.code.trim(); } } catch (err) { if (err instanceof ThinkSignal) { return iteration.end({ type: "thinking_requested", thinking_requested: { variables: err.context, reason: err.reason } }); } return iteration.end({ type: "execution_error", execution_error: { message: `Error in onBeforeExecution hook: ${getErrorMessage(err)}`, stack: cleanStackTrace(err.stack ?? "No stack trace available") } }); } } iteration.llm = { cached: output.meta.cached || false, ended_at: Date.now(), started_at: startedAt, status: "success", tokens: output.meta.tokens.input + output.meta.tokens.output, spend: output.meta.cost.input + output.meta.cost.output, output: assistantResponse.raw, model: `${output.meta.model.integration}:${output.meta.model.model}`, usage: output.output.usage }; traces.push({ type: "llm_call_success", started_at: startedAt, ended_at: iteration.llm.ended_at, model: model.ref, code: iteration.code }); const vmContext = { ...stripInvalidIdentifiers(iteration.variables) }; for (const obj of iteration.objects) { const internalValues = {}; const instance = {}; for (const { name, value, writable, type } of obj.properties ?? []) { internalValues[name] = value; const initialValue = value; const schema = type ?? exports_exports.any(); Object.defineProperty(instance, name, { enumerable: true, configurable: true, get() { return internalValues[name]; }, set(value2) { if (isEqual_default(value2, internalValues[name])) { return; } if (!writable) { throw new AssignmentError(`Property ${obj.name}.${name} is read-only and cannot be modified`); } if (value2 === internalValues[name]) { return; } const parsed = schema.safeParse(value2); if (!parsed.success) { throw new AssignmentError(`Invalid value for Object property ${obj.name}.${name}: ${getErrorMessage(parsed.error)}`); } internalValues[name] = parsed.data; traces.push({ type: "property", started_at: Date.now(), object: obj.name, property: name, value: parsed.data }); iteration.trackMutation({ object: obj.name, property: name, before: initialValue, after: parsed.data }); } }); } for (const tool of obj.tools ?? []) { instance[tool.name] = wrapTool({ chat: ctx.chat, tool, traces, object: obj.name, iteration, beforeHook: onBeforeTool, afterHook: onAfterTool, controller }); } Object.preventExtensions(instance); Object.seal(instance); vmContext[obj.name] = instance; } for (const tool of iteration.tools) { const wrapped = wrapTool({ chat: ctx.chat, tool, traces, iteration, beforeHook: onBeforeTool, afterHook: onAfterTool, controller }); for (const key of [tool.name, ...tool.aliases ?? []]) { vmContext[key] = wrapped; } } if (controller.signal.aborted) { traces.push({ type: "abort_signal", started_at: Date.now(), reason: "The operation was aborted by user." }); return iteration.end({ type: "aborted", aborted: { reason: controller.signal.reason ?? "The operation was aborted" } }); } startedAt = Date.now(); const result = await runAsyncFunction(vmContext, iteration.code, traces, controller.signal, ctx.timeout).catch((err) => { return { success: false, error: err, lines_executed: [], traces: [], variables: {} }; }); if (result.error && result.error instanceof InvalidCodeError) { return iteration.end({ type: "invalid_code_error", invalid_code_error: { message: result.error.message } }); } traces.push({ type: "code_execution", lines_executed: result.lines_executed ?? 0, started_at: startedAt, ended_at: Date.now() }); if (controller.signal.aborted) { return iteration.end({ type: "aborted", aborted: { reason: controller.signal.reason ?? "The operation was aborted" } }); } if (result.error && result.error instanceof CodeExecutionError) { return iteration.end({ type: "execution_error", execution_error: { message: result.error.message, stack: cleanStackTrace(result.error.stacktrace ?? result.error.stack ?? "No stack trace available") } }); } if (!result.success) { return iteration.end({ type: "execution_error", execution_error: { message: ((_d = result == null ? undefined : result.error) == null ? undefined : _d.message) ?? "Unknown error occurred", stack: cleanStackTrace(result.error.stack ?? "No stack trace available") } }); } if (result.signal instanceof ThinkSignal) { return iteration.end({ type: "thinking_requested", thinking_requested: { variables: result.signal.context, reason: result.signal.reason, metadata: result.signal.metadata } }); } if (result.signal instanceof SnapshotSignal) { return iteration.end({ type: "callback_requested", callback_requested: { signal: result.signal } }); } let returnValue = result.success && result.return_value ? result.return_value : null; const returnAction = returnValue == null ? undefined : returnValue.action; if (returnAction === "think") { const variables = omit_default(returnValue ?? {}, "action"); if (isPlainObject_default(variables) && Object.keys(variables).length > 0) { return iteration.end({ type: "thinking_requested", thinking_requested: { variables, reason: "Thinking requested" } }); } return iteration.end({ type: "thinking_requested", thinking_requested: { reason: "Thinking requested", variables: iteration.variables } }); } const parsedExit = parseExit(returnValue, iteration.exits); if (!parsedExit.success) { return iteration.end({ type: "exit_error", exit_error: { exit: (returnValue == null ? undefined : returnValue.action) ?? "n/a", message: parsedExit.error, return_value: returnValue } }); } const returnExit = parsedExit.exit; returnValue = { action: returnExit.name, value: parsedExit.value }; try { await (onExit == null ? undefined : onExit({ exit: returnExit, result: returnValue == null ? undefined : returnValue.value })); } catch (err) { return iteration.end({ type: "exit_error", exit_error: { exit: returnExit.name, message: `Error executing exit ${returnExit.name}: ${getErrorMessage(err)}`, return_value: returnValue } }); } return iteration.end({ type: "exit_success", exit_success: { exit_name: returnExit.name, return_value: returnValue == null ? undefined : returnValue.value } }); }; function wrapTool({ chat, tool, traces, object, iteration, beforeHook, afterHook, controller }) { const getToolInput = (input) => tool.zInput.safeParse(input).data ?? input; return function(input) { const toolCallId = `tcall_${ulid()}`; const alertSlowTool = setTimeout(() => traces.push({ type: "tool_slow", tool_name: tool.name, tool_call_id: toolCallId, started_at: Date.now(), input: getToolInput(input), object, duration: SLOW_TOOL_WARNING }), SLOW_TOOL_WARNING); const cancelSlowTool = () => clearTimeout(alertSlowTool); const toolStart = Date.now(); let output; let error; let success = true; const handleSignals = (error2) => { if (output === error2) { return true; } if (error2 instanceof SnapshotSignal) { error2.toolCall = { name: tool.name, inputSchema: tool.input, outputSchema: tool.output, input }; error2.message = Signals.serializeError(error2); } if (error2 instanceof ThinkSignal) { traces.push({ type: "think_signal", started_at: Date.now(), line: 0, ended_at: Date.now() }); success = true; output = error2; return true; } return false; }; try { const withHooks = async (input2) => { const beforeRes = await (beforeHook == null ? undefined : beforeHook({ iteration, tool, input: input2, controller, object, toolCallId })); if (typeof (beforeRes == null ? undefined : beforeRes.input) !== "undefined") { input2 = beforeRes.input; } let output2 = await tool.execute(input2, { callId: toolCallId }, chat); const afterRes = await (afterHook == null ? undefined : afterHook({ iteration, tool, input: input2, output: output2, controller, object, toolCallId })); if (typeof (afterRes == null ? undefined : afterRes.output) !== "undefined") { output2 = afterRes.output; } return output2; }; const result = withHooks(input); if (result instanceof Promise || (result == null ? undefined : result.then) && (result == null ? undefined : result.catch)) { return result.then((res) => { output = res; success = true; return res; }).catch(async (err) => { if (!handleSignals(err)) { success = false; error = err; } else { const afterRes = await (afterHook == null ? undefined : afterHook({ iteration, tool, input, output, controller, object, toolCallId })); if (typeof (afterRes == null ? undefined : afterRes.output) !== "undefined") { output = afterRes.output; } } throw err; }).finally(() => { cancelSlowTool(); traces.push({ type: "tool_call", tool_call_id: toolCallId, started_at: toolStart, ended_at: Date.now(), tool_name: tool.name, object, input: getToolInput(input), output, error, success }); }); } success = true; output = result; } catch (err) { if (!handleSignals(err)) { success = false; error = err; } } cancelSlowTool(); traces.push({ type: "tool_call", tool_call_id: toolCallId, started_at: toolStart, ended_at: Date.now(), tool_name: tool.name, object, input: getToolInput(input), output, error, success }); if (!success) { throw error; } if (output instanceof VMSignal) { throw output; } return output; }; } var executeOnIterationStartHook = async (props) => { const { iteration, ctx, onIterationStart, controller, onIterationEnd } = props; try { const hookRes = await (onIterationStart == null ? undefined : onIterationStart(iteration, controller, ctx)); if (hookRes) { Object.assign(iteration, hookRes); } } catch (err) { if (err instanceof ThinkSignal) { iteration.end({ type: "thinking_requested", thinking_requested: { variables: err.context, reason: err.reason } }); try { await (onIterationEnd == null ? undefined : onIterationEnd(iteration, controller)); } catch (err2) { console.error(err2); } } else { iteration.end({ type: "execution_error", execution_error: { message: `Error in onIterationStart hook: ${getErrorMessage(err)}`, stack: cleanStackTrace(err.stack ?? "No stack trace available") } }); } throw err; } }; export { executeContext, _executeContext };