|
|
import { Request, RequestHandler, Router } from "express"; |
|
|
import { createPreprocessorMiddleware } from "./middleware/request"; |
|
|
import { ipLimiter } from "./rate-limit"; |
|
|
import { createQueuedProxyMiddleware } from "./middleware/request/proxy-middleware-factory"; |
|
|
import { addKey, finalizeBody } from "./middleware/request"; |
|
|
import { ProxyResHandlerWithBody } from "./middleware/response"; |
|
|
import axios from "axios"; |
|
|
import { DeepseekKey, keyPool } from "../shared/key-management"; |
|
|
|
|
|
let modelsCache: any = null; |
|
|
let modelsCacheTime = 0; |
|
|
|
|
|
const deepseekResponseHandler: ProxyResHandlerWithBody = async ( |
|
|
_proxyRes, |
|
|
req, |
|
|
res, |
|
|
body |
|
|
) => { |
|
|
if (typeof body !== "object") { |
|
|
throw new Error("Expected body to be an object"); |
|
|
} |
|
|
|
|
|
let newBody = body; |
|
|
|
|
|
res.status(200).json({ ...newBody, proxy: body.proxy }); |
|
|
}; |
|
|
|
|
|
const getModelsResponse = async () => { |
|
|
|
|
|
if (new Date().getTime() - modelsCacheTime < 1000 * 60) { |
|
|
return modelsCache; |
|
|
} |
|
|
|
|
|
try { |
|
|
|
|
|
const modelToUse = "deepseek-chat"; |
|
|
const deepseekKey = keyPool.get(modelToUse, "deepseek") as DeepseekKey; |
|
|
|
|
|
if (!deepseekKey || !deepseekKey.key) { |
|
|
throw new Error("Failed to get valid Deepseek key"); |
|
|
} |
|
|
|
|
|
|
|
|
const response = await axios.get("https://api.deepseek.com/models", { |
|
|
headers: { |
|
|
"Content-Type": "application/json", |
|
|
"Authorization": `Bearer ${deepseekKey.key}` |
|
|
}, |
|
|
}); |
|
|
|
|
|
|
|
|
if (response.data && response.data.data) { |
|
|
modelsCache = { |
|
|
object: "list", |
|
|
data: response.data.data.map((model: any) => ({ |
|
|
id: model.id, |
|
|
object: "model", |
|
|
owned_by: "deepseek", |
|
|
})), |
|
|
}; |
|
|
} else { |
|
|
throw new Error("Unexpected response format from Deepseek API"); |
|
|
} |
|
|
} catch (error) { |
|
|
console.error("Error fetching Deepseek models:", error); |
|
|
throw error; |
|
|
} |
|
|
|
|
|
modelsCacheTime = new Date().getTime(); |
|
|
return modelsCache; |
|
|
}; |
|
|
|
|
|
const handleModelRequest: RequestHandler = async (_req, res) => { |
|
|
try { |
|
|
const modelsResponse = await getModelsResponse(); |
|
|
res.status(200).json(modelsResponse); |
|
|
} catch (error) { |
|
|
console.error("Error in handleModelRequest:", error); |
|
|
res.status(500).json({ error: "Failed to fetch models" }); |
|
|
} |
|
|
}; |
|
|
|
|
|
const deepseekProxy = createQueuedProxyMiddleware({ |
|
|
mutations: [addKey, finalizeBody], |
|
|
target: "https://api.deepseek.com/beta", |
|
|
blockingResponseHandler: deepseekResponseHandler, |
|
|
}); |
|
|
|
|
|
const deepseekRouter = Router(); |
|
|
|
|
|
|
|
|
|
|
|
function enablePrefill(req: Request) { |
|
|
|
|
|
if (process.env.NO_DEEPSEEK_PREFILL) return |
|
|
|
|
|
const msgs = req.body.messages; |
|
|
if (msgs.at(-1)?.role !== 'assistant') return; |
|
|
|
|
|
let i = msgs.length - 1; |
|
|
let content = ''; |
|
|
|
|
|
while (i >= 0 && msgs[i].role === 'assistant') { |
|
|
|
|
|
content = msgs[i--].content + content; |
|
|
} |
|
|
|
|
|
msgs.splice(i + 1, msgs.length, { role: 'assistant', content, prefix: true }); |
|
|
} |
|
|
|
|
|
function removeReasonerStuff(req: Request) { |
|
|
if (req.body.model === "deepseek-reasoner") { |
|
|
|
|
|
delete req.body.presence_penalty; |
|
|
delete req.body.frequency_penalty; |
|
|
delete req.body.temperature; |
|
|
delete req.body.top_p; |
|
|
delete req.body.logprobs; |
|
|
delete req.body.top_logprobs; |
|
|
} |
|
|
} |
|
|
|
|
|
deepseekRouter.post( |
|
|
"/v1/chat/completions", |
|
|
ipLimiter, |
|
|
createPreprocessorMiddleware( |
|
|
{ inApi: "openai", outApi: "openai", service: "deepseek" }, |
|
|
{ afterTransform: [ enablePrefill, removeReasonerStuff ] } |
|
|
), |
|
|
deepseekProxy |
|
|
); |
|
|
|
|
|
deepseekRouter.get("/v1/models", handleModelRequest); |
|
|
|
|
|
export const deepseek = deepseekRouter; |