UNPKG

@vfarcic/dot-ai

Version:

AI-powered development productivity platform that enhances software development workflows through intelligent automation and AI-driven assistance

360 lines (359 loc) 16.5 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.HostProvider = void 0; const path = __importStar(require("path")); const prompts_1 = require("../../tools/prompts"); const model_config_1 = require("../model-config"); const provider_debug_utils_1 = require("./provider-debug-utils"); const ai_tracing_1 = require("../tracing/ai-tracing"); const tool_utils_1 = require("./tool-utils"); const investigation_1 = require("../constants/investigation"); class HostProvider { static samplingHandler; debugMode; constructor() { this.debugMode = process.env.DEBUG_DOT_AI === 'true'; } setSamplingHandler(handler) { HostProvider.samplingHandler = handler; } isInitialized() { return !!HostProvider.samplingHandler; } getDefaultModel() { return model_config_1.CURRENT_MODELS.host; } getProviderType() { return model_config_1.CURRENT_MODELS.host; } getModelName() { return model_config_1.CURRENT_MODELS.host; } logDebugIfEnabled(operation, prompt, response) { if (!this.debugMode) return null; const debugId = (0, provider_debug_utils_1.generateDebugId)(operation); (0, provider_debug_utils_1.debugLogInteraction)(debugId, prompt, response, operation, this.getProviderType(), this.getModelName(), this.debugMode); return { promptFile: `${debugId}_prompt.md`, responseFile: `${debugId}_response.md`, }; } async sendMessage(message, operation = 'generic', evaluationContext) { if (!HostProvider.samplingHandler) { throw new Error('Host provider is not connected to MCP server'); } return await (0, ai_tracing_1.withAITracing)({ provider: this.getProviderType(), model: this.getModelName(), operation: 'chat', }, async () => { const startTime = Date.now(); const messages = [ { role: 'user', content: { type: 'text', text: message } }, ]; try { const result = await HostProvider.samplingHandler(messages, undefined, { operation, evaluationContext, }); let content = ''; if (typeof result.content === 'object' && result.content.type === 'text') { content = result.content.text; } else if (typeof result.content === 'string') { content = result.content; } else { content = JSON.stringify(result.content); } const response = { content, usage: { input_tokens: 0, output_tokens: 0, }, }; const durationMs = Date.now() - startTime; // Debug log the interaction if enabled if (this.debugMode) { this.logDebugIfEnabled(operation, message, response); const evaluationMetrics = { operation, sdk: this.getProviderType(), inputTokens: response.usage.input_tokens, outputTokens: response.usage.output_tokens, durationMs, iterationCount: 1, toolCallCount: 0, status: 'completed', completionReason: 'stop', modelVersion: this.getModelName(), test_scenario: operation, ai_response_summary: response.content, user_intent: evaluationContext?.user_intent || '', interaction_id: evaluationContext?.interaction_id || '', }; (0, provider_debug_utils_1.logEvaluationDataset)(evaluationMetrics, this.debugMode); } return response; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); if (this.debugMode) { const debugId = (0, provider_debug_utils_1.generateDebugId)(operation); (0, provider_debug_utils_1.debugLogPromptOnly)(debugId, message, operation, this.getProviderType(), this.getModelName(), this.debugMode); if (evaluationContext) { const failureMetrics = { operation, user_intent: evaluationContext.user_intent || '', ai_response_summary: `Error: ${errorMessage}`, durationMs: Date.now() - startTime, inputTokens: 0, outputTokens: 0, iterationCount: 0, toolCallCount: 0, status: 'failed', completionReason: 'error', sdk: this.getProviderType(), modelVersion: this.getModelName(), test_scenario: operation, interaction_id: evaluationContext.interaction_id || (0, provider_debug_utils_1.generateDebugId)(operation), failure_analysis: { failure_type: 'error', failure_reason: `Host API error: ${errorMessage}`, time_to_failure: Date.now() - startTime, }, }; (0, provider_debug_utils_1.logEvaluationDataset)(failureMetrics, this.debugMode); } } throw new Error(`Host sampling error: ${errorMessage}`, { cause: error, }); } }, (response) => ({ inputTokens: response.usage.input_tokens, outputTokens: response.usage.output_tokens, })); } /** * Execute a tool loop with the host model * * The tool loop relies on a specific JSON format embedded in markdown code blocks: * ```json * { * "tool": "toolName", * "arguments": { ... } * } * ``` */ async toolLoop(config) { if (!HostProvider.samplingHandler) { throw new Error('Host provider is not connected to MCP server'); } return await (0, ai_tracing_1.withAITracing)({ provider: this.getProviderType(), model: this.getModelName(), operation: 'tool_loop', }, async () => { const maxIterations = config.maxIterations || 20; const messages = [ { role: 'user', content: { type: 'text', text: config.userMessage } }, ]; // Construct system prompt with tool definitions const promptPath = path.join(__dirname, '..', '..', '..', 'prompts', 'host-tools.md'); const promptTemplate = (0, prompts_1.loadPromptFile)(promptPath).content; const toolDefinitions = (0, tool_utils_1.formatToolDefinitions)(config.tools); const systemPrompt = config.systemPrompt + '\n\n' + promptTemplate.replace('{{TOOL_DEFINITIONS}}', toolDefinitions); const toolCallsExecuted = []; let iterations = 0; while (iterations < maxIterations) { iterations++; try { const result = await HostProvider.samplingHandler(messages, systemPrompt, { operation: config.operation, evaluationContext: config.evaluationContext, interaction_id: config.interaction_id, }); let content = ''; if (typeof result.content === 'object' && result.content.type === 'text') { content = result.content.text; } else if (typeof result.content === 'string') { content = result.content; } else { content = JSON.stringify(result.content); } // Add assistant response to history messages.push({ role: 'assistant', content: { type: 'text', text: content }, }); // Check for tool calls const toolCalls = (0, tool_utils_1.extractToolCalls)(content); if (toolCalls.length > 0) { for (const toolCall of toolCalls) { try { const toolName = toolCall.tool; const toolArgs = toolCall.arguments || {}; // Validate tool exists const toolExists = config.tools.some(t => t.name === toolName); if (!toolExists) { messages.push({ role: 'user', content: { type: 'text', text: `Unknown tool '${toolName}'. Available tools: ${config.tools.map(t => t.name).join(', ')}`, }, }); continue; } // Execute tool const toolOutput = await config.toolExecutor(toolName, toolArgs); toolCallsExecuted.push({ tool: toolName, input: toolArgs, output: toolOutput, }); // Add tool result to history messages.push({ role: 'user', content: { type: 'text', text: (0, tool_utils_1.formatToolOutput)(toolName, toolOutput), }, }); } catch (executionError) { messages.push({ role: 'user', content: { type: 'text', text: `Error executing tool '${toolCall.tool}': ${executionError instanceof Error ? executionError.message : String(executionError)}`, }, }); } } if (config.onIteration) { try { config.onIteration(iterations, toolCallsExecuted); } catch { // Ignore errors in callback } } } else { // No tool call, assume final response return { finalMessage: content, iterations, toolCallsExecuted, totalTokens: { input: 0, output: 0 }, }; } } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new Error(`Host sampling error in tool loop: ${message}`, { cause: error, }); } } // Max iterations reached - make one final wrap-up call WITHOUT tools // to force the AI to summarize findings rather than continue investigating const wrapUpMessage = investigation_1.INVESTIGATION_MESSAGES.WRAP_UP; messages.push({ role: 'user', content: { type: 'text', text: wrapUpMessage }, }); try { // Make final call WITHOUT tools in system prompt - use base system prompt only const wrapUpResult = await HostProvider.samplingHandler(messages, config.systemPrompt, // Original system prompt without tool definitions { operation: config.operation, evaluationContext: config.evaluationContext, interaction_id: config.interaction_id, }); let wrapUpContent = ''; if (typeof wrapUpResult.content === 'object' && wrapUpResult.content.type === 'text') { wrapUpContent = wrapUpResult.content.text; } else if (typeof wrapUpResult.content === 'string') { wrapUpContent = wrapUpResult.content; } else { wrapUpContent = JSON.stringify(wrapUpResult.content); } return { finalMessage: wrapUpContent, iterations: iterations + 1, // Include wrap-up iteration toolCallsExecuted, totalTokens: { input: 0, output: 0 }, status: 'timeout', completionReason: 'max_iterations', }; } catch { // If wrap-up call fails, fall back to last message const lastMessage = messages[messages.length - 2]; // -2 because we added wrap-up message const lastContent = typeof lastMessage.content === 'string' ? lastMessage.content : (lastMessage.content?.text ?? ''); return { finalMessage: lastContent, iterations, toolCallsExecuted, totalTokens: { input: 0, output: 0 }, status: 'timeout', completionReason: 'max_iterations', }; } }, (result) => ({ inputTokens: result.totalTokens.input, outputTokens: result.totalTokens.output, })); } } exports.HostProvider = HostProvider;