feat(agents): add configurable DeepSeek Harness model catalog
- Separate default model selection from catalog configuration - Use API metadata with per-model and provider overrides - Expose documented capability, reasoning, protocol, and request settings - Preserve customizations with conflict detection and transactional rollback
This commit is contained in:
1 parent
145b585c3f
commit
4d8d549e04
28 files changed
+2047
-20
No files matched your search
@@ -0,0 +1,95 @@
|
||||
import schemaSource from './deepSeekHarnessSchema.json';
|
||||
|
||||
export type HarnessProfile = Record<string, unknown>;
|
||||
export type HarnessDraft = Record<string, string>;
|
||||
export type HarnessField = {
|
||||
name: string;
|
||||
kind: string;
|
||||
group?: string;
|
||||
values?: string[];
|
||||
apis?: string[];
|
||||
default?: unknown;
|
||||
example?: unknown;
|
||||
min?: number;
|
||||
max?: number;
|
||||
exclusiveMin?: number;
|
||||
};
|
||||
export const harnessSchema = schemaSource as unknown as Record<string, HarnessField[]>;
|
||||
export const harnessReasoningLevels = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
|
||||
export type HarnessEditorModel = { id: string; defaults: HarnessProfile; configuration: HarnessProfile };
|
||||
export type HarnessEditorSnapshot = {
|
||||
revision: string;
|
||||
models: HarnessEditorModel[];
|
||||
provider: HarnessProfile;
|
||||
baseUrl: string;
|
||||
defaultModel: string | null;
|
||||
configured: boolean;
|
||||
};
|
||||
export const isHarnessRecord = (value: unknown): value is HarnessProfile =>
|
||||
typeof value === 'object' && value !== null && !Array.isArray(value);
|
||||
|
||||
export function harnessDraft(profile: HarnessProfile, group: string, prefix = ''): HarnessDraft {
|
||||
return Object.fromEntries(harnessSchema[group].flatMap(field => {
|
||||
const value = profile[field.name];
|
||||
const key = `${prefix}${field.name}`;
|
||||
if (value === undefined) return [];
|
||||
if (field.group && isHarnessRecord(value)) return Object.entries(harnessDraft(value, field.group, `${key}.`));
|
||||
return [[key, ['string', 'enum'].includes(field.kind) ? String(value) : JSON.stringify(value)]];
|
||||
}));
|
||||
}
|
||||
|
||||
export function updateHarnessDraft(draft: HarnessDraft, key: string, value: string): HarnessDraft {
|
||||
const next = { ...draft };
|
||||
if (!value.length) delete next[key];
|
||||
else return { ...next, [key]: value };
|
||||
return next;
|
||||
}
|
||||
|
||||
export function parseHarnessDraft(draft: HarnessDraft, group: string, api: string, prefix = ''): HarnessProfile {
|
||||
const profile: HarnessProfile = {};
|
||||
for (const field of harnessSchema[group]) {
|
||||
const key = `${prefix}${field.name}`;
|
||||
if (field.group) {
|
||||
const nested = parseHarnessDraft(draft, field.group, api, `${key}.`);
|
||||
if (Object.keys(nested).length) profile[field.name] = nested;
|
||||
continue;
|
||||
}
|
||||
if (!Object.prototype.hasOwnProperty.call(draft, key) || !draft[key].trim()) continue;
|
||||
let value: unknown;
|
||||
try {
|
||||
value = ['string', 'enum'].includes(field.kind) ? draft[key].trim() : JSON.parse(draft[key]);
|
||||
} catch { throw new Error(`${key}: JSON`); }
|
||||
if (field.apis && !field.apis.includes(api)) throw new Error(`${key}: ${api}`);
|
||||
let valid = true;
|
||||
if (['integer', 'number'].includes(field.kind)) valid = typeof value === 'number' && Number.isFinite(value)
|
||||
&& Math.abs(value) <= Number.MAX_SAFE_INTEGER
|
||||
&& (field.kind !== 'integer' || Number.isInteger(value))
|
||||
&& (field.min === undefined || value >= field.min)
|
||||
&& (field.max === undefined || value <= field.max)
|
||||
&& (field.exclusiveMin === undefined || value > field.exclusiveMin);
|
||||
if (field.kind === 'enum') valid = field.values!.includes(value as string);
|
||||
if (field.kind === 'boolean') valid = typeof value === 'boolean';
|
||||
if (['modalities', 'strings'].includes(field.kind)) valid = Array.isArray(value) && (value.length > 0 || field.name === 'input')
|
||||
&& value.every(v => typeof v === 'string' && v.trim() && (field.kind !== 'modalities' || ['text', 'image'].includes(v)))
|
||||
&& new Set(value).size === value.length;
|
||||
if (field.kind === 'reasoning') valid = value === false || (isHarnessRecord(value) && Object.keys(value).length > 0
|
||||
&& Object.entries(value).every(([level, wire]) => harnessReasoningLevels.includes(level)
|
||||
&& ((level === 'off' && wire === null) || (typeof wire === 'string' && wire.trim()))));
|
||||
if (field.kind === 'headers') valid = isHarnessRecord(value)
|
||||
&& Object.entries(value).every(([name, v]) => /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name)
|
||||
&& typeof v === 'string' && [...v].every(c => c.charCodeAt(0) <= 255) && !/[\r\n\0]/.test(v));
|
||||
if (field.kind === 'kwargs') valid = isHarnessRecord(value) && Object.values(value).every(v => !Array.isArray(v)
|
||||
&& (!isHarnessRecord(v) || (Object.keys(v).every(key => ['$var', 'omitWhenOff'].includes(key))
|
||||
&& ['thinking.enabled', 'thinking.effort', 'thinking.budget'].includes(v.$var as string)
|
||||
&& (v.omitWhenOff === undefined || typeof v.omitWhenOff === 'boolean'))));
|
||||
if (!valid) throw new Error(key);
|
||||
profile[field.name] = value;
|
||||
}
|
||||
if (group === 'retryPolicy' && Object.keys(profile).length && profile.mode === undefined) profile.mode = 'normal';
|
||||
if (group === 'backoff' && Number(profile.initialDelayMs ?? 500) > Number(profile.maxDelayMs ?? 10000)) throw new Error(`${prefix}initialDelayMs > maxDelayMs`);
|
||||
return profile;
|
||||
}
|
||||
|
||||
export function sameHarnessDraft(left: HarnessDraft, right: HarnessDraft): boolean {
|
||||
return Object.keys(left).length === Object.keys(right).length && Object.keys(left).every(key => left[key] === right[key]);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
{
|
||||
"sources": [
|
||||
"https://deepseek-harness.github.io/deepseek-harness/guide/providers",
|
||||
"https://deepseek-harness.github.io/deepseek-harness/reference/config-catalog#deepseek-ai-dsh-llm-pi-ai",
|
||||
"https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/llm/llm/src/retry-policy.ts"
|
||||
],
|
||||
"model": [
|
||||
{"name":"name","kind":"string"},
|
||||
{"name":"contextWindow","kind":"integer","min":1},
|
||||
{"name":"maxTokens","kind":"integer","min":1},
|
||||
{"name":"input","kind":"modalities"},
|
||||
{"name":"reasoningEfforts","kind":"reasoning","example":{"off":null,"low":"low","high":"high"}},
|
||||
{"name":"compat","kind":"group","group":"compat"}
|
||||
],
|
||||
"provider": [
|
||||
{"name":"displayName","kind":"string","default":"EasyCLIProxyAPI"},
|
||||
{"name":"api","kind":"enum","values":["openai-completions","openai-responses","anthropic-messages"],"default":"openai-completions"},
|
||||
{"name":"defaultContextWindow","kind":"integer","min":1,"default":262144},
|
||||
{"name":"defaultMaxTokens","kind":"integer","min":1,"default":32768},
|
||||
{"name":"defaultInput","kind":"modalities","default":["text"]},
|
||||
{"name":"reasoning","kind":"enum","values":["off","minimal","low","medium","high","xhigh","max"]},
|
||||
{"name":"thinkingBudgets","kind":"group","group":"thinkingBudgets"},
|
||||
{"name":"cacheRetention","kind":"enum","values":["none","short","long"]},
|
||||
{"name":"transport","kind":"enum","values":["sse","websocket","websocket-cached","auto"]},
|
||||
{"name":"timeoutMs","kind":"integer","min":0},
|
||||
{"name":"websocketConnectTimeoutMs","kind":"integer","min":0},
|
||||
{"name":"streamIdleTimeoutMs","kind":"number","exclusiveMin":0,"max":2147483647,"default":300000},
|
||||
{"name":"maxRequestImageBytes","kind":"integer","min":1,"default":20971520},
|
||||
{"name":"requestImagePixelBudget","kind":"integer","min":1,"default":4194304},
|
||||
{"name":"requestImageMaxBytes","kind":"integer","min":1,"default":1048576},
|
||||
{"name":"headers","kind":"headers","example":{"X-Custom-Header":"value"}},
|
||||
{"name":"retryPolicy","kind":"group","group":"retryPolicy"},
|
||||
{"name":"compat","kind":"group","group":"compat"}
|
||||
],
|
||||
"thinkingBudgets": [
|
||||
{"name":"minimal","kind":"number"},
|
||||
{"name":"low","kind":"number"},
|
||||
{"name":"medium","kind":"number"},
|
||||
{"name":"high","kind":"number"}
|
||||
],
|
||||
"retryPolicy": [
|
||||
{"name":"mode","kind":"enum","values":["normal","always"],"default":"normal","required":true},
|
||||
{"name":"maxRetries","kind":"integer","min":0,"default":5},
|
||||
{"name":"retryableCodes","kind":"strings","default":["EMPTY_RESPONSE","RATE_LIMIT","SERVER","TIMEOUT","TRANSPORT"]},
|
||||
{"name":"backoff","kind":"group","group":"backoff"}
|
||||
],
|
||||
"backoff": [
|
||||
{"name":"initialDelayMs","kind":"number","exclusiveMin":0,"max":2147483647,"default":500},
|
||||
{"name":"maxDelayMs","kind":"number","exclusiveMin":0,"max":2147483647,"default":10000},
|
||||
{"name":"jitterRatio","kind":"number","min":0,"max":1,"default":0.1}
|
||||
],
|
||||
"compat": [
|
||||
{"name":"supportsStore","kind":"boolean","apis":["openai-completions"]},
|
||||
{"name":"supportsDeveloperRole","kind":"boolean","apis":["openai-completions","openai-responses"]},
|
||||
{"name":"supportsReasoningEffort","kind":"boolean","apis":["openai-completions"]},
|
||||
{"name":"supportsUsageInStreaming","kind":"boolean","apis":["openai-completions"]},
|
||||
{"name":"supportsFinishReason","kind":"boolean","apis":["openai-completions"]},
|
||||
{"name":"maxTokensField","kind":"enum","values":["max_completion_tokens","max_tokens"],"apis":["openai-completions"]},
|
||||
{"name":"requiresToolResultName","kind":"boolean","apis":["openai-completions"]},
|
||||
{"name":"requiresAssistantAfterToolResult","kind":"boolean","apis":["openai-completions"]},
|
||||
{"name":"requiresThinkingAsText","kind":"boolean","apis":["openai-completions"]},
|
||||
{"name":"requiresReasoningContentOnAssistantMessages","kind":"boolean","apis":["openai-completions"]},
|
||||
{"name":"thinkingFormat","kind":"enum","values":["openai","deepseek","openrouter","together","baseten","zai","qwen","chat-template","qwen-chat-template","string-thinking","ant-ling"],"apis":["openai-completions"]},
|
||||
{"name":"chatTemplateKwargs","kind":"kwargs","example":{"enable_thinking":{"$var":"thinking.enabled"}},"apis":["openai-completions"]},
|
||||
{"name":"chatTemplateArgs","kind":"kwargs","example":{"reasoning_effort":{"$var":"thinking.effort","omitWhenOff":true}},"apis":["openai-completions"]},
|
||||
{"name":"supportsThinkingTokenBudget","kind":"boolean","apis":["openai-completions"]},
|
||||
{"name":"thinkingTokenBudgetField","kind":"enum","values":["thinking_token_budget","thinking_budget","thinking_budget_tokens"],"apis":["openai-completions"]},
|
||||
{"name":"vllmPriority","kind":"integer","apis":["openai-completions"]},
|
||||
{"name":"supportsMaxOutputTokens","kind":"boolean","apis":["openai-responses"]},
|
||||
{"name":"supportsStrictMode","kind":"boolean","apis":["openai-completions","openai-responses"]},
|
||||
{"name":"cacheControlFormat","kind":"enum","values":["anthropic"],"apis":["openai-completions"]},
|
||||
{"name":"supportsLongCacheRetention","kind":"boolean","apis":["openai-completions","openai-responses","anthropic-messages"]},
|
||||
{"name":"supportsEagerToolInputStreaming","kind":"boolean","apis":["anthropic-messages"]},
|
||||
{"name":"supportsCacheControlOnTools","kind":"boolean","apis":["anthropic-messages"]},
|
||||
{"name":"supportsTemperature","kind":"boolean","apis":["anthropic-messages"]},
|
||||
{"name":"forceAdaptiveThinking","kind":"boolean","apis":["anthropic-messages"]},
|
||||
{"name":"allowEmptySignature","kind":"boolean","apis":["anthropic-messages"]},
|
||||
{"name":"supportsStrictTools","kind":"boolean","apis":["anthropic-messages"]}
|
||||
]
|
||||
}
|
||||
@@ -8,6 +8,7 @@ export type ModelOption = {
|
||||
alias?: string;
|
||||
isAlias?: boolean;
|
||||
contextWindow?: number;
|
||||
inputModalities?: Array<'text' | 'image'>;
|
||||
thinking?: Record<string, unknown>;
|
||||
};
|
||||
export type ModelProvider = 'gemini' | 'codex' | 'claude' | 'openai';
|
||||
|
||||
Reference in new issue
Block a user