const DEFAULT_TIMEOUT_MS = 45_000; function completionEndpoint(apiUrl) { const value = String(apiUrl || "").replace(/\/+$/, ""); if (/\/chat\/completions$/i.test(value)) return value; return `${value}/chat/completions`; } function extractJson(text) { const value = String(text || "").trim(); if (!value) throw new Error("model_response_empty"); try { return JSON.parse(value); } catch { const fenced = value.match(/```(?:json)?\s*([\s\S]*?)```/i); if (fenced) return JSON.parse(fenced[1]); const first = value.indexOf("{"); const last = value.lastIndexOf("}"); if (first >= 0 && last > first) return JSON.parse(value.slice(first, last + 1)); throw new Error("model_response_not_json"); } } export class DeepSeekJsonClient { constructor({ apiKey = process.env.DEEPSEEK_API_KEY, apiUrl = process.env.DEEPSEEK_API_URL || "https://api.deepseek.com/v1", model = process.env.DEEPSEEK_MODEL || "deepseek-chat", fetchImpl = globalThis.fetch, timeoutMs = DEFAULT_TIMEOUT_MS, } = {}) { if (!apiKey) throw new Error("deepseek_api_key_missing"); if (!fetchImpl) throw new Error("fetch_unavailable"); this.apiKey = apiKey; this.apiUrl = String(apiUrl).replace(/\/+$/, ""); this.model = model; this.fetchImpl = fetchImpl; this.timeoutMs = timeoutMs; } async generate({ role, instruction, input, validatorError = null }) { const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), this.timeoutMs); try { const response = await this.fetchImpl(completionEndpoint(this.apiUrl), { method: "POST", headers: { authorization: `Bearer ${this.apiKey}`, "content-type": "application/json", }, body: JSON.stringify({ model: this.model, stream: false, temperature: role === "controller_witness" ? 0.1 : 0.2, response_format: { type: "json_object" }, messages: [ { role: "system", content: "你是光湖语言世界中的有界模型运行位。只输出一个JSON对象,不输出思维过程、Markdown或额外说明。不得伪造现实成功、权限、冰朔的新意志或未提供的事实。", }, { role: "user", content: JSON.stringify({ role, instruction, validator_error_from_previous_attempt: validatorError, runtime_input: input, }), }, ], }), signal: controller.signal, }); if (!response.ok) { throw new Error(`model_http_${response.status}`); } const payload = await response.json(); const text = payload?.choices?.[0]?.message?.content; return extractJson(text); } finally { clearTimeout(timer); } } } export { completionEndpoint, extractJson };