import { OpenRouterClient } from "./openrouter"; import { TradingGraph } from "../agents/tradingGraph"; import { db } from "./db.server"; type AnalyzeInput = { financialData: string; technicalData: { prices: number[]; sma: number; ema: number; rsi: number; macd: number }; sentimentData: { headlines: string[]; source?: "news" | "social" | "stocktwits" }; }; type Job = { id: string; type: "analyze"; ticker: string; input: AnalyzeInput; }; const queue: Job[] = []; let processing = false; function makeId() { return `${Date.now()}-${Math.floor(Math.random() * 100000)}`; } export function enqueueAnalyze(ticker: string, input: AnalyzeInput) { const id = makeId(); queue.push({ id, type: "analyze", ticker, input }); if (!processing) { processQueue().catch((err) => console.error("jobQueue error:", err)); } return id; } async function processQueue() { processing = true; while (queue.length > 0) { const job = queue.shift()!; console.log("[jobQueue] Processing job", job.id, job.type, job.ticker); try { if (job.type === "analyze") { const apiKey = process.env.OPENROUTER_API_KEY; if (!apiKey || apiKey === "your_openrouter_api_key_here") { console.log("[jobQueue] mock mode for analyze", job.ticker); const mockDecision = { action: "hold", confidence: 0.6, reasoning: `${job.ticker} analysis - Mock mode (background)`, }; await db.stock.upsert({ where: { ticker: job.ticker }, create: { ticker: job.ticker, lastDecision: mockDecision.action, lastExplanation: mockDecision.reasoning }, update: { lastDecision: mockDecision.action, lastExplanation: mockDecision.reasoning }, }); continue; } const client = new OpenRouterClient(apiKey); const graph = new TradingGraph(client); const decision = await graph.propagate(job.ticker, job.input); await db.stock.upsert({ where: { ticker: job.ticker }, create: { ticker: job.ticker, lastDecision: decision.action as string, lastExplanation: (decision as any).reasoning || null, lastExecutionPlan: decision.executionPlan ? JSON.stringify(decision.executionPlan) : null, }, update: { lastDecision: decision.action as string, lastExplanation: (decision as any).reasoning || null, lastExecutionPlan: decision.executionPlan ? JSON.stringify(decision.executionPlan) : null, }, }); console.log("[jobQueue] Saved background decision for", job.ticker); } } catch (err) { console.error("[jobQueue] job failed:", err); } } processing = false; }