11import { Agent } from 'undici'
22
3- import { deepseekModels } from '@codebuff/common/constants/model-config'
43import { PROFIT_MARGIN } from '@codebuff/common/constants/limits'
54import { getErrorObject } from '@codebuff/common/util/error'
65import { env } from '@codebuff/internal/env'
@@ -10,6 +9,10 @@ import {
109 extractRequestMetadata ,
1110 insertMessageToBigQuery ,
1211} from './helpers'
12+ import {
13+ buildDeepSeekRequestBody ,
14+ DEEPSEEK_MODEL_IDS ,
15+ } from './deepseek-request-body'
1316
1417import type { UsageData } from './helpers'
1518import type { InsertMessageBigqueryFn } from '@codebuff/common/types/contracts/bigquery'
@@ -40,32 +43,25 @@ const DEEPSEEK_V4_PRO_PRICING: DeepSeekPricing = {
4043 outputCostPerToken : 0.87 / 1_000_000 ,
4144}
4245
43- /** Single source of truth for DeepSeek model metadata and pricing.
44- * Kept as one map so adding a model can't drift between routing and billing. */
4546const DEEPSEEK_MODELS : Record <
4647 string ,
4748 { deepseekId : string ; pricing : DeepSeekPricing }
48- > = {
49- [ deepseekModels . deepseekV4ProDirect ] : {
50- deepseekId : deepseekModels . deepseekV4ProDirect ,
51- pricing : DEEPSEEK_V4_PRO_PRICING ,
52- } ,
53- [ deepseekModels . deepseekV4Pro ] : {
54- deepseekId : deepseekModels . deepseekV4ProDirect ,
55- pricing : DEEPSEEK_V4_PRO_PRICING ,
56- } ,
57- }
49+ > = Object . fromEntries (
50+ Object . entries ( DEEPSEEK_MODEL_IDS ) . map ( ( [ model , deepseekId ] ) => [
51+ model ,
52+ {
53+ deepseekId,
54+ pricing : DEEPSEEK_V4_PRO_PRICING ,
55+ } ,
56+ ] ) ,
57+ )
5858
5959const DEEPSEEK_ROUTED_MODELS = new Set < string > ( Object . keys ( DEEPSEEK_MODELS ) )
6060
6161export function isDeepSeekModel ( model : string ) : boolean {
6262 return DEEPSEEK_ROUTED_MODELS . has ( model )
6363}
6464
65- function getDeepSeekModelId ( openrouterModel : string ) : string {
66- return DEEPSEEK_MODELS [ openrouterModel ] ?. deepseekId ?? openrouterModel
67- }
68-
6965function getDeepSeekPricing ( model : string ) : DeepSeekPricing {
7066 const entry = DEEPSEEK_MODELS [ model ]
7167 if ( ! entry ) {
@@ -87,52 +83,13 @@ type LineResult = {
8783 patchedLine : string
8884}
8985
90- function toDeepSeekReasoningEffort ( effort : unknown ) : 'high' | 'max' {
91- return effort === 'max' || effort === 'xhigh' ? 'max' : 'high'
92- }
93-
94- function createDeepSeekRequest ( params : {
86+ export function createDeepSeekRequest ( params : {
9587 body : ChatCompletionRequestBody
9688 originalModel : string
9789 fetch : typeof globalThis . fetch
9890} ) {
9991 const { body, originalModel, fetch } = params
100- const deepseekBody : Record < string , unknown > = {
101- ...body ,
102- model : getDeepSeekModelId ( originalModel ) ,
103- }
104-
105- // DeepSeek uses `thinking` instead of OpenRouter's `reasoning`.
106- if ( deepseekBody . reasoning && typeof deepseekBody . reasoning === 'object' ) {
107- const reasoning = deepseekBody . reasoning as {
108- enabled ?: boolean
109- effort ?: 'high' | 'medium' | 'low'
110- }
111- deepseekBody . thinking = {
112- type : reasoning . enabled === false ? 'disabled' : 'enabled' ,
113- reasoning_effort : toDeepSeekReasoningEffort ( reasoning . effort ) ,
114- }
115- } else if ( deepseekBody . reasoning_effort ) {
116- deepseekBody . thinking = {
117- type : 'enabled' ,
118- reasoning_effort : toDeepSeekReasoningEffort (
119- deepseekBody . reasoning_effort ,
120- ) ,
121- }
122- }
123- delete deepseekBody . reasoning
124- delete deepseekBody . reasoning_effort
125-
126- // Strip OpenRouter-specific / internal fields
127- delete deepseekBody . provider
128- delete deepseekBody . transforms
129- delete deepseekBody . codebuff_metadata
130- delete deepseekBody . usage
131-
132- // For streaming, request usage in the final chunk
133- if ( deepseekBody . stream ) {
134- deepseekBody . stream_options = { include_usage : true }
135- }
92+ const deepseekBody = buildDeepSeekRequestBody ( body , originalModel )
13693
13794 if ( ! env . DEEPSEEK_API_KEY ) {
13895 throw new Error ( 'DEEPSEEK_API_KEY is not configured' )
0 commit comments