cortico-provider-deepseek 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +32 -0
- package/console/client.ts +9 -0
- package/console/pricing-panel.ts +130 -0
- package/console/schedule-panel.ts +97 -0
- package/console/server.ts +63 -0
- package/dist/console.js +1234 -0
- package/index.ts +60 -0
- package/package.json +15 -0
- package/pricing.ts +92 -0
package/index.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { ProviderModule } from 'cortico/providers/base.ts';
|
|
2
|
+
import type { ReasoningTier } from 'cortico/core/types.ts';
|
|
3
|
+
import type { Language } from 'cortico/core/language.ts';
|
|
4
|
+
import responsesCompat from 'cortico/providers/openai-responses-compat/index.ts';
|
|
5
|
+
import { pricesForSchedule } from './pricing.ts';
|
|
6
|
+
import { deepseekPricingConsole } from './console/server.ts';
|
|
7
|
+
|
|
8
|
+
const DEFAULT_MODEL = 'deepseek-flash';
|
|
9
|
+
const LEGACY_EFFORTS: Record<string, string> = {
|
|
10
|
+
minimal: 'low', medium: 'high', xhigh: 'high', ultra: 'max',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const reasoningTiers = (language: Language): ReasoningTier[] => {
|
|
14
|
+
const labels = language === 'zh'
|
|
15
|
+
? { high: '高', none: '关闭', low: '低', max: '最大' }
|
|
16
|
+
: { high: 'High', none: 'Off', low: 'Low', max: 'Max' };
|
|
17
|
+
return [
|
|
18
|
+
{ id: 'high', label: labels.high, thinking: true, effort: 'high' },
|
|
19
|
+
{ id: 'none', label: labels.none, thinking: false },
|
|
20
|
+
{ id: 'low', label: labels.low, thinking: true, effort: 'low' },
|
|
21
|
+
{ id: 'max', label: labels.max, thinking: true, effort: 'max' },
|
|
22
|
+
];
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const DEEPSEEK_PROVIDER: ProviderModule = {
|
|
26
|
+
...responsesCompat,
|
|
27
|
+
id: 'deepseek',
|
|
28
|
+
title: 'DeepSeek',
|
|
29
|
+
description: 'DeepSeek Responses API.',
|
|
30
|
+
defaultBaseUrl: 'https://api.deepseek.com',
|
|
31
|
+
baseUrlSuggestions: ['https://api.deepseek.com'],
|
|
32
|
+
console: deepseekPricingConsole,
|
|
33
|
+
reasoningTiers: reasoningTiers('zh'),
|
|
34
|
+
localize: (language) => ({
|
|
35
|
+
...responsesCompat.localize?.(language),
|
|
36
|
+
reasoningTiers: reasoningTiers(language),
|
|
37
|
+
}),
|
|
38
|
+
normalize(entry) {
|
|
39
|
+
const normalized = responsesCompat.normalize?.(entry) ?? structuredClone(entry);
|
|
40
|
+
if (!normalized.spec) return normalized;
|
|
41
|
+
const spec = { ...normalized.spec };
|
|
42
|
+
if (typeof spec.model !== 'string' || !spec.model.trim()) spec.model = DEFAULT_MODEL;
|
|
43
|
+
if (spec.thinking) {
|
|
44
|
+
const effort = spec.reasoningEffort?.trim();
|
|
45
|
+
if (effort === 'none') {
|
|
46
|
+
spec.thinking = false;
|
|
47
|
+
delete spec.reasoningEffort;
|
|
48
|
+
} else {
|
|
49
|
+
spec.reasoningEffort = effort ? LEGACY_EFFORTS[effort] ?? effort : 'high';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return { ...normalized, spec };
|
|
53
|
+
},
|
|
54
|
+
prices: (entry) => [pricesForSchedule(entry.options?.deepseekPricingSchedule)],
|
|
55
|
+
validateEntry(entry) {
|
|
56
|
+
pricesForSchedule(entry.options?.deepseekPricingSchedule);
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export default DEEPSEEK_PROVIDER;
|
package/package.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cortico-provider-deepseek",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Cortico 的 DeepSeek Responses API 模型供应商扩展。",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"engines": { "node": ">=22" },
|
|
8
|
+
"main": "./index.ts",
|
|
9
|
+
"keywords": ["cortico-provider"],
|
|
10
|
+
"files": ["index.ts", "pricing.ts", "console", "dist", "README.md", "LICENSE"],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build:console": "esbuild console/client.ts --bundle --format=esm --outfile=dist/console.js"
|
|
13
|
+
},
|
|
14
|
+
"cortico": { "kind": "provider", "api": 5, "consoleClient": "dist/console.js" }
|
|
15
|
+
}
|
package/pricing.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { PriceDefinition } from 'cortico/providers/pricebook.ts';
|
|
2
|
+
|
|
3
|
+
export interface DeepSeekPricingSchedule {
|
|
4
|
+
windows: Array<{ from: string; to: string }>;
|
|
5
|
+
exceptDates: string[];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const defaultPricingSchedule: DeepSeekPricingSchedule = {
|
|
9
|
+
windows: [
|
|
10
|
+
{ from: '09:00', to: '12:00' },
|
|
11
|
+
{ from: '14:00', to: '18:00' },
|
|
12
|
+
],
|
|
13
|
+
exceptDates: [
|
|
14
|
+
'2026-01-01', '2026-01-02', '2026-01-03',
|
|
15
|
+
'2026-02-15', '2026-02-16', '2026-02-17', '2026-02-18', '2026-02-19', '2026-02-20', '2026-02-21', '2026-02-22', '2026-02-23',
|
|
16
|
+
'2026-04-04', '2026-04-05', '2026-04-06',
|
|
17
|
+
'2026-05-01', '2026-05-02', '2026-05-03', '2026-05-04', '2026-05-05',
|
|
18
|
+
'2026-06-19', '2026-06-20', '2026-06-21',
|
|
19
|
+
'2026-09-25', '2026-09-26', '2026-09-27',
|
|
20
|
+
'2026-10-01', '2026-10-02', '2026-10-03', '2026-10-04', '2026-10-05', '2026-10-06', '2026-10-07',
|
|
21
|
+
],
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const TIME = /^(?:[01]\d|2[0-3]):[0-5]\d$/;
|
|
25
|
+
const DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
26
|
+
|
|
27
|
+
export function parsePricingSchedule(value: unknown): DeepSeekPricingSchedule {
|
|
28
|
+
if (value === undefined || value === '') return structuredClone(defaultPricingSchedule);
|
|
29
|
+
let parsed: unknown;
|
|
30
|
+
try {
|
|
31
|
+
parsed = typeof value === 'string' ? JSON.parse(value) : value;
|
|
32
|
+
} catch {
|
|
33
|
+
throw new Error('Invalid DeepSeek pricing schedule JSON');
|
|
34
|
+
}
|
|
35
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error('Invalid DeepSeek pricing schedule');
|
|
36
|
+
const candidate = parsed as Partial<DeepSeekPricingSchedule>;
|
|
37
|
+
if (!Array.isArray(candidate.windows) || candidate.windows.length !== 2
|
|
38
|
+
|| candidate.windows.some((window) => !window || !TIME.test(window.from) || !TIME.test(window.to) || window.from >= window.to)
|
|
39
|
+
|| candidate.windows[0].to > candidate.windows[1].from) {
|
|
40
|
+
throw new Error('Peak windows must be two ordered, non-overlapping HH:MM ranges');
|
|
41
|
+
}
|
|
42
|
+
if (!Array.isArray(candidate.exceptDates) || candidate.exceptDates.some((date) => {
|
|
43
|
+
if (typeof date !== 'string' || !DATE.test(date)) return true;
|
|
44
|
+
const value = new Date(`${date}T00:00:00.000Z`);
|
|
45
|
+
return !Number.isFinite(value.valueOf()) || value.toISOString().slice(0, 10) !== date;
|
|
46
|
+
})) throw new Error('Exception dates must use YYYY-MM-DD');
|
|
47
|
+
return {
|
|
48
|
+
windows: candidate.windows.map(({ from, to }) => ({ from, to })),
|
|
49
|
+
exceptDates: [...new Set(candidate.exceptDates)].sort(),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function currentPricingBand(scheduleValue: unknown, at: Date): 'peak' | 'offPeak' {
|
|
54
|
+
const schedule = parsePricingSchedule(scheduleValue);
|
|
55
|
+
const parts = new Intl.DateTimeFormat('en-GB', {
|
|
56
|
+
timeZone: 'Asia/Shanghai', weekday: 'short', year: 'numeric', month: '2-digit', day: '2-digit',
|
|
57
|
+
hour: '2-digit', minute: '2-digit', hourCycle: 'h23',
|
|
58
|
+
}).formatToParts(at);
|
|
59
|
+
const part = (type: Intl.DateTimeFormatPartTypes) => parts.find((item) => item.type === type)?.value ?? '';
|
|
60
|
+
const date = `${part('year')}-${part('month')}-${part('day')}`;
|
|
61
|
+
const weekday = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'].indexOf(part('weekday')) + 1;
|
|
62
|
+
const time = `${part('hour')}:${part('minute')}`;
|
|
63
|
+
if (weekday < 1 || weekday > 5 || schedule.exceptDates.includes(date)) return 'offPeak';
|
|
64
|
+
return schedule.windows.some(({ from, to }) => from <= time && time < to) ? 'peak' : 'offPeak';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const IDLE_RULES: PriceDefinition['rules'] = [
|
|
68
|
+
{ meter: 'cachedInput', perMillion: 0.02 },
|
|
69
|
+
{ meter: 'uncachedInput', perMillion: 1 },
|
|
70
|
+
{ meter: 'output', perMillion: 4 },
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
const PEAK_RULES: PriceDefinition['rules'] = [
|
|
74
|
+
{ meter: 'cachedInput', perMillion: 0.04 },
|
|
75
|
+
{ meter: 'uncachedInput', perMillion: 2 },
|
|
76
|
+
{ meter: 'output', perMillion: 8 },
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
export function pricesForSchedule(scheduleValue: unknown): PriceDefinition {
|
|
80
|
+
const schedule = parsePricingSchedule(scheduleValue);
|
|
81
|
+
return {
|
|
82
|
+
models: ['deepseek-flash'],
|
|
83
|
+
currency: 'RMB',
|
|
84
|
+
basis: 'marginal',
|
|
85
|
+
rules: IDLE_RULES,
|
|
86
|
+
source: 'DeepSeek official pricing',
|
|
87
|
+
timeWindows: schedule.windows.map(({ from, to }) => ({
|
|
88
|
+
from, to, timezone: 'Asia/Shanghai', weekdays: [1, 2, 3, 4, 5],
|
|
89
|
+
exceptDates: schedule.exceptDates, rules: PEAK_RULES,
|
|
90
|
+
})),
|
|
91
|
+
};
|
|
92
|
+
}
|