UNPKG

jorel

Version:

A unified wrapper for working with LLMs from multiple providers, including streams, images, documents & automatic tool use.

282 lines (281 loc) 9.69 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TaskExecution = exports.__mainTaskExecutionThreadId = exports.TaskExecutionError = exports.TaskCreationError = void 0; const providers_1 = require("../providers"); const task_execution_thread_1 = require("./task-execution-thread"); const shared_1 = require("../shared"); /** Thrown when a task creation fails */ class TaskCreationError extends Error { constructor(message) { super(`Task creation error: ${message}`); } } exports.TaskCreationError = TaskCreationError; /** Thrown when a task execution fails */ class TaskExecutionError extends Error { constructor(message, taskId) { super(`Task ${taskId}}: ${message}`); this.taskId = taskId; } } exports.TaskExecutionError = TaskExecutionError; exports.__mainTaskExecutionThreadId = "__main__"; /** * Represents a task execution that manages multiple threads and their events. */ class TaskExecution { /** * Create a new task execution * @param data - The task execution definition * @param jorEl - The agent manager instance */ constructor(data, jorEl) { /** @internal */ this._modified = false; this.id = data.id; this._status = data.status; this.threads = {}; for (const threadId of Object.keys(data.threads)) { this.threads[threadId] = new task_execution_thread_1.TaskExecutionThread(data.threads[threadId], jorEl); } this._activeThreadId = data.activeThreadId; this.jorEl = jorEl; for (const thread of Object.values(this.threads)) { if (thread.parentThreadId) { if (!this.threads[thread.parentThreadId]) { throw new TaskExecutionError(`Parent thread ${thread.parentThreadId} not found (thread ${thread.id})`, this.id); } } if (!this.jorEl.getAgent(thread.agentId)) { throw new TaskExecutionError(`Agent ${thread.agentId} not found (thread ${thread.id})`, this.id); } } if (!this.threads[this._activeThreadId]) { throw new TaskExecutionError(`Active thread ${this._activeThreadId} not found`, this.id); } this.stats = data.stats; this._modified = data.modified; this._haltReason = data.haltReason; } /** * Get the task execution definition */ get status() { return this._status; } /** * Set the task execution status */ set status(status) { this._status = status; this._modified = true; if (status === "completed") { this._haltReason = "completed"; } else if (status === "halted") { this._haltReason = null; } } /** * Get the active thread ID */ get activeThreadId() { return this._activeThreadId; } /** * Set the active thread ID */ set activeThreadId(threadId) { this._activeThreadId = threadId; this._modified = true; } /** * Whether this task execution (or any thread) has been modified */ get modified() { return this._modified || Object.values(this.threads).some((thread) => thread.modified); } /** * Set the modified state of this task execution */ set modified(modified) { this._modified = modified; } /** * Get the reason for halting the task execution */ get haltReason() { return this._haltReason; } /** * Set the reason for halting the task execution */ set haltReason(reason) { this._haltReason = reason; this._modified = true; } /** * Events for this task execution along with aggregated usage statistics */ get eventsWithStatistics() { const events = this.getEventsByThread(); const stats = this.stats; const tokens = {}; for (const event of events) { if (event.eventType === "generation") { if (!tokens[event.model]) { tokens[event.model] = { input: 0, output: 0, }; } tokens[event.model].input += event.tokenUsage.input || 0; tokens[event.model].output += event.tokenUsage.output || 0; } } return { events, stats, tokens }; } /** * Get the result of the task execution */ get result() { if (!this.activeThread.isMain) return null; if (this.activeThread.latestMessage.role !== "assistant") return null; return this.activeThread.latestMessage.content; } /** * Get the active thread for this task execution */ get activeThread() { const thread = this.threads[this._activeThreadId]; if (!thread) { throw new TaskExecutionError(`Active thread ${this._activeThreadId} not found in available threads`, this.id); } return thread; } /** * Get the task execution definition */ get definition() { const { id, status, _activeThreadId, stats } = this; const threads = {}; for (const threadId of Object.keys(this.threads)) { threads[threadId] = this.threads[threadId].definition; } return { id, status, threads, activeThreadId: _activeThreadId, stats: { ...stats }, modified: this._modified, haltReason: this._haltReason, }; } /** * Get all events for this task execution */ get events() { return this.getEventsByThread(); } /** * Create a new instance of this execution - e.g. to avoid modifying the original */ get copy() { return new TaskExecution(this.definition, this.jorEl); } /** * Get the tool calls with pending approvals for this task execution */ get toolCallsWithPendingApprovals() { const toolCalls = []; for (const thread of Object.values(this.threads)) { toolCalls.push(...thread.toolCallsWithPendingApprovals); } return toolCalls; } /** * Generate a new task execution from a task description * @param task * @param agentId * @param jorEl */ static async fromTask(task, agentId, jorEl) { return new TaskExecution({ id: (0, shared_1.generateUniqueId)(), status: "pending", threads: { [exports.__mainTaskExecutionThreadId]: { id: exports.__mainTaskExecutionThreadId, agentId, messages: [await (0, providers_1.generateUserMessage)(task)], parentThreadId: null, parentToolCallId: null, events: [], modified: false, }, }, activeThreadId: exports.__mainTaskExecutionThreadId, stats: { generations: 0, delegations: 0, }, modified: false, haltReason: null, }, jorEl); } /** * Add a follow-up user message to the main thread * @param message */ async addFollowUpUserMessage(message) { if (!this.activeThread.isMain) { throw new TaskExecutionError("Cannot add a message to a non-main thread", this.id); } if (this.activeThread.latestMessage.role !== "assistant") { throw new TaskExecutionError("The last message is not an assistant response", this.id); } const task = this.copy; task.activeThread.addMessage(await (0, providers_1.generateUserMessage)(message)); task.status = "running"; return task; } /** * Get events by thread * @param threadId */ getEventsByThread(threadId) { const events = []; const threadIds = threadId ? [threadId] : Object.keys(this.threads); for (const threadId of threadIds) { const thread = this.threads[threadId]; if (!thread) throw new Error(`Thread ${threadId} not found`); const parentThread = thread.parentThreadId ? this.threads[thread.parentThreadId] : null; for (const event of thread.events) { events.push({ ...event, threadId, parentThreadId: parentThread?.id ?? null }); } } events.sort((a, b) => a.timestamp - b.timestamp); return events; } /** * Halt the task execution * @param reason The reason for halting the task execution. * Can be one of * - `maxIterations`: The task execution has reached the maximum number of iterations (steps). This is useful to prevent infinite loops. * - `maxGenerations`: The task execution has reached the maximum number of generations (model calls). This can be used to control the cost of the task execution. * - `maxDelegations`: The task execution has reached the maximum number of delegations. This can be used to prevent excessive delegation. * - `approvalRequired`: The task execution contains tool-calls which requires approval. * - `invalidState`: The task execution is in an invalid state. * - `error` : The task execution has encountered an error. */ halt(reason) { this.status = "halted"; this.haltReason = reason; return this; } } exports.TaskExecution = TaskExecution;