openmerit 0.1.4 → 0.1.6-preview.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.
- package/CHANGELOG.md +40 -0
- package/README.md +121 -386
- package/dist/core/src/index.d.ts +101 -0
- package/dist/core/src/index.js +1649 -0
- package/dist/core/src/store.d.ts +35 -0
- package/dist/core/src/store.js +102 -0
- package/dist/pi/src/index.d.ts +32 -0
- package/dist/pi/src/index.js +794 -0
- package/dist/pi/src/scheduler.d.ts +11 -0
- package/dist/pi/src/scheduler.js +137 -0
- package/dist/pi/src/wakeup.d.ts +2 -0
- package/dist/pi/src/wakeup.js +108 -0
- package/dist/protocol/src/index.d.ts +484 -0
- package/dist/protocol/src/index.js +47 -0
- package/dist/protocol/src/schemas.d.ts +576 -0
- package/dist/protocol/src/schemas.js +280 -0
- package/dist/terminal/public/app.js +297 -0
- package/dist/terminal/public/brands/anthropic.png +0 -0
- package/dist/terminal/public/brands/baai.png +0 -0
- package/dist/terminal/public/brands/baseten.png +0 -0
- package/dist/terminal/public/brands/cerebras.png +0 -0
- package/dist/terminal/public/brands/cohere.png +0 -0
- package/dist/terminal/public/brands/deepseek.ico +0 -0
- package/dist/terminal/public/brands/google.png +0 -0
- package/dist/terminal/public/brands/groq.ico +0 -0
- package/dist/terminal/public/brands/lm-studio.png +0 -0
- package/dist/terminal/public/brands/meta.ico +0 -0
- package/dist/terminal/public/brands/mistral.png +0 -0
- package/dist/terminal/public/brands/nomic.png +0 -0
- package/dist/terminal/public/brands/ollama.png +0 -0
- package/dist/terminal/public/brands/openai.png +0 -0
- package/dist/terminal/public/brands/openrouter.png +0 -0
- package/dist/terminal/public/brands/qwen.png +0 -0
- package/dist/terminal/public/brands/vllm.ico +0 -0
- package/dist/terminal/public/brands/vllm.png +0 -0
- package/dist/terminal/public/favicon.svg +1 -0
- package/dist/terminal/public/flow.css +1 -0
- package/dist/terminal/public/flow.js +770 -0
- package/dist/terminal/public/index.html +21 -0
- package/dist/terminal/public/styles.css +779 -0
- package/dist/terminal/src/activity-merge.mjs +64 -0
- package/dist/terminal/src/browser.mjs +29 -0
- package/dist/terminal/src/cli.mjs +60 -0
- package/dist/terminal/src/collect.mjs +311 -0
- package/dist/terminal/src/discovery.mjs +93 -0
- package/dist/terminal/src/hardware.mjs +57 -0
- package/dist/terminal/src/project-activity.mjs +156 -0
- package/dist/terminal/src/sample.mjs +171 -0
- package/dist/terminal/src/server.mjs +56 -0
- package/dist/terminal/src/services.mjs +62 -0
- package/dist/terminal/src/topology.mjs +30 -0
- package/docs/adapter-guide.md +189 -0
- package/docs/architecture.md +59 -0
- package/docs/automation.md +74 -0
- package/docs/budgets.md +37 -0
- package/docs/commands.md +85 -0
- package/docs/demo-backfill.md +29 -0
- package/docs/demo-fieldkit.md +47 -0
- package/docs/demo-placement.md +30 -0
- package/docs/demo-spam.md +15 -0
- package/docs/demo-support.md +42 -0
- package/docs/demo.md +57 -0
- package/docs/first-trial.md +60 -0
- package/docs/getting-started.md +65 -0
- package/docs/index.md +40 -0
- package/docs/inference-terminal.md +439 -0
- package/docs/lifecycle.md +30 -0
- package/docs/memo.md +126 -0
- package/docs/metrics-and-evidence.md +48 -0
- package/docs/operations.md +40 -0
- package/docs/pareto-spec.md +76 -0
- package/docs/pi-extension.md +54 -0
- package/docs/roadmap.md +28 -0
- package/docs/security.md +37 -0
- package/docs/site-artwork-linocut.md +23 -0
- package/docs/site-artwork-miniature-diverse.md +28 -0
- package/docs/site-artwork-miniature.md +26 -0
- package/docs/site-demo.md +177 -0
- package/docs/site-design.md +94 -0
- package/docs/site-documentation.md +83 -0
- package/docs/site-dynamic-og.md +35 -0
- package/docs/site-faq-maintenance.md +115 -0
- package/docs/site-hero-resolution.md +60 -0
- package/docs/site-illustration-sequences.md +227 -0
- package/docs/site-inference-terminal.md +203 -0
- package/docs/site-memo.md +39 -0
- package/docs/site-og-image.md +38 -0
- package/docs/site-og-workshop.md +21 -0
- package/docs/site-section-artwork.md +56 -0
- package/docs/site-skill-review.md +57 -0
- package/docs/site-terminal-preview.md +85 -0
- package/docs/testing.md +118 -0
- package/docs/troubleshooting.md +55 -0
- package/docs/ux-reference.md +32 -0
- package/package.json +74 -42
- package/benchmark/invoice_ocr/data/invoice_01_ground_truth.json +0 -38
- package/benchmark/invoice_ocr/data/invoice_01_row_2.jpg +0 -0
- package/benchmark/invoice_ocr/data/invoice_02_ground_truth.json +0 -32
- package/benchmark/invoice_ocr/data/invoice_02_row_5.jpg +0 -0
- package/benchmark/invoice_ocr/data/invoice_03_ground_truth.json +0 -26
- package/benchmark/invoice_ocr/data/invoice_03_row_6.jpg +0 -0
- package/benchmark/invoice_ocr/data/invoice_04_ground_truth.json +0 -26
- package/benchmark/invoice_ocr/data/invoice_04_row_7.jpg +0 -0
- package/benchmark/invoice_ocr/data/invoice_05_ground_truth.json +0 -38
- package/benchmark/invoice_ocr/data/invoice_05_row_947.jpg +0 -0
- package/benchmark/invoice_ocr/data/invoice_06_ground_truth.json +0 -38
- package/benchmark/invoice_ocr/data/invoice_06_row_948.jpg +0 -0
- package/benchmark/invoice_ocr/data/invoice_07_ground_truth.json +0 -20
- package/benchmark/invoice_ocr/data/invoice_07_row_949.jpg +0 -0
- package/benchmark/invoice_ocr/data/invoice_08_ground_truth.json +0 -38
- package/benchmark/invoice_ocr/data/invoice_08_row_1888.jpg +0 -0
- package/benchmark/invoice_ocr/data/invoice_09_ground_truth.json +0 -26
- package/benchmark/invoice_ocr/data/invoice_09_row_1890.jpg +0 -0
- package/benchmark/invoice_ocr/data/invoice_10_ground_truth.json +0 -20
- package/benchmark/invoice_ocr/data/invoice_10_row_1892.jpg +0 -0
- package/benchmark/invoice_ocr/data/manifest.json +0 -97
- package/dist/benchmarks.js +0 -98
- package/dist/catalog.js +0 -61
- package/dist/cli.js +0 -188
- package/dist/daemon.js +0 -407
- package/dist/diagnostics.js +0 -227
- package/dist/frontier.js +0 -56
- package/dist/harness.js +0 -1
- package/dist/integrations.js +0 -19
- package/dist/invoice-eval.js +0 -33
- package/dist/invoice-score.js +0 -124
- package/dist/judge.js +0 -43
- package/dist/llm.js +0 -207
- package/dist/pi-config.js +0 -46
- package/dist/pi-trials.js +0 -373
- package/dist/policy.js +0 -185
- package/dist/providers.js +0 -1
- package/dist/recommend.js +0 -76
- package/dist/routes.js +0 -74
- package/dist/standalone.js +0 -224
- package/dist/store.js +0 -89
- package/dist/strategist.js +0 -68
- package/dist/task-input.js +0 -54
- package/dist/traces.js +0 -127
- package/dist/trials.js +0 -140
- package/dist/types.js +0 -2
- package/examples/invoice-prompt.txt +0 -19
- package/examples/task.example.json +0 -7
- package/extension/openmerit.ts +0 -947
- package/instructions/OPENMERIT.md +0 -63
- package/instructions/openmerit.policy.json +0 -37
- package/rules.md +0 -43
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { opendir, lstat, open, realpath } from 'node:fs/promises';
|
|
2
|
+
import { constants } from 'node:fs';
|
|
3
|
+
import { join, relative, sep } from 'node:path';
|
|
4
|
+
import { createHash } from 'node:crypto';
|
|
5
|
+
import { StringDecoder } from 'node:string_decoder';
|
|
6
|
+
|
|
7
|
+
const FILE_BYTES = 512 * 1024, TOTAL_BYTES = 4 * 1024 * 1024;
|
|
8
|
+
const MAX_FILES = 128, MAX_ENTRIES = 2048, MAX_RECORDS = 10000;
|
|
9
|
+
const finite = value => typeof value === 'number' && Number.isFinite(value) && value >= 0;
|
|
10
|
+
const object = value => value && typeof value === 'object' && !Array.isArray(value);
|
|
11
|
+
const id = value => typeof value === 'string' && value.length > 0 && value.length <= 160;
|
|
12
|
+
const hash = value => createHash('sha256').update(value).digest('hex').slice(0, 24);
|
|
13
|
+
|
|
14
|
+
// Recognize recorded request objects, not arbitrary JSON or a model catalog.
|
|
15
|
+
// No source-code execution, prompt interpretation, or filename-based providers.
|
|
16
|
+
export function projectActivityRecord(value, sourceId, file, normalize) {
|
|
17
|
+
if (!object(value)) return null;
|
|
18
|
+
const body = object(value.response) ? value.response : value;
|
|
19
|
+
const usage = body.usage;
|
|
20
|
+
const responses = body.object === 'response';
|
|
21
|
+
const chat = body.object === 'chat.completion';
|
|
22
|
+
const structured = !responses && !chat && id(value.id) && typeof value.occurredAt === 'string';
|
|
23
|
+
if (!responses && !chat && !structured) return null;
|
|
24
|
+
const measured = object(usage) && [usage.input_tokens, usage.output_tokens, usage.prompt_tokens, usage.completion_tokens].some(finite);
|
|
25
|
+
const terminal = ['completed', 'failed', 'running'].includes(value.status);
|
|
26
|
+
if (!measured && !(structured && terminal && (finite(value.httpStatus) || value.event === 'inference.request'))) return null;
|
|
27
|
+
const requestId = structured ? value.requestId : body.id;
|
|
28
|
+
const recordId = structured ? value.id : body.id;
|
|
29
|
+
if (!id(recordId) || !id(body.model ?? value.model)) return null;
|
|
30
|
+
let occurredAt = value.occurredAt;
|
|
31
|
+
if (!occurredAt && (responses || chat)) {
|
|
32
|
+
const seconds = responses ? body.created_at : body.created;
|
|
33
|
+
if (finite(seconds) && seconds < 8640000000000) occurredAt = new Date(seconds * 1000).toISOString();
|
|
34
|
+
}
|
|
35
|
+
if (typeof occurredAt !== 'string' || !Number.isFinite(Date.parse(occurredAt))) return null;
|
|
36
|
+
// An API-compatible response shape doesn't prove which company served it.
|
|
37
|
+
// Connection, workload, and agent/task fields must be explicitly recorded.
|
|
38
|
+
const status = structured ? value.status : responses
|
|
39
|
+
? ({ completed: 'completed', failed: 'failed', cancelled: 'failed', incomplete: 'failed', in_progress: 'running', queued: 'running' })[body.status]
|
|
40
|
+
: body.choices?.length && body.choices.every(choice => typeof choice.finish_reason === 'string')
|
|
41
|
+
? body.choices.some(choice => ['error', 'content_filter'].includes(choice.finish_reason)) ? 'failed' : 'completed' : null;
|
|
42
|
+
if (!['completed', 'failed', 'running'].includes(status)) return null;
|
|
43
|
+
const record = normalize({
|
|
44
|
+
...value, id: recordId, requestId, occurredAt, status, model: body.model ?? value.model,
|
|
45
|
+
provider: value.provider ?? body.provider,
|
|
46
|
+
workload: value.workload || value.application || file,
|
|
47
|
+
inputTokens: structured && finite(value.inputTokens) ? value.inputTokens : usage?.input_tokens ?? usage?.prompt_tokens,
|
|
48
|
+
outputTokens: structured && finite(value.outputTokens) ? value.outputTokens : usage?.output_tokens ?? usage?.completion_tokens,
|
|
49
|
+
cacheReadTokens: structured && finite(value.cacheReadTokens) ? value.cacheReadTokens : usage?.input_tokens_details?.cached_tokens ?? usage?.prompt_tokens_details?.cached_tokens,
|
|
50
|
+
cost: finite(value.cost) ? value.cost : usage?.cost,
|
|
51
|
+
}, sourceId);
|
|
52
|
+
return record && { ...record, evidenceFormat: responses ? 'responses' : chat ? 'chat-completion' : 'structured-request' };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function createProjectActivity(root, normalize, { exclude = [] } = {}) {
|
|
56
|
+
const readers = new Map();
|
|
57
|
+
return async function collect() {
|
|
58
|
+
const files = [], issues = []; let visited = 0, omitted = false, budget = TOTAL_BYTES;
|
|
59
|
+
const canonicalRoot = await realpath(root);
|
|
60
|
+
const excludedPaths = exclude.map(path => join(canonicalRoot, relative(root, path)));
|
|
61
|
+
const allowed = path => path.startsWith(canonicalRoot + sep);
|
|
62
|
+
async function scan(path, depth) {
|
|
63
|
+
let directory;
|
|
64
|
+
try {
|
|
65
|
+
const info = await lstat(path);
|
|
66
|
+
if (!info.isDirectory() || info.isSymbolicLink() || await realpath(path) !== path) return;
|
|
67
|
+
directory = await opendir(path);
|
|
68
|
+
for await (const entry of directory) {
|
|
69
|
+
if (++visited > MAX_ENTRIES) { omitted = true; break; }
|
|
70
|
+
if (entry.name.startsWith('.') || entry.isSymbolicLink()) continue;
|
|
71
|
+
const child = join(path, entry.name);
|
|
72
|
+
if (entry.isDirectory() && depth > 0) await scan(child, depth - 1);
|
|
73
|
+
else if (entry.isFile() && /\.jsonl(?:\.\d+)?$/.test(entry.name) && !excludedPaths.includes(child)) {
|
|
74
|
+
const info = await lstat(child);
|
|
75
|
+
if (info.isFile() && !info.isSymbolicLink()) files.push({ path: child, info });
|
|
76
|
+
}
|
|
77
|
+
if (visited >= MAX_ENTRIES) { omitted = true; break; }
|
|
78
|
+
}
|
|
79
|
+
} catch (error) { if (error.code !== 'ENOENT') issues.push(relative(canonicalRoot, path)); }
|
|
80
|
+
}
|
|
81
|
+
// Fixed project-local log roots. Never recursively inspect the repository,
|
|
82
|
+
// home directory, dependency trees, credentials, or arbitrary databases.
|
|
83
|
+
for (const name of ['logs', 'runs', 'results']) {
|
|
84
|
+
if (visited >= MAX_ENTRIES) break;
|
|
85
|
+
await scan(join(canonicalRoot, name), 2);
|
|
86
|
+
}
|
|
87
|
+
files.sort((a, b) => b.info.mtimeMs - a.info.mtimeMs || a.path.localeCompare(b.path));
|
|
88
|
+
if (files.length > MAX_FILES) omitted = true;
|
|
89
|
+
const chosen = files.slice(0, MAX_FILES), active = new Set(chosen.map(file => file.path));
|
|
90
|
+
for (const path of readers.keys()) if (!active.has(path)) readers.delete(path);
|
|
91
|
+
const sources = [], records = [];
|
|
92
|
+
for (const file of chosen) {
|
|
93
|
+
const name = relative(canonicalRoot, file.path), sourceId = `project-log:${hash(name)}`;
|
|
94
|
+
const source = { id: sourceId, name: 'Project request log', kind: 'Project activity', detail: name,
|
|
95
|
+
access: 'Selected project', coverage: ['Recorded requests', 'Reported identities', 'Tokens', 'Reported or estimated cost'],
|
|
96
|
+
note: 'Supported request metadata read from an existing JSONL log. Names and measurements are copied from recorded fields. Prompts, outputs, headers, and credentials are discarded. No account API or inference call is made.' };
|
|
97
|
+
try {
|
|
98
|
+
if (!allowed(file.path) || await realpath(file.path) !== file.path) throw new Error('Outside project or symlink');
|
|
99
|
+
const handle = await open(file.path, constants.O_RDONLY | (constants.O_NOFOLLOW || 0) | (constants.O_NONBLOCK || 0));
|
|
100
|
+
try {
|
|
101
|
+
const info = await handle.stat();
|
|
102
|
+
if (!info.isFile()) throw new Error('Not a regular file');
|
|
103
|
+
let reader = readers.get(file.path);
|
|
104
|
+
const identity = `${info.dev}:${info.ino}`;
|
|
105
|
+
if (!reader || reader.identity !== identity || info.size < reader.offset || info.size === reader.offset && info.mtimeMs !== reader.mtime) {
|
|
106
|
+
reader = { identity, offset: 0, mtime: 0, pending: '', decoder: new StringDecoder('utf8'), records: new Map(), invalid: 0, unsupported: 0, truncated: false };
|
|
107
|
+
}
|
|
108
|
+
const limit = Math.min(FILE_BYTES, budget);
|
|
109
|
+
if (limit === 0 && info.size > reader.offset) { omitted = true; sources.push({ ...source, state: 'partial', records: 0, note: source.note + ' Waiting for the next bounded read.' }); continue; }
|
|
110
|
+
let skip = false;
|
|
111
|
+
if (info.size - reader.offset > limit) { reader.offset = info.size - limit; reader.pending = ''; reader.decoder = new StringDecoder('utf8'); reader.records.clear(); reader.truncated = true; skip = true; }
|
|
112
|
+
const buffer = Buffer.alloc(Math.min(limit, info.size - reader.offset));
|
|
113
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, reader.offset);
|
|
114
|
+
reader.offset += bytesRead; reader.mtime = info.mtimeMs; budget -= bytesRead;
|
|
115
|
+
const lines = (reader.pending + reader.decoder.write(buffer.subarray(0, bytesRead))).split('\n');
|
|
116
|
+
reader.pending = lines.pop() ?? ''; if (skip) lines.shift();
|
|
117
|
+
for (const line of lines) {
|
|
118
|
+
if (!line.trim()) continue;
|
|
119
|
+
try {
|
|
120
|
+
const record = projectActivityRecord(JSON.parse(line), sourceId, name, normalize);
|
|
121
|
+
if (!record) { reader.unsupported++; continue; }
|
|
122
|
+
reader.records.delete(record.id); reader.records.set(record.id, record);
|
|
123
|
+
} catch { reader.invalid++; }
|
|
124
|
+
}
|
|
125
|
+
if (reader.pending.length > 128 * 1024) { reader.pending = ''; reader.invalid++; }
|
|
126
|
+
for (const [key, record] of reader.records) if (Date.parse(record.occurredAt) < Date.now() - 86400000) reader.records.delete(key);
|
|
127
|
+
while (reader.records.size > 1000) { reader.records.delete(reader.records.keys().next().value); reader.truncated = true; }
|
|
128
|
+
readers.set(file.path, reader);
|
|
129
|
+
const retained = [...reader.records.values()].filter(record => Date.parse(record.occurredAt) <= Date.now());
|
|
130
|
+
records.push(...retained);
|
|
131
|
+
// Even an unsupported log is visible as a coverage gap, never a
|
|
132
|
+
// silently successful connection with zero fabricated requests.
|
|
133
|
+
sources.push({ ...source, state: reader.invalid || reader.unsupported || reader.truncated ? 'partial' : 'connected', records: retained.length,
|
|
134
|
+
updatedAt: new Date().toISOString(), invalid: reader.invalid, unsupported: reader.unsupported, truncated: reader.truncated,
|
|
135
|
+
note: source.note + (reader.truncated ? ' The bounded reader omitted older records.' : '') + (reader.invalid || reader.unsupported ? ' Some lines were malformed or did not match a supported request format.' : '') });
|
|
136
|
+
} finally { await handle.close(); }
|
|
137
|
+
} catch { readers.delete(file.path); sources.push({ ...source, state: 'unavailable', records: 0 }); }
|
|
138
|
+
}
|
|
139
|
+
records.sort((a, b) => Date.parse(b.occurredAt) - Date.parse(a.occurredAt));
|
|
140
|
+
if (records.length > MAX_RECORDS) {
|
|
141
|
+
omitted = true;
|
|
142
|
+
const retained = new Set(records.slice(0, MAX_RECORDS));
|
|
143
|
+
for (const [path, reader] of readers) {
|
|
144
|
+
let removed = false;
|
|
145
|
+
for (const [id, record] of reader.records) if (!retained.has(record)) { reader.records.delete(id); removed = true; }
|
|
146
|
+
if (removed) {
|
|
147
|
+
reader.truncated = true;
|
|
148
|
+
const source = sources.find(source => source.detail === relative(canonicalRoot, path));
|
|
149
|
+
if (source) { source.state = 'partial'; source.records = reader.records.size; source.truncated = true; source.note += ' The shared 10,000-record cache limit omitted older records.'; }
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (omitted || issues.length) sources.push({ id: 'project-log-coverage', name: 'Project log coverage', kind: 'Project activity', state: 'partial', detail: 'logs/ · runs/ · results/', note: 'The bounded scan omitted files or records, or a log directory was unreadable. These sources do not establish complete project usage.' });
|
|
154
|
+
return { records: records.slice(0, MAX_RECORDS), sources };
|
|
155
|
+
};
|
|
156
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// Deterministic, deliberately fictional data. Never mixed with local sources.
|
|
2
|
+
// Rates, latencies, allocations, and utilization are illustrative, not benchmarks.
|
|
3
|
+
const GiB = 1024 ** 3;
|
|
4
|
+
const TEAMS = [
|
|
5
|
+
['Support', ['billing escalation', 'refund queue', 'onboarding tickets', 'SLA exceptions', 'customer handoff', 'incident follow-up', 'CSAT themes']],
|
|
6
|
+
['Engineering', ['checkout regression', 'API migration', 'dependency upgrade', 'flaky test suite', 'release notes', 'auth refactor', 'performance budget']],
|
|
7
|
+
['Research', ['competitor landscape', 'market signals', 'customer interviews', 'pricing study', 'industry brief', 'source verification', 'product opportunities']],
|
|
8
|
+
['Finance', ['invoice exceptions', 'expense reconciliation', 'renewal forecast', 'vendor spending', 'revenue variance', 'purchase approvals', 'quarterly narrative']],
|
|
9
|
+
['Security', ['access review', 'dependency advisory', 'policy exceptions', 'incident timeline', 'threat brief', 'audit evidence', 'service permissions']],
|
|
10
|
+
['Operations', ['capacity forecast', 'deployment checklist', 'on-call handoff', 'runbook updates', 'queue anomalies', 'availability report', 'change requests']],
|
|
11
|
+
['Sales', ['account research', 'proposal draft', 'pipeline review', 'meeting preparation', 'lead enrichment', 'deal risks', 'renewal outreach']],
|
|
12
|
+
['Legal', ['supplier terms', 'contract changes', 'policy updates', 'obligation register', 'clause comparison', 'document redaction', 'agreement summary']],
|
|
13
|
+
['Data', ['schema changes', 'quality checks', 'warehouse queries', 'catalog enrichment', 'metric definitions', 'report narrative', 'lineage review']],
|
|
14
|
+
['Knowledge', ['document ingestion', 'retrieval quality', 'index refresh', 'duplicate articles', 'citation coverage', 'search relevance', 'content freshness']],
|
|
15
|
+
];
|
|
16
|
+
// name, provider/runtime, location, typical latency, fictional rate multiplier, role
|
|
17
|
+
const MODEL_DEFINITIONS = [
|
|
18
|
+
['GPT-5', 'OpenAI', 'cloud', 3600, 2.2, 'generation'],
|
|
19
|
+
['Claude Sonnet 4.5', 'Anthropic', 'cloud', 4200, 2.6, 'generation'],
|
|
20
|
+
['Gemini 2.5 Flash', 'Google', 'cloud', 900, .3, 'generation'],
|
|
21
|
+
['DeepSeek R1', 'Baseten', 'cloud', 14000, .8, 'reasoning'],
|
|
22
|
+
['Mistral Medium 3', 'Mistral', 'cloud', 1800, .7, 'generation'],
|
|
23
|
+
['GPT-5 mini', 'OpenAI', 'cloud', 1400, .4, 'generation'],
|
|
24
|
+
['Claude Opus 4.1', 'Anthropic', 'cloud', 11000, 6, 'reasoning'],
|
|
25
|
+
['Gemini 2.5 Pro', 'Google', 'cloud', 7000, 1.8, 'reasoning'],
|
|
26
|
+
['GPT-5 nano', 'OpenAI', 'cloud', 480, .08, 'generation'],
|
|
27
|
+
['Claude Haiku 3.5', 'Anthropic', 'cloud', 700, .2, 'generation'],
|
|
28
|
+
['gpt-oss-120b', 'Groq', 'cloud', 2000, .25, 'generation'],
|
|
29
|
+
['qwen-3.8-27b', 'Cerebras', 'cloud', 2400, .65, 'generation'],
|
|
30
|
+
['text-embedding-3-large', 'OpenAI', 'cloud', 160, .02, 'embedding'],
|
|
31
|
+
['embed-v4', 'Cohere', 'cloud', 190, .025, 'embedding'],
|
|
32
|
+
['Qwen3 · 8B', 'Ollama', 'local', 1400, 0, 'generation'],
|
|
33
|
+
['Qwen3 · 30B', 'Ollama', 'local', 4200, 0, 'reasoning'],
|
|
34
|
+
['Llama 3.3 · 70B', 'vLLM', 'local', 3100, 0, 'generation'],
|
|
35
|
+
['Gemma 3 · 27B', 'LM Studio', 'local', 2600, 0, 'generation'],
|
|
36
|
+
['DeepSeek R1 · 14B', 'Ollama', 'local', 6200, 0, 'reasoning'],
|
|
37
|
+
['nomic-embed-text', 'Ollama', 'local', 90, 0, 'embedding'],
|
|
38
|
+
['bge-reranker-v2-m3', 'vLLM', 'local', 75, 0, 'reranking'],
|
|
39
|
+
];
|
|
40
|
+
// Explicit fictional metadata, never inferred from the serving company.
|
|
41
|
+
const MODEL_CREATORS = ['OpenAI', 'Anthropic', 'Google', 'DeepSeek', 'Mistral', 'OpenAI', 'Anthropic', 'Google', 'OpenAI', 'Anthropic', 'OpenAI', 'Qwen', 'OpenAI', 'Cohere', 'Qwen', 'Qwen', 'Meta', 'Google', 'DeepSeek', 'Nomic', 'BAAI'];
|
|
42
|
+
const ROUTERS = ['router.quality', 'router.latency', 'router.cost', 'router.private'];
|
|
43
|
+
const GATEWAYS = ['llm-gateway', 'llm-gateway-eu', 'batch-proxy'];
|
|
44
|
+
const ROLES = ['Intake', 'Research', 'Delivery', 'Review'];
|
|
45
|
+
const MODEL_PATHS = [
|
|
46
|
+
['direct', 'router'], ['gateway', 'router'], ['direct', 'gateway'],
|
|
47
|
+
['router', 'gateway'], ['direct'], ['direct', 'router', 'gateway'],
|
|
48
|
+
['gateway'], ['router'], ['direct'], ['gateway', 'router'],
|
|
49
|
+
['gateway', 'direct'], ['gateway'], ['direct'], ['gateway'],
|
|
50
|
+
['local', 'router'], ['local', 'router'], ['local', 'gateway'],
|
|
51
|
+
['local'], ['router', 'local'], ['local'], ['local', 'gateway'],
|
|
52
|
+
];
|
|
53
|
+
const TEAM_MODELS = [
|
|
54
|
+
[2, 5, 8, 9, 14], [0, 1, 3, 5, 15, 16], [3, 6, 7, 10, 11],
|
|
55
|
+
[0, 4, 7, 14, 17], [1, 3, 6, 15, 18], [2, 4, 5, 14, 16, 18],
|
|
56
|
+
[1, 2, 5, 9, 11], [0, 1, 6, 7, 17], [0, 5, 10, 14, 15, 16],
|
|
57
|
+
[2, 8, 11, 14, 17],
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
export function sampleSnapshot(now = Date.now()) {
|
|
61
|
+
let seed = 41;
|
|
62
|
+
const random = () => { seed = (seed * 16807) % 2147483647; return (seed - 1) / 2147483646; };
|
|
63
|
+
const choose = values => values[Math.floor(random() * values.length)];
|
|
64
|
+
const agents = TEAMS.flatMap(([team], teamIndex) => ROLES.map((role, index) => ({ id: `${team.toLowerCase()}.${role.toLowerCase()}`, name: index === 3 ? null : `${team.toLowerCase()}.${role.toLowerCase()}` })));
|
|
65
|
+
const tasks = TEAMS.flatMap(([, subjects], teamIndex) => subjects.flatMap((subject, index) => ['Review', 'Prepare'].map((verb, variant) => ({ id: `task-${teamIndex * 14 + index * 2 + variant + 1}`, name: index % 3 === 0 ? null : `${subject.replaceAll(' ', '_').toLowerCase()}.${verb.toLowerCase()}`, teamIndex, agent: agents[teamIndex * 4 + (index * 2 + variant) % 4] }))));
|
|
66
|
+
let totalTaskWeight = 0;
|
|
67
|
+
const taskWeights = tasks.map((task, index) => { totalTaskWeight += [1.7, 1.5, .8, .65, 1.1, 1.4, .9, .6, 1.2, 1.8][task.teamIndex] * (.5 + index % 7 * .25); return totalTaskWeight; });
|
|
68
|
+
const pickTask = () => { const target = random() * totalTaskWeight; return tasks[taskWeights.findIndex(weight => weight > target)]; };
|
|
69
|
+
const records = [];
|
|
70
|
+
const weights = Array.from({ length: 96 }, (_, bin) => 30 + 80 * (1 + Math.sin((bin - 24) / 15)) + (bin >= 67 && bin <= 74 ? 170 : 0) + random() * 45);
|
|
71
|
+
const totalWeight = weights.reduce((sum, value) => sum + value, 0);
|
|
72
|
+
let cumulative = 0;
|
|
73
|
+
for (let bin = 0; bin < 96; bin++) {
|
|
74
|
+
cumulative += weights[bin];
|
|
75
|
+
const end = Math.round(cumulative / totalWeight * 9600);
|
|
76
|
+
while (records.length < end) {
|
|
77
|
+
const index = records.length;
|
|
78
|
+
const running = index >= 9576;
|
|
79
|
+
const task = running ? choose(tasks.filter(task => task.agent.id === agents[(index - 9576) * 7 % agents.length].id)) : index < tasks.length ? tasks[index * 53 % tasks.length] : pickTask();
|
|
80
|
+
// Cover every catalog entry, then vary model mix with retrieval and generation.
|
|
81
|
+
const modelIndex = index < 21 ? index : [2, 8, 9].includes(task.teamIndex) && index % 9 === 0 ? 20 : index % 11 === 0 ? [12, 13, 19][task.teamIndex % 3] : choose(TEAM_MODELS[task.teamIndex]);
|
|
82
|
+
const [model, provider, location, baseLatency, rate, role] = MODEL_DEFINITIONS[modelIndex];
|
|
83
|
+
const pick = random();
|
|
84
|
+
const routeKind = pick < .09 && [4, 11, 13, 20].includes(modelIndex) ? 'unknown' : choose(MODEL_PATHS[modelIndex]);
|
|
85
|
+
const route = routeKind === 'unknown' ? 'Unattributed route' : routeKind === 'local' ? provider : routeKind === 'direct' ? `${provider} API` : routeKind === 'router' ? location === 'local' ? 'router.private' : choose(ROUTERS.slice(0, 3)) : choose(GATEWAYS);
|
|
86
|
+
const failed = !running && random() < (bin >= 68 && bin <= 73 ? .085 : .012);
|
|
87
|
+
const inputTokens = Math.round(320 + random() ** 2 * (role === 'embedding' ? 32000 : 24000));
|
|
88
|
+
const outputTokens = running ? null : failed || role === 'embedding' || role === 'reranking' ? 0 : Math.round(60 + random() ** 2 * (role === 'reasoning' ? 6500 : 2400));
|
|
89
|
+
const cacheReadTokens = role === 'embedding' || role === 'reranking' ? 0 : Math.round(inputTokens * (index % 5 === 0 ? .7 : random() * .35));
|
|
90
|
+
const cost = location === 'local' || running || index % 19 === 0 ? null : ((inputTokens - cacheReadTokens * .8) + outputTokens * 4) * rate / 1_000_000;
|
|
91
|
+
const occurredAt = running ? now - (9600 - index) * 2300 : now - (96 - bin) * 900000 + random() * 900000;
|
|
92
|
+
records.push({ id: `sample-request-${index + 1}`, sourceId: 'sample-activity', requestId: `req-${String(index + 1).padStart(5, '0')}`, occurredAt: new Date(occurredAt).toISOString(), model, modelCreator: MODEL_CREATORS[modelIndex], provider, location, workload: `services/${TEAMS[task.teamIndex][0].toLowerCase()}`, scope: 'agent', agentId: task.agent.id, agent: task.agent.name, taskId: task.id, task: task.name, route, routeKind, status: running ? 'running' : failed ? 'failed' : 'completed', inputTokens, outputTokens, cacheReadTokens, durationMs: running || index % 43 === 0 ? null : Math.round(baseLatency * (.4 + random() * 1.4) * (index % 71 === 0 ? 5 : 1)), cost, currency: 'USD', costKind: index % 9 === 0 ? 'estimated' : 'reported' });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const time = new Date(now).toISOString();
|
|
96
|
+
const localMemory = [5.2, 18.6, 42.4, 16.8, 9.1, .27, 1.2];
|
|
97
|
+
const runningModels = new Set(records.filter(item => item.status === 'running').map(item => item.model));
|
|
98
|
+
const models = MODEL_DEFINITIONS.map(([name, provider, location], index) => {
|
|
99
|
+
const loaded = location === 'local' && (index !== 15 && index !== 18 || runningModels.has(name));
|
|
100
|
+
return { id: `sample-model-${index}`, name, modelCreator: MODEL_CREATORS[index], creatorSources: ['sample-activity'], provider, location, state: location === 'cloud' ? 'observed' : loaded ? 'loaded' : 'installed', sourceId: location === 'cloud' ? 'sample-activity' : provider === 'Ollama' ? 'ollama' : provider === 'vLLM' ? 'runtime-vllm' : 'runtime-studio', memoryBytes: loaded ? localMemory[index - 14] * GiB : null, parameters: index >= 14 ? ['8B', '30B', '70B', '27B', '14B', '137M', '568M'][index - 14] : null, quantization: index >= 14 ? index >= 19 ? 'F16' : 'Q4_K_M' : null };
|
|
101
|
+
});
|
|
102
|
+
const modelCounts = new Map();
|
|
103
|
+
for (const record of records) modelCounts.set(`${record.provider}:${record.model}`, (modelCounts.get(`${record.provider}:${record.model}`) ?? 0) + 1);
|
|
104
|
+
models.sort((a, b) => modelCounts.get(`${b.provider}:${b.name}`) - modelCounts.get(`${a.provider}:${a.name}`));
|
|
105
|
+
const history = Array.from({ length: 240 }, (_, index) => ({ at: new Date(now - (239 - index) * 15000).toISOString(), cpuPercent: Math.min(97, 46 + Math.sin(index / 13) * 15 + (index > 155 && index < 184 ? 24 : 0) + random() * 12), memoryUsed: (110 + Math.sin(index / 32) * 12 + (index > 155 ? 18 : 0) + random() * 2) * GiB }));
|
|
106
|
+
const providers = [...new Set(models.filter(model => model.location === 'cloud').map(model => model.provider))];
|
|
107
|
+
const poolId = name => `pool-${name.toLowerCase().replaceAll(' ', '-')}`;
|
|
108
|
+
const deployments = MODEL_DEFINITIONS.flatMap(([model, provider, location], index) => Array.from({ length: index < 6 ? 2 : 1 }, (_, variant) => ({ id: `deployment-${index}-${variant}`, name: location === 'local' ? `${provider.toLowerCase().replaceAll(' ', '-')}-local` : `${provider.toLowerCase()}-${variant ? 'eu-1' : 'us-1'}`, model, modelCreator: MODEL_CREATORS[index], servingMode: location === 'local' ? 'self-hosted' : provider === 'Baseten' ? 'dedicated' : 'shared', provider, location, state: location === 'local' ? models.find(item => item.name === model).state : 'configured', sourceId: location === 'cloud' ? 'inventory' : models.find(item => item.name === model).sourceId, poolIds: [poolId(provider)] })));
|
|
109
|
+
records.forEach((record, index) => {
|
|
110
|
+
const modelIndex = MODEL_DEFINITIONS.findIndex(([model]) => model === record.model);
|
|
111
|
+
record.deploymentId = record.routeKind === 'unknown' ? null : `deployment-${modelIndex}-${modelIndex < 6 && index % 4 === 0 ? 1 : 0}`;
|
|
112
|
+
record.path = record.routeKind === 'unknown' ? [] : [{ name: record.route, kind: record.routeKind }];
|
|
113
|
+
if (record.routeKind === 'router') record.path.push(record.location === 'local' ? { name: record.provider, kind: 'local' } : { name: index % 4 === 0 ? 'llm-gateway-eu' : 'llm-gateway', kind: 'gateway' });
|
|
114
|
+
if (['router', 'gateway'].includes(record.routeKind) && record.location === 'cloud') record.path.push({ name: `${record.provider} API`, kind: 'direct' });
|
|
115
|
+
if (record.routeKind === 'gateway' && record.location === 'local') record.path.push({ name: record.provider, kind: 'local' });
|
|
116
|
+
});
|
|
117
|
+
const capacityPools = [
|
|
118
|
+
...providers.map((name, index) => ({ id: poolId(name), name: `${name.toLowerCase()}-project-01`, kind: 'quota', sourceId: 'sample-capacity', windowSeconds: 60, observedAt: name === 'Cohere' ? null : new Date(now - (name === 'Baseten' ? 180000 : 12000)).toISOString(), resetAt: new Date(now + 28000).toISOString(), metrics: name === 'Cohere' ? {} : { requestsUsed: [842, 318, 126, 427, 193, 256][index % 6], requestsLimit: 1000, tokensUsed: name === 'Anthropic' ? 882000 : 180000 + index * 71000, tokensLimit: 1000000, running: 8 + index * 3, concurrencyLimit: 80 } })),
|
|
119
|
+
...['Ollama', 'vLLM', 'LM Studio'].map((name, index) => ({ id: poolId(name), name: `${name} / host unspecified`, kind: 'runtime', sourceId: 'sample-capacity', observedAt: new Date(now - 8000).toISOString(), metrics: { running: [8, 18, 3][index], waiting: [2, 7, 0][index], ...(index < 2 ? { concurrencyLimit: [12, 24][index], cachePercent: [54, 86][index] } : {}), memoryUsedBytes: [33.2, 43.6, 16.8][index] * GiB, memoryTotalBytes: [64, 64, 24][index] * GiB } })),
|
|
120
|
+
...GATEWAYS.map((name, index) => ({ id: poolId(name), name, kind: 'gateway', sourceId: 'sample-capacity', observedAt: new Date(now - 11000).toISOString(), metrics: { running: [46, 14, 6][index], concurrencyLimit: [50, 40, 20][index], waiting: [4, 0, 0][index] } })),
|
|
121
|
+
];
|
|
122
|
+
const hostDefinitions = [
|
|
123
|
+
['this-machine', 'devbox-01', '', '', 'AMD EPYC · 32 cores', 32, 256, ['RTX 4090', 24, 1], ['LM Studio']],
|
|
124
|
+
['gpu-01', 'gpu-01', 'dc-01', 'rack-07', 'AMD EPYC · 64 cores', 64, 256, ['NVIDIA L40S', 48, 2], ['Ollama']],
|
|
125
|
+
['gpu-02', 'gpu-02', 'dc-01', 'rack-07', 'AMD EPYC · 96 cores', 96, 512, ['NVIDIA H100', 80, 2], ['vLLM']],
|
|
126
|
+
['gpu-03', 'gpu-03', 'dc-01', 'rack-07', 'AMD EPYC · 64 cores', 64, 256, ['NVIDIA L40S', 48, 2], []],
|
|
127
|
+
['gpu-04', 'gpu-04', 'dc-02', 'rack-03', 'Intel Xeon · 48 cores', 48, 256, ['NVIDIA A100', 80, 2], []],
|
|
128
|
+
['gpu-05', 'gpu-05', 'dc-02', 'rack-03', 'Intel Xeon · 48 cores', 48, 256, ['NVIDIA A100', 80, 2], []],
|
|
129
|
+
];
|
|
130
|
+
const hosts = hostDefinitions.map(([id, name, site, rack, chip, cores, ram, [gpu, vram, count], runtimes], index) => {
|
|
131
|
+
const observedAt = index === 5 ? null : new Date(now - (index === 4 ? 180000 : 0)).toISOString();
|
|
132
|
+
const hostHistory = observedAt ? history.map((point, i) => ({ at: new Date(Date.parse(point.at) - (index === 4 ? 180000 : 0)).toISOString(), cpuPercent: Math.max(0, Math.min(100, point.cpuPercent * [.55, 1, 1.2, .12, .4][index] + Math.sin(i / (11 + index)) * 4)), memoryUsed: point.memoryUsed * [.65, 1, 1.6, .15, .8][index] })) : [];
|
|
133
|
+
const loadedModels = models.filter(model => model.state === 'loaded' && runtimes.includes(model.provider)).map(model => ({ ...model, runtime: model.provider, memoryKind: 'accelerator', deploymentId: deployments.find(deployment => deployment.model === model.name)?.id }));
|
|
134
|
+
return { id, name, hostname: name, nameField: 'hostname', site, rack, chip, cores, platform: 'linux', arch: 'x64', sourceId: id === 'this-machine' ? 'machine' : 'hardware', observedAt, memoryTotal: ram * GiB, memoryUsed: hostHistory.at(-1)?.memoryUsed ?? null, cpuPercent: hostHistory.at(-1)?.cpuPercent ?? null, history: hostHistory, loadedModels, residencyReported: Boolean(observedAt),
|
|
135
|
+
accelerators: Array.from({ length: count }, (_, device) => ({ id: `gpu-${device}`, name: gpu, memoryTotal: vram * GiB, memoryUsed: observedAt ? ([17.8, 35.2, 52.6, 1.2, 12][index] / count) * GiB : null, utilization: observedAt ? [32, 68, 84, 0, 21][index] + device : null })) };
|
|
136
|
+
});
|
|
137
|
+
for (const deployment of deployments.filter(item => item.location === 'local')) {
|
|
138
|
+
const host = hosts.find(item => item.loadedModels.some(model => model.name === deployment.model));
|
|
139
|
+
if (host) { deployment.hostId = host.id; deployment.name = `${host.name} / ${deployment.provider}`; deployment.location = host.id === 'this-machine' ? 'local' : 'private'; }
|
|
140
|
+
}
|
|
141
|
+
const privateModels = new Set(deployments.filter(item => item.location === 'private').map(item => `${item.provider}:${item.model}`));
|
|
142
|
+
for (const model of models) if (privateModels.has(`${model.provider}:${model.name}`)) model.location = 'private';
|
|
143
|
+
for (const record of records) if (privateModels.has(`${record.provider}:${record.model}`)) record.location = 'private';
|
|
144
|
+
for (const pool of capacityPools.filter(item => item.kind === 'runtime')) {
|
|
145
|
+
const host = hosts.find(item => item.loadedModels.some(model => pool.id === poolId(model.provider)));
|
|
146
|
+
if (host) pool.name = `${pool.name.split(' / ')[0]} / ${host.name}`;
|
|
147
|
+
}
|
|
148
|
+
return { schemaVersion: 1, mode: 'sample', observedAt: time, interval: 15, project: { name: 'inference-sample', scope: 'Enterprise sample environment' }, models, deployments, capacityPools, hosts,
|
|
149
|
+
runtimeStats: [{ name: 'vLLM', sourceId: 'runtime-vllm', hostId: 'gpu-02', running: 18, waiting: 7, inputTokens: 48200000, outputTokens: 9400000, finished: 74620 }],
|
|
150
|
+
inventory: [
|
|
151
|
+
...['Baseten', 'Groq', 'Cerebras'].map(name => ({ name, kind: 'provider', location: 'cloud', state: 'configured', sourceId: 'environment', evidence: [{ field: `${name.toUpperCase()}_API_KEY`, basis: 'credential-name' }] })),
|
|
152
|
+
...ROUTERS.map(name => ({ name, kind: 'router', location: name === 'router.private' ? 'local' : 'cloud', state: 'observed' })),
|
|
153
|
+
...GATEWAYS.map(name => ({ name, kind: 'gateway', location: 'cloud', state: 'observed', poolIds: [poolId(name)] })),
|
|
154
|
+
...['Ollama', 'vLLM', 'LM Studio'].map(name => ({ name, kind: 'runtime', location: name === 'LM Studio' ? 'local' : 'private', state: 'observed' })),
|
|
155
|
+
], records,
|
|
156
|
+
sources: [
|
|
157
|
+
{ id: 'environment', name: 'Service configuration', kind: 'Process environment', state: 'connected', updatedAt: time, detail: 'BASETEN_API_KEY · GROQ_API_KEY · CEREBRAS_API_KEY', note: 'Fictional examples of environment-variable evidence. No credentials were read for this sample. Configuration alone does not establish usage, deployments, or hardware.' },
|
|
158
|
+
{ id: 'hardware', name: 'Hardware export', kind: 'Local file', state: 'partial', updatedAt: time, detail: '.openmerit/hosts.json', note: 'Fictional fleet across two racks and a workstation. gpu-04 has stale readings; gpu-05 has not reported a reading.' },
|
|
159
|
+
{ id: 'sample-capacity', name: 'Capacity export', kind: 'Local file', state: 'partial', updatedAt: time, detail: '.openmerit/capacity.json', note: 'Fictional shared quotas and runtime readings. Baseten is stale; Cohere has no reading. Pool measurements cover all consumers, including work outside this project.' },
|
|
160
|
+
{ id: 'sample-activity', name: 'Inference activity', kind: 'Local file', state: 'connected', updatedAt: time, detail: '.openmerit/inference.jsonl', note: 'Fictional metadata for 40 agent IDs and 140 task IDs. Names represent exported fields, not discovered code. Some display names are absent. Measurements and prices are illustrative.', records: records.length },
|
|
161
|
+
{ id: 'ollama', name: 'Ollama', kind: 'Runtime export', state: 'connected', updatedAt: time, detail: 'gpu-01 / Ollama', note: 'Fictional private-runtime inventory and resident allocations from existing exports.' },
|
|
162
|
+
{ id: 'runtime-studio', name: 'LM Studio', kind: 'Local runtime', state: 'connected', updatedAt: time, detail: '127.0.0.1:1234', note: 'Fictional compatible runtime inventory.' },
|
|
163
|
+
{ id: 'runtime-vllm', name: 'vLLM', kind: 'Runtime export', state: 'connected', updatedAt: time, detail: 'gpu-02 / vLLM', note: 'Fictional runtime and host exports. Remote endpoints are not queried by this terminal.' },
|
|
164
|
+
{ id: 'machine', name: 'This machine', kind: 'Operating system', state: 'connected', updatedAt: time, detail: 'Linux · x64', note: 'Fictional whole-machine CPU and memory samples. These are not attributed to individual requests.' },
|
|
165
|
+
{ id: 'openmerit', name: 'OpenMerit', kind: 'Project evidence', state: 'connected', updatedAt: time, detail: '.openmerit/events.jsonl', note: 'Fictional audit source. The activity export takes precedence; request totals are not added together.', records: 140 },
|
|
166
|
+
{ id: 'inventory', name: 'Declared inventory', kind: 'Project configuration', state: 'connected', updatedAt: time, detail: '.openmerit/terminal.json', note: 'Fictional configuration keys for four routers, three gateways, and three local runtimes. llm-gateway is an example service name, not a built-in gateway.' },
|
|
167
|
+
{ id: 'batch-archive', name: 'Batch archive', kind: 'Local file', state: 'unavailable', updatedAt: null, detail: '.openmerit/archive/inference.jsonl', note: 'Fictional unavailable source. No requests from this archive are included.' },
|
|
168
|
+
],
|
|
169
|
+
machine: hosts[0],
|
|
170
|
+
};
|
|
171
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { createCollector, loadConfig } from './collect.mjs';
|
|
5
|
+
import { sampleSnapshot } from './sample.mjs';
|
|
6
|
+
|
|
7
|
+
const assets = new Map([['/', ['index.html', 'text/html']], ['/app.js', ['app.js', 'text/javascript']], ['/flow.js', ['flow.js', 'text/javascript']], ['/flow.css', ['flow.css', 'text/css']], ['/styles.css', ['styles.css', 'text/css']], ['/favicon.svg', ['favicon.svg', 'image/svg+xml']]]);
|
|
8
|
+
for (const file of ['baseten.png', 'groq.ico', 'cerebras.png', 'qwen.png', 'meta.ico', 'nomic.png', 'baai.png', 'ollama.png', 'lm-studio.png', 'vllm.png', 'anthropic.png', 'google.png', 'deepseek.ico', 'cohere.png', 'openai.png', 'mistral.png', 'openrouter.png']) assets.set(`/brands/${file}`, [`brands/${file}`, file.endsWith('.svg') ? 'image/svg+xml' : file.endsWith('.ico') ? 'image/x-icon' : 'image/png']);
|
|
9
|
+
|
|
10
|
+
export async function startTerminal({ root = process.cwd(), port = 4310, interval = 15, demo = false, collect: customCollect, config, fallbackPort = false } = {}) {
|
|
11
|
+
const collect = customCollect ?? (demo ? async () => sampleSnapshot() : createCollector(root, config ?? await loadConfig(root), interval));
|
|
12
|
+
let snapshot = await collect();
|
|
13
|
+
let busy = false;
|
|
14
|
+
const timer = setInterval(async () => {
|
|
15
|
+
if (busy) return;
|
|
16
|
+
busy = true;
|
|
17
|
+
try { snapshot = await collect(); } catch { /* Keep timestamp: the interface marks stale data. */ }
|
|
18
|
+
finally { busy = false; }
|
|
19
|
+
}, interval * 1000);
|
|
20
|
+
timer.unref();
|
|
21
|
+
const server = createServer(async (request, response) => {
|
|
22
|
+
const address = server.address();
|
|
23
|
+
const validHosts = [`127.0.0.1:${address.port}`, `localhost:${address.port}`];
|
|
24
|
+
const host = request.headers.host;
|
|
25
|
+
const headers = { 'Cache-Control': 'no-store', 'X-Content-Type-Options': 'nosniff', 'Referrer-Policy': 'no-referrer', 'Content-Security-Policy': "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self'; img-src 'self' data:; font-src 'self'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'" };
|
|
26
|
+
const send = (code, body, type = 'application/json') => { response.writeHead(code, { ...headers, 'Content-Type': `${type}; charset=utf-8` }); response.end(body); };
|
|
27
|
+
if (!validHosts.includes(host) || (request.headers.origin && request.headers.origin !== `http://${host}`) || request.headers['sec-fetch-site'] === 'cross-site') return send(403, '{"error":"Local access only"}');
|
|
28
|
+
if (request.method !== 'GET') return send(405, '{"error":"Read-only terminal"}');
|
|
29
|
+
let path;
|
|
30
|
+
try { path = new URL(request.url, `http://${host}`).pathname; }
|
|
31
|
+
catch { return send(400, '{"error":"Invalid request URL"}'); }
|
|
32
|
+
if (path === '/api/snapshot') return send(200, JSON.stringify(snapshot));
|
|
33
|
+
if (path === '/api/sample') return send(200, JSON.stringify(sampleSnapshot()));
|
|
34
|
+
if (path === '/api/info') return send(200, JSON.stringify({ version: 1, defaultMode: demo ? 'sample' : 'live', interval }));
|
|
35
|
+
const asset = assets.get(path);
|
|
36
|
+
if (!asset) return send(404, '{"error":"Not found"}');
|
|
37
|
+
try { return send(200, await readFile(fileURLToPath(new URL(`../public/${asset[0]}`, import.meta.url))), asset[1]); }
|
|
38
|
+
catch { return send(500, '{"error":"Terminal assets unavailable. Run npm run build."}'); }
|
|
39
|
+
});
|
|
40
|
+
server.on('close', () => clearInterval(timer));
|
|
41
|
+
const listen = selectedPort => new Promise((resolve, reject) => {
|
|
42
|
+
const onError = error => { server.off('listening', onListening); reject(error); };
|
|
43
|
+
const onListening = () => { server.off('error', onError); resolve(); };
|
|
44
|
+
server.once('error', onError); server.once('listening', onListening);
|
|
45
|
+
server.listen(selectedPort, '127.0.0.1');
|
|
46
|
+
});
|
|
47
|
+
try {
|
|
48
|
+
try { await listen(port); }
|
|
49
|
+
catch (error) {
|
|
50
|
+
if (!fallbackPort || error.code !== 'EADDRINUSE') throw error;
|
|
51
|
+
await listen(0);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch (error) { clearInterval(timer); throw error; }
|
|
55
|
+
return { server, url: `http://127.0.0.1:${server.address().port}`, close: () => new Promise(resolve => { clearInterval(timer); server.close(resolve); server.closeIdleConnections(); }) };
|
|
56
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Configuration evidence only. Never call these services or return credentials,
|
|
2
|
+
// endpoint paths, queries, or user-supplied URLs to the browser.
|
|
3
|
+
const groups = [
|
|
4
|
+
['OpenAI', ['OPENAI_API_KEY'], ['OPENAI_BASE_URL', 'OPENAI_API_BASE']],
|
|
5
|
+
['Anthropic', ['ANTHROPIC_API_KEY'], ['ANTHROPIC_BASE_URL']],
|
|
6
|
+
['OpenRouter', ['OPENROUTER_API_KEY'], []],
|
|
7
|
+
['Google', ['GOOGLE_GENERATIVE_AI_API_KEY', 'GEMINI_API_KEY'], []],
|
|
8
|
+
['Groq', ['GROQ_API_KEY'], ['GROQ_BASE_URL']],
|
|
9
|
+
['Cerebras', ['CEREBRAS_API_KEY'], ['CEREBRAS_BASE_URL']],
|
|
10
|
+
['Baseten', ['BASETEN_API_KEY'], ['BASETEN_BASE_URL']],
|
|
11
|
+
];
|
|
12
|
+
const hosts = new Map([
|
|
13
|
+
['api.openai.com', 'OpenAI'], ['api.anthropic.com', 'Anthropic'],
|
|
14
|
+
['openrouter.ai', 'OpenRouter'], ['api.groq.com', 'Groq'],
|
|
15
|
+
['api.cerebras.ai', 'Cerebras'], ['inference.baseten.co', 'Baseten'],
|
|
16
|
+
]);
|
|
17
|
+
export function serviceFromEndpoint(value) {
|
|
18
|
+
if (typeof value !== 'string' || value.length > 2048) return null;
|
|
19
|
+
try {
|
|
20
|
+
const url = new URL(value);
|
|
21
|
+
if (url.protocol !== 'https:' || url.username || url.password || url.port) return null;
|
|
22
|
+
return hosts.get(url.hostname) ?? (/^model-[a-z0-9-]+\.api\.baseten\.co$/.test(url.hostname) ? 'Baseten' : null);
|
|
23
|
+
} catch { return null; }
|
|
24
|
+
}
|
|
25
|
+
export function configuredServices(env = process.env) {
|
|
26
|
+
const found = new Map(), unrecognizedEndpoints = [];
|
|
27
|
+
const present = key => typeof env[key] === 'string' && env[key].trim().length > 0;
|
|
28
|
+
const add = (name, field, basis) => {
|
|
29
|
+
const item = found.get(name) ?? { name, kind: name === 'OpenRouter' ? 'gateway' : 'provider', location: 'cloud', state: 'configured', sourceId: 'environment', evidence: [] };
|
|
30
|
+
item.evidence.push({ field, basis, ...(basis === 'endpoint-host' ? { endpointHost: new URL(env[field]).hostname.replace(/^model-[a-z0-9-]+(?=\.api\.baseten\.co$)/, 'model-…') } : {}) }); found.set(name, item);
|
|
31
|
+
};
|
|
32
|
+
for (const [name, credentials, endpoints] of groups) {
|
|
33
|
+
const overrides = endpoints.filter(present);
|
|
34
|
+
if (overrides.length) {
|
|
35
|
+
for (const field of overrides) {
|
|
36
|
+
const service = serviceFromEndpoint(env[field]);
|
|
37
|
+
if (service) add(service, field, 'endpoint-host');
|
|
38
|
+
else unrecognizedEndpoints.push(field);
|
|
39
|
+
}
|
|
40
|
+
// A custom base URL can repurpose the SDK's credential variable. Its name
|
|
41
|
+
// alone must not establish the SDK vendor as a configured service.
|
|
42
|
+
} else for (const field of credentials.filter(present)) add(name, field, 'credential-name');
|
|
43
|
+
}
|
|
44
|
+
return { services: [...found.values()], unrecognizedEndpoints };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Multiple reports for one model/service may disagree. Preserve the gap rather
|
|
48
|
+
// than choosing whichever file or record happened to be processed first.
|
|
49
|
+
export function modelCreators(models, reports) {
|
|
50
|
+
const evidence = new Map();
|
|
51
|
+
for (const item of [...models, ...reports]) {
|
|
52
|
+
if (!item.modelCreator) continue;
|
|
53
|
+
const key = JSON.stringify([item.provider, item.model ?? item.name]);
|
|
54
|
+
const entry = evidence.get(key) ?? { names: new Set(), sources: new Set() };
|
|
55
|
+
entry.names.add(item.modelCreator); if (item.sourceId) entry.sources.add(item.sourceId);
|
|
56
|
+
evidence.set(key, entry);
|
|
57
|
+
}
|
|
58
|
+
return models.map(model => {
|
|
59
|
+
const entry = evidence.get(JSON.stringify([model.provider, model.name]));
|
|
60
|
+
return { ...model, modelCreator: entry?.names.size === 1 ? [...entry.names][0] : null, creatorConflict: (entry?.names.size ?? 0) > 1, creatorSources: [...(entry?.sources ?? [])] };
|
|
61
|
+
});
|
|
62
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Bounded metadata contracts. Never retain endpoints, credentials, or payloads.
|
|
2
|
+
const label = value => typeof value === 'string' ? value.slice(0, 160).replace(/[\x00-\x1f]/g, '') : '';
|
|
3
|
+
const count = value => typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : null;
|
|
4
|
+
const timestamp = value => typeof value === 'string' && Number.isFinite(Date.parse(value)) ? new Date(value).toISOString() : null;
|
|
5
|
+
const list = (value, limit) => Array.isArray(value) ? value.slice(0, limit).filter(item => item && typeof item === 'object') : [];
|
|
6
|
+
export const poolIds = value => Array.isArray(value) ? [...new Set(value.slice(0, 16).map(label).filter(Boolean))] : [];
|
|
7
|
+
export function normalizePath(value) {
|
|
8
|
+
// Do not turn a broken path into an apparently complete chain by dropping hops.
|
|
9
|
+
if (!Array.isArray(value) || !value.length || value.length > 8 || value.some(hop => !hop || !label(hop.name))) return [];
|
|
10
|
+
return value.map(hop => ({ name: label(hop.name), kind: ['direct', 'local', 'router', 'gateway'].includes(hop.kind) ? hop.kind : 'unknown' }));
|
|
11
|
+
}
|
|
12
|
+
function unique(items) {
|
|
13
|
+
const counts = new Map();
|
|
14
|
+
for (const item of items) counts.set(item.id, (counts.get(item.id) ?? 0) + 1);
|
|
15
|
+
return items.filter(item => item.id && counts.get(item.id) === 1);
|
|
16
|
+
}
|
|
17
|
+
export function normalizeDeployments(value) {
|
|
18
|
+
return unique(list(value, 200).map(item => ({ id: label(item.id), name: label(item.name) || label(item.id), model: label(item.model), modelCreator: label(item.modelCreator) || null, servingMode: ['shared', 'dedicated', 'self-hosted'].includes(item.servingMode) ? item.servingMode : 'unknown', provider: label(item.provider) || 'Unattributed', location: ['local', 'private', 'cloud'].includes(item.location) ? item.location : 'unknown', poolIds: poolIds(item.poolIds), hostId: label(item.hostId) || null, sourceId: 'inventory', state: 'configured' })).filter(item => item.model && !item.id.startsWith('runtime:')));
|
|
19
|
+
}
|
|
20
|
+
export function normalizePools(value) {
|
|
21
|
+
return unique(list(value, 100).map(item => ({ id: label(item.id), name: label(item.name) || label(item.id), kind: ['quota', 'runtime', 'gateway'].includes(item.kind) ? item.kind : 'unknown', windowSeconds: count(item.windowSeconds), sourceId: 'capacity', observedAt: null, metrics: {} })).filter(item => !item.id.startsWith('runtime:')));
|
|
22
|
+
}
|
|
23
|
+
export function normalizeCapacity(value, now = Date.now()) {
|
|
24
|
+
return unique(list(value?.pools, 100).map(item => {
|
|
25
|
+
const observedAt = timestamp(item.observedAt);
|
|
26
|
+
const metrics = Object.fromEntries(['running', 'waiting', 'concurrencyLimit', 'requestsUsed', 'requestsLimit', 'tokensUsed', 'tokensLimit', 'cachePercent', 'memoryUsedBytes', 'memoryTotalBytes'].map(key => [key, count(item.metrics?.[key])]).filter(([, value]) => value !== null));
|
|
27
|
+
if (metrics.cachePercent > 100) delete metrics.cachePercent;
|
|
28
|
+
return { id: label(item.id), observedAt, resetAt: timestamp(item.resetAt), metrics };
|
|
29
|
+
}).filter(item => item.observedAt && Date.parse(item.observedAt) <= now));
|
|
30
|
+
}
|