UNPKG

braiin

Version:

Behavioral Reasoning AI for Intelligent Navigation

168 lines (167 loc) 7.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createOrchestrator = void 0; const action_factory_1 = require("../factory/action.factory"); const string_factory_1 = require("../factory/string.factory"); const llm_1 = require("../model/llm"); const llm_service_1 = require("../service/llm.service"); const prompt_service_1 = require("../service/prompt.service"); const stream_filter_1 = require("../service/stream.filter"); const timer_1 = require("../service/timer"); const agent_1 = require("./agent"); const DEFAULT_MAX_STEPS = 50; const DEFAULT_TIMEOUT_MS = 60000; const DEFAULT_SERVER_URL = 'https://api.openai.com/v1'; const DEFAULT_MODEL = 'gpt-4o'; const DEFAULT_TEMPERATURE = 0; const buildLLMConfig = (config) => { const backend = config.backend ?? 'openai'; if (backend === 'claude-code') { if (!config.sessionId) { throw new Error('OrchestratorConfig.sessionId is required when backend is "claude-code"'); } return { kind: 'claude-code', sessionId: config.sessionId, cliPath: config.cliPath, timeoutMs: config.timeoutMs ?? DEFAULT_TIMEOUT_MS, signal: config.signal }; } if (!config.apiKey) { throw new Error('OrchestratorConfig.apiKey is required when backend is "openai"'); } return { kind: 'openai', apiKey: config.apiKey, serverUrl: config.serverUrl ?? DEFAULT_SERVER_URL, model: config.model ?? DEFAULT_MODEL, temperature: config.temperature ?? DEFAULT_TEMPERATURE, maxTokens: config.maxTokens, timeoutMs: config.timeoutMs ?? DEFAULT_TIMEOUT_MS, signal: config.signal, enablePromptCaching: config.enablePromptCaching, enforceJsonOutput: config.enforceJsonOutput }; }; const runLLM = async (ctx, prompt, history) => { const { logCallback, onToken } = ctx; if (!logCallback && !onToken) { return ctx.llm.ask(ctx.globalContext, prompt, history); } const answerFilter = onToken ? (0, stream_filter_1.createAnswerStreamFilter)(onToken) : undefined; const callback = (delta) => { logCallback?.(delta); answerFilter?.(delta); }; return ctx.llm.ask(ctx.globalContext, prompt, history, callback); }; const chain = async (prompt, history, toolTraces, step, ctx) => { if (step >= ctx.maxSteps) { ctx.logCallback?.('Max steps reached'); return { status: 'error', answer: 'Max steps reached', toolTraces }; } if (ctx.stepsInterval) await (0, timer_1.freeze)(ctx.stepsInterval); const answer = await runLLM(ctx, prompt, history); if (answer?.error) { return { status: 'error', answer: `An error occured when calling the LLM: ${answer.error.message}`, toolTraces }; } const raw = answer?.choices[0]?.message.content?.trim() ?? ''; if (!raw) { ctx.logCallback?.('LLM returned an empty response'); return { status: 'error', answer: 'LLM returned an empty response', toolTraces }; } ctx.logCallback?.(raw); const parsed = (0, string_factory_1.extractJson)(raw); if (!parsed) { return { status: 'error', answer: 'Data in wrong format: ' + raw, toolTraces }; } const validation = (0, action_factory_1.parseLLMAction)(parsed); if (!validation.ok) { return { status: 'error', answer: `Invalid action: ${validation.error}. Raw: ${raw}`, toolTraces }; } return dispatch(validation.action, raw, prompt, history, toolTraces, step + 1, ctx); }; const dispatch = async (action, raw, prompt, history, toolTraces, nextStep, ctx) => { const chainNext = (nextPrompt, nextHistory) => chain(nextPrompt, nextHistory, toolTraces, nextStep, ctx); switch (action.action) { case 'finish': return { status: 'success', answer: action.answer, toolTraces }; case 'abort': return { status: 'error', answer: action.reason, toolTraces }; case 'describe': { const agent = ctx.agents.find(a => a.name === action.agent); if (!agent) { ctx.logCallback?.('No matching agent found for this task'); return { status: 'error', answer: 'No matching agent found for this task', toolTraces }; } return chainNext(JSON.stringify(agent.tools), [ ...history, { role: llm_1.LLMMessageRole.USER, content: prompt }, { role: llm_1.LLMMessageRole.ASSISTANT, content: raw } ]); } case 'call': { const agent = ctx.agents.find(a => a.name === action.agent); if (!agent) { ctx.logCallback?.('No matching agent found for this task'); return { status: 'error', answer: 'No matching agent found for this task', toolTraces }; } const tool = (0, agent_1.findTool)(agent, action.tool); if (!tool) { ctx.logCallback?.('No matching tool found for this task'); return { status: 'error', answer: 'No matching tool found for this task', toolTraces }; } let toolResponse; try { toolResponse = await tool.call(action.input); } catch (error) { const message = error instanceof Error ? error.message : 'Unknown tool error'; ctx.logCallback?.(`Tool "${action.tool}" failed: ${message}`); return { status: 'error', answer: `Tool "${action.tool}" failed: ${message}`, toolTraces }; } toolTraces.push({ tool: action.tool, input: action.input ?? '', result: toolResponse }); return chainNext(toolResponse, [ ...history, { role: llm_1.LLMMessageRole.USER, content: prompt }, { role: llm_1.LLMMessageRole.ASSISTANT, content: raw }, { role: llm_1.LLMMessageRole.USER, content: `tool response: ${toolResponse}` } ]); } } }; const createOrchestrator = (agents, config) => { const maxSteps = config.maxSteps ?? DEFAULT_MAX_STEPS; const globalContext = (0, prompt_service_1.buildSystemPrompt)(agents, config.optionalPrompt); const llm = config.llmService ?? (0, llm_service_1.createLLMService)(buildLLMConfig(config)); return { executeTask: async (prompt, history, toolTraces, logCallback, onToken) => { const contextHistory = [...(history || [])]; if (toolTraces?.length) { contextHistory.unshift({ role: llm_1.LLMMessageRole.SYSTEM, content: 'Known context from previous interactions: ' + JSON.stringify(toolTraces) }); } const ctx = { agents, llm, globalContext, maxSteps, stepsInterval: config.stepsInterval, logCallback, onToken }; // Prior toolTraces are injected as context above, but the accumulator // starts empty: TaskResult.toolTraces holds only what THIS call produces. // Callers accumulate across turns themselves if they need a running history. return chain(prompt, contextHistory, [], 0, ctx); }, askLLM: async (systemPrompt, prompt, history, logCallback) => { return llm.ask(systemPrompt, prompt, history || [], logCallback); } }; }; exports.createOrchestrator = createOrchestrator;