Repository navigation
Expand file tree
/
Copy pathdeepseek.ts
More file actions
105 lines (88 loc) · 3.07 KB
/
Copy pathdeepseek.ts
File metadata and controls
105 lines (88 loc) · 3.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import type { ModelRecord } from "@roo-code/types"
import { deepSeekModels, DEEP_SEEK_DEFAULT_TEMPERATURE } from "@roo-code/types"
import { DEFAULT_HEADERS } from "../constants"
import { throwIfAborted } from "../utils/abort-signal"
/**
* Fetches available models from the DeepSeek API and merges them with known specs.
*
* The DeepSeek /models endpoint only returns basic model IDs without pricing
* or context window info, so we merge the API response with the static
* `deepSeekModels` map for known models. Unknown models get sensible defaults.
*/
export async function getDeepSeekModels(
baseUrl?: string,
apiKey?: string,
opts?: { signal?: AbortSignal },
): Promise<ModelRecord> {
const normalizedBase = (baseUrl || "https://api.deepseek.com").replace(/\/?v1\/?$/, "")
const url = `${normalizedBase}/models`
const allowModelListFallback = process.env.E2E_MOCK_MODEL_LIST_FALLBACK === "true"
const headers: Record<string, string> = {
"Content-Type": "application/json",
...DEFAULT_HEADERS,
}
if (apiKey) {
headers["Authorization"] = `Bearer ${apiKey}`
}
const response = await fetch(url, {
headers,
signal: opts?.signal,
})
if (!response.ok) {
// An aborted request must reject instead of taking the static-models
// fallback below: that would present a cancelled fetch to callers as a
// usable catalog.
throwIfAborted(opts?.signal)
let errorBody = ""
try {
errorBody = await response.text()
} catch {
errorBody = "(unable to read response body)"
}
console.error(`[getDeepSeekModels] HTTP error:`, {
status: response.status,
statusText: response.statusText,
url,
body: errorBody,
})
// In mocked e2e environments, /models may be intentionally unimplemented.
// Allow an explicit test-only fallback to static DeepSeek model metadata.
if (allowModelListFallback && response.status === 404) {
const models: ModelRecord = Object.create(null)
for (const [modelId, modelInfo] of Object.entries(deepSeekModels)) {
models[modelId] = { ...modelInfo }
}
return models
}
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
}
const data = await response.json()
if (!data?.data || !Array.isArray(data.data)) {
console.error("[getDeepSeekModels] Unexpected response format:", data)
throw new Error("Failed to fetch DeepSeek models: Unexpected response format.")
}
// Use null-prototype object to prevent prototype pollution
const models: ModelRecord = Object.create(null)
for (const model of data.data) {
const modelId = typeof model.id === "string" && model.id ? model.id : null
if (!modelId) continue
const knownSpecs = deepSeekModels[modelId as keyof typeof deepSeekModels]
if (knownSpecs) {
models[modelId] = { ...knownSpecs }
} else {
models[modelId] = {
maxTokens: 8192,
contextWindow: 128_000,
supportsImages: false,
supportsPromptCache: true,
inputPrice: 0.28,
outputPrice: 0.42,
cacheWritesPrice: 0.28,
cacheReadsPrice: 0.028,
defaultTemperature: DEEP_SEEK_DEFAULT_TEMPERATURE,
description: `DeepSeek model: ${modelId}`,
}
}
}
return models
}