dsh-llm-verifier 0.1.0

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.
@@ -0,0 +1,27 @@
1
+ import type { UsageStats } from './caller.ts';
2
+ export interface CachedPairScore {
3
+ scoreA: number;
4
+ scoreB: number;
5
+ usage: UsageStats;
6
+ scoringMode: 'top-logprobs' | 'explicit-tag';
7
+ createdAt: number;
8
+ }
9
+ export declare function stableHash(value: unknown): string;
10
+ export declare function resolveCacheFile(cacheDir: string, cwd?: string): string;
11
+ export declare class ScoreCache {
12
+ private readonly file;
13
+ private readonly maxEntries;
14
+ private loaded;
15
+ private entries;
16
+ private readonly inflight;
17
+ private writing;
18
+ constructor(file: string, maxEntries: number);
19
+ load(): Promise<void>;
20
+ getOrCreate(key: string, create: () => Promise<CachedPairScore>): Promise<{
21
+ value: CachedPairScore;
22
+ hit: boolean;
23
+ }>;
24
+ private trim;
25
+ private persist;
26
+ }
27
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1,82 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { mkdir, readFile, rename, unlink, writeFile } from 'node:fs/promises';
3
+ import { dirname, isAbsolute, join, resolve } from 'node:path';
4
+ export function stableHash(value) {
5
+ return createHash('sha256').update(JSON.stringify(value)).digest('hex');
6
+ }
7
+ export function resolveCacheFile(cacheDir, cwd = process.cwd()) {
8
+ const root = isAbsolute(cacheDir) ? cacheDir : resolve(cwd, cacheDir);
9
+ return join(root, 'scores-v1.json');
10
+ }
11
+ export class ScoreCache {
12
+ file;
13
+ maxEntries;
14
+ loaded = false;
15
+ entries = new Map();
16
+ inflight = new Map();
17
+ writing = Promise.resolve();
18
+ constructor(file, maxEntries) {
19
+ this.file = file;
20
+ this.maxEntries = maxEntries;
21
+ }
22
+ async load() {
23
+ if (this.loaded)
24
+ return;
25
+ this.loaded = true;
26
+ try {
27
+ const document = JSON.parse(await readFile(this.file, 'utf8'));
28
+ if (document.version !== 1 || typeof document.entries !== 'object' || document.entries === null)
29
+ return;
30
+ this.entries = new Map(Object.entries(document.entries).map(([key, value]) => [key, { ...value, scoringMode: value.scoringMode ?? 'explicit-tag' }]));
31
+ }
32
+ catch (error) {
33
+ if (error.code !== 'ENOENT')
34
+ throw error;
35
+ }
36
+ }
37
+ async getOrCreate(key, create) {
38
+ await this.load();
39
+ const cached = this.entries.get(key);
40
+ if (cached !== undefined)
41
+ return { value: cached, hit: true };
42
+ const existing = this.inflight.get(key);
43
+ if (existing !== undefined)
44
+ return { value: await existing, hit: true };
45
+ const pending = create();
46
+ this.inflight.set(key, pending);
47
+ try {
48
+ const value = await pending;
49
+ this.entries.set(key, value);
50
+ this.trim();
51
+ await this.persist();
52
+ return { value, hit: false };
53
+ }
54
+ finally {
55
+ this.inflight.delete(key);
56
+ }
57
+ }
58
+ trim() {
59
+ if (this.entries.size <= this.maxEntries)
60
+ return;
61
+ const sorted = [...this.entries].sort((a, b) => a[1].createdAt - b[1].createdAt);
62
+ for (let index = 0; index < sorted.length - this.maxEntries; index += 1)
63
+ this.entries.delete(sorted[index][0]);
64
+ }
65
+ async persist() {
66
+ const snapshot = { version: 1, entries: Object.fromEntries(this.entries) };
67
+ this.writing = this.writing.then(async () => {
68
+ await mkdir(dirname(this.file), { recursive: true });
69
+ const temporary = this.file + '.tmp-' + process.pid;
70
+ await writeFile(temporary, JSON.stringify(snapshot), 'utf8');
71
+ try {
72
+ await rename(temporary, this.file);
73
+ }
74
+ catch (error) {
75
+ await unlink(temporary).catch(() => { });
76
+ throw error;
77
+ }
78
+ });
79
+ await this.writing;
80
+ }
81
+ }
82
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1,48 @@
1
+ import { type LlmRuntime } from '@deepseek-ai/dsh-llm';
2
+ import type { AttachmentStore } from '@deepseek-ai/dsh-attachment';
3
+ import type { CompletionLogprobs } from './core.ts';
4
+ import { TopLogprobCapabilityCache } from './top-logprobs.ts';
5
+ import type { Context } from '@deepseek-ai/cordis';
6
+ export interface VerifierClientConfig {
7
+ ctx: Context;
8
+ llm: LlmRuntime;
9
+ attachments: AttachmentStore;
10
+ topLogprobCapabilities: TopLogprobCapabilityCache;
11
+ provider: string;
12
+ model: string;
13
+ reasoningEffort?: string;
14
+ maxTokens: number;
15
+ timeoutMs: number;
16
+ maxRetries: number;
17
+ retryBaseDelayMs: number;
18
+ limiter?: RequestLimiter;
19
+ }
20
+ export interface VerifierImage {
21
+ data: Uint8Array;
22
+ mediaType: 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif';
23
+ }
24
+ export interface UsageStats {
25
+ calls: number;
26
+ attempts: number;
27
+ retries: number;
28
+ inputTokens: number;
29
+ cachedInputTokens: number;
30
+ outputTokens: number;
31
+ reasoningTokens: number;
32
+ }
33
+ export type ScoringMode = 'top-logprobs' | 'explicit-tag';
34
+ export interface VerifierCompletion extends CompletionLogprobs {
35
+ usage: UsageStats;
36
+ scoringMode: ScoringMode;
37
+ }
38
+ export declare class RequestLimiter {
39
+ readonly limit: number;
40
+ private active;
41
+ private readonly queue;
42
+ constructor(limit: number);
43
+ run<T>(operation: () => Promise<T>, signal?: AbortSignal): Promise<T>;
44
+ }
45
+ export declare function callVerifier(config: VerifierClientConfig, prompt: string, signal?: AbortSignal, images?: readonly VerifierImage[]): Promise<VerifierCompletion>;
46
+ export declare function addUsage(target: UsageStats, source: UsageStats): void;
47
+ export declare function emptyUsage(): UsageStats;
48
+ //# sourceMappingURL=caller.d.ts.map
@@ -0,0 +1,125 @@
1
+ import { BlockAssembler, ReasoningEffortId, createUserMessage, deepFreeze } from '@deepseek-ai/dsh-llm';
2
+ import { TopLogprobCapabilityCache, TopLogprobsUnsupportedError, callTopLogprobs, resolveTopLogprobRoute } from "./top-logprobs.js";
3
+ function failureMessage(finish) {
4
+ if (finish.kind === 'error' || finish.kind === 'aborted')
5
+ return finish.failure.message;
6
+ if (finish.kind === 'max-tokens')
7
+ return 'verifier response reached max tokens before completing its answer';
8
+ return undefined;
9
+ }
10
+ async function delay(ms, signal) {
11
+ if (signal?.aborted)
12
+ throw signal.reason;
13
+ await new Promise((resolve, reject) => {
14
+ const timer = setTimeout(resolve, ms);
15
+ const abort = () => { clearTimeout(timer); reject(signal?.reason); };
16
+ signal?.addEventListener('abort', abort, { once: true });
17
+ });
18
+ }
19
+ function usage(attempts, value = {}) {
20
+ return { calls: 1, attempts, retries: attempts - 1, inputTokens: value.inputTokens ?? 0, cachedInputTokens: (value.cacheReadTokens ?? 0) + (value.cacheWriteTokens ?? 0), outputTokens: value.outputTokens ?? 0, reasoningTokens: value.reasoningTokens ?? 0 };
21
+ }
22
+ async function callExplicitTag(config, prompt, signal, images) {
23
+ let attempt = 0;
24
+ while (true) {
25
+ attempt += 1;
26
+ const controller = new AbortController();
27
+ const timeout = setTimeout(() => controller.abort(new Error('llm-verifier: request timed out')), config.timeoutMs);
28
+ const abort = () => controller.abort(signal?.reason);
29
+ signal?.addEventListener('abort', abort, { once: true });
30
+ try {
31
+ const content = [{ type: 'text', text: prompt }];
32
+ for (const image of images ?? []) {
33
+ const ref = await config.attachments.saveImage({ data: image.data, mediaType: image.mediaType });
34
+ content.push({ type: 'image', attachment: ref });
35
+ }
36
+ const messages = [createUserMessage({ content, source: { kind: 'plugin', plugin: 'dsh-llm-verifier' } })];
37
+ const assembler = new BlockAssembler();
38
+ const options = deepFreeze({
39
+ provider: config.provider,
40
+ model: config.model,
41
+ ...(config.reasoningEffort ? { reasoningEffort: ReasoningEffortId(config.reasoningEffort) } : {}),
42
+ messages,
43
+ maxTokens: config.maxTokens,
44
+ temperature: 1,
45
+ signal: controller.signal,
46
+ });
47
+ for await (const chunk of config.llm.stream(options))
48
+ assembler.push(chunk);
49
+ const failed = failureMessage(assembler.finish);
50
+ if (failed !== undefined)
51
+ throw new Error('llm-verifier: model call failed: ' + failed);
52
+ const text = assembler.blocks().filter((block) => block.type === 'text').map(block => block.text).join('');
53
+ if (!text.trim())
54
+ throw new Error('llm-verifier: selected DSH model produced no text');
55
+ // DSH adapters expose provider-neutral text/usage but not top-logprob candidates.
56
+ // extractScore() therefore uses the model's explicit final A–T tags.
57
+ return { text, tokens: [], positions: [], scoringMode: 'explicit-tag', usage: usage(attempt, assembler.usage) };
58
+ }
59
+ catch (error) {
60
+ if (signal?.aborted)
61
+ throw signal.reason;
62
+ if (attempt > config.maxRetries || !(error instanceof Error) || !/rate|quota|timeout|timed out|temporar|network|fetch|socket|5dd/i.test(error.message))
63
+ throw error;
64
+ await delay(Math.min(30000, config.retryBaseDelayMs * 2 ** (attempt - 1) * (0.8 + Math.random() * 0.4)), signal);
65
+ }
66
+ finally {
67
+ clearTimeout(timeout);
68
+ signal?.removeEventListener('abort', abort);
69
+ }
70
+ }
71
+ }
72
+ export class RequestLimiter {
73
+ limit;
74
+ active = 0;
75
+ queue = [];
76
+ constructor(limit) {
77
+ this.limit = limit;
78
+ }
79
+ async run(operation, signal) {
80
+ if (this.active >= this.limit)
81
+ await new Promise((resolve, reject) => {
82
+ const enter = () => { signal?.removeEventListener('abort', abort); resolve(); };
83
+ const abort = () => { const index = this.queue.indexOf(enter); if (index >= 0)
84
+ this.queue.splice(index, 1); reject(signal?.reason); };
85
+ this.queue.push(enter);
86
+ signal?.addEventListener('abort', abort, { once: true });
87
+ });
88
+ if (signal?.aborted)
89
+ throw signal.reason;
90
+ this.active += 1;
91
+ try {
92
+ return await operation();
93
+ }
94
+ finally {
95
+ this.active -= 1;
96
+ this.queue.shift()?.();
97
+ }
98
+ }
99
+ }
100
+ async function callAutomatic(config, prompt, signal, images) {
101
+ if (!config.topLogprobCapabilities.isUnsupported(config.provider, config.model)) {
102
+ const route = await resolveTopLogprobRoute(config.ctx, config.provider);
103
+ if (route !== undefined) {
104
+ try {
105
+ return await callTopLogprobs(route, config.model, prompt, config.maxTokens, config.reasoningEffort, signal, images);
106
+ }
107
+ catch (error) {
108
+ if (!(error instanceof TopLogprobsUnsupportedError))
109
+ throw error;
110
+ config.topLogprobCapabilities.markUnsupported(config.provider, config.model);
111
+ }
112
+ }
113
+ else
114
+ config.topLogprobCapabilities.markUnsupported(config.provider, config.model);
115
+ }
116
+ return callExplicitTag(config, prompt, signal, images);
117
+ }
118
+ export async function callVerifier(config, prompt, signal, images) {
119
+ const invoke = () => callAutomatic(config, prompt, signal, images);
120
+ return config.limiter === undefined ? invoke() : config.limiter.run(invoke, signal);
121
+ }
122
+ export function addUsage(target, source) { for (const key of ['calls', 'attempts', 'retries', 'inputTokens', 'cachedInputTokens', 'outputTokens', 'reasoningTokens'])
123
+ target[key] += source[key]; }
124
+ export function emptyUsage() { return { calls: 0, attempts: 0, retries: 0, inputTokens: 0, cachedInputTokens: 0, outputTokens: 0, reasoningTokens: 0 }; }
125
+ //# sourceMappingURL=caller.js.map
@@ -0,0 +1,4 @@
1
+ import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
2
+ export declare const inject: string[];
3
+ export declare function apply(ctx: ClientContext): void;
4
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1,66 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button, Input, StateDot } from '@deepseek-ai/dsh-client-ui-primitives';
3
+ import { useEffect, useMemo, useState } from 'react';
4
+ const NS = 'llm-verifier';
5
+ const shell = { display: 'flex', flexDirection: 'column', gap: 18, padding: '8px 4px 32px', color: 'var(--dsw-text-primary)' };
6
+ const card = { display: 'flex', flexDirection: 'column', gap: 0, padding: '16px 16px 0', border: '1px solid var(--dsw-alias-border-l2, rgba(255, 255, 255, 0.16))', borderRadius: 12, background: 'var(--dsw-alias-bg-module, rgba(20, 31, 57, 0.42))', overflow: 'hidden' };
7
+ const sectionTitle = { display: 'flex', gap: 10, alignItems: 'center', padding: '0 0 12px', borderBottom: '1px solid var(--dsw-alias-border-l2, rgba(255, 255, 255, 0.16))' };
8
+ const row = { display: 'grid', gridTemplateColumns: 'minmax(150px, 1fr) minmax(220px, 1.4fr)', gap: 18, alignItems: 'center', padding: '14px 0', borderBottom: '1px solid var(--dsw-alias-border-l2, rgba(255, 255, 255, 0.16))' };
9
+ const selectStyle = { width: '100%', minHeight: 38, padding: '0 12px', borderRadius: 10, color: 'var(--dsw-text-primary)', background: 'var(--dsw-surface-sunken)', border: '1px solid var(--dsw-alias-border-l2, rgba(255, 255, 255, 0.16))' };
10
+ function record(value) { return typeof value === 'object' && value !== null && !Array.isArray(value) ? value : {}; }
11
+ function values(view) { const v = record(view.value); return { provider: String(v.provider ?? ''), model: String(v.model ?? ''), ...(typeof v.reasoningEffort === 'string' ? { reasoningEffort: v.reasoningEffort } : {}), maxTokens: Number(v.maxTokens ?? 32768), maxConcurrency: Number(v.maxConcurrency ?? 8), maxRetries: Number(v.maxRetries ?? 3), timeoutMs: Number(v.timeoutMs ?? 300000), cacheMaxEntries: Number(v.cacheMaxEntries ?? 10000), estimatedInputUsdPerMillion: Number(v.estimatedInputUsdPerMillion ?? 0), estimatedOutputUsdPerMillion: Number(v.estimatedOutputUsdPerMillion ?? 0) }; }
12
+ function message(error) { return error instanceof Error ? error.message : String(error); }
13
+ function Label({ title, help }) { return _jsxs("div", { children: [_jsx("div", { style: { fontWeight: 600 }, children: title }), _jsx("div", { style: { fontSize: 12, color: 'var(--dsw-text-secondary)', marginTop: 3 }, children: help })] }); }
14
+ function VerifierSettings({ api }) {
15
+ const [loaded, setLoaded] = useState(null);
16
+ const [draft, setDraft] = useState(null);
17
+ const [busy, setBusy] = useState(false);
18
+ const [error, setError] = useState(null);
19
+ const [saved, setSaved] = useState(false);
20
+ const load = async () => { setError(null); try {
21
+ const [m, s] = await Promise.all([api.llm.models({}), api.settings.describe({})]);
22
+ if (!m.result.ok)
23
+ throw new Error(m.result.error.message);
24
+ if (!s.result.ok)
25
+ throw new Error(s.result.error.message);
26
+ const view = s.result.value.namespaces.find((x) => x.ns === NS);
27
+ if (!view)
28
+ throw new Error('Verifier settings namespace is not registered. Restart the DSH host.');
29
+ const next = { groups: m.result.value.groups, settings: view, writable: s.result.value.writable, failures: m.result.value.failures.map((f) => f.name + ': ' + f.message) };
30
+ setLoaded(next);
31
+ setDraft(values(view));
32
+ }
33
+ catch (e) {
34
+ setError(message(e));
35
+ } };
36
+ useEffect(() => { void load(); }, []);
37
+ const models = useMemo(() => loaded?.groups.find(g => g.id === draft?.provider)?.models ?? [], [loaded, draft?.provider]);
38
+ const selected = models.find(m => m.id === draft?.model);
39
+ const efforts = selected?.reasoning?.efforts ?? [];
40
+ const patch = (key, value) => setDraft(v => v ? { ...v, [key]: value } : v);
41
+ const save = async () => { if (!loaded || !draft)
42
+ return; setBusy(true); setSaved(false); setError(null); try {
43
+ const section = { ...record(loaded.settings.user), ...draft };
44
+ if (!draft.reasoningEffort)
45
+ delete section.reasoningEffort;
46
+ const res = await api.settings.update({ ns: NS, patch: section, expectedRevision: loaded.settings.revision });
47
+ if (!res.result.ok)
48
+ throw new Error(res.result.error.message);
49
+ setLoaded(v => v ? { ...v, settings: res.result.value } : v);
50
+ setDraft(values(res.result.value));
51
+ setSaved(true);
52
+ }
53
+ catch (e) {
54
+ setError(message(e));
55
+ }
56
+ finally {
57
+ setBusy(false);
58
+ } };
59
+ if (!loaded || !draft)
60
+ return _jsxs("div", { style: shell, children: [_jsx("h2", { children: "LLM Verifier" }), _jsx("p", { children: error ?? '正在读取 DSH 模型和设置…' }), error && _jsx(Button, { onClick: () => void load(), children: "\u91CD\u8BD5" })] });
61
+ const numeric = (key, min = 0) => _jsx(Input, { type: "number", min: min, value: String(draft[key]), onChange: e => patch(key, Number(e.target.value)) });
62
+ return _jsxs("div", { style: shell, children: [_jsxs("div", { children: [_jsx("h2", { style: { margin: '0 0 6px' }, children: "LLM Verifier" }), _jsx("p", { style: { margin: 0, color: 'var(--dsw-text-secondary)' }, children: "\u9009\u62E9\u4EFB\u610F\u5DF2\u5728 DSH\u300C\u6A21\u578B\u300D\u9875\u914D\u7F6E\u5E76\u542F\u7528\u7684\u6A21\u578B\u4F5C\u4E3A\u72EC\u7ACB\u88C1\u5224\u3002\u8BBE\u7F6E\u5B9E\u65F6\u751F\u6548\u3002" })] }), _jsxs("div", { style: card, children: [_jsxs("div", { style: sectionTitle, children: [_jsx(StateDot, { state: "done" }), _jsx("strong", { children: "\u88C1\u5224\u6A21\u578B" })] }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u4F9B\u5E94\u5546", help: "\u53EA\u663E\u793A\u5F53\u524D DSH \u4E2D\u53EF\u8DEF\u7531\u7684\u4F9B\u5E94\u5546" }), _jsx("select", { style: selectStyle, value: draft.provider, onChange: e => { const provider = e.target.value; const first = loaded.groups.find(g => g.id === provider)?.models[0]; setDraft({ ...draft, provider, ...(first ? { model: first.id, reasoningEffort: first.reasoning?.defaultEffort } : {}) }); }, children: loaded.groups.map(g => _jsxs("option", { value: g.id, children: [g.name, " \u00B7 ", g.id] }, g.id)) })] }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u6A21\u578B", help: "\u6A21\u578B\u76EE\u5F55\u6765\u81EA DSH adapter\uFF0C\u9009\u62E9\u7ED3\u679C\u4F1A\u6301\u4E45\u5316" }), _jsx("select", { style: selectStyle, value: draft.model, onChange: e => { const model = e.target.value; const found = models.find(m => m.id === model); setDraft({ ...draft, model, ...(found?.reasoning?.defaultEffort ? { reasoningEffort: found.reasoning.defaultEffort } : { reasoningEffort: undefined }) }); }, children: models.map(m => _jsxs("option", { value: m.id, children: [m.name, " \u00B7 ", m.id] }, m.id)) })] }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u63A8\u7406\u5F3A\u5EA6", help: "\u7531\u6240\u9009\u6A21\u578B adapter \u58F0\u660E\uFF1B\u7559\u7A7A\u4F7F\u7528\u6A21\u578B\u9ED8\u8BA4\u503C" }), _jsxs("select", { style: selectStyle, value: draft.reasoningEffort ?? '', onChange: e => patch('reasoningEffort', e.target.value || undefined), children: [_jsx("option", { value: "", children: "\u6A21\u578B\u9ED8\u8BA4" }), efforts.map(e => _jsx("option", { value: e.id, children: e.name }, e.id))] })] }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u6700\u5927\u8F93\u51FA Token", help: "\u6BCF\u4E2A\u88C1\u5224\u8BF7\u6C42\u7684\u8F93\u51FA\u4E0A\u9650" }), numeric('maxTokens', 1)] })] }), _jsxs("div", { style: card, children: [_jsx("div", { style: sectionTitle, children: _jsx("strong", { children: "\u6267\u884C\u63A7\u5236" }) }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u6700\u5927\u5E76\u53D1", help: "\u6240\u6709 verifier \u5DE5\u5177\u5171\u4EAB\u7684\u8BF7\u6C42\u5E76\u53D1\u4E0A\u9650" }), numeric('maxConcurrency', 1)] }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u6700\u591A\u91CD\u8BD5", help: "\u77ED\u6682\u7F51\u7EDC\u3001\u9650\u6D41\u548C\u670D\u52A1\u7AEF\u9519\u8BEF\u7684\u91CD\u8BD5\u6B21\u6570" }), numeric('maxRetries', 0)] }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u8BF7\u6C42\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09", help: "\u5355\u4E2A\u6A21\u578B\u8BF7\u6C42\u7684\u8D85\u65F6\u65F6\u95F4" }), numeric('timeoutMs', 1)] }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u7F13\u5B58\u6761\u76EE\u4E0A\u9650", help: "\u6301\u4E45\u8BC4\u5206\u7F13\u5B58\u4FDD\u7559\u7684\u6700\u5927\u6761\u76EE\u6570" }), numeric('cacheMaxEntries', 1)] })] }), _jsxs("div", { style: card, children: [_jsx("div", { style: sectionTitle, children: _jsx("strong", { children: "\u8D39\u7528\u4F30\u7B97\uFF08\u6BCF\u767E\u4E07 Token\uFF0CUSD\uFF09" }) }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u8F93\u5165\u4EF7\u683C", help: "\u4EC5\u7528\u4E8E\u7ED3\u679C\u4E2D\u7684 estimatedCostUsd" }), numeric('estimatedInputUsdPerMillion', 0)] }), _jsxs("div", { style: row, children: [_jsx(Label, { title: "\u8F93\u51FA\u4EF7\u683C", help: "\u4EC5\u7528\u4E8E\u7ED3\u679C\u4E2D\u7684 estimatedCostUsd" }), numeric('estimatedOutputUsdPerMillion', 0)] })] }), loaded.failures.length > 0 && _jsxs("div", { style: { ...card, borderColor: 'var(--dsw-alias-state-warn-primary, #d9a441)', paddingBottom: 16 }, children: [_jsx("strong", { children: "\u90E8\u5206\u6A21\u578B\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25" }), loaded.failures.map(x => _jsx("div", { children: x }, x))] }), error && _jsx("div", { style: { color: 'var(--dsw-danger)' }, children: error }), saved && _jsx("div", { style: { color: 'var(--dsw-success)' }, children: "\u5DF2\u4FDD\u5B58\uFF0C\u540E\u7EED verifier \u8C03\u7528\u5C06\u4F7F\u7528\u65B0\u8BBE\u7F6E\u3002" }), _jsxs("div", { style: { display: 'flex', gap: 10 }, children: [_jsx(Button, { disabled: busy || !loaded.writable, onClick: () => void save(), children: busy ? '保存中…' : '保存设置' }), _jsx(Button, { variant: "outline", disabled: busy, onClick: () => void load(), children: "\u91CD\u65B0\u8F7D\u5165" })] })] });
63
+ }
64
+ export const inject = ['slots', 'connection'];
65
+ export function apply(ctx) { const connection = ctx.get('connection'); ctx.slots.inject('settings.section', () => ctx.slots.register({ name: 'settings.section', id: 'llm-verifier', order: 35, label: 'LLM Verifier', inject: () => ({ api: connection.api }) }, VerifierSettings)); }
66
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1,35 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import z from 'schemastery';
3
+ export declare const VERIFIER_SETTINGS_NAMESPACE: import("@deepseek-ai/dsh-settings").SettingsNamespace;
4
+ export interface Config {
5
+ provider?: string;
6
+ model?: string;
7
+ reasoningEffort?: string;
8
+ maxTokens?: number;
9
+ timeoutMs?: number;
10
+ maxConcurrency?: number;
11
+ maxRetries?: number;
12
+ retryBaseDelayMs?: number;
13
+ cacheDir?: string;
14
+ cacheMaxEntries?: number;
15
+ estimatedInputUsdPerMillion?: number;
16
+ estimatedOutputUsdPerMillion?: number;
17
+ }
18
+ export interface ResolvedConfig {
19
+ provider: string;
20
+ model: string;
21
+ reasoningEffort?: string;
22
+ maxTokens: number;
23
+ timeoutMs: number;
24
+ maxConcurrency: number;
25
+ maxRetries: number;
26
+ retryBaseDelayMs: number;
27
+ cacheDir: string;
28
+ cacheMaxEntries: number;
29
+ estimatedInputUsdPerMillion: number;
30
+ estimatedOutputUsdPerMillion: number;
31
+ }
32
+ export declare const Config: z<Config>;
33
+ export declare function resolveConfig(config?: Config): ResolvedConfig;
34
+ export declare function installVerifierSettings(ctx: Context, entry: ResolvedConfig, onChange: () => void): () => ResolvedConfig;
35
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1,57 @@
1
+ import { installSettingsSection, settingsNamespace } from '@deepseek-ai/dsh-settings';
2
+ import z from 'schemastery';
3
+ export const VERIFIER_SETTINGS_NAMESPACE = settingsNamespace('llm-verifier');
4
+ export const Config = z.object({
5
+ provider: z.string().default('deepseek-official'),
6
+ model: z.string().default('deepseek-v4-flash'),
7
+ reasoningEffort: z.string(),
8
+ maxTokens: z.number().step(1).min(1).default(32768),
9
+ timeoutMs: z.number().step(1).min(1).default(300000),
10
+ maxConcurrency: z.number().step(1).min(1).default(8),
11
+ maxRetries: z.number().step(1).min(0).default(3),
12
+ retryBaseDelayMs: z.number().step(1).min(1).default(500),
13
+ cacheDir: z.string().default('.dsh-verifier-cache'),
14
+ cacheMaxEntries: z.number().step(1).min(1).default(10000),
15
+ estimatedInputUsdPerMillion: z.number().min(0).default(0),
16
+ estimatedOutputUsdPerMillion: z.number().min(0).default(0),
17
+ });
18
+ export function resolveConfig(config = {}) {
19
+ const provider = (config.provider ?? 'deepseek-official').trim();
20
+ const model = (config.model ?? 'deepseek-v4-flash').trim();
21
+ if (!provider)
22
+ throw new Error('llm-verifier: provider must be non-empty');
23
+ if (!model)
24
+ throw new Error('llm-verifier: model must be non-empty');
25
+ const values = {
26
+ maxTokens: config.maxTokens ?? 32768,
27
+ timeoutMs: config.timeoutMs ?? 300000,
28
+ maxConcurrency: config.maxConcurrency ?? 8,
29
+ retryBaseDelayMs: config.retryBaseDelayMs ?? 500,
30
+ cacheMaxEntries: config.cacheMaxEntries ?? 10000,
31
+ };
32
+ for (const [name, value] of Object.entries(values))
33
+ if (!Number.isSafeInteger(value) || value <= 0)
34
+ throw new Error('llm-verifier: ' + name + ' must be a positive safe integer');
35
+ const maxRetries = config.maxRetries ?? 3;
36
+ if (!Number.isSafeInteger(maxRetries) || maxRetries < 0)
37
+ throw new Error('llm-verifier: maxRetries must be a non-negative safe integer');
38
+ const cacheDir = (config.cacheDir ?? '.dsh-verifier-cache').trim();
39
+ if (!cacheDir)
40
+ throw new Error('llm-verifier: cacheDir must be non-empty');
41
+ const estimatedInputUsdPerMillion = config.estimatedInputUsdPerMillion ?? 0;
42
+ const estimatedOutputUsdPerMillion = config.estimatedOutputUsdPerMillion ?? 0;
43
+ if (![estimatedInputUsdPerMillion, estimatedOutputUsdPerMillion].every(value => Number.isFinite(value) && value >= 0))
44
+ throw new Error('llm-verifier: estimated token prices must be finite non-negative numbers');
45
+ const reasoningEffort = config.reasoningEffort?.trim();
46
+ return { provider, model, ...(reasoningEffort ? { reasoningEffort } : {}), maxRetries, cacheDir, estimatedInputUsdPerMillion, estimatedOutputUsdPerMillion, ...values };
47
+ }
48
+ export function installVerifierSettings(ctx, entry, onChange) {
49
+ let source = () => entry;
50
+ installSettingsSection(ctx, VERIFIER_SETTINGS_NAMESPACE, Config, entry, {
51
+ setSource(current) { source = current; },
52
+ onChange,
53
+ validate(value) { resolveConfig(value); },
54
+ });
55
+ return () => resolveConfig(source());
56
+ }
57
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1,38 @@
1
+ /** Pure scoring and Probabilistic Pivot Tournament primitives. */
2
+ export interface Criterion {
3
+ id: string;
4
+ name: string;
5
+ description: string;
6
+ }
7
+ export interface TokenAlternative {
8
+ token: string;
9
+ logprob: number;
10
+ }
11
+ export interface CompletionLogprobs {
12
+ text: string;
13
+ tokens: string[];
14
+ positions: TokenAlternative[][];
15
+ }
16
+ export interface CandidateScore {
17
+ index: number;
18
+ score: number;
19
+ }
20
+ export declare const GRANULARITY = 20;
21
+ export declare const LETTERS: string[];
22
+ export declare const SCALE_DESCRIPTION: string;
23
+ export declare const DEFAULT_CRITERIA: Criterion[];
24
+ export declare const DEFAULT_GROUND_TRUTH_NOTE = "**IMPORTANT:** Focus on observed tool and terminal output as ground truth. Do NOT trust the agent's self-assessment or claims of success.";
25
+ export declare function normalizeScoreLetter(token: string): string | undefined;
26
+ export declare function extractScore(completion: CompletionLogprobs, tag: string): number;
27
+ export declare function buildPairwisePrompt(problem: string, traceA: string, traceB: string, criterion: Criterion, groundTruthNote?: string): string;
28
+ export declare function buildProgressPrompt(problem: string, steps: readonly string[], checkpoints: readonly number[]): string;
29
+ /** Progress uses A=NO..T=YES, the reverse of pairwise success scoring. */
30
+ export declare function extractProgressScore(completion: CompletionLogprobs, tag: string): number;
31
+ export declare function bradleyTerry(rewardA: number, rewardB: number): number;
32
+ export declare function seededRandom(seed: number): () => number;
33
+ export declare function ringCycle(count: number, seed?: number): Array<[number, number]>;
34
+ export declare function pivotRoundPairs(count: number, pivots: readonly number[]): Array<[number, number]>;
35
+ export declare function accumulatePairs(pairs: readonly [number, number][], rewards: ReadonlyMap<string, readonly [number, number]>, wins: number[], counts: number[]): void;
36
+ export declare function topPivots(wins: readonly number[], counts: readonly number[], requested: number): number[];
37
+ export declare function rankScores(wins: readonly number[], counts: readonly number[]): CandidateScore[];
38
+ //# sourceMappingURL=core.d.ts.map