/** * Services: Business logic layer. * * This module contains the business logic for the application. * Services coordinate between controllers and repositories. */ import type { AgentRequest, AgentResponse, AgentConnection } from '../models' /** * Build OpenAI client and full prompt from request body. * Returns { openai, modelId, fullPrompt } or { error }. */ export function buildAgentRequest(body: AgentRequest): { openai: unknown; modelId: string; fullPrompt: string } | { error: string } { const { prompt, context, contextNodes, connection: conn, reasoning } = body ?? {} const reasoningEnabled = Boolean(reasoning) let baseURL = process.env.AI_BASE_URL?.trim() || null let apiKey = process.env.OPENAI_API_KEY?.trim() || null let modelId = process.env.AI_MODEL?.trim() || (baseURL ? 'local-model' : 'gpt-4o-mini') if (conn && typeof conn === 'object') { const c = conn as AgentConnection const provider = c.provider === 'openai' ? 'openai' : 'local' if (provider === 'local') { baseURL = (typeof c.baseURL === 'string' && c.baseURL.trim()) ? c.baseURL.trim() : baseURL apiKey = (typeof c.apiKey === 'string' && c.apiKey.trim()) ? c.apiKey.trim() : (apiKey || 'lm-studio') } else { baseURL = null apiKey = (typeof c.apiKey === 'string' && c.apiKey.trim()) ? c.apiKey.trim() : apiKey } if (typeof c.model === 'string' && c.model.trim()) modelId = c.model.trim() } if (!baseURL && !apiKey) { return { error: 'No AI configured. Set connection in Settings (AI) or env: OPENAI_API_KEY or AI_BASE_URL.' } } const basePrompt = [ typeof prompt === 'string' ? prompt : 'No prompt provided.', context && typeof context === 'string' ? `\n\nAdditional context:\n${context}` : '', Array.isArray(contextNodes) && contextNodes.length > 0 ? `\n\nContext from connected nodes:\n${contextNodes.map((n) => (n.content != null ? n.content : `${n.id}: (no content)`)).join('\n\n')}` : '', ].join('') const fullPrompt = reasoningEnabled ? basePrompt + '\n\nRespond in exactly two markdown sections. First: "## Reasoning" with your step-by-step reasoning. Then: "## Output" with only the final answer. No preamble.' : basePrompt + '\n\nRespond with structured markdown only. No preamble.' const { createOpenAI } = require('@ai-sdk/openai') const openai = createOpenAI({ apiKey: apiKey || 'lm-studio', ...(baseURL && { baseURL, compatibility: 'compatible' }), }) return { openai, modelId, fullPrompt } } /** * Process agent response */ export function processAgentResponse(result: { text?: string }): AgentResponse { const markdown = result?.text ?? '' return { markdown } }