@openvulcan/dsh-vulcancode 0.0.0-stage → 0.4.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 +126 -2
- package/assets/openvulcan-icon.svg +33 -0
- package/cordis.patch.yml +19 -0
- package/node_modules/@openvulcan/dsh-decision-guard/LICENSE +21 -0
- package/node_modules/@openvulcan/dsh-decision-guard/README.md +14 -0
- package/node_modules/@openvulcan/dsh-decision-guard/assets/decision-guard-icon.png +0 -0
- package/node_modules/@openvulcan/dsh-decision-guard/lib/calls.d.ts +40 -0
- package/node_modules/@openvulcan/dsh-decision-guard/lib/calls.js +150 -0
- package/node_modules/@openvulcan/dsh-decision-guard/lib/client.js +2250 -0
- package/node_modules/@openvulcan/dsh-decision-guard/lib/decide.d.ts +90 -0
- package/node_modules/@openvulcan/dsh-decision-guard/lib/decide.js +108 -0
- package/node_modules/@openvulcan/dsh-decision-guard/lib/index.d.ts +129 -0
- package/node_modules/@openvulcan/dsh-decision-guard/lib/index.js +572 -0
- package/node_modules/@openvulcan/dsh-decision-guard/lib/intent.d.ts +22 -0
- package/node_modules/@openvulcan/dsh-decision-guard/lib/intent.js +77 -0
- package/node_modules/@openvulcan/dsh-decision-guard/lib/panel.d.ts +158 -0
- package/node_modules/@openvulcan/dsh-decision-guard/lib/panel.js +340 -0
- package/node_modules/@openvulcan/dsh-decision-guard/lib/records.d.ts +133 -0
- package/node_modules/@openvulcan/dsh-decision-guard/lib/records.js +186 -0
- package/node_modules/@openvulcan/dsh-decision-guard/lib/review.d.ts +55 -0
- package/node_modules/@openvulcan/dsh-decision-guard/lib/review.js +202 -0
- package/node_modules/@openvulcan/dsh-decision-guard/lib/session-state.d.ts +62 -0
- package/node_modules/@openvulcan/dsh-decision-guard/lib/session-state.js +155 -0
- package/node_modules/@openvulcan/dsh-decision-guard/lib/version.d.ts +2 -0
- package/node_modules/@openvulcan/dsh-decision-guard/lib/version.js +4 -0
- package/node_modules/@openvulcan/dsh-decision-guard/package.json +68 -0
- package/node_modules/@openvulcan/dsh-vulcan-account/LICENSE +21 -0
- package/node_modules/@openvulcan/dsh-vulcan-account/assets/account-icon.png +0 -0
- package/node_modules/@openvulcan/dsh-vulcan-account/lib/client.js +573 -0
- package/node_modules/@openvulcan/dsh-vulcan-account/lib/index.d.ts +92 -0
- package/node_modules/@openvulcan/dsh-vulcan-account/lib/index.js +194 -0
- package/node_modules/@openvulcan/dsh-vulcan-account/package.json +57 -0
- package/node_modules/@openvulcan/dsh-vulcan-answers/LICENSE +21 -0
- package/node_modules/@openvulcan/dsh-vulcan-answers/assets/answers-icon.png +0 -0
- package/node_modules/@openvulcan/dsh-vulcan-answers/assets/prompts/answer-mode-interactive.md +79 -0
- package/node_modules/@openvulcan/dsh-vulcan-answers/assets/prompts/answer-mode-standard.md +1 -0
- package/node_modules/@openvulcan/dsh-vulcan-answers/lib/answer-model.d.ts +258 -0
- package/node_modules/@openvulcan/dsh-vulcan-answers/lib/answer-model.js +698 -0
- package/node_modules/@openvulcan/dsh-vulcan-answers/lib/client.js +671 -0
- package/node_modules/@openvulcan/dsh-vulcan-answers/lib/fences.d.ts +29 -0
- package/node_modules/@openvulcan/dsh-vulcan-answers/lib/fences.js +70 -0
- package/node_modules/@openvulcan/dsh-vulcan-answers/lib/index.d.ts +19 -0
- package/node_modules/@openvulcan/dsh-vulcan-answers/lib/index.js +52 -0
- package/node_modules/@openvulcan/dsh-vulcan-answers/lib/limits.d.ts +9 -0
- package/node_modules/@openvulcan/dsh-vulcan-answers/lib/limits.js +9 -0
- package/node_modules/@openvulcan/dsh-vulcan-answers/lib/prompt.d.ts +22 -0
- package/node_modules/@openvulcan/dsh-vulcan-answers/lib/prompt.js +39 -0
- package/node_modules/@openvulcan/dsh-vulcan-answers/lib/reply-format.d.ts +20 -0
- package/node_modules/@openvulcan/dsh-vulcan-answers/lib/reply-format.js +17 -0
- package/node_modules/@openvulcan/dsh-vulcan-answers/lib/session-state.d.ts +66 -0
- package/node_modules/@openvulcan/dsh-vulcan-answers/lib/session-state.js +118 -0
- package/node_modules/@openvulcan/dsh-vulcan-answers/package.json +59 -0
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/LICENSE +21 -0
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/assets/luaskills-icon.svg +1 -0
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/client.js +949 -0
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/constants.d.ts +46 -0
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/constants.js +51 -0
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/engine.d.ts +55 -0
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/engine.js +149 -0
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/hub.d.ts +49 -0
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/hub.js +116 -0
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/index.d.ts +26 -0
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/index.js +276 -0
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/protocol.d.ts +65 -0
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/protocol.js +2 -0
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/runtime.d.ts +111 -0
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/runtime.js +281 -0
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/sdk-install.d.ts +26 -0
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/sdk-install.js +87 -0
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/settings.d.ts +79 -0
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/settings.js +165 -0
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/skills.d.ts +34 -0
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/skills.js +61 -0
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/tools.d.ts +59 -0
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/tools.js +107 -0
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/worker.d.ts +10 -0
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/worker.js +140 -0
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/package.json +57 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/LICENSE +21 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/assets/markdown-memory-guide.md +21 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/assets/memory-curator/SKILL.md +31 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/assets/memory-icon.png +0 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/client.js +1090 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/index.d.ts +30 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/index.js +240 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/inject.d.ts +30 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/inject.js +46 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/constants.d.ts +23 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/constants.js +25 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/document.d.ts +108 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/document.js +373 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/errors.d.ts +14 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/errors.js +31 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/index.d.ts +196 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/index.js +204 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/operations.d.ts +92 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/operations.js +472 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/package.d.ts +126 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/package.js +553 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/registry.d.ts +56 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/registry.js +182 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/scope.d.ts +13 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/scope.js +24 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/search.d.ts +65 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/search.js +227 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/snapshot.d.ts +34 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/snapshot.js +171 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/store.d.ts +72 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/store.js +250 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/timestamp.d.ts +6 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/timestamp.js +12 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/tokens.d.ts +8 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/tokens.js +38 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/page.d.ts +132 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/page.js +235 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/project.d.ts +14 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/project.js +27 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/settings.d.ts +29 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/settings.js +62 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/package.json +63 -0
- package/node_modules/@openvulcan/dsh-vulcan-reasoning/LICENSE +21 -0
- package/node_modules/@openvulcan/dsh-vulcan-reasoning/lib/client.js +509 -0
- package/node_modules/@openvulcan/dsh-vulcan-reasoning/lib/index.d.ts +6 -0
- package/node_modules/@openvulcan/dsh-vulcan-reasoning/lib/index.js +8 -0
- package/node_modules/@openvulcan/dsh-vulcan-reasoning/package.json +42 -0
- package/node_modules/@openvulcan/dsh-vulcan-style/LICENSE +21 -0
- package/node_modules/@openvulcan/dsh-vulcan-style/assets/style-icon.png +0 -0
- package/node_modules/@openvulcan/dsh-vulcan-style/lib/client.js +1121 -0
- package/node_modules/@openvulcan/dsh-vulcan-style/lib/index.d.ts +34 -0
- package/node_modules/@openvulcan/dsh-vulcan-style/lib/index.js +42 -0
- package/node_modules/@openvulcan/dsh-vulcan-style/package.json +51 -0
- package/node_modules/@openvulcan/openvulcan-account/LICENSE +21 -0
- package/node_modules/@openvulcan/openvulcan-account/lib/account.d.ts +140 -0
- package/node_modules/@openvulcan/openvulcan-account/lib/account.js +347 -0
- package/node_modules/@openvulcan/openvulcan-account/lib/index.d.ts +1 -0
- package/node_modules/@openvulcan/openvulcan-account/lib/index.js +1 -0
- package/node_modules/@openvulcan/openvulcan-account/package.json +29 -0
- package/node_modules/@openvulcan/vulcan-decision/LICENSE +21 -0
- package/node_modules/@openvulcan/vulcan-decision/README.md +23 -0
- package/node_modules/@openvulcan/vulcan-decision/bin/vulcan-decision.js +4 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/cli.d.ts +5 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/cli.js +267 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/cloud/edge.d.ts +211 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/cloud/edge.js +594 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/download.d.ts +36 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/download.js +99 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/engine.d.ts +37 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/engine.js +39 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/hardware.d.ts +105 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/hardware.js +207 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/home.d.ts +26 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/home.js +54 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/index.d.ts +17 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/index.js +17 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/local/engine.d.ts +54 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/local/engine.js +157 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/local/models.d.ts +126 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/local/models.js +261 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/local/packs.d.ts +102 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/local/packs.js +195 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/local/worker.d.ts +61 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/local/worker.js +123 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/protocol.d.ts +74 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/protocol.js +31 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/render.d.ts +15 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/render.js +33 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/version.d.ts +2 -0
- package/node_modules/@openvulcan/vulcan-decision/lib/version.js +4 -0
- package/node_modules/@openvulcan/vulcan-decision/package.json +40 -0
- package/node_modules/@openvulcan/vulcan-decision/python/vulcan_decision_worker.py +363 -0
- package/node_modules/@openvulcan/vulcan-decision/runtime-packs/onnx-cpu.json +627 -0
- package/node_modules/@openvulcan/vulcan-decision/runtime-packs/onnx-cuda.json +423 -0
- package/node_modules/@openvulcan/vulcan-decision/runtime-packs/torch-cpu.json +1047 -0
- package/node_modules/@openvulcan/vulcan-decision/runtime-packs/torch-cu128.json +647 -0
- package/package.json +55 -4
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Host side of the settings panel: the account status, cloud endpoints, runtime packs, local
|
|
3
|
+
* models and the decision log, served as exact `/api/vulcan-decision/*` routes inside dsh's authenticated
|
|
4
|
+
* connection. Only owner peers reach these routes.
|
|
5
|
+
*
|
|
6
|
+
* Signing in belongs to the OpenVulcan account plugin; the guard only reads the account through
|
|
7
|
+
* its service. Packs and models live under `$DSH_HOME/vulcan-decision/`.
|
|
8
|
+
*/
|
|
9
|
+
import { DecisionEngine, type AccountSource, type AccountStatus, type Credential, type DecisionHome, type DownloadSource, type EngineSettings, type EndpointSnapshot, type TestResult, type Verdict } from '@openvulcan/vulcan-decision';
|
|
10
|
+
import { type DecisionRecord, type RecordExport, type RecordPage, type RecordStore } from './records.js';
|
|
11
|
+
/** Prefix of every panel route, below dsh's `/api`. Also restated in `client/index.tsx`. */
|
|
12
|
+
export declare const ROUTE_PREFIX = "/api/vulcan-decision/";
|
|
13
|
+
/**
|
|
14
|
+
* The account the cloud backend signs with: the OpenVulcan account plugin's service, looked up on
|
|
15
|
+
* every use. The guard never injects the service, because a required one that is missing would
|
|
16
|
+
* leave the guard silently pending and its gate gone; without it, cloud judging fails as not
|
|
17
|
+
* signed in and the call is not allowed.
|
|
18
|
+
*/
|
|
19
|
+
export declare class ServiceAccount implements AccountSource {
|
|
20
|
+
private readonly service;
|
|
21
|
+
constructor(service: () => AccountSource | undefined);
|
|
22
|
+
/** Whether the account plugin is running. */
|
|
23
|
+
available(): boolean;
|
|
24
|
+
status(): Promise<AccountStatus>;
|
|
25
|
+
credential(): Promise<Credential>;
|
|
26
|
+
}
|
|
27
|
+
/** A long-running install the panel started; one runs at a time. */
|
|
28
|
+
export interface Job {
|
|
29
|
+
readonly kind: 'pack' | 'model-download' | 'model-directory';
|
|
30
|
+
readonly target: string;
|
|
31
|
+
readonly phase: 'download' | 'unpack' | 'install' | 'probe' | 'verify' | 'done' | 'failed' | 'cancelled';
|
|
32
|
+
readonly done?: number;
|
|
33
|
+
readonly total?: number;
|
|
34
|
+
readonly error?: string;
|
|
35
|
+
readonly startedAt: number;
|
|
36
|
+
}
|
|
37
|
+
/** The panel's settings, as `src/index.ts` reads them for each request. */
|
|
38
|
+
export interface PanelSettings {
|
|
39
|
+
readonly engine: () => EngineSettings;
|
|
40
|
+
readonly downloadSource: () => DownloadSource;
|
|
41
|
+
/** Whether the OpenVulcan account plugin is running; without it the cloud backend is signed out. */
|
|
42
|
+
readonly accountPlugin: () => boolean;
|
|
43
|
+
}
|
|
44
|
+
export declare class Panel {
|
|
45
|
+
readonly engine: DecisionEngine;
|
|
46
|
+
private readonly settings;
|
|
47
|
+
private readonly records;
|
|
48
|
+
private job;
|
|
49
|
+
private jobAbort;
|
|
50
|
+
constructor(engine: DecisionEngine, settings: PanelSettings, records: RecordStore);
|
|
51
|
+
get home(): DecisionHome;
|
|
52
|
+
/** Everything the panel shows, in one answer. */
|
|
53
|
+
state(): Promise<{
|
|
54
|
+
account: {
|
|
55
|
+
plugin: boolean;
|
|
56
|
+
signedIn: boolean;
|
|
57
|
+
site?: import("@openvulcan/vulcan-decision").Site;
|
|
58
|
+
authorizationId?: string;
|
|
59
|
+
expiresAt?: string;
|
|
60
|
+
reauthRequired?: boolean;
|
|
61
|
+
};
|
|
62
|
+
cloud: EndpointSnapshot;
|
|
63
|
+
hardware: {
|
|
64
|
+
gpus: {
|
|
65
|
+
generation?: import("@openvulcan/vulcan-decision").GpuGeneration;
|
|
66
|
+
name: string;
|
|
67
|
+
vendor: "nvidia" | "amd" | "intel" | "apple" | "other";
|
|
68
|
+
memoryBytes?: number;
|
|
69
|
+
computeCapability?: string;
|
|
70
|
+
driver?: string;
|
|
71
|
+
}[];
|
|
72
|
+
platform: NodeJS.Platform;
|
|
73
|
+
arch: string;
|
|
74
|
+
target?: import("@openvulcan/vulcan-decision").Target;
|
|
75
|
+
totalMemory: number;
|
|
76
|
+
cpus: number;
|
|
77
|
+
npus: readonly import("@openvulcan/vulcan-decision").NpuInfo[];
|
|
78
|
+
};
|
|
79
|
+
recommendation: import("@openvulcan/vulcan-decision").Recommendation;
|
|
80
|
+
packs: {
|
|
81
|
+
installed: {
|
|
82
|
+
outdated: boolean;
|
|
83
|
+
installedAt: string;
|
|
84
|
+
} | null;
|
|
85
|
+
needsDriver?: string;
|
|
86
|
+
bytes?: number;
|
|
87
|
+
id: "onnx-cpu" | "onnx-cuda" | "torch-cpu" | "torch-cu128";
|
|
88
|
+
backend: import("@openvulcan/vulcan-decision").Backend;
|
|
89
|
+
device: import("@openvulcan/vulcan-decision").Device;
|
|
90
|
+
fit: import("@openvulcan/vulcan-decision").PackFit;
|
|
91
|
+
}[];
|
|
92
|
+
planned: readonly [{
|
|
93
|
+
readonly id: "onnx-openvino";
|
|
94
|
+
readonly backend: "onnx";
|
|
95
|
+
readonly accelerator: "intel-npu";
|
|
96
|
+
}, {
|
|
97
|
+
readonly id: "onnx-qnn";
|
|
98
|
+
readonly backend: "onnx";
|
|
99
|
+
readonly accelerator: "qualcomm-npu";
|
|
100
|
+
}];
|
|
101
|
+
models: {
|
|
102
|
+
family?: "mini" | "flash" | "standard";
|
|
103
|
+
id: string;
|
|
104
|
+
name: string;
|
|
105
|
+
format: import("@openvulcan/vulcan-decision").ModelFormat;
|
|
106
|
+
bytes: number;
|
|
107
|
+
installedAt: string;
|
|
108
|
+
}[];
|
|
109
|
+
catalog: {
|
|
110
|
+
family: "mini" | "flash" | "standard";
|
|
111
|
+
name: string;
|
|
112
|
+
parameters: string;
|
|
113
|
+
format: import("@openvulcan/vulcan-decision").ModelFormat;
|
|
114
|
+
sources: ("huggingface" | "modelscope-intl" | "modelscope-cn")[];
|
|
115
|
+
}[];
|
|
116
|
+
local: import("@openvulcan/vulcan-decision").LocalStatus;
|
|
117
|
+
job: Job | null;
|
|
118
|
+
home: string;
|
|
119
|
+
}>;
|
|
120
|
+
/** The models OpenVulcan lists for this account; the first is the default. */
|
|
121
|
+
cloudModels(): Promise<string[]>;
|
|
122
|
+
/** The endpoint table of the saved (or default) model, without measuring. */
|
|
123
|
+
endpoints(): Promise<EndpointSnapshot>;
|
|
124
|
+
/** Measure the endpoints of the saved (or default) model now. */
|
|
125
|
+
probe(): Promise<EndpointSnapshot>;
|
|
126
|
+
/** Check an endpoint the user is about to lock against the model it will be saved with. */
|
|
127
|
+
checkLock(model: unknown, endpoint: unknown): Promise<void>;
|
|
128
|
+
/** Time five real judgements of a sample call on one endpoint. */
|
|
129
|
+
requestTest(endpoint: unknown, input: unknown): Promise<TestResult>;
|
|
130
|
+
installPack(pack: string | null, source: string | null): Job;
|
|
131
|
+
removePack(pack: string | null): boolean;
|
|
132
|
+
downloadModel(family: string | null, source: string | null): Job;
|
|
133
|
+
installModelDirectory(dir: unknown): Job;
|
|
134
|
+
removeModel(id: string | null): boolean;
|
|
135
|
+
cancelJob(): void;
|
|
136
|
+
/** Judge a sample call with the saved settings, so the user sees the backend answer. */
|
|
137
|
+
test(tool: unknown, args: unknown): Promise<Verdict>;
|
|
138
|
+
/** One page of the decision log, newest first. */
|
|
139
|
+
recordPage(url: URL): RecordPage;
|
|
140
|
+
/** Mark the label a record should have had, or clear the mark with null. */
|
|
141
|
+
markRecord(id: unknown, correction: unknown): DecisionRecord;
|
|
142
|
+
exportRecords(): RecordExport;
|
|
143
|
+
clearRecords(): void;
|
|
144
|
+
close(): void;
|
|
145
|
+
private source;
|
|
146
|
+
private assertIdle;
|
|
147
|
+
private running;
|
|
148
|
+
private run;
|
|
149
|
+
private update;
|
|
150
|
+
private finish;
|
|
151
|
+
}
|
|
152
|
+
/** Fetch-shaped handler of one panel route. */
|
|
153
|
+
type Handler = (panel: Panel, request: Request, url: URL) => Promise<unknown>;
|
|
154
|
+
/** Every panel route: method, path below {@link ROUTE_PREFIX}, handler. */
|
|
155
|
+
export declare const ROUTES: readonly (readonly ['GET' | 'POST', string, Handler])[];
|
|
156
|
+
/** Answer one panel request; failures carry their code, never a stack. */
|
|
157
|
+
export declare function handle(panel: Panel, handler: Handler, request: Request): Promise<Response>;
|
|
158
|
+
export {};
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Host side of the settings panel: the account status, cloud endpoints, runtime packs, local
|
|
3
|
+
* models and the decision log, served as exact `/api/vulcan-decision/*` routes inside dsh's authenticated
|
|
4
|
+
* connection. Only owner peers reach these routes.
|
|
5
|
+
*
|
|
6
|
+
* Signing in belongs to the OpenVulcan account plugin; the guard only reads the account through
|
|
7
|
+
* its service. Packs and models live under `$DSH_HOME/vulcan-decision/`.
|
|
8
|
+
*/
|
|
9
|
+
import { DECISIONS, DecisionEngine, DecisionError, DOWNLOAD_SOURCES, MODEL_CATALOG, MODEL_FAMILIES, MODEL_SOURCES, PACK_IDS, PLANNED_PACKS, detectHardware, gpuGeneration, installModelFromDirectory, installPack, installedPack, isPackId, listModels, preferredPrecision, readLock, recommend, removeModel, removePack, downloadModel, sourcesOf, AccountError, } from '@openvulcan/vulcan-decision';
|
|
10
|
+
import { PAGE_LIMIT_MAX } from './records.js';
|
|
11
|
+
/** Prefix of every panel route, below dsh's `/api`. Also restated in `client/index.tsx`. */
|
|
12
|
+
export const ROUTE_PREFIX = '/api/vulcan-decision/';
|
|
13
|
+
/**
|
|
14
|
+
* The account the cloud backend signs with: the OpenVulcan account plugin's service, looked up on
|
|
15
|
+
* every use. The guard never injects the service, because a required one that is missing would
|
|
16
|
+
* leave the guard silently pending and its gate gone; without it, cloud judging fails as not
|
|
17
|
+
* signed in and the call is not allowed.
|
|
18
|
+
*/
|
|
19
|
+
export class ServiceAccount {
|
|
20
|
+
service;
|
|
21
|
+
constructor(service) {
|
|
22
|
+
this.service = service;
|
|
23
|
+
}
|
|
24
|
+
/** Whether the account plugin is running. */
|
|
25
|
+
available() {
|
|
26
|
+
return this.service() !== undefined;
|
|
27
|
+
}
|
|
28
|
+
async status() {
|
|
29
|
+
return (await this.service()?.status()) ?? { signedIn: false };
|
|
30
|
+
}
|
|
31
|
+
async credential() {
|
|
32
|
+
const service = this.service();
|
|
33
|
+
if (service === undefined)
|
|
34
|
+
throw new AccountError('the OpenVulcan account plugin is not running; enable it and sign in');
|
|
35
|
+
return await service.credential();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export class Panel {
|
|
39
|
+
engine;
|
|
40
|
+
settings;
|
|
41
|
+
records;
|
|
42
|
+
job;
|
|
43
|
+
jobAbort;
|
|
44
|
+
constructor(engine, settings, records) {
|
|
45
|
+
this.engine = engine;
|
|
46
|
+
this.settings = settings;
|
|
47
|
+
this.records = records;
|
|
48
|
+
}
|
|
49
|
+
get home() {
|
|
50
|
+
return this.engine.home;
|
|
51
|
+
}
|
|
52
|
+
/** Everything the panel shows, in one answer. */
|
|
53
|
+
async state() {
|
|
54
|
+
const hardware = detectHardware();
|
|
55
|
+
const recommendation = recommend(hardware);
|
|
56
|
+
const settings = this.settings.engine();
|
|
57
|
+
return {
|
|
58
|
+
account: { ...await this.engine.account.status(), plugin: this.settings.accountPlugin() },
|
|
59
|
+
cloud: this.engine.cloud.snapshot(settings.cloudModel === '' ? undefined : settings.cloudModel, settings.cloudEndpoint),
|
|
60
|
+
hardware: {
|
|
61
|
+
...hardware,
|
|
62
|
+
gpus: hardware.gpus.map(gpu => ({
|
|
63
|
+
...gpu,
|
|
64
|
+
...gpu.computeCapability === undefined ? {} : { generation: gpuGeneration(gpu.computeCapability) },
|
|
65
|
+
})),
|
|
66
|
+
},
|
|
67
|
+
recommendation,
|
|
68
|
+
packs: PACK_IDS.map((pack) => {
|
|
69
|
+
const lock = readLock(pack);
|
|
70
|
+
const installed = installedPack(this.home, pack);
|
|
71
|
+
const fit = recommendation.packs.find(item => item.pack === pack);
|
|
72
|
+
return {
|
|
73
|
+
id: pack, backend: lock.backend, device: lock.device, fit: fit.fit,
|
|
74
|
+
...fit.bytes === undefined ? {} : { bytes: fit.bytes },
|
|
75
|
+
...fit.needsDriver === undefined ? {} : { needsDriver: fit.needsDriver },
|
|
76
|
+
installed: installed === undefined ? null : { outdated: installed.lock !== lock.id, installedAt: installed.installed_at },
|
|
77
|
+
};
|
|
78
|
+
}),
|
|
79
|
+
planned: PLANNED_PACKS,
|
|
80
|
+
models: listModels(this.home).map(model => ({
|
|
81
|
+
id: model.id, name: model.name, format: model.package_format, bytes: model.bytes, installedAt: model.installed_at,
|
|
82
|
+
...model.family === undefined ? {} : { family: model.family },
|
|
83
|
+
})),
|
|
84
|
+
catalog: MODEL_CATALOG.map(model => ({
|
|
85
|
+
family: model.family, name: model.name, parameters: model.parameters, format: model.format,
|
|
86
|
+
sources: sourcesOf(model),
|
|
87
|
+
})),
|
|
88
|
+
local: this.engine.local.status(),
|
|
89
|
+
job: this.job ?? null,
|
|
90
|
+
home: this.home.root,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/** The models OpenVulcan lists for this account; the first is the default. */
|
|
94
|
+
async cloudModels() {
|
|
95
|
+
return await this.engine.cloud.models();
|
|
96
|
+
}
|
|
97
|
+
/** The endpoint table of the saved (or default) model, without measuring. */
|
|
98
|
+
async endpoints() {
|
|
99
|
+
const settings = this.settings.engine();
|
|
100
|
+
if (!(await this.engine.account.status()).signedIn)
|
|
101
|
+
return { mode: 'auto', endpoints: [] };
|
|
102
|
+
let model;
|
|
103
|
+
try {
|
|
104
|
+
model = await this.engine.cloud.resolveModel(settings.cloudModel);
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
return { mode: 'auto', error: messageOf(error), endpoints: [] };
|
|
108
|
+
}
|
|
109
|
+
this.engine.cloud.refreshIfStale(model);
|
|
110
|
+
return this.engine.cloud.snapshot(model, settings.cloudEndpoint);
|
|
111
|
+
}
|
|
112
|
+
/** Measure the endpoints of the saved (or default) model now. */
|
|
113
|
+
async probe() {
|
|
114
|
+
const settings = this.settings.engine();
|
|
115
|
+
const model = await this.engine.cloud.resolveModel(settings.cloudModel);
|
|
116
|
+
try {
|
|
117
|
+
return await this.engine.cloud.probe(model, settings.cloudEndpoint);
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
if (error instanceof DecisionError && error.code !== 'not_signed_in')
|
|
121
|
+
return this.engine.cloud.snapshot(model, settings.cloudEndpoint);
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/** Check an endpoint the user is about to lock against the model it will be saved with. */
|
|
126
|
+
async checkLock(model, endpoint) {
|
|
127
|
+
if (typeof endpoint !== 'string' || endpoint === '')
|
|
128
|
+
throw new DecisionError('invalid_request', 'endpoint is required');
|
|
129
|
+
await this.engine.cloud.checkEndpoint(await this.engine.cloud.resolveModel(typeof model === 'string' ? model : this.settings.engine().cloudModel), endpoint);
|
|
130
|
+
}
|
|
131
|
+
/** Time five real judgements of a sample call on one endpoint. */
|
|
132
|
+
async requestTest(endpoint, input) {
|
|
133
|
+
if (typeof endpoint !== 'string' || endpoint === '')
|
|
134
|
+
throw new DecisionError('invalid_request', 'endpoint is required');
|
|
135
|
+
if (typeof input !== 'object' || input === null)
|
|
136
|
+
throw new DecisionError('invalid_request', 'input is required');
|
|
137
|
+
const model = await this.engine.cloud.resolveModel(this.settings.engine().cloudModel);
|
|
138
|
+
return await this.engine.cloud.requestTest(model, endpoint, input);
|
|
139
|
+
}
|
|
140
|
+
installPack(pack, source) {
|
|
141
|
+
if (!isPackId(pack))
|
|
142
|
+
throw new DecisionError('invalid_request', `pack must be one of ${PACK_IDS.join(', ')}`);
|
|
143
|
+
const from = this.source(source);
|
|
144
|
+
return this.run('pack', pack, signal => installPack(this.home, pack, {
|
|
145
|
+
source: from, signal,
|
|
146
|
+
onProgress: progress => this.update(progress.phase === 'download'
|
|
147
|
+
? { phase: 'download', done: progress.done, total: progress.total }
|
|
148
|
+
: { phase: progress.phase }),
|
|
149
|
+
}));
|
|
150
|
+
}
|
|
151
|
+
removePack(pack) {
|
|
152
|
+
if (!isPackId(pack))
|
|
153
|
+
throw new DecisionError('invalid_request', `pack must be one of ${PACK_IDS.join(', ')}`);
|
|
154
|
+
this.assertIdle();
|
|
155
|
+
// A running worker of this pack holds files open.
|
|
156
|
+
if (this.engine.local.status().selection?.pack === pack)
|
|
157
|
+
this.engine.local.stop();
|
|
158
|
+
return removePack(this.home, pack);
|
|
159
|
+
}
|
|
160
|
+
downloadModel(family, source) {
|
|
161
|
+
if (!MODEL_FAMILIES.includes(family))
|
|
162
|
+
throw new DecisionError('invalid_request', `model must be one of ${MODEL_FAMILIES.join(', ')}`);
|
|
163
|
+
if (!MODEL_SOURCES.includes(source))
|
|
164
|
+
throw new DecisionError('invalid_request', `source must be one of ${MODEL_SOURCES.join(', ')}`);
|
|
165
|
+
const model = MODEL_CATALOG.find(entry => entry.family === family);
|
|
166
|
+
const precision = preferredPrecision(model, recommend(detectHardware()).onnxPack);
|
|
167
|
+
if (precision === undefined)
|
|
168
|
+
throw new DecisionError('invalid_request', `${model.family} has no published package`);
|
|
169
|
+
return this.run('model-download', family, signal => downloadModel(this.home, model.family, precision, source, {
|
|
170
|
+
signal,
|
|
171
|
+
onProgress: (done, total) => this.update({ phase: 'download', done, total }),
|
|
172
|
+
}));
|
|
173
|
+
}
|
|
174
|
+
installModelDirectory(dir) {
|
|
175
|
+
if (typeof dir !== 'string' || dir.trim() === '')
|
|
176
|
+
throw new DecisionError('invalid_request', 'dir must name a model package directory');
|
|
177
|
+
return this.run('model-directory', dir, signal => installModelFromDirectory(this.home, dir.trim(), {
|
|
178
|
+
signal,
|
|
179
|
+
onProgress: (done, total) => this.update({ phase: 'verify', done, total }),
|
|
180
|
+
}));
|
|
181
|
+
}
|
|
182
|
+
removeModel(id) {
|
|
183
|
+
if (id === null || id === '')
|
|
184
|
+
throw new DecisionError('invalid_request', 'id is required');
|
|
185
|
+
this.assertIdle();
|
|
186
|
+
if (this.engine.local.status().selection?.model === id)
|
|
187
|
+
this.engine.local.stop();
|
|
188
|
+
return removeModel(this.home, id);
|
|
189
|
+
}
|
|
190
|
+
cancelJob() {
|
|
191
|
+
this.jobAbort?.abort(new DecisionError('invalid_request', 'cancelled'));
|
|
192
|
+
}
|
|
193
|
+
/** Judge a sample call with the saved settings, so the user sees the backend answer. */
|
|
194
|
+
async test(tool, args) {
|
|
195
|
+
if (typeof tool !== 'string' || tool === '' || typeof args !== 'object' || args === null || Array.isArray(args)) {
|
|
196
|
+
throw new DecisionError('invalid_request', 'tool must be a name and args an object');
|
|
197
|
+
}
|
|
198
|
+
return await this.engine.judge({ tool, arguments: args }, this.settings.engine());
|
|
199
|
+
}
|
|
200
|
+
/** One page of the decision log, newest first. */
|
|
201
|
+
recordPage(url) {
|
|
202
|
+
const param = (name) => {
|
|
203
|
+
const value = url.searchParams.get(name);
|
|
204
|
+
return value === null || value === '' ? undefined : value;
|
|
205
|
+
};
|
|
206
|
+
const limit = Number(param('limit') ?? PAGE_LIMIT_MAX);
|
|
207
|
+
if (!Number.isFinite(limit))
|
|
208
|
+
throw new DecisionError('invalid_request', 'limit must be a number');
|
|
209
|
+
const label = param('label');
|
|
210
|
+
const workspace = param('workspace');
|
|
211
|
+
const before = param('before');
|
|
212
|
+
return logged(() => this.records.page({
|
|
213
|
+
limit,
|
|
214
|
+
...label === undefined ? {} : { label },
|
|
215
|
+
...workspace === undefined ? {} : { workspace },
|
|
216
|
+
...before === undefined ? {} : { before },
|
|
217
|
+
markedOnly: param('marked') === '1',
|
|
218
|
+
}));
|
|
219
|
+
}
|
|
220
|
+
/** Mark the label a record should have had, or clear the mark with null. */
|
|
221
|
+
markRecord(id, correction) {
|
|
222
|
+
if (typeof id !== 'string' || id === '')
|
|
223
|
+
throw new DecisionError('invalid_request', 'id is required');
|
|
224
|
+
if (correction !== null && !DECISIONS.includes(correction)) {
|
|
225
|
+
throw new DecisionError('invalid_request', `correction must be null or one of ${DECISIONS.join(', ')}`);
|
|
226
|
+
}
|
|
227
|
+
return logged(() => this.records.mark(id, correction));
|
|
228
|
+
}
|
|
229
|
+
exportRecords() {
|
|
230
|
+
return logged(() => this.records.export());
|
|
231
|
+
}
|
|
232
|
+
clearRecords() {
|
|
233
|
+
this.records.clear();
|
|
234
|
+
}
|
|
235
|
+
close() {
|
|
236
|
+
this.cancelJob();
|
|
237
|
+
}
|
|
238
|
+
source(value) {
|
|
239
|
+
if (value === null)
|
|
240
|
+
return this.settings.downloadSource();
|
|
241
|
+
if (!DOWNLOAD_SOURCES.includes(value))
|
|
242
|
+
throw new DecisionError('invalid_request', `source must be one of ${DOWNLOAD_SOURCES.join(', ')}`);
|
|
243
|
+
return value;
|
|
244
|
+
}
|
|
245
|
+
assertIdle() {
|
|
246
|
+
if (this.running())
|
|
247
|
+
throw new DecisionError('invalid_request', 'another install is running');
|
|
248
|
+
}
|
|
249
|
+
running() {
|
|
250
|
+
return this.job !== undefined && !['done', 'failed', 'cancelled'].includes(this.job.phase);
|
|
251
|
+
}
|
|
252
|
+
run(kind, target, work) {
|
|
253
|
+
this.assertIdle();
|
|
254
|
+
const abort = new AbortController();
|
|
255
|
+
this.jobAbort = abort;
|
|
256
|
+
this.job = { kind, target, phase: kind === 'model-directory' ? 'verify' : 'download', startedAt: Date.now() };
|
|
257
|
+
const job = this.job;
|
|
258
|
+
work(abort.signal).then(() => {
|
|
259
|
+
this.finish(job, { phase: 'done' });
|
|
260
|
+
}, (error) => {
|
|
261
|
+
this.finish(job, abort.signal.aborted ? { phase: 'cancelled' } : { phase: 'failed', error: messageOf(error) });
|
|
262
|
+
});
|
|
263
|
+
return job;
|
|
264
|
+
}
|
|
265
|
+
update(change) {
|
|
266
|
+
if (this.job !== undefined && this.running())
|
|
267
|
+
this.job = { ...this.job, ...change };
|
|
268
|
+
}
|
|
269
|
+
finish(started, change) {
|
|
270
|
+
if (this.job?.startedAt !== started.startedAt)
|
|
271
|
+
return;
|
|
272
|
+
const { done: _done, total: _total, ...rest } = this.job;
|
|
273
|
+
this.job = { ...rest, ...change };
|
|
274
|
+
this.jobAbort = undefined;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
/** Every panel route: method, path below {@link ROUTE_PREFIX}, handler. */
|
|
278
|
+
export const ROUTES = [
|
|
279
|
+
['GET', 'state', panel => panel.state()],
|
|
280
|
+
['GET', 'cloud/models', panel => panel.cloudModels()],
|
|
281
|
+
['GET', 'cloud/endpoints', panel => panel.endpoints()],
|
|
282
|
+
['POST', 'cloud/probe', panel => panel.probe()],
|
|
283
|
+
['POST', 'cloud/check-lock', async (panel, request) => {
|
|
284
|
+
const body = await bodyOf(request);
|
|
285
|
+
return await panel.checkLock(body['model'], body['endpoint']);
|
|
286
|
+
}],
|
|
287
|
+
['POST', 'cloud/test', async (panel, request) => {
|
|
288
|
+
const body = await bodyOf(request);
|
|
289
|
+
return await panel.requestTest(body['endpoint'], body['input']);
|
|
290
|
+
}],
|
|
291
|
+
['POST', 'packs/install', async (panel, _request, url) => panel.installPack(url.searchParams.get('pack'), url.searchParams.get('source'))],
|
|
292
|
+
['POST', 'packs/remove', async (panel, _request, url) => panel.removePack(url.searchParams.get('pack'))],
|
|
293
|
+
['POST', 'models/download', async (panel, _request, url) => panel.downloadModel(url.searchParams.get('family'), url.searchParams.get('source'))],
|
|
294
|
+
['POST', 'models/install-dir', async (panel, request) => panel.installModelDirectory((await bodyOf(request))['dir'])],
|
|
295
|
+
['POST', 'models/remove', async (panel, _request, url) => panel.removeModel(url.searchParams.get('id'))],
|
|
296
|
+
['POST', 'job/cancel', async (panel) => panel.cancelJob()],
|
|
297
|
+
['POST', 'test', async (panel, request) => {
|
|
298
|
+
const body = await bodyOf(request);
|
|
299
|
+
return await panel.test(body['tool'], body['args']);
|
|
300
|
+
}],
|
|
301
|
+
['GET', 'records', async (panel, _request, url) => panel.recordPage(url)],
|
|
302
|
+
['POST', 'records/mark', async (panel, request) => {
|
|
303
|
+
const body = await bodyOf(request);
|
|
304
|
+
return panel.markRecord(body['id'], body['correction']);
|
|
305
|
+
}],
|
|
306
|
+
['POST', 'records/export', async (panel) => panel.exportRecords()],
|
|
307
|
+
['POST', 'records/clear', async (panel) => panel.clearRecords()],
|
|
308
|
+
];
|
|
309
|
+
/** Answer one panel request; failures carry their code, never a stack. */
|
|
310
|
+
export async function handle(panel, handler, request) {
|
|
311
|
+
try {
|
|
312
|
+
const value = await handler(panel, request, new URL(request.url));
|
|
313
|
+
return Response.json({ ok: true, value: value ?? null }, { headers: { 'cache-control': 'no-store' } });
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
const code = error instanceof DecisionError ? error.code : 'failed';
|
|
317
|
+
const status = code === 'invalid_request' ? 400 : code === 'not_signed_in' ? 401 : code === 'rate_limited' ? 429 : 500;
|
|
318
|
+
return Response.json({ ok: false, error: { code, message: messageOf(error) } }, { status, headers: { 'cache-control': 'no-store' } });
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
async function bodyOf(request) {
|
|
322
|
+
const value = await request.json().catch(() => undefined);
|
|
323
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
324
|
+
throw new DecisionError('invalid_request', 'the body must be a JSON object');
|
|
325
|
+
return value;
|
|
326
|
+
}
|
|
327
|
+
/** A decision log call; a record or filter the log does not know is the caller's mistake. */
|
|
328
|
+
function logged(work) {
|
|
329
|
+
try {
|
|
330
|
+
return work();
|
|
331
|
+
}
|
|
332
|
+
catch (error) {
|
|
333
|
+
if (error instanceof RangeError)
|
|
334
|
+
throw new DecisionError('invalid_request', error.message);
|
|
335
|
+
throw error;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
function messageOf(error) {
|
|
339
|
+
return error instanceof Error ? error.message : String(error);
|
|
340
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The decision log: every call the decision model or the review LLM took part in, kept in one
|
|
3
|
+
* JSON Lines file in this plugin's data directory with a rolling limit. It is written only while
|
|
4
|
+
* the user's `recordDecisions` switch is on.
|
|
5
|
+
*
|
|
6
|
+
* A record keeps the call's arguments as the agent sent them, so the user can look back at what
|
|
7
|
+
* was judged and export the records they marked as training samples; clearing the log is the only
|
|
8
|
+
* way to remove it.
|
|
9
|
+
*/
|
|
10
|
+
import { type Decision, type DecisionHome, type DecisionOptions } from '@openvulcan/vulcan-decision';
|
|
11
|
+
/** Most records the log keeps; older ones are dropped when the log is compacted. */
|
|
12
|
+
export declare const RETENTION_RECORDS = 5000;
|
|
13
|
+
/** Most days the log keeps a record. */
|
|
14
|
+
export declare const RETENTION_DAYS = 30;
|
|
15
|
+
/** Most records one page returns. */
|
|
16
|
+
export declare const PAGE_LIMIT_MAX = 200;
|
|
17
|
+
/** Log file name; the version changes whenever the line format changes, and other versions are not read. */
|
|
18
|
+
export declare const RECORDS_FILE = "records.v1.jsonl";
|
|
19
|
+
/**
|
|
20
|
+
* How a recorded call ended.
|
|
21
|
+
*
|
|
22
|
+
* - `judge_allowed` / `review_allowed`: the decision model or the review LLM let it run.
|
|
23
|
+
* - `review_denied`: the review LLM refused it without asking anyone (a forbid rule, or an
|
|
24
|
+
* unattended session).
|
|
25
|
+
* - `unattended_denied`: nobody could be asked and no review allowed it.
|
|
26
|
+
* - `user_allowed` / `user_denied`: the user answered the approval prompt.
|
|
27
|
+
* - `approval_expired`: the approval countdown ran out.
|
|
28
|
+
* - `not_asked`: the user could not be asked, so the call was refused.
|
|
29
|
+
* - `host_denied`: another dsh integration refused the call before the user was asked.
|
|
30
|
+
*/
|
|
31
|
+
export type RecordOutcome = 'judge_allowed' | 'review_allowed' | 'review_denied' | 'unattended_denied' | 'user_allowed' | 'user_denied' | 'approval_expired' | 'not_asked' | 'host_denied';
|
|
32
|
+
/** What the decision model read and produced, when the call could be rendered. */
|
|
33
|
+
export interface RecordTrace {
|
|
34
|
+
readonly backend: 'cloud' | 'local';
|
|
35
|
+
/** Cloud model id or installed local model id; empty when the default cloud model did not resolve. */
|
|
36
|
+
readonly model: string;
|
|
37
|
+
/** Cloud endpoint id or local runtime pack, when the model answered. */
|
|
38
|
+
readonly via?: string;
|
|
39
|
+
readonly policyVersion: string;
|
|
40
|
+
readonly settings: DecisionOptions;
|
|
41
|
+
/** The call text the model read. */
|
|
42
|
+
readonly rendered: string;
|
|
43
|
+
/** Raw allow/review/block scores. */
|
|
44
|
+
readonly logits?: readonly number[];
|
|
45
|
+
readonly inputTokens?: number;
|
|
46
|
+
}
|
|
47
|
+
/** The decision model's part of one recorded call. */
|
|
48
|
+
export interface RecordJudge {
|
|
49
|
+
/** Absent when the model could not answer. */
|
|
50
|
+
readonly label?: Decision;
|
|
51
|
+
/** Why the model could not answer. */
|
|
52
|
+
readonly error?: string;
|
|
53
|
+
/** Set when a rule decided without evaluating the model. */
|
|
54
|
+
readonly reasonCode?: string;
|
|
55
|
+
readonly elapsedMs: number;
|
|
56
|
+
readonly trace?: RecordTrace;
|
|
57
|
+
}
|
|
58
|
+
/** The review LLM's part of one recorded call. */
|
|
59
|
+
export interface RecordReview {
|
|
60
|
+
/** Absent when the review failed. */
|
|
61
|
+
readonly verdict?: 'allow' | 'review' | 'deny' | 'forbid';
|
|
62
|
+
readonly reason?: string;
|
|
63
|
+
readonly error?: string;
|
|
64
|
+
readonly elapsedMs: number;
|
|
65
|
+
}
|
|
66
|
+
/** One recorded call. */
|
|
67
|
+
export interface DecisionRecord {
|
|
68
|
+
readonly id: string;
|
|
69
|
+
/** When the call reached the gate, in Unix milliseconds. */
|
|
70
|
+
readonly judgedAtMs: number;
|
|
71
|
+
readonly sessionId: string;
|
|
72
|
+
readonly workspaceRoot?: string;
|
|
73
|
+
readonly toolName: string;
|
|
74
|
+
/** The arguments as the agent sent them, as JSON. */
|
|
75
|
+
readonly arguments: string;
|
|
76
|
+
/** Whether someone could be asked. */
|
|
77
|
+
readonly attended: boolean;
|
|
78
|
+
readonly judge: RecordJudge;
|
|
79
|
+
readonly review?: RecordReview;
|
|
80
|
+
readonly outcome: RecordOutcome;
|
|
81
|
+
/** The label the user says the decision model should have given. */
|
|
82
|
+
readonly correction?: Decision;
|
|
83
|
+
}
|
|
84
|
+
/** A finished call before it is given its identity. */
|
|
85
|
+
export type NewRecord = Omit<DecisionRecord, 'id' | 'correction'>;
|
|
86
|
+
/** One page request, newest first. */
|
|
87
|
+
export interface PageRequest {
|
|
88
|
+
/** Model result to keep: a decision or `failed`. */
|
|
89
|
+
readonly label?: string;
|
|
90
|
+
readonly workspace?: string;
|
|
91
|
+
readonly markedOnly?: boolean;
|
|
92
|
+
/** Return records older than this record. */
|
|
93
|
+
readonly before?: string;
|
|
94
|
+
readonly limit: number;
|
|
95
|
+
}
|
|
96
|
+
export interface RecordPage {
|
|
97
|
+
readonly records: readonly DecisionRecord[];
|
|
98
|
+
readonly hasMore: boolean;
|
|
99
|
+
readonly total: number;
|
|
100
|
+
readonly marked: number;
|
|
101
|
+
/** Workspaces of every kept record, for the filter. */
|
|
102
|
+
readonly workspaces: readonly string[];
|
|
103
|
+
readonly retentionRecords: number;
|
|
104
|
+
readonly retentionDays: number;
|
|
105
|
+
}
|
|
106
|
+
export interface RecordExport {
|
|
107
|
+
readonly fileName: string;
|
|
108
|
+
/** JSON Lines training samples. */
|
|
109
|
+
readonly content: string;
|
|
110
|
+
readonly count: number;
|
|
111
|
+
}
|
|
112
|
+
/** The log; loaded on first use and kept in memory with the file as its authority. */
|
|
113
|
+
export declare class RecordStore {
|
|
114
|
+
private readonly home;
|
|
115
|
+
private readonly now;
|
|
116
|
+
private loaded;
|
|
117
|
+
constructor(home: DecisionHome, now?: () => number);
|
|
118
|
+
/** Keep one finished call. */
|
|
119
|
+
append(record: NewRecord): DecisionRecord;
|
|
120
|
+
/** One page of matching records, newest first. */
|
|
121
|
+
page(request: PageRequest): RecordPage;
|
|
122
|
+
/** Mark the label the model should have given a record, or clear the mark with null. */
|
|
123
|
+
mark(id: string, correction: Decision | null): DecisionRecord;
|
|
124
|
+
/** Export the marked records that carry model input as training samples. */
|
|
125
|
+
export(): RecordExport;
|
|
126
|
+
/** Remove every record and the file; this also recovers from an unreadable file. */
|
|
127
|
+
clear(): void;
|
|
128
|
+
private path;
|
|
129
|
+
private records;
|
|
130
|
+
/** Read the file, dropping records past retention; a line that is not a record makes the whole file unreadable until it is cleared. */
|
|
131
|
+
private load;
|
|
132
|
+
private rewrite;
|
|
133
|
+
}
|