"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LangChainService = exports.TaskBreakdownSchema = void 0;
const openai_1 = require("@langchain/openai");
const prompts_1 = require("@langchain/core/prompts");
const output_parsers_1 = require("@langchain/core/output_parsers");
const memory_1 = require("langchain/memory");
const messages_1 = require("@langchain/core/messages");
const zod_1 = require("zod");
const ai_config_1 = require("../../config/ai.config");
/**
 * Task breakdown structure
 */
exports.TaskBreakdownSchema = zod_1.z.object({
    title: zod_1.z.string(),
    description: zod_1.z.string(),
    subtasks: zod_1.z.array(zod_1.z.object({
        id: zod_1.z.string(),
        title: zod_1.z.string(),
        description: zod_1.z.string(),
        estimatedHours: zod_1.z.number().optional(),
        dependencies: zod_1.z.array(zod_1.z.string()).optional(),
    })),
    estimatedTotalHours: zod_1.z.number().optional(),
    complexity: zod_1.z.enum(['trivial', 'minor', 'major', 'critical']),
    suggestedApproach: zod_1.z.string().optional(),
});
/**
 * Service for LangChain operations with OpenAI integration
 * Implements chains, agents, and memory management for complex task processing
 */
class LangChainService {
    constructor(config) {
        this.config = config || (0, ai_config_1.getAIConfig)();
        this.memories = new Map();
        // Initialize LangChain with OpenAI
        this.llm = new openai_1.ChatOpenAI({
            openAIApiKey: this.config.openai.apiKey,
            modelName: this.config.openai.model,
            temperature: this.config.openai.temperature,
            maxTokens: this.config.openai.maxTokens,
            maxRetries: this.config.openai.retryAttempts,
            verbose: this.config.langchain.verbose,
        });
    }
    /**
     * Create a simple completion chain
     * @param systemPrompt System prompt for the chain
     * @param options Chain options
     * @returns Runnable chain
     */
    createCompletionChain(systemPrompt, options = {}) {
        const prompt = prompts_1.ChatPromptTemplate.fromMessages([
            prompts_1.SystemMessagePromptTemplate.fromTemplate(systemPrompt),
            prompts_1.HumanMessagePromptTemplate.fromTemplate('{input}'),
        ]);
        // Configure LLM with options
        const configuredLLM = new openai_1.ChatOpenAI({
            openAIApiKey: this.config.openai.apiKey,
            modelName: this.config.openai.model,
            temperature: options.temperature ?? this.config.openai.temperature,
            maxTokens: options.maxTokens ?? this.config.openai.maxTokens,
        });
        // Create chain manually to avoid type issues
        const chain = {
            invoke: async (input) => {
                const formatted = await prompt.formatMessages(input);
                const response = await configuredLLM.invoke(formatted);
                return typeof response.content === 'string'
                    ? response.content
                    : JSON.stringify(response.content);
            },
            stream: async function* (input) {
                const formatted = await prompt.formatMessages(input);
                const stream = await configuredLLM.stream(formatted);
                for await (const chunk of stream) {
                    yield chunk.content;
                }
            },
        };
        return chain;
    }
    /**
     * Create a chain with memory for conversation context
     * @param systemPrompt System prompt
     * @param sessionId Session ID for memory management
     * @param options Chain options
     * @returns Runnable chain with memory
     */
    createConversationChain(systemPrompt, sessionId, options = {}) {
        // Get or create memory for this session
        const memory = this.getOrCreateMemory(sessionId, options);
        const prompt = prompts_1.ChatPromptTemplate.fromMessages([
            prompts_1.SystemMessagePromptTemplate.fromTemplate(systemPrompt),
            new prompts_1.MessagesPlaceholder('history'),
            prompts_1.HumanMessagePromptTemplate.fromTemplate('{input}'),
        ]);
        // Configure LLM with options
        const configuredLLM = new openai_1.ChatOpenAI({
            openAIApiKey: this.config.openai.apiKey,
            modelName: this.config.openai.model,
            temperature: options.temperature ?? this.config.openai.temperature,
            maxTokens: options.maxTokens ?? this.config.openai.maxTokens,
        });
        // Create chain with memory manually
        const chain = {
            invoke: async (input) => {
                const messages = await memory.chatHistory.getMessages();
                const formatted = await prompt.formatMessages({
                    history: messages,
                    input: input.input || input,
                });
                const response = await configuredLLM.invoke(formatted);
                const result = typeof response.content === 'string'
                    ? response.content
                    : JSON.stringify(response.content);
                // Add to memory
                await memory.chatHistory.addMessage(new messages_1.HumanMessage(input.input || input));
                await memory.chatHistory.addMessage(new messages_1.AIMessage(result));
                return result;
            },
        };
        return chain;
    }
    /**
     * Create a task breakdown agent
     * @param options Agent options
     * @returns Task breakdown chain
     */
    createTaskBreakdownAgent(options = {}) {
        const systemPrompt = `You are an expert task breakdown specialist.
    Given a task description, break it down into manageable subtasks.
    Consider dependencies, complexity, and provide time estimates.
    
    Respond with a JSON object matching this structure:
    {{
      "title": "Main task title",
      "description": "Task description",
      "subtasks": [
        {{
          "id": "1",
          "title": "Subtask title",
          "description": "Subtask description",
          "estimatedHours": 2,
          "dependencies": []
        }}
      ],
      "estimatedTotalHours": 10,
      "complexity": "major",
      "suggestedApproach": "Recommended implementation approach"
    }}`;
        const prompt = prompts_1.ChatPromptTemplate.fromMessages([
            prompts_1.SystemMessagePromptTemplate.fromTemplate(systemPrompt),
            prompts_1.HumanMessagePromptTemplate.fromTemplate('Break down this task: {taskDescription}\nContext: {context}'),
        ]);
        const parser = new output_parsers_1.JsonOutputParser();
        // Configure LLM with options
        const configuredLLM = new openai_1.ChatOpenAI({
            openAIApiKey: this.config.openai.apiKey,
            modelName: this.config.openai.model,
            temperature: options.temperature ?? 0.3, // Lower temperature for structured output
            maxTokens: options.maxTokens ?? 2000,
        });
        // Create chain manually
        const chain = {
            invoke: async (input) => {
                const formatted = await prompt.formatMessages(input);
                const response = await configuredLLM.invoke(formatted);
                try {
                    const content = typeof response.content === 'string' ? response.content : JSON.stringify(response.content);
                    return parser.parse(content);
                }
                catch (e) {
                    // If parsing fails, try to extract JSON from the response
                    const content = typeof response.content === 'string' ? response.content : JSON.stringify(response.content);
                    const jsonMatch = content.match(/\{[\s\S]*\}/);
                    if (jsonMatch) {
                        return JSON.parse(jsonMatch[0]);
                    }
                    throw e;
                }
            },
        };
        return chain;
    }
    /**
     * Create a chain for natural language task creation
     * @param options Chain options
     * @returns Task creation chain
     */
    createTaskCreationChain(options = {}) {
        const systemPrompt = `You are a task creation assistant.
    Convert natural language descriptions into structured task objects.
    Extract key information like title, description, priority, complexity, and acceptance criteria.
    
    Output format:
    {{
      "title": "Clear, concise task title",
      "description": "Detailed task description",
      "priority": "low|medium|high|critical",
      "complexity": "trivial|minor|major|critical",
      "acceptanceCriteria": ["Criterion 1", "Criterion 2"],
      "tags": ["relevant", "tags"],
      "estimatedHours": number or null
    }}`;
        const prompt = prompts_1.ChatPromptTemplate.fromMessages([
            prompts_1.SystemMessagePromptTemplate.fromTemplate(systemPrompt),
            prompts_1.HumanMessagePromptTemplate.fromTemplate('Create a task from: {description}\nProject context: {projectContext}'),
        ]);
        // Configure LLM with options
        const configuredLLM = new openai_1.ChatOpenAI({
            openAIApiKey: this.config.openai.apiKey,
            modelName: this.config.openai.model,
            temperature: options.temperature ?? 0.5,
            maxTokens: options.maxTokens ?? 1000,
        });
        // Create chain manually
        const chain = {
            invoke: async (input) => {
                const formatted = await prompt.formatMessages(input);
                const response = await configuredLLM.invoke(formatted);
                const parser = new output_parsers_1.JsonOutputParser();
                try {
                    const content = typeof response.content === 'string' ? response.content : JSON.stringify(response.content);
                    return parser.parse(content);
                }
                catch (e) {
                    // Try to extract JSON from response
                    const content = typeof response.content === 'string' ? response.content : JSON.stringify(response.content);
                    const jsonMatch = content.match(/\{[\s\S]*\}/);
                    if (jsonMatch) {
                        return JSON.parse(jsonMatch[0]);
                    }
                    throw e;
                }
            },
        };
        return chain;
    }
    /**
     * Create a chain for generating task suggestions
     * @param options Chain options
     * @returns Task suggestion chain
     */
    createTaskSuggestionChain(options = {}) {
        const systemPrompt = `You are a project planning assistant.
    Based on the current project state and completed tasks, suggest next logical tasks.
    Consider dependencies, project goals, and best practices.
    
    Provide 3-5 task suggestions with rationale.`;
        const prompt = prompts_1.ChatPromptTemplate.fromMessages([
            prompts_1.SystemMessagePromptTemplate.fromTemplate(systemPrompt),
            prompts_1.HumanMessagePromptTemplate.fromTemplate(`Project: {projectDescription}
        Completed tasks: {completedTasks}
        Current tasks: {currentTasks}
        Project goals: {goals}
        
        Suggest next tasks:`),
        ]);
        // Configure LLM with options
        const configuredLLM = new openai_1.ChatOpenAI({
            openAIApiKey: this.config.openai.apiKey,
            modelName: this.config.openai.model,
            temperature: options.temperature ?? 0.7,
            maxTokens: options.maxTokens ?? 1500,
        });
        // Create chain manually
        const chain = {
            invoke: async (input) => {
                const formatted = await prompt.formatMessages(input);
                const response = await configuredLLM.invoke(formatted);
                return typeof response.content === 'string'
                    ? response.content
                    : JSON.stringify(response.content);
            },
        };
        return chain;
    }
    /**
     * Execute a task breakdown
     * @param taskDescription Task to break down
     * @param context Additional context
     * @param options Execution options
     * @returns Task breakdown result
     */
    async breakdownTask(taskDescription, context = '', options = {}) {
        const startTime = Date.now();
        const chain = this.createTaskBreakdownAgent(options);
        try {
            const result = await chain.invoke({
                taskDescription,
                context,
            });
            // Validate result with schema
            const validated = exports.TaskBreakdownSchema.parse(result);
            return validated;
        }
        catch (error) {
            console.error('Task breakdown failed:', error);
            throw new Error(`Failed to breakdown task: ${error}`);
        }
    }
    /**
     * Process natural language task creation
     * @param description Natural language description
     * @param projectContext Project context
     * @param options Execution options
     * @returns Created task object
     */
    async createTaskFromNaturalLanguage(description, projectContext = '', options = {}) {
        const startTime = Date.now();
        const chain = this.createTaskCreationChain(options);
        const result = await chain.invoke({
            description,
            projectContext,
        });
        return {
            ...result,
            executionTimeMs: Date.now() - startTime,
        };
    }
    /**
     * Get task suggestions based on project state
     * @param projectData Project information
     * @param options Execution options
     * @returns Task suggestions
     */
    async getTaskSuggestions(projectData, options = {}) {
        const chain = this.createTaskSuggestionChain(options);
        const result = await chain.invoke({
            projectDescription: projectData.projectDescription,
            completedTasks: projectData.completedTasks.join('\n'),
            currentTasks: projectData.currentTasks.join('\n'),
            goals: projectData.goals.join('\n'),
        });
        return result;
    }
    /**
     * Execute a chain with streaming support
     * @param chain The chain to execute
     * @param input Input data
     * @param onChunk Callback for each streamed chunk
     * @returns Final result
     */
    async streamChain(chain, input, onChunk) {
        const stream = await chain.stream(input);
        let result = '';
        for await (const chunk of stream) {
            const text = typeof chunk === 'string' ? chunk : chunk.content || '';
            result += text;
            onChunk(text);
        }
        return result;
    }
    /**
     * Get or create memory for a session
     * @param sessionId Session identifier
     * @param options Memory options
     * @returns Memory instance
     */
    getOrCreateMemory(sessionId, options = {}) {
        if (!this.memories.has(sessionId)) {
            const bufferSize = this.config.langchain.memoryBufferSize;
            // Use BufferMemory for short conversations
            if (bufferSize <= 20) {
                this.memories.set(sessionId, new memory_1.BufferMemory({
                    returnMessages: true,
                    memoryKey: 'history',
                }));
            }
            else {
                // Use ConversationSummaryMemory for longer conversations
                const summaryLLM = new openai_1.ChatOpenAI({
                    openAIApiKey: this.config.openai.apiKey,
                    modelName: this.config.openai.model,
                    temperature: 0.5,
                });
                this.memories.set(sessionId, new memory_1.ConversationSummaryMemory({
                    llm: summaryLLM,
                    returnMessages: true,
                    memoryKey: 'history',
                }));
            }
        }
        return this.memories.get(sessionId);
    }
    /**
     * Add a message to session memory
     * @param sessionId Session identifier
     * @param message Message to add
     * @param role Message role (human or ai)
     */
    async addToMemory(sessionId, message, role) {
        const memory = this.getOrCreateMemory(sessionId);
        const msg = role === 'human'
            ? new messages_1.HumanMessage(message)
            : new messages_1.AIMessage(message);
        await memory.chatHistory.addMessage(msg);
    }
    /**
     * Clear memory for a session
     * @param sessionId Session identifier
     */
    clearMemory(sessionId) {
        const memory = this.memories.get(sessionId);
        if (memory) {
            memory.clear();
        }
        this.memories.delete(sessionId);
    }
    /**
     * Clear all memories
     */
    clearAllMemories() {
        for (const memory of this.memories.values()) {
            memory.clear();
        }
        this.memories.clear();
    }
    /**
     * Get memory summary for a session
     * @param sessionId Session identifier
     * @returns Memory summary or null
     */
    async getMemorySummary(sessionId) {
        const memory = this.memories.get(sessionId);
        if (!memory)
            return null;
        const messages = await memory.chatHistory.getMessages();
        return messages.map(m => `${m._getType()}: ${m.content}`).join('\n');
    }
    /**
     * Test LangChain connection
     * @returns True if connection successful
     */
    async testConnection() {
        try {
            const chain = this.createCompletionChain('You are a helpful assistant.');
            const result = await chain.invoke({ input: 'Say "OK" if you can hear me.' });
            return result.toLowerCase().includes('ok');
        }
        catch (error) {
            console.error('LangChain connection test failed:', error);
            return false;
        }
    }
}
exports.LangChainService = LangChainService;
//# sourceMappingURL=langchain.service.js.map