1 line
12 KiB
JSON
1 line
12 KiB
JSON
{"repo": "ardahk/replay26", "files": {"src/lib/agents/deepseek.ts": {"f2f5e94babaebb23967c38638faf07f9f2103c75": {"t": 1785745570.9649444, "n": 5697, "h": "56b7977d296dd675", "c": "import type { AgentDecision } from \"./types\";\n\ninterface DeepSeekMessage {\n role: \"system\" | \"user\";\n content: string;\n}\n\ninterface DeepSeekResponse {\n choices?: Array<{\n message?: {\n content?: string | null;\n reasoning_content?: string | null;\n };\n }>;\n model?: string;\n error?: {\n message?: string;\n };\n}\n\nconst DEFAULT_BASE_URL = \"https://api.deepseek.com\";\nconst DEFAULT_MODEL = \"deepseek-v4-flash\";\n\nfunction trimSlash(value: string): string {\n return value.replace(/\\/+$/, \"\");\n}\n\nfunction systemPrompt(role: AgentDecision[\"role\"]): string {\n const shared =\n \"You are an agentic brewery copilot. Use the supplied tool plan and observations as ground truth. Do not invent tool results. Keep the answer concise, operational, and action-oriented. Never claim you already performed a pending action unless the supplied decision says it was confirmed. Return plain text only: no Markdown, no bullets, no bold markers, no headings.\";\n if (role === \"support\") {\n return `${shared} You are customer-safe: do not expose internal alarms, QA task ids, workflow internals, or sensor failure details.`;\n }\n return `${shared} You are operator-facing: you may discuss alarms, telemetry, QA tasks, and workflow signals.`;\n}\n\nfunction userPrompt(userMessage: string, decision: AgentDecision): string {\n return [\n `User asked: ${userMessage}`,\n `Local copilot response: ${decision.message}`,\n `Plan: ${decision.plan.join(\" | \") || \"none\"}`,\n `Tools used: ${decision.toolsUsed.join(\", \") || \"none\"}`,\n `Observations: ${decision.observations.join(\" | \") || \"none\"}`,\n decision.pendingAction\n ? `Pending action proposed: ${decision.pendingAction.type}. Mention that operator confirmation is required.`\n : \"Pending action proposed: none.\",\n \"Rewrite the local copilot response so it feels like a proactive agent. Return one short plain-text paragraph only, at least 12 words, with a complete final sentence.\"\n ].join(\"\\n\");\n}\n\nfunction fallbackRewritePrompt(userMessage: string, decision: AgentDecision): string {\n return [\n `User asked: ${userMessage}`,\n `Ground truth answer: ${decision.message}`,\n decision.pendingAction\n ? `The answer must say operator confirmation is required for ${decision.pendingAction.type}.`\n : \"No action is pending.\",\n \"Write one concise plain-text sentence of at least 12 words. Do not use Markdown.\"\n ].join(\"\\n\");\n}\n\nfunction cleanProviderMessage(value: string): string {\n const cleaned = value\n .replace(/\\*\\*(.*?)\\*\\*/g, \"$1\")\n .replace(/__(.*?)__/g, \"$1\")\n .replace(/`([^`]+)`/g, \"$1\")\n .replace(/^#{1,6}\\s+/gm, \"\")\n .replace(/^\\s*[-*]\\s+/gm, \"\")\n .replace(/\\s+\\n/g, \"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .replace(/^[\"']|[\"']$/g, \"\")\n .trim();\n if (!cleaned) return cleaned;\n return /[.!?]$/.test(cleaned) ? cleaned : `${cleaned}.`;\n}\n\nfunction qualityError(message: string): string | null {\n if (!message) return \"DeepSeek returned an empty message\";\n const words = message.split(/\\s+/).filter(Boolean);\n if (words.length < 8) return \"DeepSeek returned a clipped message\";\n if (/\\b(and|or|the|to|for|with|of|a|an|is|are)\\.$/i.test(message)) {\n return \"DeepSeek returned an incomplete message\";\n }\n return null;\n}\n\nasync function callDeepSeek(messages: DeepSeekMessage[]): Promise<{ message: string; model: string }> {\n const apiKey = process.env.DEEPSEEK_API_KEY;\n if (!apiKey) throw new Error(\"DEEPSEEK_API_KEY is not set\");\n\n const baseUrl = trimSlash(process.env.DEEPSEEK_BASE_URL || DEFAULT_BASE_URL);\n const model = process.env.DEEPSEEK_MODEL || DEFAULT_MODEL;\n const response = await fetch(`${baseUrl}/chat/completions`, {\n method: \"POST\",\n headers: {\n authorization: `Bearer ${apiKey}`,\n \"content-type\": \"application/json\"\n },\n body: JSON.stringify({\n model,\n messages,\n temperature: 0.1,\n max_tokens: 280\n })\n });\n const json = (await response.json().catch(() => ({}))) as DeepSeekResponse;\n if (!response.ok) {\n throw new Error(json.error?.message ?? `DeepSeek request failed with ${response.status}`);\n }\n const message = cleanProviderMessage(json.choices?.[0]?.message?.content?.trim() ?? \"\");\n const badMessage = qualityError(message);\n if (badMessage) throw new Error(badMessage);\n return { message, model: json.model ?? model };\n}\n\nasync function callDeepSeekWithRetry(userMessage: string, decision: AgentDecision): Promise<{ message: string; model: string }> {\n try {\n return await callDeepSeek([\n { role: \"system\", content: systemPrompt(decision.role) },\n { role: \"user\", content: userPrompt(userMessage, decision) }\n ]);\n } catch (error) {\n if (\n !(error instanceof Error) ||\n (!error.message.includes(\"empty message\") &&\n !error.message.includes(\"clipped message\") &&\n !error.message.includes(\"incomplete message\"))\n ) {\n throw error;\n }\n return callDeepSeek([\n { role: \"system\", content: systemPrompt(decision.role) },\n { role: \"user\", content: fallbackRewritePrompt(userMessage, decision) }\n ]);\n }\n}\n\nexport async function enhanceWithDeepSeek(userMessage: string, decision: AgentDecision): Promise<AgentDecision> {\n if (!process.env.DEEPSEEK_API_KEY) {\n return { ...decision, provider: \"deterministic\" };\n }\n\n try {\n const result = await callDeepSeekWithRetry(userMessage, decision);\n return {\n ...decision,\n message: result.message,\n provider: \"deepseek\",\n model: result.model\n };\n } catch (error) {\n return {\n ...decision,\n provider: \"deterministic\",\n providerError: error instanceof Error ? error.message : String(error)\n };\n }\n}\n"}, "8e40a0c60ab890883d614cce8d530f997c2509b2": {"t": 1785754353.2183235, "n": 5697, "h": "56b7977d296dd675", "c": "import type { AgentDecision } from \"./types\";\n\ninterface DeepSeekMessage {\n role: \"system\" | \"user\";\n content: string;\n}\n\ninterface DeepSeekResponse {\n choices?: Array<{\n message?: {\n content?: string | null;\n reasoning_content?: string | null;\n };\n }>;\n model?: string;\n error?: {\n message?: string;\n };\n}\n\nconst DEFAULT_BASE_URL = \"https://api.deepseek.com\";\nconst DEFAULT_MODEL = \"deepseek-v4-flash\";\n\nfunction trimSlash(value: string): string {\n return value.replace(/\\/+$/, \"\");\n}\n\nfunction systemPrompt(role: AgentDecision[\"role\"]): string {\n const shared =\n \"You are an agentic brewery copilot. Use the supplied tool plan and observations as ground truth. Do not invent tool results. Keep the answer concise, operational, and action-oriented. Never claim you already performed a pending action unless the supplied decision says it was confirmed. Return plain text only: no Markdown, no bullets, no bold markers, no headings.\";\n if (role === \"support\") {\n return `${shared} You are customer-safe: do not expose internal alarms, QA task ids, workflow internals, or sensor failure details.`;\n }\n return `${shared} You are operator-facing: you may discuss alarms, telemetry, QA tasks, and workflow signals.`;\n}\n\nfunction userPrompt(userMessage: string, decision: AgentDecision): string {\n return [\n `User asked: ${userMessage}`,\n `Local copilot response: ${decision.message}`,\n `Plan: ${decision.plan.join(\" | \") || \"none\"}`,\n `Tools used: ${decision.toolsUsed.join(\", \") || \"none\"}`,\n `Observations: ${decision.observations.join(\" | \") || \"none\"}`,\n decision.pendingAction\n ? `Pending action proposed: ${decision.pendingAction.type}. Mention that operator confirmation is required.`\n : \"Pending action proposed: none.\",\n \"Rewrite the local copilot response so it feels like a proactive agent. Return one short plain-text paragraph only, at least 12 words, with a complete final sentence.\"\n ].join(\"\\n\");\n}\n\nfunction fallbackRewritePrompt(userMessage: string, decision: AgentDecision): string {\n return [\n `User asked: ${userMessage}`,\n `Ground truth answer: ${decision.message}`,\n decision.pendingAction\n ? `The answer must say operator confirmation is required for ${decision.pendingAction.type}.`\n : \"No action is pending.\",\n \"Write one concise plain-text sentence of at least 12 words. Do not use Markdown.\"\n ].join(\"\\n\");\n}\n\nfunction cleanProviderMessage(value: string): string {\n const cleaned = value\n .replace(/\\*\\*(.*?)\\*\\*/g, \"$1\")\n .replace(/__(.*?)__/g, \"$1\")\n .replace(/`([^`]+)`/g, \"$1\")\n .replace(/^#{1,6}\\s+/gm, \"\")\n .replace(/^\\s*[-*]\\s+/gm, \"\")\n .replace(/\\s+\\n/g, \"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .replace(/^[\"']|[\"']$/g, \"\")\n .trim();\n if (!cleaned) return cleaned;\n return /[.!?]$/.test(cleaned) ? cleaned : `${cleaned}.`;\n}\n\nfunction qualityError(message: string): string | null {\n if (!message) return \"DeepSeek returned an empty message\";\n const words = message.split(/\\s+/).filter(Boolean);\n if (words.length < 8) return \"DeepSeek returned a clipped message\";\n if (/\\b(and|or|the|to|for|with|of|a|an|is|are)\\.$/i.test(message)) {\n return \"DeepSeek returned an incomplete message\";\n }\n return null;\n}\n\nasync function callDeepSeek(messages: DeepSeekMessage[]): Promise<{ message: string; model: string }> {\n const apiKey = process.env.DEEPSEEK_API_KEY;\n if (!apiKey) throw new Error(\"DEEPSEEK_API_KEY is not set\");\n\n const baseUrl = trimSlash(process.env.DEEPSEEK_BASE_URL || DEFAULT_BASE_URL);\n const model = process.env.DEEPSEEK_MODEL || DEFAULT_MODEL;\n const response = await fetch(`${baseUrl}/chat/completions`, {\n method: \"POST\",\n headers: {\n authorization: `Bearer ${apiKey}`,\n \"content-type\": \"application/json\"\n },\n body: JSON.stringify({\n model,\n messages,\n temperature: 0.1,\n max_tokens: 280\n })\n });\n const json = (await response.json().catch(() => ({}))) as DeepSeekResponse;\n if (!response.ok) {\n throw new Error(json.error?.message ?? `DeepSeek request failed with ${response.status}`);\n }\n const message = cleanProviderMessage(json.choices?.[0]?.message?.content?.trim() ?? \"\");\n const badMessage = qualityError(message);\n if (badMessage) throw new Error(badMessage);\n return { message, model: json.model ?? model };\n}\n\nasync function callDeepSeekWithRetry(userMessage: string, decision: AgentDecision): Promise<{ message: string; model: string }> {\n try {\n return await callDeepSeek([\n { role: \"system\", content: systemPrompt(decision.role) },\n { role: \"user\", content: userPrompt(userMessage, decision) }\n ]);\n } catch (error) {\n if (\n !(error instanceof Error) ||\n (!error.message.includes(\"empty message\") &&\n !error.message.includes(\"clipped message\") &&\n !error.message.includes(\"incomplete message\"))\n ) {\n throw error;\n }\n return callDeepSeek([\n { role: \"system\", content: systemPrompt(decision.role) },\n { role: \"user\", content: fallbackRewritePrompt(userMessage, decision) }\n ]);\n }\n}\n\nexport async function enhanceWithDeepSeek(userMessage: string, decision: AgentDecision): Promise<AgentDecision> {\n if (!process.env.DEEPSEEK_API_KEY) {\n return { ...decision, provider: \"deterministic\" };\n }\n\n try {\n const result = await callDeepSeekWithRetry(userMessage, decision);\n return {\n ...decision,\n message: result.message,\n provider: \"deepseek\",\n model: result.model\n };\n } catch (error) {\n return {\n ...decision,\n provider: \"deterministic\",\n providerError: error instanceof Error ? error.message : String(error)\n };\n }\n}\n"}}}} |