UNPKG

@nomyx/assistant

Version:

A powerful assistant library and cli for your AI projects. works with Vertex AI (Claude and Gemini)

185 lines (184 loc) 7.63 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PromptExecution = void 0; const events_1 = require("events"); const uuid_1 = require("uuid"); const promises_1 = __importDefault(require("fs/promises")); const path_1 = __importDefault(require("path")); const ajv_1 = __importDefault(require("ajv")); const expr_eval_1 = require("expr-eval"); class PromptExecution extends events_1.EventEmitter { constructor(registry, initialPrompt, provider, availablePrompts, options = {}, logger) { super(); this.registry = registry; this.initialPrompt = initialPrompt; this.provider = provider; this.availablePrompts = availablePrompts; this.options = options; this.logger = logger; this.state = { id: (0, uuid_1.v4)(), variables: {}, history: [], currentPrompt: initialPrompt.definition.name }; this.ajv = new ajv_1.default(); this.toolManager = { registerTool: (tool) => { }, getTool: (name) => undefined, getAllTools: () => [] }; } async execute(initialReplacements = {}) { this.state.variables = { ...this.state.variables, ...initialReplacements }; while (true) { try { await this.loadState(); const currentPrompt = this.registry.get(this.state.currentPrompt); const { shouldContinue, nextPrompt } = await this.executePromptWithRetry(currentPrompt); if (!shouldContinue) break; if (nextPrompt) this.state.currentPrompt = nextPrompt.definition.name; await this.saveState(); } catch (error) { this.emit('error', error); break; } } this.emit('executionComplete', this.state); } registerTool(tool) { this.toolManager.registerTool(tool); } async executePromptWithRetry(prompt) { let retries = 0; while (retries <= (this.options.maxRetries || 3)) { try { return await this.executePromptWithTimeout(prompt); } catch (error) { if (error instanceof Error && error.name === 'TimeoutError') { this.emit('promptTimeout', { prompt: prompt.definition.name, retries }); } else { this.emit('promptError', { prompt: prompt.definition.name, error, retries }); } retries++; if (retries <= (this.options.maxRetries || 3)) { await new Promise(resolve => setTimeout(resolve, this.options.retryDelayMs || 1000)); } else { throw error; } } } throw new Error('Max retries exceeded'); } async executePromptWithTimeout(prompt) { return new Promise((resolve, reject) => { const timeout = setTimeout(() => { reject(new Error('Prompt execution timed out')); }, this.options.timeoutMs || 30000); this.executePrompt(prompt) .then(resolve) .catch(reject) .finally(() => clearTimeout(timeout)); }); } async executePrompt(prompt) { let renderedPrompt = prompt.render(this.state.variables, this.availablePrompts); this.emit('promptStart', { prompt: prompt.definition.name, renderedPrompt }); const messages = [{ role: 'user', content: renderedPrompt }]; const chatOptions = { ...this.options, ...prompt.getConfig() }; const response = await this.provider.chat(messages, chatOptions); let parsedResponse; try { parsedResponse = JSON.parse(response.content); } catch (error) { throw new Error(`Invalid JSON response for prompt: ${prompt.definition.name}`); } if (!this.validateResponse(parsedResponse, prompt.definition.responseFormat)) { throw new Error(`Invalid response format for prompt: ${prompt.definition.name}`); } this.state.history.push({ prompt: renderedPrompt, response: parsedResponse }); this.emit('promptComplete', { prompt: prompt.definition.name, response: parsedResponse }); // Process tool calls if (response.toolCalls) { for (const toolCall of response.toolCalls) { await this.processToolCall(toolCall); } } // Update state variables this.state.variables = { ...this.state.variables, ...parsedResponse.response }; // Handle next steps if (parsedResponse.next) { if (parsedResponse.next.if) { const nextPromptName = this.evaluateCondition(parsedResponse.next.if) ? parsedResponse.next.then : parsedResponse.next.else; return { shouldContinue: true, nextPrompt: this.registry.get(nextPromptName) }; } else if (parsedResponse.next.each) { for (const item of parsedResponse.next.each) { const nextPromptName = this.evaluateCondition(item.if) ? item.then : item.else; await this.executePrompt(this.registry.get(nextPromptName)); } return { shouldContinue: true }; } } return { shouldContinue: false }; } async processToolCall(toolCall) { this.emit('toolCallStart', toolCall); try { const tool = this.toolManager.getTool(toolCall.name); if (!tool) { throw new Error(`Tool not found: ${toolCall.name}`); } const result = await tool.execute(toolCall.arguments, { user: { id: this.state.id, name: 'User' }, session: { id: this.state.id, timestamp: new Date().toISOString() } }); this.state.variables[toolCall.name] = result; this.emit('toolCallComplete', { toolCall, result }); } catch (error) { this.emit('toolCallError', { toolCall, error }); throw error; } } validateResponse(response, schema) { const validate = this.ajv.compile(JSON.parse(schema)); return validate(response); } evaluateCondition(condition) { const parser = new expr_eval_1.Parser(); const expr = parser.parse(condition); return expr.evaluate(this.state.variables); } async saveState() { if (this.options.statePersistenceDir) { const filePath = path_1.default.join(this.options.statePersistenceDir, `${this.state.id}.json`); await promises_1.default.writeFile(filePath, JSON.stringify(this.state), 'utf8'); } } async loadState() { if (this.options.statePersistenceDir) { const filePath = path_1.default.join(this.options.statePersistenceDir, `${this.state.id}.json`); try { const data = await promises_1.default.readFile(filePath, 'utf8'); this.state = JSON.parse(data); } catch (error) { if (error instanceof Error && error.code !== 'ENOENT') { throw error; } } } } } exports.PromptExecution = PromptExecution;