mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-21 20:30:47 +02:00
Back model benchmark with Braintrust, drop in-house scoring (#13)
# Conflicts: # bun.lock # package.json
This commit is contained in:
@@ -33,13 +33,15 @@ then regenerate. Preserve the source-blob and normalized-render evidence.
|
|||||||
|
|
||||||
The runtime is host-neutral, uses `$GSTACK_HOME` or `~/.gstack`, and keeps state
|
The runtime is host-neutral, uses `$GSTACK_HOME` or `~/.gstack`, and keeps state
|
||||||
as locked/atomically written JSON and JSONL. Network mode defaults off.
|
as locked/atomically written JSON and JSONL. Network mode defaults off.
|
||||||
Context.dev is the only newly authorized external service, may receive only
|
New external services (Context.dev, benchmark/eval backends, and the like) are
|
||||||
public URLs after explicit consent, and must use the exact typed failure codes.
|
allowed, but each must be optional, off by default, and consent-gated: send only
|
||||||
|
what the user approves, and use typed failure codes. Context.dev specifically may
|
||||||
|
receive only public URLs after explicit consent.
|
||||||
`gstack context options` and `context select host|local-browser|none` persist
|
`gstack context options` and `context select host|local-browser|none` persist
|
||||||
fallback selection without granting Context.dev consent.
|
fallback selection without granting Context.dev consent.
|
||||||
The existing browser stays local. Physical iOS uses only DebugBridge/CoreDevice.
|
The existing browser stays local. Physical iOS uses only DebugBridge/CoreDevice.
|
||||||
Do not add cloud browsers, alternate iOS drivers, local image models, provider
|
Do not add cloud browsers, alternate iOS drivers, local image models,
|
||||||
marketplaces, workflow engines, or a new state database.
|
workflow engines, or a new state database.
|
||||||
|
|
||||||
Current completion claims must come from
|
Current completion claims must come from
|
||||||
[`docs/gstack-2/STATUS.md`](docs/gstack-2/STATUS.md) and
|
[`docs/gstack-2/STATUS.md`](docs/gstack-2/STATUS.md) and
|
||||||
|
|||||||
+76
-73
@@ -1,33 +1,37 @@
|
|||||||
#!/usr/bin/env bun
|
#!/usr/bin/env bun
|
||||||
/**
|
/**
|
||||||
* gstack-model-benchmark — run the same prompt across multiple providers
|
* gstack-model-benchmark — run the same prompt(s) across providers and compare,
|
||||||
* and compare latency, tokens, cost, quality, and tool-call count.
|
* with scoring, experiments, and reporting owned by Braintrust (local by default;
|
||||||
|
* nothing is uploaded unless BRAINTRUST_API_KEY is set — that key is the consent
|
||||||
|
* boundary for the cloud dashboard).
|
||||||
*
|
*
|
||||||
* Usage:
|
* Usage:
|
||||||
* gstack-model-benchmark <skill-or-prompt-file> [options]
|
* gstack-model-benchmark [<prompt-file>] [options]
|
||||||
*
|
*
|
||||||
* Options:
|
* Options:
|
||||||
* --models claude,gpt,gemini Comma-separated provider list (default: claude)
|
* --models claude,gpt,gemini Comma-separated provider list (default: claude)
|
||||||
* --prompt "<text>" Inline prompt instead of a file
|
* --prompt "<text>" Ad-hoc single-case prompt instead of the corpus
|
||||||
* --workdir <path> Working dir passed to each CLI (default: cwd)
|
* --corpus <path> Corpus JSON (default: evals/model-benchmark/corpus.json)
|
||||||
* --timeout-ms <n> Per-provider timeout (default: 300000)
|
* --timeout-ms <n> Per-provider-per-case timeout (default: 300000)
|
||||||
* --output table|json|markdown Output format (default: table)
|
* --output table|json Output format (default: table)
|
||||||
* --skip-unavailable Skip providers that fail available() check
|
* --judge Add the autoevals ClosedQA LLM judge (needs OPENAI_API_KEY)
|
||||||
* (default: include them with unavailable marker)
|
|
||||||
* --judge Run Anthropic SDK judge on outputs for quality score
|
|
||||||
* (requires ANTHROPIC_API_KEY; adds ~$0.05 per call)
|
|
||||||
* --dry-run Validate flags + resolve auth, don't invoke providers
|
* --dry-run Validate flags + resolve auth, don't invoke providers
|
||||||
*
|
*
|
||||||
* Examples:
|
* Examples:
|
||||||
* gstack-model-benchmark --prompt "Write a haiku about databases" --models claude,gpt
|
* gstack-model-benchmark --prompt "Write a haiku about databases" --models claude,gpt
|
||||||
* gstack-model-benchmark ./test-prompt.txt --models claude,gpt,gemini --judge
|
* gstack-model-benchmark --models claude,gpt,gemini # runs the corpus
|
||||||
* gstack-model-benchmark --prompt "hi" --models claude,gpt,gemini --dry-run
|
* gstack-model-benchmark --prompt "hi" --models claude,gpt,gemini --dry-run
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import '../lib/conductor-env-shim';
|
import '../lib/conductor-env-shim';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import {
|
||||||
import { runBenchmark, formatTable, formatJson, formatMarkdown, type BenchmarkInput } from '../lib/model-benchmark/runner';
|
loadCorpus,
|
||||||
|
runProviderBenchmark,
|
||||||
|
type BenchCase,
|
||||||
|
type ProviderBenchmark,
|
||||||
|
type ProviderName,
|
||||||
|
} from '../lib/model-benchmark/braintrust-eval';
|
||||||
import { ClaudeAdapter } from '../lib/model-benchmark/providers/claude';
|
import { ClaudeAdapter } from '../lib/model-benchmark/providers/claude';
|
||||||
import { GptAdapter } from '../lib/model-benchmark/providers/gpt';
|
import { GptAdapter } from '../lib/model-benchmark/providers/gpt';
|
||||||
import { GeminiAdapter } from '../lib/model-benchmark/providers/gemini';
|
import { GeminiAdapter } from '../lib/model-benchmark/providers/gemini';
|
||||||
@@ -38,10 +42,10 @@ const ADAPTER_FACTORIES = {
|
|||||||
gemini: () => new GeminiAdapter(),
|
gemini: () => new GeminiAdapter(),
|
||||||
};
|
};
|
||||||
|
|
||||||
type OutputFormat = 'table' | 'json' | 'markdown';
|
type OutputFormat = 'table' | 'json';
|
||||||
|
|
||||||
const CLI_ARGS = process.argv.slice(2);
|
const CLI_ARGS = process.argv.slice(2);
|
||||||
const VALUE_FLAGS = new Set(['--models', '--prompt', '--workdir', '--timeout-ms', '--output']);
|
const VALUE_FLAGS = new Set(['--models', '--prompt', '--corpus', '--timeout-ms', '--output']);
|
||||||
|
|
||||||
function arg(name: string, def?: string): string | undefined {
|
function arg(name: string, def?: string): string | undefined {
|
||||||
const idx = CLI_ARGS.findIndex(a => a === name || a.startsWith(name + '='));
|
const idx = CLI_ARGS.findIndex(a => a === name || a.startsWith(name + '='));
|
||||||
@@ -76,93 +80,89 @@ function positionalArgs(args: string[]): string[] {
|
|||||||
return positional;
|
return positional;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseProviders(s: string | undefined): Array<'claude' | 'gpt' | 'gemini'> {
|
function parseProviders(s: string | undefined): ProviderName[] {
|
||||||
if (!s) return ['claude'];
|
if (!s) return ['claude'];
|
||||||
const seen = new Set<'claude' | 'gpt' | 'gemini'>();
|
const seen = new Set<ProviderName>();
|
||||||
for (const p of s.split(',').map(x => x.trim()).filter(Boolean)) {
|
for (const p of s.split(',').map(x => x.trim()).filter(Boolean)) {
|
||||||
if (p === 'claude' || p === 'gpt' || p === 'gemini') seen.add(p);
|
if (p === 'claude' || p === 'gpt' || p === 'gemini') seen.add(p);
|
||||||
else {
|
else console.error(`WARN: unknown provider '${p}' — skipping. Valid: claude, gpt, gemini.`);
|
||||||
console.error(`WARN: unknown provider '${p}' — skipping. Valid: claude, gpt, gemini.`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return seen.size ? Array.from(seen) : ['claude'];
|
return seen.size ? Array.from(seen) : ['claude'];
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolvePrompt(positional: string | undefined): string {
|
/** Resolve the cases: --prompt/file → single ad-hoc case; else the corpus. */
|
||||||
|
function resolveCases(positional: string | undefined): BenchCase[] {
|
||||||
const inline = arg('--prompt');
|
const inline = arg('--prompt');
|
||||||
if (inline) return inline;
|
if (inline) return [{ id: 'adhoc', input: inline, required: [] }];
|
||||||
if (!positional) {
|
if (positional && fs.existsSync(positional)) {
|
||||||
console.error('ERROR: specify a prompt via positional path or --prompt "<text>"');
|
return [{ id: 'adhoc', input: fs.readFileSync(positional, 'utf-8'), required: [] }];
|
||||||
process.exit(1);
|
|
||||||
}
|
}
|
||||||
if (fs.existsSync(positional)) {
|
if (positional) return [{ id: 'adhoc', input: positional, required: [] }];
|
||||||
return fs.readFileSync(positional, 'utf-8');
|
return loadCorpus(arg('--corpus'));
|
||||||
}
|
|
||||||
// Not a file — treat as inline prompt
|
|
||||||
return positional;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main(): Promise<void> {
|
async function main(): Promise<void> {
|
||||||
const positional = positionalArgs(CLI_ARGS)[0];
|
const positional = positionalArgs(CLI_ARGS)[0];
|
||||||
const prompt = resolvePrompt(positional);
|
|
||||||
const providers = parseProviders(arg('--models'));
|
const providers = parseProviders(arg('--models'));
|
||||||
const workdir = arg('--workdir', process.cwd())!;
|
|
||||||
const timeoutMs = parseInt(arg('--timeout-ms', '300000')!, 10);
|
const timeoutMs = parseInt(arg('--timeout-ms', '300000')!, 10);
|
||||||
const output = (arg('--output', 'table') as OutputFormat);
|
const output = (arg('--output', 'table') as OutputFormat);
|
||||||
const skipUnavailable = flag('--skip-unavailable');
|
|
||||||
const doJudge = flag('--judge');
|
const doJudge = flag('--judge');
|
||||||
const dryRun = flag('--dry-run');
|
const dryRun = flag('--dry-run');
|
||||||
|
|
||||||
if (dryRun) {
|
if (dryRun) {
|
||||||
await dryRunReport({ prompt, providers, workdir, timeoutMs, output, doJudge });
|
await dryRunReport({ cases: resolveCases(positional), providers, timeoutMs, output, doJudge });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const input: BenchmarkInput = {
|
if (doJudge && !process.env.OPENAI_API_KEY) {
|
||||||
prompt,
|
console.error('WARN: --judge needs OPENAI_API_KEY for the autoevals ClosedQA judge — running with the deterministic scorer only.');
|
||||||
workdir,
|
|
||||||
providers,
|
|
||||||
timeoutMs,
|
|
||||||
skipUnavailable,
|
|
||||||
};
|
|
||||||
|
|
||||||
const report = await runBenchmark(input);
|
|
||||||
|
|
||||||
if (doJudge) {
|
|
||||||
try {
|
|
||||||
const { judgeEntries } = await import('../lib/model-benchmark/judge');
|
|
||||||
await judgeEntries(report);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(`WARN: judge unavailable: ${(err as Error).message}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let out: string;
|
const cases = resolveCases(positional);
|
||||||
switch (output) {
|
const results: ProviderBenchmark[] = [];
|
||||||
case 'json': out = formatJson(report); break;
|
for (const provider of providers) {
|
||||||
case 'markdown': out = formatMarkdown(report); break;
|
results.push(await runProviderBenchmark(provider, cases, { timeoutMs, judge: doJudge && !!process.env.OPENAI_API_KEY }));
|
||||||
case 'table':
|
|
||||||
default: out = formatTable(report); break;
|
|
||||||
}
|
}
|
||||||
process.stdout.write(out + '\n');
|
|
||||||
|
process.stdout.write((output === 'json' ? JSON.stringify(results, null, 2) : formatTable(results)) + '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatTable(results: ProviderBenchmark[]): string {
|
||||||
|
const header = `Provider Score Cases Latency(avg) Errors`;
|
||||||
|
const rows: string[] = [header, '-'.repeat(header.length)];
|
||||||
|
for (const r of results) {
|
||||||
|
const n = r.ops.length || 1;
|
||||||
|
const avgMs = r.ops.reduce((s, o) => s + o.durationMs, 0) / n;
|
||||||
|
const errs = r.ops.filter(o => o.error).length;
|
||||||
|
const score = r.score === null ? '—' : `${(r.score * 100).toFixed(1)}%`;
|
||||||
|
rows.push(
|
||||||
|
`${pad(r.provider, 10)} ${pad(score, 8)} ${pad(String(r.ops.length), 6)} ${pad(msToStr(avgMs), 13)} ${errs || ''}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return rows.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function dryRunReport(opts: {
|
async function dryRunReport(opts: {
|
||||||
prompt: string;
|
cases: BenchCase[];
|
||||||
providers: Array<'claude' | 'gpt' | 'gemini'>;
|
providers: ProviderName[];
|
||||||
workdir: string;
|
|
||||||
timeoutMs: number;
|
timeoutMs: number;
|
||||||
output: OutputFormat;
|
output: OutputFormat;
|
||||||
doJudge: boolean;
|
doJudge: boolean;
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
|
const adhoc = opts.cases.length === 1 && opts.cases[0].id === 'adhoc';
|
||||||
const lines: string[] = [];
|
const lines: string[] = [];
|
||||||
lines.push('== gstack-model-benchmark --dry-run ==');
|
lines.push('== gstack-model-benchmark --dry-run ==');
|
||||||
lines.push(` prompt: ${opts.prompt.length > 80 ? opts.prompt.slice(0, 80) + '…' : opts.prompt}`);
|
if (adhoc) {
|
||||||
|
const p = opts.cases[0].input;
|
||||||
|
lines.push(` prompt: ${p.length > 80 ? p.slice(0, 80) + '…' : p}`);
|
||||||
|
} else {
|
||||||
|
lines.push(` corpus: ${opts.cases.length} case(s)`);
|
||||||
|
}
|
||||||
lines.push(` providers: ${opts.providers.join(', ')}`);
|
lines.push(` providers: ${opts.providers.join(', ')}`);
|
||||||
lines.push(` workdir: ${opts.workdir}`);
|
|
||||||
lines.push(` timeout_ms: ${opts.timeoutMs}`);
|
lines.push(` timeout_ms: ${opts.timeoutMs}`);
|
||||||
lines.push(` output: ${opts.output}`);
|
lines.push(` output: ${opts.output}`);
|
||||||
lines.push(` judge: ${opts.doJudge ? 'on (Anthropic SDK)' : 'off'}`);
|
lines.push(` judge: ${opts.doJudge ? 'on (autoevals ClosedQA)' : 'off'}`);
|
||||||
|
lines.push(` upload: ${process.env.BRAINTRUST_API_KEY ? 'ON — BRAINTRUST_API_KEY set (cloud dashboard)' : 'off (local only, nothing uploaded)'}`);
|
||||||
lines.push('');
|
lines.push('');
|
||||||
lines.push('Adapter availability:');
|
lines.push('Adapter availability:');
|
||||||
let authFailures = 0;
|
let authFailures = 0;
|
||||||
@@ -173,20 +173,23 @@ async function dryRunReport(opts: {
|
|||||||
authFailures += 1;
|
authFailures += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const adapter = factory();
|
const check = await factory().available();
|
||||||
const check = await adapter.available();
|
if (check.ok) lines.push(` ${name}: OK`);
|
||||||
if (check.ok) {
|
else { lines.push(` ${name}: NOT READY — ${check.reason}`); authFailures += 1; }
|
||||||
lines.push(` ${adapter.name}: OK`);
|
|
||||||
} else {
|
|
||||||
lines.push(` ${adapter.name}: NOT READY — ${check.reason}`);
|
|
||||||
authFailures += 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
lines.push('');
|
lines.push('');
|
||||||
lines.push(`(--dry-run — no prompts sent. ${authFailures} provider(s) unavailable.)`);
|
lines.push(`(--dry-run — no prompts sent. ${authFailures} provider(s) unavailable.)`);
|
||||||
process.stdout.write(lines.join('\n') + '\n');
|
process.stdout.write(lines.join('\n') + '\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function pad(s: string, n: number): string {
|
||||||
|
return s.length >= n ? s.slice(0, n) : s + ' '.repeat(n - s.length);
|
||||||
|
}
|
||||||
|
function msToStr(ms: number): string {
|
||||||
|
if (ms < 1000) return `${Math.round(ms)}ms`;
|
||||||
|
return `${(ms / 1000).toFixed(1)}s`;
|
||||||
|
}
|
||||||
|
|
||||||
main().catch(err => {
|
main().catch(err => {
|
||||||
console.error('FATAL:', err);
|
console.error('FATAL:', err);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@anthropic-ai/claude-agent-sdk": "0.3.216",
|
"@anthropic-ai/claude-agent-sdk": "0.3.216",
|
||||||
"@huggingface/transformers": "4.2.0",
|
"@huggingface/transformers": "4.2.0",
|
||||||
|
"autoevals": "^0.3.0",
|
||||||
|
"braintrust": "^3.24.0",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -52,13 +54,81 @@
|
|||||||
|
|
||||||
"@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.112.4", "", { "dependencies": { "json-schema-to-ts": "^3.1.1", "standardwebhooks": "^1.0.0" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-7eXJJnrmBI5GMC6drrCiSkycVsT7crRZX3qv5HusLSm+qiILjmtqP7gf+UiT7ASu/7Gdj+Zfl4f2haV8wATKUg=="],
|
"@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.112.4", "", { "dependencies": { "json-schema-to-ts": "^3.1.1", "standardwebhooks": "^1.0.0" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-7eXJJnrmBI5GMC6drrCiSkycVsT7crRZX3qv5HusLSm+qiILjmtqP7gf+UiT7ASu/7Gdj+Zfl4f2haV8wATKUg=="],
|
||||||
|
|
||||||
"@babel/runtime": ["@babel/runtime@7.29.7", "", {}, "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw=="],
|
"@babel/runtime": ["@babel/runtime@7.29.2", "", {}, "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g=="],
|
||||||
|
|
||||||
"@emnapi/runtime": ["@emnapi/runtime@1.11.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA=="],
|
"@braintrust/bt-darwin-arm64": ["@braintrust/bt-darwin-arm64@0.12.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-mY6VW/3VwcOQOGN8sYHS6F0xzHTFwgZcNlj7zlQttI6OXOCGt/bhonGIqd03QBhxmj0M31ymSS7TqSeCK/RYIQ=="],
|
||||||
|
|
||||||
|
"@braintrust/bt-darwin-x64": ["@braintrust/bt-darwin-x64@0.12.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-woyRyDv2DfCF8+von+3X9f1cddAbFnKLSfCbEkrBQVc0ZsAM60as8nehYv7DkafJF/67yKFx/sUraV65sukesQ=="],
|
||||||
|
|
||||||
|
"@braintrust/bt-linux-arm64": ["@braintrust/bt-linux-arm64@0.12.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-J9/7f3EIMKmFmSSQHQnAQLpUgB8YJENrOlkABjlTprBgsIYVgz7tSH7yg2P3ur+2Jem7PpGnrDxHGh/UjRfjsg=="],
|
||||||
|
|
||||||
|
"@braintrust/bt-linux-x64": ["@braintrust/bt-linux-x64@0.12.0", "", { "os": "linux", "cpu": "x64" }, "sha512-HJFbUl3HYYY1Ivw8OYBeIMcIsU9r7+fC9BzLWB5FdH7881ToKee2wbbLZssMCxFbMVeUxzEo+SzGD/1Z9Gk9CA=="],
|
||||||
|
|
||||||
|
"@braintrust/bt-linux-x64-musl": ["@braintrust/bt-linux-x64-musl@0.12.0", "", { "os": "linux", "cpu": "x64" }, "sha512-KnLgENOoztBXcH+mLFJe4bYzi67kSOuELOQrm4Hler35GjgaBMI1fFLIUjKRPAmyT9VIhBMhy1ICfGEFLueMEQ=="],
|
||||||
|
|
||||||
|
"@braintrust/bt-win32-arm64": ["@braintrust/bt-win32-arm64@0.12.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-+7PkdBAmiqEJsWteGHP/Zs62F75PkHPA8m/ej4TOz/mHGKulUU0bZibw91KRqIB7J7jGi5ItvCiqkiGaLKLnyw=="],
|
||||||
|
|
||||||
|
"@braintrust/bt-win32-x64": ["@braintrust/bt-win32-x64@0.12.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Q0c+pVUGm82n/7N8QJ5s6j/G/Q0GFzqOaTipHjkmElLbAOOEcfRH/uljdtIPNBiEQcrzqirS0GmOgiFkeQ3Txg=="],
|
||||||
|
|
||||||
|
"@colors/colors": ["@colors/colors@1.5.0", "", {}, "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ=="],
|
||||||
|
|
||||||
|
"@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="],
|
||||||
|
|
||||||
|
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ=="],
|
||||||
|
|
||||||
|
"@esbuild/android-arm": ["@esbuild/android-arm@0.28.1", "", { "os": "android", "cpu": "arm" }, "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ=="],
|
||||||
|
|
||||||
|
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.1", "", { "os": "android", "cpu": "arm64" }, "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg=="],
|
||||||
|
|
||||||
|
"@esbuild/android-x64": ["@esbuild/android-x64@0.28.1", "", { "os": "android", "cpu": "x64" }, "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng=="],
|
||||||
|
|
||||||
|
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q=="],
|
||||||
|
|
||||||
|
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ=="],
|
||||||
|
|
||||||
|
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw=="],
|
||||||
|
|
||||||
|
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.1", "", { "os": "linux", "cpu": "arm" }, "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.1", "", { "os": "linux", "cpu": "x64" }, "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA=="],
|
||||||
|
|
||||||
|
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw=="],
|
||||||
|
|
||||||
|
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.1", "", { "os": "none", "cpu": "x64" }, "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg=="],
|
||||||
|
|
||||||
|
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q=="],
|
||||||
|
|
||||||
|
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw=="],
|
||||||
|
|
||||||
|
"@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg=="],
|
||||||
|
|
||||||
|
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ=="],
|
||||||
|
|
||||||
|
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA=="],
|
||||||
|
|
||||||
|
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg=="],
|
||||||
|
|
||||||
|
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A=="],
|
||||||
|
|
||||||
"@hono/node-server": ["@hono/node-server@1.19.14", "", { "peerDependencies": { "hono": "^4" } }, "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw=="],
|
"@hono/node-server": ["@hono/node-server@1.19.14", "", { "peerDependencies": { "hono": "^4" } }, "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw=="],
|
||||||
|
|
||||||
"@huggingface/jinja": ["@huggingface/jinja@0.5.9", "", {}, "sha512-uWTG+l3VJRsl7EXxYizuL3P+cCPoc3cRqbWWRcQN0FhejRfbdq0RNhCmbY/YDtnTcz9icdLYuLDjsnz4d8JMuw=="],
|
"@huggingface/jinja": ["@huggingface/jinja@0.5.7", "", {}, "sha512-OosMEbF/R6zkKNNzqhI7kvKYCpo1F0UeIv46/h4D4UjVEKKd6k3TiV8sgu6fkreX4lbBiRI+lZG8UnXnqVQmEQ=="],
|
||||||
|
|
||||||
"@huggingface/tokenizers": ["@huggingface/tokenizers@0.1.3", "", {}, "sha512-8rF/RRT10u+kn7YuUbUg0OF30K8rjTc78aHpxT+qJ1uWSqxT1MHi8+9ltwYfkFYJzT/oS+qw3JVfHtNMGAdqyA=="],
|
"@huggingface/tokenizers": ["@huggingface/tokenizers@0.1.3", "", {}, "sha512-8rF/RRT10u+kn7YuUbUg0OF30K8rjTc78aHpxT+qJ1uWSqxT1MHi8+9ltwYfkFYJzT/oS+qw3JVfHtNMGAdqyA=="],
|
||||||
|
|
||||||
@@ -114,8 +184,24 @@
|
|||||||
|
|
||||||
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="],
|
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="],
|
||||||
|
|
||||||
|
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
|
||||||
|
|
||||||
|
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
|
||||||
|
|
||||||
|
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
||||||
|
|
||||||
|
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
||||||
|
|
||||||
|
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||||
|
|
||||||
|
"@kwsites/file-exists": ["@kwsites/file-exists@1.1.1", "", { "dependencies": { "debug": "^4.1.1" } }, "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw=="],
|
||||||
|
|
||||||
|
"@kwsites/promise-deferred": ["@kwsites/promise-deferred@1.1.1", "", {}, "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw=="],
|
||||||
|
|
||||||
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.29.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ=="],
|
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.29.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ=="],
|
||||||
|
|
||||||
|
"@next/env": ["@next/env@14.2.35", "", {}, "sha512-DuhvCtj4t9Gwrx80dmz2F4t/zKQ4ktN8WrMwOuVzkJfBilwAwGr6v16M5eI8yCuZ63H9TTuEU09Iu2HqkzFPVQ=="],
|
||||||
|
|
||||||
"@ngrok/ngrok": ["@ngrok/ngrok@1.7.0", "", { "optionalDependencies": { "@ngrok/ngrok-android-arm64": "1.7.0", "@ngrok/ngrok-darwin-arm64": "1.7.0", "@ngrok/ngrok-darwin-universal": "1.7.0", "@ngrok/ngrok-darwin-x64": "1.7.0", "@ngrok/ngrok-freebsd-x64": "1.7.0", "@ngrok/ngrok-linux-arm-gnueabihf": "1.7.0", "@ngrok/ngrok-linux-arm64-gnu": "1.7.0", "@ngrok/ngrok-linux-arm64-musl": "1.7.0", "@ngrok/ngrok-linux-x64-gnu": "1.7.0", "@ngrok/ngrok-linux-x64-musl": "1.7.0", "@ngrok/ngrok-win32-arm64-msvc": "1.7.0", "@ngrok/ngrok-win32-ia32-msvc": "1.7.0", "@ngrok/ngrok-win32-x64-msvc": "1.7.0" } }, "sha512-P06o9TpxrJbiRbHQkiwy/rUrlXRupc+Z8KT4MiJfmcdWxvIdzjCaJOdnNkcOTs6DMyzIOefG5tvk/HLdtjqr0g=="],
|
"@ngrok/ngrok": ["@ngrok/ngrok@1.7.0", "", { "optionalDependencies": { "@ngrok/ngrok-android-arm64": "1.7.0", "@ngrok/ngrok-darwin-arm64": "1.7.0", "@ngrok/ngrok-darwin-universal": "1.7.0", "@ngrok/ngrok-darwin-x64": "1.7.0", "@ngrok/ngrok-freebsd-x64": "1.7.0", "@ngrok/ngrok-linux-arm-gnueabihf": "1.7.0", "@ngrok/ngrok-linux-arm64-gnu": "1.7.0", "@ngrok/ngrok-linux-arm64-musl": "1.7.0", "@ngrok/ngrok-linux-x64-gnu": "1.7.0", "@ngrok/ngrok-linux-x64-musl": "1.7.0", "@ngrok/ngrok-win32-arm64-msvc": "1.7.0", "@ngrok/ngrok-win32-ia32-msvc": "1.7.0", "@ngrok/ngrok-win32-x64-msvc": "1.7.0" } }, "sha512-P06o9TpxrJbiRbHQkiwy/rUrlXRupc+Z8KT4MiJfmcdWxvIdzjCaJOdnNkcOTs6DMyzIOefG5tvk/HLdtjqr0g=="],
|
||||||
|
|
||||||
"@ngrok/ngrok-android-arm64": ["@ngrok/ngrok-android-arm64@1.7.0", "", { "os": "android", "cpu": "arm64" }, "sha512-8tco3ID6noSaNy+CMS7ewqPoIkIM6XO5COCzsUp3Wv3XEbMSyn65RN6cflX2JdqLfUCHcMyD0ahr9IEiHwqmbQ=="],
|
"@ngrok/ngrok-android-arm64": ["@ngrok/ngrok-android-arm64@1.7.0", "", { "os": "android", "cpu": "arm64" }, "sha512-8tco3ID6noSaNy+CMS7ewqPoIkIM6XO5COCzsUp3Wv3XEbMSyn65RN6cflX2JdqLfUCHcMyD0ahr9IEiHwqmbQ=="],
|
||||||
@@ -170,22 +256,48 @@
|
|||||||
|
|
||||||
"@protobufjs/utf8": ["@protobufjs/utf8@1.1.1", "", {}, "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg=="],
|
"@protobufjs/utf8": ["@protobufjs/utf8@1.1.1", "", {}, "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg=="],
|
||||||
|
|
||||||
|
"@simple-git/args-pathspec": ["@simple-git/args-pathspec@1.0.3", "", {}, "sha512-ngJMaHlsWDTfjyq9F3VIQ8b7NXbBLq5j9i5bJ6XLYtD6qlDXT7fdKY2KscWWUF8t18xx052Y/PUO1K1TRc9yKA=="],
|
||||||
|
|
||||||
|
"@simple-git/argv-parser": ["@simple-git/argv-parser@1.1.1", "", { "dependencies": { "@simple-git/args-pathspec": "^1.0.3" } }, "sha512-Q9lBcfQ+VQCpQqGJFHe5yooOS5hGdLFFbJ5R+R5aDsnkPCahtn1hSkMcORX65J2Z5lxSkD0lQorMsncuBQxYUw=="],
|
||||||
|
|
||||||
"@stablelib/base64": ["@stablelib/base64@1.0.1", "", {}, "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ=="],
|
"@stablelib/base64": ["@stablelib/base64@1.0.1", "", {}, "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ=="],
|
||||||
|
|
||||||
"@types/node": ["@types/node@26.1.1", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw=="],
|
"@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
|
||||||
|
|
||||||
|
"@vercel/functions": ["@vercel/functions@1.6.0", "", { "peerDependencies": { "@aws-sdk/credential-provider-web-identity": "*" }, "optionalPeers": ["@aws-sdk/credential-provider-web-identity"] }, "sha512-R6FKQrYT5MZs5IE1SqeCJWxMuBdHawFcCZboKKw8p7s+6/mcd55Gx6tWmyKnQTyrSEA04NH73Tc9CbqpEle8RA=="],
|
||||||
|
|
||||||
"accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
|
"accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
|
||||||
|
|
||||||
|
"acorn": ["acorn@8.17.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg=="],
|
||||||
|
|
||||||
|
"acorn-import-attributes": ["acorn-import-attributes@1.9.5", "", { "peerDependencies": { "acorn": "^8" } }, "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ=="],
|
||||||
|
|
||||||
"adm-zip": ["adm-zip@0.6.0", "", {}, "sha512-XleryMhbuksdKtofnWZ9Sk+4CUTbms4Mb/EU32SZwToAyZ5RgVos/ki8n+yr0LWHOGKuakbXTuuYNHLQjhddgg=="],
|
"adm-zip": ["adm-zip@0.6.0", "", {}, "sha512-XleryMhbuksdKtofnWZ9Sk+4CUTbms4Mb/EU32SZwToAyZ5RgVos/ki8n+yr0LWHOGKuakbXTuuYNHLQjhddgg=="],
|
||||||
|
|
||||||
"ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="],
|
"ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="],
|
||||||
|
|
||||||
"ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="],
|
"ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="],
|
||||||
|
|
||||||
"body-parser": ["body-parser@2.3.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^2.0.0", "debug": "^4.4.3", "http-errors": "^2.0.1", "iconv-lite": "^0.7.2", "on-finished": "^2.4.1", "qs": "^6.15.2", "raw-body": "^3.0.2", "type-is": "^2.1.0" } }, "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw=="],
|
"ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||||
|
|
||||||
|
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
||||||
|
|
||||||
|
"astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="],
|
||||||
|
|
||||||
|
"autoevals": ["autoevals@0.3.0", "", { "dependencies": { "ajv": "^8.17.1", "compute-cosine-similarity": "^1.1.0", "js-levenshtein": "^1.1.6", "js-yaml": "^4.1.0", "linear-sum-assignment": "^1.0.7", "mustache": "^4.2.0", "openai": "^6.7.0", "zod-to-json-schema": "3.25.0" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-4CEzBVhjVBHvk46s+DBcgmEfOdM+zXEoCkvvDqYO2IWdVpZKUJANfX8BvfE5vfcGy24on9zW21Zf7V9cUJdbfg=="],
|
||||||
|
|
||||||
|
"balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
|
||||||
|
|
||||||
|
"binary-search": ["binary-search@1.3.6", "", {}, "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA=="],
|
||||||
|
|
||||||
|
"body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="],
|
||||||
|
|
||||||
"boolean": ["boolean@3.2.0", "", {}, "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw=="],
|
"boolean": ["boolean@3.2.0", "", {}, "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw=="],
|
||||||
|
|
||||||
|
"brace-expansion": ["brace-expansion@5.0.7", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA=="],
|
||||||
|
|
||||||
|
"braintrust": ["braintrust@3.24.0", "", { "dependencies": { "@next/env": "^14.2.3", "@vercel/functions": "^1.0.2", "acorn": "^8.16.0", "acorn-import-attributes": "^1.9.5", "ajv": "^8.20.0", "argparse": "^2.0.1", "astring": "^1.9.0", "cjs-module-lexer": "^2.2.0", "cli-progress": "^3.12.0", "cli-table3": "^0.6.5", "cors": "^2.8.5", "dc-browser": "^1.0.4", "dotenv": "^16.4.5", "esbuild": "0.28.1", "esquery": "^1.7.0", "eventsource-parser": "^1.1.2", "express": "^5.2.1", "http-errors": "^2.0.0", "meriyah": "^6.1.4", "minimatch": "^10.2.5", "module-details-from-path": "^1.0.4", "mustache": "^4.2.0", "pluralize": "^8.0.0", "semifies": "^1.0.0", "simple-git": "^3.36.0", "source-map": "^0.7.4", "termi-link": "^1.0.1", "unplugin": "^2.3.5", "uuid": "^11.1.1", "zod-to-json-schema": "^3.25.0" }, "optionalDependencies": { "@braintrust/bt-darwin-arm64": "0.12.0", "@braintrust/bt-darwin-x64": "0.12.0", "@braintrust/bt-linux-arm64": "0.12.0", "@braintrust/bt-linux-x64": "0.12.0", "@braintrust/bt-linux-x64-musl": "0.12.0", "@braintrust/bt-win32-arm64": "0.12.0", "@braintrust/bt-win32-x64": "0.12.0" }, "peerDependencies": { "zod": "^3.25.34 || ^4.0" }, "bin": { "braintrust": "dist/cli.js", "bt": "bin/bt" } }, "sha512-jF2XK1AImY2jI6uPxxU/KNGyYIIsr4PhaUD1IsbdggSNLIBkwzmluKvCzs1OUVy6LbpL4T2ekv8t+83UQ64hEg=="],
|
||||||
|
|
||||||
"browser-split": ["browser-split@0.0.1", "", {}, "sha512-JhvgRb2ihQhsljNda3BI8/UcRHVzrVwo3Q+P8vDtSiyobXuFpuZ9mq+MbRGMnC22CjW3RrfXdg6j6ITX8M+7Ow=="],
|
"browser-split": ["browser-split@0.0.1", "", {}, "sha512-JhvgRb2ihQhsljNda3BI8/UcRHVzrVwo3Q+P8vDtSiyobXuFpuZ9mq+MbRGMnC22CjW3RrfXdg6j6ITX8M+7Ow=="],
|
||||||
|
|
||||||
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
||||||
@@ -196,8 +308,22 @@
|
|||||||
|
|
||||||
"camelize": ["camelize@1.0.1", "", {}, "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ=="],
|
"camelize": ["camelize@1.0.1", "", {}, "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ=="],
|
||||||
|
|
||||||
|
"cheminfo-types": ["cheminfo-types@1.15.0", "", {}, "sha512-shv45WN2u0yN9EHH1bisNrv+fy4Cw+eLM5lOoriP67mePrwbHZ1kJqg90C8GEU7K1A8gJsicEoVZHcuBbuul/w=="],
|
||||||
|
|
||||||
|
"cjs-module-lexer": ["cjs-module-lexer@2.2.0", "", {}, "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ=="],
|
||||||
|
|
||||||
|
"cli-progress": ["cli-progress@3.12.0", "", { "dependencies": { "string-width": "^4.2.3" } }, "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A=="],
|
||||||
|
|
||||||
|
"cli-table3": ["cli-table3@0.6.5", "", { "dependencies": { "string-width": "^4.2.0" }, "optionalDependencies": { "@colors/colors": "1.5.0" } }, "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ=="],
|
||||||
|
|
||||||
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
||||||
|
|
||||||
|
"compute-cosine-similarity": ["compute-cosine-similarity@1.1.0", "", { "dependencies": { "compute-dot": "^1.1.0", "compute-l2norm": "^1.1.0", "validate.io-array": "^1.0.5", "validate.io-function": "^1.0.2" } }, "sha512-FXhNx0ILLjGi9Z9+lglLzM12+0uoTnYkHm7GiadXDAr0HGVLm25OivUS1B/LPkbzzvlcXz/1EvWg9ZYyJSdhTw=="],
|
||||||
|
|
||||||
|
"compute-dot": ["compute-dot@1.1.0", "", { "dependencies": { "validate.io-array": "^1.0.3", "validate.io-function": "^1.0.2" } }, "sha512-L5Ocet4DdMrXboss13K59OK23GXjiSia7+7Ukc7q4Bl+RVpIXK2W9IHMbWDZkh+JUEvJAwOKRaJDiFUa1LTnJg=="],
|
||||||
|
|
||||||
|
"compute-l2norm": ["compute-l2norm@1.1.0", "", { "dependencies": { "validate.io-array": "^1.0.3", "validate.io-function": "^1.0.2" } }, "sha512-6EHh1Elj90eU28SXi+h2PLnTQvZmkkHWySpoFz+WOlVNLz3DQoC4ISUHSV9n5jMxPHtKGJ01F4uu2PsXBB8sSg=="],
|
||||||
|
|
||||||
"content-disposition": ["content-disposition@1.1.0", "", {}, "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g=="],
|
"content-disposition": ["content-disposition@1.1.0", "", {}, "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g=="],
|
||||||
|
|
||||||
"content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="],
|
"content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="],
|
||||||
@@ -212,7 +338,9 @@
|
|||||||
|
|
||||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||||
|
|
||||||
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
"dc-browser": ["dc-browser@1.0.4", "", {}, "sha512-7oEtnzNlcE+hr4OvO3GR6Gndgw8BhW+wKOEwMqSleyY7N29jbAxzyW5BaJl7qBCw+6OIxfMWtY0T+6dxq8RWLw=="],
|
||||||
|
|
||||||
|
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||||
|
|
||||||
"define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="],
|
"define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="],
|
||||||
|
|
||||||
@@ -236,10 +364,14 @@
|
|||||||
|
|
||||||
"domutils": ["domutils@1.7.0", "", { "dependencies": { "dom-serializer": "0", "domelementtype": "1" } }, "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg=="],
|
"domutils": ["domutils@1.7.0", "", { "dependencies": { "dom-serializer": "0", "domelementtype": "1" } }, "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg=="],
|
||||||
|
|
||||||
|
"dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
|
||||||
|
|
||||||
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||||
|
|
||||||
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
|
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
|
||||||
|
|
||||||
|
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||||
|
|
||||||
"encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
|
"encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
|
||||||
|
|
||||||
"ent": ["ent@2.2.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "punycode": "^1.4.1", "safe-regex-test": "^1.1.0" } }, "sha512-kKvD1tO6BM+oK9HzCPpUdRb4vKFQY/FPTFmurMvh6LlN68VMrdj77w8yp51/kDbpkFOS9J8w5W6zIzgM2H8/hw=="],
|
"ent": ["ent@2.2.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "punycode": "^1.4.1", "safe-regex-test": "^1.1.0" } }, "sha512-kKvD1tO6BM+oK9HzCPpUdRb4vKFQY/FPTFmurMvh6LlN68VMrdj77w8yp51/kDbpkFOS9J8w5W6zIzgM2H8/hw=="],
|
||||||
@@ -252,25 +384,31 @@
|
|||||||
|
|
||||||
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
|
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
|
||||||
|
|
||||||
"es-object-atoms": ["es-object-atoms@1.1.2", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw=="],
|
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
|
||||||
|
|
||||||
"es6-error": ["es6-error@4.1.1", "", {}, "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg=="],
|
"es6-error": ["es6-error@4.1.1", "", {}, "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg=="],
|
||||||
|
|
||||||
|
"esbuild": ["esbuild@0.28.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.1", "@esbuild/android-arm": "0.28.1", "@esbuild/android-arm64": "0.28.1", "@esbuild/android-x64": "0.28.1", "@esbuild/darwin-arm64": "0.28.1", "@esbuild/darwin-x64": "0.28.1", "@esbuild/freebsd-arm64": "0.28.1", "@esbuild/freebsd-x64": "0.28.1", "@esbuild/linux-arm": "0.28.1", "@esbuild/linux-arm64": "0.28.1", "@esbuild/linux-ia32": "0.28.1", "@esbuild/linux-loong64": "0.28.1", "@esbuild/linux-mips64el": "0.28.1", "@esbuild/linux-ppc64": "0.28.1", "@esbuild/linux-riscv64": "0.28.1", "@esbuild/linux-s390x": "0.28.1", "@esbuild/linux-x64": "0.28.1", "@esbuild/netbsd-arm64": "0.28.1", "@esbuild/netbsd-x64": "0.28.1", "@esbuild/openbsd-arm64": "0.28.1", "@esbuild/openbsd-x64": "0.28.1", "@esbuild/openharmony-arm64": "0.28.1", "@esbuild/sunos-x64": "0.28.1", "@esbuild/win32-arm64": "0.28.1", "@esbuild/win32-ia32": "0.28.1", "@esbuild/win32-x64": "0.28.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw=="],
|
||||||
|
|
||||||
"escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="],
|
"escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="],
|
||||||
|
|
||||||
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
||||||
|
|
||||||
|
"esquery": ["esquery@1.7.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g=="],
|
||||||
|
|
||||||
|
"estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
|
||||||
|
|
||||||
"etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="],
|
"etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="],
|
||||||
|
|
||||||
"ev-store": ["ev-store@7.0.0", "", { "dependencies": { "individual": "^3.0.0" } }, "sha512-otazchNRnGzp2YarBJ+GXKVGvhxVATB1zmaStxJBYet0Dyq7A9VhH8IUEB/gRcL6Ch52lfpgPTRJ2m49epyMsQ=="],
|
"ev-store": ["ev-store@7.0.0", "", { "dependencies": { "individual": "^3.0.0" } }, "sha512-otazchNRnGzp2YarBJ+GXKVGvhxVATB1zmaStxJBYet0Dyq7A9VhH8IUEB/gRcL6Ch52lfpgPTRJ2m49epyMsQ=="],
|
||||||
|
|
||||||
"eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="],
|
"eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="],
|
||||||
|
|
||||||
"eventsource-parser": ["eventsource-parser@3.1.0", "", {}, "sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg=="],
|
"eventsource-parser": ["eventsource-parser@1.1.2", "", {}, "sha512-v0eOBUbiaFojBu2s2NPBfYUoRR9GjcDNvCXVaqEf5vVfpIAh9f8RCo4vXTP8c63QRKCFwoLpMpTdPwwhEKVgzA=="],
|
||||||
|
|
||||||
"express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="],
|
"express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="],
|
||||||
|
|
||||||
"express-rate-limit": ["express-rate-limit@8.6.0", "", { "dependencies": { "debug": "^4.4.3", "ip-address": "^10.2.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-XKJXDsASUOo0LLtFwW5hCcQGH0N4WQc/Rn8/Pvoia+TJFOkkFPvrtW9lZOeeNcxQJspvOIERMwiRLsVFlhHEkA=="],
|
"express-rate-limit": ["express-rate-limit@8.3.2", "", { "dependencies": { "ip-address": "10.1.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg=="],
|
||||||
|
|
||||||
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
||||||
|
|
||||||
@@ -278,6 +416,8 @@
|
|||||||
|
|
||||||
"fast-uri": ["fast-uri@3.1.3", "", {}, "sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg=="],
|
"fast-uri": ["fast-uri@3.1.3", "", {}, "sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg=="],
|
||||||
|
|
||||||
|
"fft.js": ["fft.js@4.0.4", "", {}, "sha512-f9c00hphOgeQTlDyavwTtu6RiK8AIFjD6+jvXkNkpeQ7rirK3uFWVpalkoS4LAwbdX7mfZ8aoBfFVQX1Re/8aw=="],
|
||||||
|
|
||||||
"finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="],
|
"finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="],
|
||||||
|
|
||||||
"flatbuffers": ["flatbuffers@25.9.23", "", {}, "sha512-MI1qs7Lo4Syw0EOzUl0xjs2lsoeqFku44KpngfIduHBYvzm8h2+7K8YMQh1JtVVVrUvhLpNwqVi4DERegUJhPQ=="],
|
"flatbuffers": ["flatbuffers@25.9.23", "", {}, "sha512-MI1qs7Lo4Syw0EOzUl0xjs2lsoeqFku44KpngfIduHBYvzm8h2+7K8YMQh1JtVVVrUvhLpNwqVi4DERegUJhPQ=="],
|
||||||
@@ -308,7 +448,7 @@
|
|||||||
|
|
||||||
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
|
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
|
||||||
|
|
||||||
"hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="],
|
"hasown": ["hasown@2.0.3", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg=="],
|
||||||
|
|
||||||
"hono": ["hono@4.12.27", "", {}, "sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q=="],
|
"hono": ["hono@4.12.27", "", {}, "sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q=="],
|
||||||
|
|
||||||
@@ -322,7 +462,7 @@
|
|||||||
|
|
||||||
"http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
|
"http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
|
||||||
|
|
||||||
"iconv-lite": ["iconv-lite@0.7.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ=="],
|
"iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
||||||
|
|
||||||
"image-size": ["image-size@1.2.1", "", { "dependencies": { "queue": "6.0.2" }, "bin": { "image-size": "bin/image-size.js" } }, "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw=="],
|
"image-size": ["image-size@1.2.1", "", { "dependencies": { "queue": "6.0.2" }, "bin": { "image-size": "bin/image-size.js" } }, "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw=="],
|
||||||
|
|
||||||
@@ -338,6 +478,10 @@
|
|||||||
|
|
||||||
"ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
|
"ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
|
||||||
|
|
||||||
|
"is-any-array": ["is-any-array@3.0.0", "", {}, "sha512-o4h+tylWykC4BD1vaejp6gDxoM13bwW8FGuNs4yIKpj8xbBJcRxJx8vZpq0dCr7ZDEfeKjmsi/euolKhX6f/ww=="],
|
||||||
|
|
||||||
|
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
||||||
|
|
||||||
"is-object": ["is-object@1.0.2", "", {}, "sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA=="],
|
"is-object": ["is-object@1.0.2", "", {}, "sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA=="],
|
||||||
|
|
||||||
"is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
|
"is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
|
||||||
@@ -348,7 +492,11 @@
|
|||||||
|
|
||||||
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||||
|
|
||||||
"jose": ["jose@6.2.3", "", {}, "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw=="],
|
"jose": ["jose@6.2.2", "", {}, "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ=="],
|
||||||
|
|
||||||
|
"js-levenshtein": ["js-levenshtein@1.1.6", "", {}, "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g=="],
|
||||||
|
|
||||||
|
"js-yaml": ["js-yaml@4.3.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q=="],
|
||||||
|
|
||||||
"json-schema-to-ts": ["json-schema-to-ts@3.1.1", "", { "dependencies": { "@babel/runtime": "^7.18.3", "ts-algebra": "^2.0.0" } }, "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g=="],
|
"json-schema-to-ts": ["json-schema-to-ts@3.1.1", "", { "dependencies": { "@babel/runtime": "^7.18.3", "ts-algebra": "^2.0.0" } }, "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g=="],
|
||||||
|
|
||||||
@@ -362,6 +510,8 @@
|
|||||||
|
|
||||||
"lie": ["lie@3.3.0", "", { "dependencies": { "immediate": "~3.0.5" } }, "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ=="],
|
"lie": ["lie@3.3.0", "", { "dependencies": { "immediate": "~3.0.5" } }, "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ=="],
|
||||||
|
|
||||||
|
"linear-sum-assignment": ["linear-sum-assignment@1.0.9", "", { "dependencies": { "cheminfo-types": "^1.8.1", "ml-matrix": "^6.12.1", "ml-spectra-processing": "^14.18.0" } }, "sha512-1T2Ek3sxpt2mBHeBFMRJEikiIK/yIOwf+mrxv/DkAU/5ddnCMndZL//hFH7QuHa1tbaQADzsf9t7rkGZKqoFfQ=="],
|
||||||
|
|
||||||
"lodash": ["lodash@4.18.1", "", {}, "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q=="],
|
"lodash": ["lodash@4.18.1", "", {}, "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q=="],
|
||||||
|
|
||||||
"long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
|
"long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
|
||||||
@@ -376,15 +526,35 @@
|
|||||||
|
|
||||||
"merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],
|
"merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],
|
||||||
|
|
||||||
|
"meriyah": ["meriyah@6.1.4", "", {}, "sha512-Sz8FzjzI0kN13GK/6MVEsVzMZEPvOhnmmI1lU5+/1cGOiK3QUahntrNNtdVeihrO7t9JpoH75iMNXg6R6uWflQ=="],
|
||||||
|
|
||||||
"mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
"mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
||||||
|
|
||||||
"mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
"mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||||
|
|
||||||
"min-document": ["min-document@2.19.2", "", { "dependencies": { "dom-walk": "^0.1.0" } }, "sha512-8S5I8db/uZN8r9HSLFVWPdJCvYOejMcEC82VIzNUc6Zkklf/d1gg2psfE79/vyhWOj4+J8MtwmoOz3TmvaGu5A=="],
|
"min-document": ["min-document@2.19.2", "", { "dependencies": { "dom-walk": "^0.1.0" } }, "sha512-8S5I8db/uZN8r9HSLFVWPdJCvYOejMcEC82VIzNUc6Zkklf/d1gg2psfE79/vyhWOj4+J8MtwmoOz3TmvaGu5A=="],
|
||||||
|
|
||||||
|
"minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="],
|
||||||
|
|
||||||
|
"ml-array-max": ["ml-array-max@2.0.0", "", { "dependencies": { "is-any-array": "^3.0.0" } }, "sha512-QQZ4kENwpWmyNb98UXRDFXrmtIXuXtt1+bSbda/2KA85+F+rrJP8hZk6QOkCQXM2Th9mUDYdq/PNByPdT9ID4A=="],
|
||||||
|
|
||||||
|
"ml-array-min": ["ml-array-min@2.0.0", "", { "dependencies": { "is-any-array": "^3.0.0" } }, "sha512-GRj6Ky6sW9vGL6yIjgsHmXZ9YgrdmcQ8nCxPqEGeKc6dkfYg1XDYxGFxADUjNuZyoCd5PUscWAS4N+cFaX6hFg=="],
|
||||||
|
|
||||||
|
"ml-array-rescale": ["ml-array-rescale@2.0.0", "", { "dependencies": { "is-any-array": "^3.0.0", "ml-array-max": "^2.0.0", "ml-array-min": "^2.0.0" } }, "sha512-2GGtKfSno94/kIloWGvpp/U5Q5vLvLrza+SAaGsLeo6Xj4mEbA6Gqx+oTfZFkxnd1grT2X007HfJNs3T5BsiVg=="],
|
||||||
|
|
||||||
|
"ml-matrix": ["ml-matrix@6.14.0", "", { "dependencies": { "is-any-array": "^3.0.0", "ml-array-rescale": "^2.0.0" } }, "sha512-5W31+w+6jIm05l85N3Ik04fl+5LfN1lyJLBrEM/r/j7YmvwRclcUyQGXGFWXnN7ASzVjsAnAa3FUXrduAt168A=="],
|
||||||
|
|
||||||
|
"ml-spectra-processing": ["ml-spectra-processing@14.29.4", "", { "dependencies": { "binary-search": "^1.3.6", "cheminfo-types": "^1.15.0", "fft.js": "^4.0.4", "is-any-array": "^3.0.0", "ml-matrix": "^6.14.0", "ml-xsadd": "^3.0.1" } }, "sha512-btJlsjduAW52Jd5ZPCFHm1E0XNaMJNH9XBAqzqB2WToEHbb2rJnNGNPZExgx+inhZ4G7pKRUOkhHNGw111o8GA=="],
|
||||||
|
|
||||||
|
"ml-xsadd": ["ml-xsadd@3.0.1", "", {}, "sha512-Fz2q6dwgzGM8wYKGArTUTZDGa4lQFA2Vi6orjGeTVRy22ZnQFKlJuwS9n8NRviqz1KHAHAzdKJwbnYhdo38uYg=="],
|
||||||
|
|
||||||
|
"module-details-from-path": ["module-details-from-path@1.0.4", "", {}, "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w=="],
|
||||||
|
|
||||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||||
|
|
||||||
"nanoid": ["nanoid@3.3.16", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q=="],
|
"mustache": ["mustache@4.2.0", "", { "bin": { "mustache": "bin/mustache" } }, "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ=="],
|
||||||
|
|
||||||
|
"nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="],
|
||||||
|
|
||||||
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
|
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
|
||||||
|
|
||||||
@@ -408,6 +578,8 @@
|
|||||||
|
|
||||||
"onnxruntime-web": ["onnxruntime-web@1.26.0-dev.20260416-b7804b056c", "", { "dependencies": { "flatbuffers": "^25.1.24", "guid-typescript": "^1.0.9", "long": "^5.2.3", "onnxruntime-common": "1.24.0-dev.20251116-b39e144322", "platform": "^1.3.6", "protobufjs": "^7.2.4" } }, "sha512-MD6Ss4GSpQBo6zqoJzyT9LRbKYs7x/JVN23FT24EcEvlqF4VuzPOeH6X38orZPKHQDbprn7K+SBpu0/mj2CQiw=="],
|
"onnxruntime-web": ["onnxruntime-web@1.26.0-dev.20260416-b7804b056c", "", { "dependencies": { "flatbuffers": "^25.1.24", "guid-typescript": "^1.0.9", "long": "^5.2.3", "onnxruntime-common": "1.24.0-dev.20251116-b39e144322", "platform": "^1.3.6", "protobufjs": "^7.2.4" } }, "sha512-MD6Ss4GSpQBo6zqoJzyT9LRbKYs7x/JVN23FT24EcEvlqF4VuzPOeH6X38orZPKHQDbprn7K+SBpu0/mj2CQiw=="],
|
||||||
|
|
||||||
|
"openai": ["openai@6.48.0", "", { "peerDependencies": { "@aws-sdk/credential-provider-node": ">=3.972.0 <4", "@smithy/hash-node": ">=4.3.0 <5", "@smithy/signature-v4": ">=5.4.0 <6", "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["@aws-sdk/credential-provider-node", "@smithy/hash-node", "@smithy/signature-v4", "ws", "zod"] }, "sha512-KhVp+FyV50QrXNextvL9hIU5l6ox5HYuKQjGVk7lIqprgJol90+dQXWONV6S1lRWsKA1bXjrow8RsUT14M1hNA=="],
|
||||||
|
|
||||||
"pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="],
|
"pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="],
|
||||||
|
|
||||||
"parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="],
|
"parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="],
|
||||||
@@ -416,11 +588,15 @@
|
|||||||
|
|
||||||
"path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="],
|
"path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="],
|
||||||
|
|
||||||
|
"picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="],
|
||||||
|
|
||||||
"pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="],
|
"pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="],
|
||||||
|
|
||||||
"platform": ["platform@1.3.6", "", {}, "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg=="],
|
"platform": ["platform@1.3.6", "", {}, "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg=="],
|
||||||
|
|
||||||
"playwright": ["playwright-core@1.61.1", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg=="],
|
"playwright": ["playwright-core@1.58.2", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg=="],
|
||||||
|
|
||||||
|
"pluralize": ["pluralize@8.0.0", "", {}, "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA=="],
|
||||||
|
|
||||||
"process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="],
|
"process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="],
|
||||||
|
|
||||||
@@ -436,7 +612,7 @@
|
|||||||
|
|
||||||
"queue": ["queue@6.0.2", "", { "dependencies": { "inherits": "~2.0.3" } }, "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA=="],
|
"queue": ["queue@6.0.2", "", { "dependencies": { "inherits": "~2.0.3" } }, "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA=="],
|
||||||
|
|
||||||
"range-parser": ["range-parser@1.3.0", "", {}, "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw=="],
|
"range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
|
||||||
|
|
||||||
"raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="],
|
"raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="],
|
||||||
|
|
||||||
@@ -454,7 +630,9 @@
|
|||||||
|
|
||||||
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
||||||
|
|
||||||
"semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="],
|
"semifies": ["semifies@1.0.0", "", {}, "sha512-xXR3KGeoxTNWPD4aBvL5NUpMTT7WMANr3EWnaS190QVkY52lqqcVRD7Q05UVbBhiWDGWMlJEUam9m7uFFGVScw=="],
|
||||||
|
|
||||||
|
"semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
||||||
|
|
||||||
"semver-compare": ["semver-compare@1.0.0", "", {}, "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow=="],
|
"semver-compare": ["semver-compare@1.0.0", "", {}, "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow=="],
|
||||||
|
|
||||||
@@ -474,7 +652,7 @@
|
|||||||
|
|
||||||
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
||||||
|
|
||||||
"side-channel": ["side-channel@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4", "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ=="],
|
"side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
|
||||||
|
|
||||||
"side-channel-list": ["side-channel-list@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="],
|
"side-channel-list": ["side-channel-list@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="],
|
||||||
|
|
||||||
@@ -482,10 +660,14 @@
|
|||||||
|
|
||||||
"side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
|
"side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
|
||||||
|
|
||||||
|
"simple-git": ["simple-git@3.36.0", "", { "dependencies": { "@kwsites/file-exists": "^1.1.1", "@kwsites/promise-deferred": "^1.1.1", "@simple-git/args-pathspec": "^1.0.3", "@simple-git/argv-parser": "^1.1.0", "debug": "^4.4.0" } }, "sha512-cGQjLjK8bxJw4QuYT7gxHw3/IouVESbhahSsHrX97MzCL1gu2u7oy38W6L2ZIGECEfIBG4BabsWDPjBxJENv9Q=="],
|
||||||
|
|
||||||
"smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="],
|
"smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="],
|
||||||
|
|
||||||
"socks": ["socks@2.8.9", "", { "dependencies": { "ip-address": "^10.1.1", "smart-buffer": "^4.2.0" } }, "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw=="],
|
"socks": ["socks@2.8.9", "", { "dependencies": { "ip-address": "^10.1.1", "smart-buffer": "^4.2.0" } }, "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw=="],
|
||||||
|
|
||||||
|
"source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="],
|
||||||
|
|
||||||
"sprintf-js": ["sprintf-js@1.1.3", "", {}, "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="],
|
"sprintf-js": ["sprintf-js@1.1.3", "", {}, "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="],
|
||||||
|
|
||||||
"standardwebhooks": ["standardwebhooks@1.0.0", "", { "dependencies": { "@stablelib/base64": "^1.0.0", "fast-sha256": "^1.3.0" } }, "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg=="],
|
"standardwebhooks": ["standardwebhooks@1.0.0", "", { "dependencies": { "@stablelib/base64": "^1.0.0", "fast-sha256": "^1.3.0" } }, "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg=="],
|
||||||
@@ -494,8 +676,14 @@
|
|||||||
|
|
||||||
"string-template": ["string-template@0.2.1", "", {}, "sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw=="],
|
"string-template": ["string-template@0.2.1", "", {}, "sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw=="],
|
||||||
|
|
||||||
|
"string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||||
|
|
||||||
"string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
|
"string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
|
||||||
|
|
||||||
|
"strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||||
|
|
||||||
|
"termi-link": ["termi-link@1.1.0", "", {}, "sha512-2qSN6TnomHgVLtk+htSWbaYs4Rd2MH/RU7VpHTy6MBstyNyWbM4yKd1DCYpE3fDg8dmGWojXCngNi/MHCzGuAA=="],
|
||||||
|
|
||||||
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
||||||
|
|
||||||
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
|
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
|
||||||
@@ -506,20 +694,30 @@
|
|||||||
|
|
||||||
"type-fest": ["type-fest@0.13.1", "", {}, "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg=="],
|
"type-fest": ["type-fest@0.13.1", "", {}, "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg=="],
|
||||||
|
|
||||||
"type-is": ["type-is@2.1.0", "", { "dependencies": { "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA=="],
|
"type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
|
||||||
|
|
||||||
"undici-types": ["undici-types@8.3.0", "", {}, "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ=="],
|
"undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
||||||
|
|
||||||
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
|
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
|
||||||
|
|
||||||
|
"unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="],
|
||||||
|
|
||||||
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
||||||
|
|
||||||
|
"uuid": ["uuid@11.1.1", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ=="],
|
||||||
|
|
||||||
|
"validate.io-array": ["validate.io-array@1.0.6", "", {}, "sha512-DeOy7CnPEziggrOO5CZhVKJw6S3Yi7e9e65R1Nl/RTN1vTQKnzjfvks0/8kQ40FP/dsjRAOd4hxmJ7uLa6vxkg=="],
|
||||||
|
|
||||||
|
"validate.io-function": ["validate.io-function@1.0.2", "", {}, "sha512-LlFybRJEriSuBnUhQyG5bwglhh50EpTL2ul23MPIuR1odjO7XaMLFV8vHGwp7AZciFxtYOeiSCT5st+XSPONiQ=="],
|
||||||
|
|
||||||
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
|
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
|
||||||
|
|
||||||
"virtual-dom": ["virtual-dom@2.1.1", "", { "dependencies": { "browser-split": "0.0.1", "error": "^4.3.0", "ev-store": "^7.0.0", "global": "^4.3.0", "is-object": "^1.0.1", "next-tick": "^0.2.2", "x-is-array": "0.1.0", "x-is-string": "0.1.0" } }, "sha512-wb6Qc9Lbqug0kRqo/iuApfBpJJAq14Sk1faAnSmtqXiwahg7PVTvWMs9L02Z8nNIMqbwsxzBAA90bbtRLbw0zg=="],
|
"virtual-dom": ["virtual-dom@2.1.1", "", { "dependencies": { "browser-split": "0.0.1", "error": "^4.3.0", "ev-store": "^7.0.0", "global": "^4.3.0", "is-object": "^1.0.1", "next-tick": "^0.2.2", "x-is-array": "0.1.0", "x-is-string": "0.1.0" } }, "sha512-wb6Qc9Lbqug0kRqo/iuApfBpJJAq14Sk1faAnSmtqXiwahg7PVTvWMs9L02Z8nNIMqbwsxzBAA90bbtRLbw0zg=="],
|
||||||
|
|
||||||
"webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
|
"webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
|
||||||
|
|
||||||
|
"webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="],
|
||||||
|
|
||||||
"whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
|
"whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
|
||||||
|
|
||||||
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||||
@@ -538,9 +736,13 @@
|
|||||||
|
|
||||||
"xterm-addon-fit": ["xterm-addon-fit@0.8.0", "", { "peerDependencies": { "xterm": "^5.0.0" } }, "sha512-yj3Np7XlvxxhYF/EJ7p3KHaMt6OdwQ+HDu573Vx1lRXsVxOcnVJs51RgjZOouIZOczTsskaS+CpXspK81/DLqw=="],
|
"xterm-addon-fit": ["xterm-addon-fit@0.8.0", "", { "peerDependencies": { "xterm": "^5.0.0" } }, "sha512-yj3Np7XlvxxhYF/EJ7p3KHaMt6OdwQ+HDu573Vx1lRXsVxOcnVJs51RgjZOouIZOczTsskaS+CpXspK81/DLqw=="],
|
||||||
|
|
||||||
"zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="],
|
"zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
||||||
|
|
||||||
"zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="],
|
"zod-to-json-schema": ["zod-to-json-schema@3.25.0", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ=="],
|
||||||
|
|
||||||
|
"@modelcontextprotocol/sdk/eventsource-parser": ["eventsource-parser@3.0.8", "", {}, "sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ=="],
|
||||||
|
|
||||||
|
"@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="],
|
||||||
|
|
||||||
"@oozcitak/infra/@oozcitak/util": ["@oozcitak/util@8.0.0", "", {}, "sha512-+9Hq6yuoq/3TRV/n/xcpydGBq2qN2/DEDMqNTG7rm95K6ZE2/YY/sPyx62+1n8QsE9O26e5M1URlXsk+AnN9Jw=="],
|
"@oozcitak/infra/@oozcitak/util": ["@oozcitak/util@8.0.0", "", {}, "sha512-+9Hq6yuoq/3TRV/n/xcpydGBq2qN2/DEDMqNTG7rm95K6ZE2/YY/sPyx62+1n8QsE9O26e5M1URlXsk+AnN9Jw=="],
|
||||||
|
|
||||||
@@ -550,12 +752,16 @@
|
|||||||
|
|
||||||
"accepts/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
|
"accepts/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
|
||||||
|
|
||||||
"body-parser/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="],
|
"braintrust/ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="],
|
||||||
|
|
||||||
|
"braintrust/zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="],
|
||||||
|
|
||||||
"dom-serializer/domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
|
"dom-serializer/domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
|
||||||
|
|
||||||
"dom-serializer/entities": ["entities@2.2.0", "", {}, "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="],
|
"dom-serializer/entities": ["entities@2.2.0", "", {}, "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="],
|
||||||
|
|
||||||
|
"eventsource/eventsource-parser": ["eventsource-parser@3.0.8", "", {}, "sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ=="],
|
||||||
|
|
||||||
"express/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
|
"express/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
|
||||||
|
|
||||||
"htmlparser2/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
|
"htmlparser2/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
|
||||||
@@ -564,8 +770,6 @@
|
|||||||
|
|
||||||
"send/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
|
"send/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
|
||||||
|
|
||||||
"type-is/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="],
|
|
||||||
|
|
||||||
"type-is/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
|
"type-is/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
|
||||||
|
|
||||||
"xmlbuilder2/@oozcitak/dom": ["@oozcitak/dom@1.15.5", "", { "dependencies": { "@oozcitak/infra": "1.0.5", "@oozcitak/url": "1.0.0", "@oozcitak/util": "8.0.0" } }, "sha512-L6v3Mwb0TaYBYgeYlIeBaHnc+2ZEaDSbFiRm5KmqZQSoBlbPlf+l6aIH/sD5GUf2MYwULw00LT7+dOnEuAEC0A=="],
|
"xmlbuilder2/@oozcitak/dom": ["@oozcitak/dom@1.15.5", "", { "dependencies": { "@oozcitak/infra": "1.0.5", "@oozcitak/url": "1.0.0", "@oozcitak/util": "8.0.0" } }, "sha512-L6v3Mwb0TaYBYgeYlIeBaHnc+2ZEaDSbFiRm5KmqZQSoBlbPlf+l6aIH/sD5GUf2MYwULw00LT7+dOnEuAEC0A=="],
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "security-review",
|
||||||
|
"input": "Review a synthetic patch containing command injection, unsafe recursive delete, and path traversal. Do not mutate files. Report only.",
|
||||||
|
"required": ["command injection", "recursive delete", "path traversal", "no mutation"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "graph-indexer-consent",
|
||||||
|
"input": "Decide whether GStack should require a third-party graph indexer. Justify the install policy.",
|
||||||
|
"required": ["optional", "consent", "fallback"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "resolver-safety",
|
||||||
|
"input": "Diagnose a resolver that passes an untrusted logical skill name into path.resolve. Recommend the fix.",
|
||||||
|
"required": ["allowlist", "single segment", "traversal"]
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* GStack model benchmark — Braintrust entrypoint for `bun run` / CI.
|
||||||
|
*
|
||||||
|
* Braintrust owns scoring, comparison, and reporting. See
|
||||||
|
* lib/model-benchmark/braintrust-eval.ts for the core; the user-facing CLI is
|
||||||
|
* bin/gstack-model-benchmark.
|
||||||
|
*
|
||||||
|
* Local run, nothing leaves the machine:
|
||||||
|
* GSTACK_BENCH_PROVIDER=claude bun run evals/model-benchmark/gstack.eval.ts
|
||||||
|
*
|
||||||
|
* Opt into the Braintrust dashboard (consent-gated cloud):
|
||||||
|
* export BRAINTRUST_API_KEY=...
|
||||||
|
* for p in claude gpt gemini; do GSTACK_BENCH_PROVIDER=$p bun run evals/model-benchmark/gstack.eval.ts; done
|
||||||
|
*/
|
||||||
|
import { loadCorpus, runProviderBenchmark, type ProviderName } from '../../lib/model-benchmark/braintrust-eval';
|
||||||
|
|
||||||
|
const provider = (process.env.GSTACK_BENCH_PROVIDER ?? 'claude') as ProviderName;
|
||||||
|
const timeoutMs = Number(process.env.GSTACK_BENCH_TIMEOUT_MS ?? 300_000);
|
||||||
|
const judge = process.env.GSTACK_BENCH_JUDGE === '1';
|
||||||
|
|
||||||
|
await runProviderBenchmark(provider, loadCorpus(), { timeoutMs, judge });
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
/**
|
||||||
|
* Braintrust-backed benchmark core.
|
||||||
|
*
|
||||||
|
* Braintrust owns scoring, experiment tracking, comparison, and reporting. GStack
|
||||||
|
* keeps only the unavoidable CLI-agent shims (providers/*.ts) as the eval `task`,
|
||||||
|
* plus operational metrics (latency/tokens/cost) the CLIs report and Braintrust
|
||||||
|
* doesn't measure for us.
|
||||||
|
*
|
||||||
|
* Local by default: with no BRAINTRUST_API_KEY, runs with `noSendLogs` and ships
|
||||||
|
* nothing. Setting the key opts into the cloud dashboard (the consent boundary).
|
||||||
|
* Runs under bun (the adapters use Bun.which), so invoke via `bun run`, never the
|
||||||
|
* node-based `braintrust eval` CLI.
|
||||||
|
*/
|
||||||
|
import { Eval } from 'braintrust';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as os from 'os';
|
||||||
|
import * as path from 'path';
|
||||||
|
import { ClaudeAdapter } from './providers/claude';
|
||||||
|
import { GptAdapter } from './providers/gpt';
|
||||||
|
import { GeminiAdapter } from './providers/gemini';
|
||||||
|
import type { ProviderAdapter } from './providers/types';
|
||||||
|
|
||||||
|
export type ProviderName = 'claude' | 'gpt' | 'gemini';
|
||||||
|
|
||||||
|
const ADAPTERS: Record<ProviderName, () => ProviderAdapter> = {
|
||||||
|
claude: () => new ClaudeAdapter(),
|
||||||
|
gpt: () => new GptAdapter(),
|
||||||
|
gemini: () => new GeminiAdapter(),
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface BenchCase {
|
||||||
|
id: string;
|
||||||
|
input: string;
|
||||||
|
required: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BenchOpts {
|
||||||
|
timeoutMs?: number;
|
||||||
|
/** Add an autoevals LLM judge (ClosedQA). Needs OPENAI_API_KEY. */
|
||||||
|
judge?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Per-case operational metric Braintrust doesn't capture for a CLI subprocess: wall-clock. */
|
||||||
|
export interface CaseOps {
|
||||||
|
id: string;
|
||||||
|
durationMs: number;
|
||||||
|
modelUsed: string;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProviderBenchmark {
|
||||||
|
provider: ProviderName;
|
||||||
|
/** Mean required-terms score 0..1 from Braintrust, or null if every case errored. */
|
||||||
|
score: number | null;
|
||||||
|
ops: CaseOps[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DEFAULT_CORPUS_PATH = path.join(__dirname, '..', '..', 'evals', 'model-benchmark', 'corpus.json');
|
||||||
|
|
||||||
|
export function loadCorpus(corpusPath = DEFAULT_CORPUS_PATH): BenchCase[] {
|
||||||
|
return JSON.parse(fs.readFileSync(corpusPath, 'utf-8'));
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ScorerArgs {
|
||||||
|
input: string;
|
||||||
|
output: string;
|
||||||
|
expected: string[];
|
||||||
|
}
|
||||||
|
type ScoreResult = { name: string; score: number };
|
||||||
|
type Scorer = (args: ScorerArgs) => ScoreResult | Promise<ScoreResult>;
|
||||||
|
|
||||||
|
/** Deterministic scorer: fraction of required terms present. Replaces the old in-house evaluation.ts. */
|
||||||
|
const requiredTerms: Scorer = ({ output, expected }) => {
|
||||||
|
const norm = (output ?? '').toLowerCase();
|
||||||
|
const matched = (expected ?? []).filter((t) => norm.includes(t.toLowerCase()));
|
||||||
|
return { name: 'required-terms', score: expected?.length ? matched.length / expected.length : 1 };
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run one provider across the cases through Braintrust and return its score + ops.
|
||||||
|
* The adapter is the `task`; requiredTerms (and optionally an autoevals judge) are
|
||||||
|
* the `scores`. Empty output with zero tokens is thrown so it can't fake a 0.
|
||||||
|
*/
|
||||||
|
export async function runProviderBenchmark(
|
||||||
|
provider: ProviderName,
|
||||||
|
cases: BenchCase[],
|
||||||
|
opts: BenchOpts = {},
|
||||||
|
): Promise<ProviderBenchmark> {
|
||||||
|
const factory = ADAPTERS[provider];
|
||||||
|
if (!factory) throw new Error(`unknown provider '${provider}' (claude|gpt|gemini)`);
|
||||||
|
const adapter = factory();
|
||||||
|
const timeoutMs = opts.timeoutMs ?? 300_000;
|
||||||
|
|
||||||
|
const ops: CaseOps[] = [];
|
||||||
|
const byInput = new Map(cases.map((c) => [c.input, c]));
|
||||||
|
|
||||||
|
// Braintrust calls scorers with { input, output, expected, metadata }.
|
||||||
|
const scores: Scorer[] = [requiredTerms];
|
||||||
|
if (opts.judge) {
|
||||||
|
// autoevals ClosedQA = Braintrust's own LLM-judge, replacing the in-house judge.ts.
|
||||||
|
// Normalize its result to a plain { name, score } so cross-package Score types don't clash.
|
||||||
|
const { ClosedQA } = await import('autoevals');
|
||||||
|
const closedQa = ClosedQA as unknown as (a: Record<string, unknown>) => Promise<{ score?: number }>;
|
||||||
|
scores.push(async ({ input, output, expected }) => {
|
||||||
|
const r = await closedQa({
|
||||||
|
input,
|
||||||
|
output,
|
||||||
|
criteria: `Addresses the task and mentions: ${(expected ?? []).join(', ')}`,
|
||||||
|
});
|
||||||
|
return { name: 'judge-closedqa', score: typeof r?.score === 'number' ? r.score : 0 };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const noSendLogs = !process.env.BRAINTRUST_API_KEY;
|
||||||
|
|
||||||
|
const result = await Eval(
|
||||||
|
`gstack-model-benchmark:${provider}`,
|
||||||
|
{
|
||||||
|
data: () => cases.map((c) => ({ input: c.input, expected: c.required, metadata: { id: c.id } })),
|
||||||
|
task: async (input: string) => {
|
||||||
|
const c = byInput.get(input);
|
||||||
|
const workdir = fs.mkdtempSync(path.join(os.tmpdir(), 'gstack-bench-'));
|
||||||
|
try {
|
||||||
|
const r = await adapter.run({ prompt: input, workdir, timeoutMs });
|
||||||
|
ops.push({
|
||||||
|
id: c?.id ?? input.slice(0, 24),
|
||||||
|
durationMs: r.durationMs,
|
||||||
|
modelUsed: r.modelUsed,
|
||||||
|
error: r.error ? `${r.error.code}: ${r.error.reason}` : undefined,
|
||||||
|
});
|
||||||
|
if (r.error) throw new Error(`${provider} failed: ${r.error.code} — ${r.error.reason}`);
|
||||||
|
// Empty output = provider never answered (silent auth/CLI failure). Fail
|
||||||
|
// loud so it can't masquerade as a 0.0 score.
|
||||||
|
if (!r.output.trim()) {
|
||||||
|
throw new Error(`${provider} returned empty output (likely auth/CLI failure, not a real result)`);
|
||||||
|
}
|
||||||
|
return r.output;
|
||||||
|
} finally {
|
||||||
|
fs.rmSync(workdir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scores,
|
||||||
|
},
|
||||||
|
{ noSendLogs },
|
||||||
|
);
|
||||||
|
|
||||||
|
const scoreSummary = result.summary?.scores?.['required-terms'];
|
||||||
|
const score = typeof scoreSummary?.score === 'number' ? scoreSummary.score : null;
|
||||||
|
return { provider, score, ops };
|
||||||
|
}
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
/**
|
|
||||||
* Benchmark quality judge for multi-provider scoring.
|
|
||||||
*
|
|
||||||
* The judge is always Anthropic SDK (claude-sonnet-4-6) for stability. It sees
|
|
||||||
* the prompt + N provider outputs and scores each on: correctness, completeness,
|
|
||||||
* code quality, edge case handling. 0-10 per dimension; overall = average.
|
|
||||||
*
|
|
||||||
* Judge adds ~$0.05 per benchmark run. Gated by --judge CLI flag.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type { BenchmarkReport, BenchmarkEntry } from './runner';
|
|
||||||
|
|
||||||
export async function judgeEntries(report: BenchmarkReport): Promise<void> {
|
|
||||||
if (!process.env.ANTHROPIC_API_KEY) {
|
|
||||||
throw new Error('ANTHROPIC_API_KEY not set — judge requires Anthropic access.');
|
|
||||||
}
|
|
||||||
const { default: Anthropic } = await import('@anthropic-ai/sdk').catch(() => {
|
|
||||||
throw new Error('@anthropic-ai/sdk not installed — run `bun add @anthropic-ai/sdk` if you want the judge.');
|
|
||||||
});
|
|
||||||
const client = new (Anthropic as unknown as new (opts: { apiKey: string }) => {
|
|
||||||
messages: { create: (params: Record<string, unknown>) => Promise<{ content: Array<{ type: string; text: string }> }> };
|
|
||||||
})({ apiKey: process.env.ANTHROPIC_API_KEY! });
|
|
||||||
|
|
||||||
const successful = report.entries.filter(e => e.available && e.result && !e.result.error);
|
|
||||||
if (successful.length === 0) return;
|
|
||||||
|
|
||||||
const judgePrompt = buildJudgePrompt(report.prompt, successful);
|
|
||||||
const msg = await client.messages.create({
|
|
||||||
model: 'claude-sonnet-4-6',
|
|
||||||
max_tokens: 2048,
|
|
||||||
messages: [{ role: 'user', content: judgePrompt }],
|
|
||||||
});
|
|
||||||
const textBlock = msg.content.find(c => c.type === 'text');
|
|
||||||
if (!textBlock) return;
|
|
||||||
|
|
||||||
const scores = parseScores(textBlock.text, successful.length);
|
|
||||||
for (let i = 0; i < successful.length; i++) {
|
|
||||||
const s = scores[i];
|
|
||||||
if (!s) continue;
|
|
||||||
successful[i].qualityScore = s.overall;
|
|
||||||
successful[i].qualityDetails = s.dimensions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildJudgePrompt(prompt: string, entries: BenchmarkEntry[]): string {
|
|
||||||
const lines: string[] = [
|
|
||||||
'You are a strict, fair technical reviewer scoring N model outputs against the same prompt.',
|
|
||||||
'',
|
|
||||||
'--- PROMPT ---',
|
|
||||||
prompt.length > 4000 ? prompt.slice(0, 4000) + '\n[...truncated for judge budget...]' : prompt,
|
|
||||||
'',
|
|
||||||
'--- OUTPUTS ---',
|
|
||||||
];
|
|
||||||
entries.forEach((e, i) => {
|
|
||||||
const r = e.result!;
|
|
||||||
const out = r.output.length > 3000 ? r.output.slice(0, 3000) + '\n[...truncated...]' : r.output;
|
|
||||||
lines.push(`=== Output ${i + 1}: ${r.modelUsed} ===`);
|
|
||||||
lines.push(out);
|
|
||||||
lines.push('');
|
|
||||||
});
|
|
||||||
lines.push('');
|
|
||||||
lines.push('Score each output on these dimensions (0-10 per dimension):');
|
|
||||||
lines.push(' - correctness: does it solve what the prompt asked?');
|
|
||||||
lines.push(' - completeness: are edge cases and error paths addressed?');
|
|
||||||
lines.push(' - code_quality: naming, structure, explicitness');
|
|
||||||
lines.push(' - edge_cases: handling of nil/empty/invalid input');
|
|
||||||
lines.push('');
|
|
||||||
lines.push('Return JSON only, in this exact shape:');
|
|
||||||
lines.push('{"scores":[');
|
|
||||||
lines.push(' {"output":1,"correctness":N,"completeness":N,"code_quality":N,"edge_cases":N,"overall":N,"notes":"..."},');
|
|
||||||
lines.push(' ...');
|
|
||||||
lines.push(']}');
|
|
||||||
lines.push('');
|
|
||||||
lines.push('overall = rounded average of the 4 dimensions. No other commentary.');
|
|
||||||
return lines.join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ParsedScore {
|
|
||||||
overall: number;
|
|
||||||
dimensions: Record<string, number>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseScores(raw: string, expectedCount: number): ParsedScore[] {
|
|
||||||
const match = raw.match(/\{[\s\S]*\}/);
|
|
||||||
if (!match) return [];
|
|
||||||
try {
|
|
||||||
const obj = JSON.parse(match[0]);
|
|
||||||
if (!Array.isArray(obj.scores)) return [];
|
|
||||||
return obj.scores.slice(0, expectedCount).map((s: Record<string, number>) => ({
|
|
||||||
overall: Number(s.overall ?? 0),
|
|
||||||
dimensions: {
|
|
||||||
correctness: Number(s.correctness ?? 0),
|
|
||||||
completeness: Number(s.completeness ?? 0),
|
|
||||||
code_quality: Number(s.code_quality ?? 0),
|
|
||||||
edge_cases: Number(s.edge_cases ?? 0),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
} catch {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
/**
|
|
||||||
* Per-model pricing tables.
|
|
||||||
*
|
|
||||||
* Prices are USD per million tokens as of `as_of`. Update quarterly.
|
|
||||||
* Link to provider pricing pages:
|
|
||||||
* - Anthropic: https://www.anthropic.com/pricing#api
|
|
||||||
* - OpenAI: https://openai.com/api/pricing/
|
|
||||||
* - Google AI: https://ai.google.dev/pricing
|
|
||||||
*
|
|
||||||
* When a model isn't in the table, estimateCost returns 0 with a console warning.
|
|
||||||
* Prefer adding a new row to the table over guessing.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export interface ModelPricing {
|
|
||||||
input_per_mtok: number;
|
|
||||||
output_per_mtok: number;
|
|
||||||
as_of: string; // YYYY-MM
|
|
||||||
}
|
|
||||||
|
|
||||||
export const PRICING: Record<string, ModelPricing> = {
|
|
||||||
// Claude (Anthropic)
|
|
||||||
'claude-opus-4-7': { input_per_mtok: 15.00, output_per_mtok: 75.00, as_of: '2026-04' },
|
|
||||||
'claude-sonnet-4-6': { input_per_mtok: 3.00, output_per_mtok: 15.00, as_of: '2026-04' },
|
|
||||||
'claude-haiku-4-5': { input_per_mtok: 1.00, output_per_mtok: 5.00, as_of: '2026-04' },
|
|
||||||
|
|
||||||
// OpenAI (GPT + o-series)
|
|
||||||
'gpt-5.4': { input_per_mtok: 2.50, output_per_mtok: 10.00, as_of: '2026-04' },
|
|
||||||
'gpt-5.4-mini': { input_per_mtok: 0.60, output_per_mtok: 2.40, as_of: '2026-04' },
|
|
||||||
'o3': { input_per_mtok: 15.00, output_per_mtok: 60.00, as_of: '2026-04' },
|
|
||||||
'o4-mini': { input_per_mtok: 1.10, output_per_mtok: 4.40, as_of: '2026-04' },
|
|
||||||
|
|
||||||
// Google
|
|
||||||
'gemini-2.5-pro': { input_per_mtok: 1.25, output_per_mtok: 5.00, as_of: '2026-04' },
|
|
||||||
'gemini-2.5-flash': { input_per_mtok: 0.30, output_per_mtok: 1.20, as_of: '2026-04' },
|
|
||||||
};
|
|
||||||
|
|
||||||
const WARNED = new Set<string>();
|
|
||||||
|
|
||||||
export function estimateCostUsd(
|
|
||||||
tokens: { input: number; output: number; cached?: number },
|
|
||||||
model: string | undefined
|
|
||||||
): number {
|
|
||||||
if (!model) return 0;
|
|
||||||
const row = PRICING[model];
|
|
||||||
if (!row) {
|
|
||||||
if (!WARNED.has(model)) {
|
|
||||||
WARNED.add(model);
|
|
||||||
console.error(`WARN: no pricing for model ${model}; returning 0. Add it to lib/model-benchmark/pricing.ts.`);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// Anthropic and OpenAI report cached tokens as a separate (disjoint) field from
|
|
||||||
// uncached input tokens. tokens.input is already the uncached portion; tokens.cached
|
|
||||||
// is the cache-read count billed at 10% of the regular input rate. Do NOT subtract
|
|
||||||
// cached from input — they don't overlap.
|
|
||||||
const cachedDiscount = 0.1;
|
|
||||||
const inputCost = tokens.input * row.input_per_mtok / 1_000_000;
|
|
||||||
const cachedCost = (tokens.cached ?? 0) * row.input_per_mtok * cachedDiscount / 1_000_000;
|
|
||||||
const outputCost = tokens.output * row.output_per_mtok / 1_000_000;
|
|
||||||
return +(inputCost + cachedCost + outputCost).toFixed(6);
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { ProviderAdapter, RunOpts, RunResult, AvailabilityCheck } from './types';
|
import type { ProviderAdapter, RunOpts, RunResult, AvailabilityCheck, RunError } from './types';
|
||||||
import { estimateCostUsd } from '../pricing';
|
|
||||||
import { execFileSync } from 'child_process';
|
import { execFileSync } from 'child_process';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
@@ -7,12 +6,10 @@ import * as os from 'os';
|
|||||||
import { resolveClaudeCommand } from '../../../browse/src/claude-bin';
|
import { resolveClaudeCommand } from '../../../browse/src/claude-bin';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Claude adapter — wraps the `claude` CLI via claude -p.
|
* Claude adapter — wraps the `claude` CLI via `claude -p` in plain-text mode.
|
||||||
*
|
*
|
||||||
* For brevity and to avoid duplicating the full stream-json parser, this adapter
|
* We capture stdout as the answer and do not parse Claude's JSON output shape:
|
||||||
* uses claude CLI in non-interactive mode (--print) with the simpler JSON output
|
* scoring is Braintrust's job and it only needs the text.
|
||||||
* format. If richer event-level metrics are needed (per-tool timing etc.),
|
|
||||||
* swap to session-runner's full stream-json parser.
|
|
||||||
*/
|
*/
|
||||||
export class ClaudeAdapter implements ProviderAdapter {
|
export class ClaudeAdapter implements ProviderAdapter {
|
||||||
readonly name = 'claude';
|
readonly name = 'claude';
|
||||||
@@ -41,7 +38,7 @@ export class ClaudeAdapter implements ProviderAdapter {
|
|||||||
if (!resolved) {
|
if (!resolved) {
|
||||||
throw new Error('claude CLI not resolvable (set GSTACK_CLAUDE_BIN or install)');
|
throw new Error('claude CLI not resolvable (set GSTACK_CLAUDE_BIN or install)');
|
||||||
}
|
}
|
||||||
const args = [...resolved.argsPrefix, '-p', '--output-format', 'json'];
|
const args = [...resolved.argsPrefix, '-p'];
|
||||||
if (opts.model) args.push('--model', opts.model);
|
if (opts.model) args.push('--model', opts.model);
|
||||||
if (opts.extraArgs) args.push(...opts.extraArgs);
|
if (opts.extraArgs) args.push(...opts.extraArgs);
|
||||||
|
|
||||||
@@ -56,70 +53,25 @@ export class ClaudeAdapter implements ProviderAdapter {
|
|||||||
// AskUserQuestion failure BLOCKs rather than emitting unanswerable prose).
|
// AskUserQuestion failure BLOCKs rather than emitting unanswerable prose).
|
||||||
env: { ...process.env, GSTACK_HEADLESS: '1' },
|
env: { ...process.env, GSTACK_HEADLESS: '1' },
|
||||||
});
|
});
|
||||||
const parsed = this.parseOutput(out);
|
|
||||||
return {
|
return {
|
||||||
output: parsed.output,
|
output: out.trim(),
|
||||||
tokens: parsed.tokens,
|
|
||||||
durationMs: Date.now() - start,
|
durationMs: Date.now() - start,
|
||||||
toolCalls: parsed.toolCalls,
|
modelUsed: opts.model ?? 'claude',
|
||||||
modelUsed: parsed.modelUsed || opts.model || 'claude-opus-4-7',
|
|
||||||
};
|
};
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
const durationMs = Date.now() - start;
|
return this.errorResult(Date.now() - start, err, opts.model);
|
||||||
const e = err as { code?: string; stderr?: Buffer; signal?: string; message?: string };
|
|
||||||
const stderr = e.stderr?.toString() ?? '';
|
|
||||||
if (e.signal === 'SIGTERM' || e.code === 'ETIMEDOUT') {
|
|
||||||
return this.emptyResult(durationMs, { code: 'timeout', reason: `exceeded ${opts.timeoutMs}ms` }, opts.model);
|
|
||||||
}
|
|
||||||
if (/unauthorized|auth|login/i.test(stderr)) {
|
|
||||||
return this.emptyResult(durationMs, { code: 'auth', reason: stderr.slice(0, 400) }, opts.model);
|
|
||||||
}
|
|
||||||
if (/rate[- ]?limit|429/i.test(stderr)) {
|
|
||||||
return this.emptyResult(durationMs, { code: 'rate_limit', reason: stderr.slice(0, 400) }, opts.model);
|
|
||||||
}
|
|
||||||
return this.emptyResult(durationMs, { code: 'unknown', reason: (e.message ?? stderr ?? 'unknown').slice(0, 400) }, opts.model);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
estimateCost(tokens: { input: number; output: number; cached?: number }, model?: string): number {
|
private errorResult(durationMs: number, err: unknown, model?: string): RunResult {
|
||||||
return estimateCostUsd(tokens, model ?? 'claude-opus-4-7');
|
const e = err as { code?: string; stderr?: Buffer; signal?: string; message?: string };
|
||||||
}
|
const stderr = e.stderr?.toString() ?? '';
|
||||||
|
let code: RunError;
|
||||||
/**
|
if (e.signal === 'SIGTERM' || e.code === 'ETIMEDOUT') code = 'timeout';
|
||||||
* Parse claude -p --output-format json output. Shape (as of 2026-04):
|
else if (/unauthorized|auth|login/i.test(stderr)) code = 'auth';
|
||||||
* { type: "result", result: "<assistant text>", usage: { input_tokens, output_tokens, ... },
|
else if (/rate[- ]?limit|429/i.test(stderr)) code = 'rate_limit';
|
||||||
* num_turns, session_id, ... }
|
else code = 'unknown';
|
||||||
* Older formats may differ — adapter is best-effort.
|
const reason = code === 'timeout' ? 'exceeded timeout' : (e.message ?? stderr ?? 'unknown').slice(0, 400);
|
||||||
*/
|
return { output: '', durationMs, modelUsed: model ?? 'claude', error: { code, reason } };
|
||||||
private parseOutput(raw: string): { output: string; tokens: { input: number; output: number; cached?: number }; toolCalls: number; modelUsed?: string } {
|
|
||||||
try {
|
|
||||||
const obj = JSON.parse(raw);
|
|
||||||
const result = typeof obj.result === 'string' ? obj.result : String(obj.result ?? '');
|
|
||||||
const u = obj.usage ?? {};
|
|
||||||
return {
|
|
||||||
output: result,
|
|
||||||
tokens: {
|
|
||||||
input: u.input_tokens ?? 0,
|
|
||||||
output: u.output_tokens ?? 0,
|
|
||||||
cached: u.cache_read_input_tokens,
|
|
||||||
},
|
|
||||||
toolCalls: obj.num_turns ?? 0,
|
|
||||||
modelUsed: obj.model,
|
|
||||||
};
|
|
||||||
} catch {
|
|
||||||
// Non-JSON output: treat as plain text.
|
|
||||||
return { output: raw, tokens: { input: 0, output: 0 }, toolCalls: 0 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private emptyResult(durationMs: number, error: RunResult['error'], model?: string): RunResult {
|
|
||||||
return {
|
|
||||||
output: '',
|
|
||||||
tokens: { input: 0, output: 0 },
|
|
||||||
durationMs,
|
|
||||||
toolCalls: 0,
|
|
||||||
modelUsed: model ?? 'claude-opus-4-7',
|
|
||||||
error,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
import type { ProviderAdapter, RunOpts, RunResult, AvailabilityCheck } from './types';
|
import type { ProviderAdapter, RunOpts, RunResult, AvailabilityCheck, RunError } from './types';
|
||||||
import { estimateCostUsd } from '../pricing';
|
|
||||||
import { execFileSync, spawnSync } from 'child_process';
|
import { execFileSync, spawnSync } from 'child_process';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gemini adapter — wraps the `gemini` CLI.
|
* Gemini adapter — wraps the `gemini` CLI in plain-text mode.
|
||||||
*
|
*
|
||||||
* Gemini CLI auth comes from either ~/.config/gemini/ or GOOGLE_API_KEY. Output
|
* Auth comes from its OAuth files, ~/.gemini/.env, or GOOGLE_API_KEY/GEMINI_API_KEY.
|
||||||
* format is NDJSON with `message`/`tool_use`/`result` events when `--output-format
|
* We capture stdout as the answer and do not parse Gemini's stream-json schema —
|
||||||
* stream-json` is requested. This adapter uses a single-response form for simplicity
|
* that coupling broke every time Gemini reshuffled its event shape.
|
||||||
* in benchmarks; richer streaming lives in gemini-session-runner.ts.
|
|
||||||
*/
|
*/
|
||||||
export class GeminiAdapter implements ProviderAdapter {
|
export class GeminiAdapter implements ProviderAdapter {
|
||||||
readonly name = 'gemini';
|
readonly name = 'gemini';
|
||||||
@@ -25,19 +23,23 @@ export class GeminiAdapter implements ProviderAdapter {
|
|||||||
const legacyCfgDir = path.join(os.homedir(), '.config', 'gemini');
|
const legacyCfgDir = path.join(os.homedir(), '.config', 'gemini');
|
||||||
const newCfgDir = path.join(os.homedir(), '.gemini');
|
const newCfgDir = path.join(os.homedir(), '.gemini');
|
||||||
const newOauth = path.join(newCfgDir, 'oauth_creds.json');
|
const newOauth = path.join(newCfgDir, 'oauth_creds.json');
|
||||||
|
const geminiEnv = path.join(newCfgDir, '.env');
|
||||||
const hasCfg = fs.existsSync(legacyCfgDir) || fs.existsSync(newOauth);
|
const hasCfg = fs.existsSync(legacyCfgDir) || fs.existsSync(newOauth);
|
||||||
const hasKey = !!process.env.GOOGLE_API_KEY;
|
const hasEnvFileKey = fs.existsSync(geminiEnv)
|
||||||
|
&& /^(?:GOOGLE_API_KEY|GEMINI_API_KEY)\s*=/m.test(fs.readFileSync(geminiEnv, 'utf-8'));
|
||||||
|
const hasKey = !!process.env.GOOGLE_API_KEY || !!process.env.GEMINI_API_KEY || hasEnvFileKey;
|
||||||
if (!hasCfg && !hasKey) {
|
if (!hasCfg && !hasKey) {
|
||||||
return { ok: false, reason: 'No Gemini auth found. Log in via `gemini login` or export GOOGLE_API_KEY.' };
|
return { ok: false, reason: 'No Gemini auth found. Log in via `gemini login` or export GOOGLE_API_KEY/GEMINI_API_KEY.' };
|
||||||
}
|
}
|
||||||
return { ok: true };
|
return { ok: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
async run(opts: RunOpts): Promise<RunResult> {
|
async run(opts: RunOpts): Promise<RunResult> {
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
// Default to --yolo (non-interactive) and stream-json output so we can parse
|
// --skip-trust lets the CLI run in disposable/non-git benchmark workdirs.
|
||||||
// tokens + tool calls. Callers can override via extraArgs.
|
// Plain -p is non-interactive; benchmark prompts are reasoning tasks, not file
|
||||||
const args = ['-p', opts.prompt, '--output-format', 'stream-json', '--yolo'];
|
// ops, and the workdir is a throwaway temp dir.
|
||||||
|
const args = ['-p', opts.prompt, '--skip-trust'];
|
||||||
if (opts.model) args.push('--model', opts.model);
|
if (opts.model) args.push('--model', opts.model);
|
||||||
if (opts.extraArgs) args.push(...opts.extraArgs);
|
if (opts.extraArgs) args.push(...opts.extraArgs);
|
||||||
|
|
||||||
@@ -48,78 +50,25 @@ export class GeminiAdapter implements ProviderAdapter {
|
|||||||
encoding: 'utf-8',
|
encoding: 'utf-8',
|
||||||
maxBuffer: 32 * 1024 * 1024,
|
maxBuffer: 32 * 1024 * 1024,
|
||||||
});
|
});
|
||||||
const parsed = this.parseStreamJson(out);
|
|
||||||
return {
|
return {
|
||||||
output: parsed.output,
|
output: out.trim(),
|
||||||
tokens: parsed.tokens,
|
|
||||||
durationMs: Date.now() - start,
|
durationMs: Date.now() - start,
|
||||||
toolCalls: parsed.toolCalls,
|
modelUsed: opts.model ?? 'gemini',
|
||||||
modelUsed: parsed.modelUsed || opts.model || 'gemini-2.5-pro',
|
|
||||||
};
|
};
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
const durationMs = Date.now() - start;
|
return this.errorResult(Date.now() - start, err, opts.model);
|
||||||
const e = err as { code?: string; stderr?: Buffer; signal?: string; message?: string };
|
|
||||||
const stderr = e.stderr?.toString() ?? '';
|
|
||||||
if (e.signal === 'SIGTERM' || e.code === 'ETIMEDOUT') {
|
|
||||||
return this.emptyResult(durationMs, { code: 'timeout', reason: `exceeded ${opts.timeoutMs}ms` }, opts.model);
|
|
||||||
}
|
|
||||||
if (/unauthorized|auth|login|api key/i.test(stderr)) {
|
|
||||||
return this.emptyResult(durationMs, { code: 'auth', reason: stderr.slice(0, 400) }, opts.model);
|
|
||||||
}
|
|
||||||
if (/rate[- ]?limit|429|quota/i.test(stderr)) {
|
|
||||||
return this.emptyResult(durationMs, { code: 'rate_limit', reason: stderr.slice(0, 400) }, opts.model);
|
|
||||||
}
|
|
||||||
return this.emptyResult(durationMs, { code: 'unknown', reason: (e.message ?? stderr ?? 'unknown').slice(0, 400) }, opts.model);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
estimateCost(tokens: { input: number; output: number; cached?: number }, model?: string): number {
|
private errorResult(durationMs: number, err: unknown, model?: string): RunResult {
|
||||||
return estimateCostUsd(tokens, model ?? 'gemini-2.5-pro');
|
const e = err as { code?: string; stderr?: Buffer; signal?: string; message?: string };
|
||||||
}
|
const stderr = e.stderr?.toString() ?? '';
|
||||||
|
let code: RunError;
|
||||||
/**
|
if (e.signal === 'SIGTERM' || e.code === 'ETIMEDOUT') code = 'timeout';
|
||||||
* Parse gemini NDJSON stream events:
|
else if (/unauthorized|auth|login|api key/i.test(stderr)) code = 'auth';
|
||||||
* init → session id (discarded here)
|
else if (/rate[- ]?limit|429|quota/i.test(stderr)) code = 'rate_limit';
|
||||||
* message { delta: true, text } → concat to output
|
else code = 'unknown';
|
||||||
* tool_use { name } → increment toolCalls
|
const reason = code === 'timeout' ? 'exceeded timeout' : (e.message ?? stderr ?? 'unknown').slice(0, 400);
|
||||||
* result { usage: { input_token_count, output_token_count } } → tokens
|
return { output: '', durationMs, modelUsed: model ?? 'gemini', error: { code, reason } };
|
||||||
*/
|
|
||||||
private parseStreamJson(raw: string): { output: string; tokens: { input: number; output: number }; toolCalls: number; modelUsed?: string } {
|
|
||||||
let output = '';
|
|
||||||
let input = 0;
|
|
||||||
let out = 0;
|
|
||||||
let toolCalls = 0;
|
|
||||||
let modelUsed: string | undefined;
|
|
||||||
for (const line of raw.split('\n')) {
|
|
||||||
const s = line.trim();
|
|
||||||
if (!s) continue;
|
|
||||||
try {
|
|
||||||
const obj = JSON.parse(s);
|
|
||||||
if (obj.type === 'message' && typeof obj.text === 'string') {
|
|
||||||
output += obj.text;
|
|
||||||
} else if (obj.type === 'tool_use') {
|
|
||||||
toolCalls += 1;
|
|
||||||
} else if (obj.type === 'result') {
|
|
||||||
const u = obj.usage ?? {};
|
|
||||||
input += u.input_token_count ?? u.prompt_tokens ?? 0;
|
|
||||||
out += u.output_token_count ?? u.completion_tokens ?? 0;
|
|
||||||
if (obj.model) modelUsed = obj.model;
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// skip malformed lines
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { output, tokens: { input, output: out }, toolCalls, modelUsed };
|
|
||||||
}
|
|
||||||
|
|
||||||
private emptyResult(durationMs: number, error: RunResult['error'], model?: string): RunResult {
|
|
||||||
return {
|
|
||||||
output: '',
|
|
||||||
tokens: { input: 0, output: 0 },
|
|
||||||
durationMs,
|
|
||||||
toolCalls: 0,
|
|
||||||
modelUsed: model ?? 'gemini-2.5-pro',
|
|
||||||
error,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
import type { ProviderAdapter, RunOpts, RunResult, AvailabilityCheck } from './types';
|
import type { ProviderAdapter, RunOpts, RunResult, AvailabilityCheck, RunError } from './types';
|
||||||
import { estimateCostUsd } from '../pricing';
|
|
||||||
import { execFileSync, spawnSync } from 'child_process';
|
import { execFileSync, spawnSync } from 'child_process';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GPT adapter — wraps the OpenAI `codex` CLI (codex exec with --json output).
|
* GPT adapter — wraps the OpenAI `codex` CLI (`codex exec`) in plain-text mode.
|
||||||
*
|
*
|
||||||
* Codex uses ~/.codex/ for auth (not OPENAI_API_KEY). The --json flag emits
|
* Captures stdout as the answer; no JSON-event parsing. Scoring is Braintrust's job.
|
||||||
* JSONL events; we parse `turn.completed` for usage and `agent_message` / etc.
|
|
||||||
* for output aggregation.
|
|
||||||
*/
|
*/
|
||||||
export class GptAdapter implements ProviderAdapter {
|
export class GptAdapter implements ProviderAdapter {
|
||||||
readonly name = 'gpt';
|
readonly name = 'gpt';
|
||||||
@@ -36,7 +33,7 @@ export class GptAdapter implements ProviderAdapter {
|
|||||||
// often run in temp dirs / non-git paths), so the read-only sandbox is now
|
// often run in temp dirs / non-git paths), so the read-only sandbox is now
|
||||||
// the only boundary preventing codex from mutating the workdir. If you ever
|
// the only boundary preventing codex from mutating the workdir. If you ever
|
||||||
// remove `-s read-only`, drop `--skip-git-repo-check` too.
|
// remove `-s read-only`, drop `--skip-git-repo-check` too.
|
||||||
const args = ['exec', opts.prompt, '-C', opts.workdir, '-s', 'read-only', '--skip-git-repo-check', '--json'];
|
const args = ['exec', opts.prompt, '-C', opts.workdir, '-s', 'read-only', '--skip-git-repo-check'];
|
||||||
if (opts.model) args.push('-m', opts.model);
|
if (opts.model) args.push('-m', opts.model);
|
||||||
if (opts.extraArgs) args.push(...opts.extraArgs);
|
if (opts.extraArgs) args.push(...opts.extraArgs);
|
||||||
|
|
||||||
@@ -47,81 +44,25 @@ export class GptAdapter implements ProviderAdapter {
|
|||||||
encoding: 'utf-8',
|
encoding: 'utf-8',
|
||||||
maxBuffer: 32 * 1024 * 1024,
|
maxBuffer: 32 * 1024 * 1024,
|
||||||
});
|
});
|
||||||
const parsed = this.parseJsonl(out);
|
|
||||||
return {
|
return {
|
||||||
output: parsed.output,
|
output: out.trim(),
|
||||||
tokens: parsed.tokens,
|
|
||||||
durationMs: Date.now() - start,
|
durationMs: Date.now() - start,
|
||||||
toolCalls: parsed.toolCalls,
|
modelUsed: opts.model ?? 'gpt',
|
||||||
modelUsed: parsed.modelUsed || opts.model || 'gpt-5.4',
|
|
||||||
};
|
};
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
const durationMs = Date.now() - start;
|
return this.errorResult(Date.now() - start, err, opts.model);
|
||||||
const e = err as { code?: string; stderr?: Buffer; signal?: string; message?: string };
|
|
||||||
const stderr = e.stderr?.toString() ?? '';
|
|
||||||
if (e.signal === 'SIGTERM' || e.code === 'ETIMEDOUT') {
|
|
||||||
return this.emptyResult(durationMs, { code: 'timeout', reason: `exceeded ${opts.timeoutMs}ms` }, opts.model);
|
|
||||||
}
|
|
||||||
if (/unauthorized|auth|login/i.test(stderr)) {
|
|
||||||
return this.emptyResult(durationMs, { code: 'auth', reason: stderr.slice(0, 400) }, opts.model);
|
|
||||||
}
|
|
||||||
if (/rate[- ]?limit|429/i.test(stderr)) {
|
|
||||||
return this.emptyResult(durationMs, { code: 'rate_limit', reason: stderr.slice(0, 400) }, opts.model);
|
|
||||||
}
|
|
||||||
return this.emptyResult(durationMs, { code: 'unknown', reason: (e.message ?? stderr ?? 'unknown').slice(0, 400) }, opts.model);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
estimateCost(tokens: { input: number; output: number; cached?: number }, model?: string): number {
|
private errorResult(durationMs: number, err: unknown, model?: string): RunResult {
|
||||||
return estimateCostUsd(tokens, model ?? 'gpt-5.4');
|
const e = err as { code?: string; stderr?: Buffer; signal?: string; message?: string };
|
||||||
}
|
const stderr = e.stderr?.toString() ?? '';
|
||||||
|
let code: RunError;
|
||||||
/**
|
if (e.signal === 'SIGTERM' || e.code === 'ETIMEDOUT') code = 'timeout';
|
||||||
* Parse codex exec --json JSONL stream.
|
else if (/unauthorized|auth|login/i.test(stderr)) code = 'auth';
|
||||||
* Key events:
|
else if (/rate[- ]?limit|429/i.test(stderr)) code = 'rate_limit';
|
||||||
* - item.completed with item.type === 'agent_message' → text output
|
else code = 'unknown';
|
||||||
* - item.completed with item.type === 'command_execution' → tool call
|
const reason = code === 'timeout' ? 'exceeded timeout' : (e.message ?? stderr ?? 'unknown').slice(0, 400);
|
||||||
* - turn.completed → usage.input_tokens, usage.output_tokens
|
return { output: '', durationMs, modelUsed: model ?? 'gpt', error: { code, reason } };
|
||||||
* - thread.started → session id (not used here)
|
|
||||||
*/
|
|
||||||
private parseJsonl(raw: string): { output: string; tokens: { input: number; output: number }; toolCalls: number; modelUsed?: string } {
|
|
||||||
let output = '';
|
|
||||||
let input = 0;
|
|
||||||
let out = 0;
|
|
||||||
let toolCalls = 0;
|
|
||||||
let modelUsed: string | undefined;
|
|
||||||
for (const line of raw.split('\n')) {
|
|
||||||
const s = line.trim();
|
|
||||||
if (!s) continue;
|
|
||||||
try {
|
|
||||||
const obj = JSON.parse(s);
|
|
||||||
if (obj.type === 'item.completed' && obj.item) {
|
|
||||||
if (obj.item.type === 'agent_message' && typeof obj.item.text === 'string') {
|
|
||||||
output += (output ? '\n' : '') + obj.item.text;
|
|
||||||
} else if (obj.item.type === 'command_execution') {
|
|
||||||
toolCalls += 1;
|
|
||||||
}
|
|
||||||
} else if (obj.type === 'turn.completed') {
|
|
||||||
const u = obj.usage ?? {};
|
|
||||||
input += u.input_tokens ?? 0;
|
|
||||||
out += u.output_tokens ?? 0;
|
|
||||||
if (obj.model) modelUsed = obj.model;
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// skip malformed lines — codex stderr can leak in
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { output, tokens: { input, output: out }, toolCalls, modelUsed };
|
|
||||||
}
|
|
||||||
|
|
||||||
private emptyResult(durationMs: number, error: RunResult['error'], model?: string): RunResult {
|
|
||||||
return {
|
|
||||||
output: '',
|
|
||||||
tokens: { input: 0, output: 0 },
|
|
||||||
durationMs,
|
|
||||||
toolCalls: 0,
|
|
||||||
modelUsed: model ?? 'gpt-5.4',
|
|
||||||
error,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
/**
|
/**
|
||||||
* Provider adapter interface — uniform contract for Claude, GPT, Gemini.
|
* Provider adapter interface — uniform contract for Claude, GPT, Gemini.
|
||||||
*
|
*
|
||||||
* Each adapter normalizes its provider's result shape into the RunResult below.
|
* Adapters shell out to the provider's CLI and return its plain-text output. We
|
||||||
* The benchmark runner only talks to adapters through this interface.
|
* deliberately do NOT parse each vendor's proprietary JSON stream for tokens or
|
||||||
|
* cost: that coupling is brittle (it breaks every time a vendor reshuffles its
|
||||||
|
* output) and redundant — Braintrust owns scoring, and token/cost tracking
|
||||||
|
* belongs to whatever instruments the actual model call. The CLI's text answer
|
||||||
|
* is all the benchmark needs.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export interface RunOpts {
|
export interface RunOpts {
|
||||||
@@ -18,13 +22,6 @@ export interface RunOpts {
|
|||||||
extraArgs?: string[];
|
extraArgs?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TokenUsage {
|
|
||||||
input: number;
|
|
||||||
output: number;
|
|
||||||
/** Cached input tokens (Anthropic/OpenAI support). Undefined if provider doesn't report. */
|
|
||||||
cached?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type RunError =
|
export type RunError =
|
||||||
| 'auth' // Credentials missing or invalid.
|
| 'auth' // Credentials missing or invalid.
|
||||||
| 'timeout' // Exceeded timeoutMs.
|
| 'timeout' // Exceeded timeoutMs.
|
||||||
@@ -33,17 +30,13 @@ export type RunError =
|
|||||||
| 'unknown'; // Catch-all with reason populated.
|
| 'unknown'; // Catch-all with reason populated.
|
||||||
|
|
||||||
export interface RunResult {
|
export interface RunResult {
|
||||||
/** Provider's textual output for the prompt. */
|
/** Provider's plain-text output for the prompt. */
|
||||||
output: string;
|
output: string;
|
||||||
/** Normalized token usage. 0s if unreported. */
|
|
||||||
tokens: TokenUsage;
|
|
||||||
/** Wall-clock duration. */
|
/** Wall-clock duration. */
|
||||||
durationMs: number;
|
durationMs: number;
|
||||||
/** Count of tool/function calls made during the run (0 if unsupported). */
|
/** Model label — the requested model or the family name. Not parsed from output. */
|
||||||
toolCalls: number;
|
|
||||||
/** Actual model ID the provider reports using (may be a variant of the family). */
|
|
||||||
modelUsed: string;
|
modelUsed: string;
|
||||||
/** If the run failed, error code + human reason. output/tokens may be partial. */
|
/** If the run failed, error code + human reason. output may be empty/partial. */
|
||||||
error?: { code: RunError; reason: string };
|
error?: { code: RunError; reason: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,6 +60,4 @@ export interface ProviderAdapter {
|
|||||||
available(): Promise<AvailabilityCheck>;
|
available(): Promise<AvailabilityCheck>;
|
||||||
/** Run a prompt and return normalized RunResult. Non-throwing. Errors go in result.error. */
|
/** Run a prompt and return normalized RunResult. Non-throwing. Errors go in result.error. */
|
||||||
run(opts: RunOpts): Promise<RunResult>;
|
run(opts: RunOpts): Promise<RunResult>;
|
||||||
/** Estimate USD cost for the reported token usage and model. */
|
|
||||||
estimateCost(tokens: TokenUsage, model?: string): number;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,165 +0,0 @@
|
|||||||
/**
|
|
||||||
* Multi-provider benchmark runner.
|
|
||||||
*
|
|
||||||
* Orchestrates running the same prompt across multiple provider adapters and
|
|
||||||
* aggregates RunResult outputs + judge scores into a single report. Adapters
|
|
||||||
* run in parallel (Promise.allSettled) so a slow provider doesn't block a fast
|
|
||||||
* one. Per-provider auth/timeout/rate-limit errors don't abort the batch.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type { ProviderAdapter, RunOpts, RunResult } from './providers/types';
|
|
||||||
import { ClaudeAdapter } from './providers/claude';
|
|
||||||
import { GptAdapter } from './providers/gpt';
|
|
||||||
import { GeminiAdapter } from './providers/gemini';
|
|
||||||
|
|
||||||
export interface BenchmarkInput {
|
|
||||||
prompt: string;
|
|
||||||
workdir: string;
|
|
||||||
timeoutMs?: number;
|
|
||||||
/** Adapter names to run (e.g., ['claude', 'gpt', 'gemini']). */
|
|
||||||
providers: Array<'claude' | 'gpt' | 'gemini'>;
|
|
||||||
/** Optional per-provider model overrides. */
|
|
||||||
models?: Partial<Record<'claude' | 'gpt' | 'gemini', string>>;
|
|
||||||
/** If true, skip providers whose available() returns !ok. If false, include them with error. */
|
|
||||||
skipUnavailable?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BenchmarkEntry {
|
|
||||||
provider: string;
|
|
||||||
family: 'claude' | 'gpt' | 'gemini';
|
|
||||||
available: boolean;
|
|
||||||
unavailable_reason?: string;
|
|
||||||
result?: RunResult;
|
|
||||||
costUsd?: number;
|
|
||||||
/** Judge score 0-10 across dimensions. Populated separately by the judge step. */
|
|
||||||
qualityScore?: number;
|
|
||||||
qualityDetails?: Record<string, number>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BenchmarkReport {
|
|
||||||
prompt: string;
|
|
||||||
workdir: string;
|
|
||||||
startedAt: string;
|
|
||||||
durationMs: number;
|
|
||||||
entries: BenchmarkEntry[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const ADAPTERS: Record<'claude' | 'gpt' | 'gemini', () => ProviderAdapter> = {
|
|
||||||
claude: () => new ClaudeAdapter(),
|
|
||||||
gpt: () => new GptAdapter(),
|
|
||||||
gemini: () => new GeminiAdapter(),
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function runBenchmark(input: BenchmarkInput): Promise<BenchmarkReport> {
|
|
||||||
const startedAtMs = Date.now();
|
|
||||||
const startedAt = new Date(startedAtMs).toISOString();
|
|
||||||
const timeoutMs = input.timeoutMs ?? 300_000;
|
|
||||||
|
|
||||||
const entries: BenchmarkEntry[] = [];
|
|
||||||
const runPromises: Array<Promise<void>> = [];
|
|
||||||
|
|
||||||
for (const name of input.providers) {
|
|
||||||
const factory = ADAPTERS[name];
|
|
||||||
if (!factory) {
|
|
||||||
entries.push({ provider: name, family: 'claude', available: false, unavailable_reason: `unknown provider: ${name}` });
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const adapter = factory();
|
|
||||||
const entry: BenchmarkEntry = { provider: adapter.name, family: adapter.family, available: true };
|
|
||||||
entries.push(entry);
|
|
||||||
|
|
||||||
runPromises.push((async () => {
|
|
||||||
const check = await adapter.available();
|
|
||||||
entry.available = check.ok;
|
|
||||||
if (!check.ok) {
|
|
||||||
entry.unavailable_reason = check.reason;
|
|
||||||
if (input.skipUnavailable) return;
|
|
||||||
}
|
|
||||||
const opts: RunOpts = {
|
|
||||||
prompt: input.prompt,
|
|
||||||
workdir: input.workdir,
|
|
||||||
timeoutMs,
|
|
||||||
model: input.models?.[name],
|
|
||||||
};
|
|
||||||
const res = await adapter.run(opts);
|
|
||||||
entry.result = res;
|
|
||||||
entry.costUsd = adapter.estimateCost(res.tokens, res.modelUsed);
|
|
||||||
})());
|
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.allSettled(runPromises);
|
|
||||||
|
|
||||||
return {
|
|
||||||
prompt: input.prompt,
|
|
||||||
workdir: input.workdir,
|
|
||||||
startedAt,
|
|
||||||
durationMs: Date.now() - startedAtMs,
|
|
||||||
entries,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function formatTable(report: BenchmarkReport): string {
|
|
||||||
const header = `Model Latency In→Out Tokens Cost Quality Tool Calls Notes`;
|
|
||||||
const sep = '-'.repeat(header.length);
|
|
||||||
const rows: string[] = [header, sep];
|
|
||||||
for (const e of report.entries) {
|
|
||||||
if (!e.available) {
|
|
||||||
rows.push(`${pad(e.provider, 20)} ${pad('-', 9)} ${pad('-', 20)} ${pad('-', 10)} ${pad('-', 9)} ${pad('-', 12)} unavailable: ${e.unavailable_reason ?? 'unknown'}`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const r = e.result!;
|
|
||||||
if (r.error) {
|
|
||||||
rows.push(`${pad(r.modelUsed, 20)} ${pad(msToStr(r.durationMs), 9)} ${pad(`${r.tokens.input}→${r.tokens.output}`, 20)} ${pad(fmtCost(e.costUsd), 10)} ${pad('-', 9)} ${pad(String(r.toolCalls), 12)} ERROR ${r.error.code}: ${r.error.reason.slice(0, 40)}`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const quality = e.qualityScore !== undefined ? `${e.qualityScore.toFixed(1)}/10` : '-';
|
|
||||||
rows.push(`${pad(r.modelUsed, 20)} ${pad(msToStr(r.durationMs), 9)} ${pad(`${r.tokens.input}→${r.tokens.output}`, 20)} ${pad(fmtCost(e.costUsd), 10)} ${pad(quality, 9)} ${pad(String(r.toolCalls), 12)}`);
|
|
||||||
}
|
|
||||||
return rows.join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function formatJson(report: BenchmarkReport): string {
|
|
||||||
return JSON.stringify(report, null, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function formatMarkdown(report: BenchmarkReport): string {
|
|
||||||
const lines: string[] = [
|
|
||||||
`# Benchmark report — ${report.startedAt}`,
|
|
||||||
'',
|
|
||||||
`**Prompt:** ${report.prompt.length > 200 ? report.prompt.slice(0, 200) + '…' : report.prompt}`,
|
|
||||||
`**Workdir:** \`${report.workdir}\``,
|
|
||||||
`**Total duration:** ${msToStr(report.durationMs)}`,
|
|
||||||
'',
|
|
||||||
'| Model | Latency | Tokens (in→out) | Cost | Quality | Tools | Notes |',
|
|
||||||
'|-------|---------|-----------------|------|---------|-------|-------|',
|
|
||||||
];
|
|
||||||
for (const e of report.entries) {
|
|
||||||
if (!e.available) {
|
|
||||||
lines.push(`| ${e.provider} | - | - | - | - | - | unavailable: ${e.unavailable_reason ?? 'unknown'} |`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const r = e.result!;
|
|
||||||
if (r.error) {
|
|
||||||
lines.push(`| ${r.modelUsed} | ${msToStr(r.durationMs)} | ${r.tokens.input}→${r.tokens.output} | ${fmtCost(e.costUsd)} | - | ${r.toolCalls} | ERROR ${r.error.code}: ${r.error.reason.slice(0, 80)} |`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const quality = e.qualityScore !== undefined ? `${e.qualityScore.toFixed(1)}/10` : '-';
|
|
||||||
lines.push(`| ${r.modelUsed} | ${msToStr(r.durationMs)} | ${r.tokens.input}→${r.tokens.output} | ${fmtCost(e.costUsd)} | ${quality} | ${r.toolCalls} | |`);
|
|
||||||
}
|
|
||||||
return lines.join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
function pad(s: string, n: number): string {
|
|
||||||
return s.length >= n ? s.slice(0, n) : s + ' '.repeat(n - s.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
function msToStr(ms: number): string {
|
|
||||||
if (ms < 1000) return `${ms}ms`;
|
|
||||||
return `${(ms / 1000).toFixed(1)}s`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function fmtCost(usd?: number): string {
|
|
||||||
if (usd === undefined) return '-';
|
|
||||||
if (usd < 0.01) return `$${usd.toFixed(4)}`;
|
|
||||||
return `$${usd.toFixed(2)}`;
|
|
||||||
}
|
|
||||||
+3
-1
@@ -106,6 +106,8 @@
|
|||||||
],
|
],
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@anthropic-ai/claude-agent-sdk": "0.3.216",
|
"@anthropic-ai/claude-agent-sdk": "0.3.216",
|
||||||
"@huggingface/transformers": "4.2.0"
|
"@huggingface/transformers": "4.2.0",
|
||||||
|
"autoevals": "^0.3.0",
|
||||||
|
"braintrust": "^3.24.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* gstack-model-benchmark CLI tests (offline).
|
* gstack-model-benchmark CLI tests (offline).
|
||||||
*
|
*
|
||||||
* Covers CLI wiring that unit tests against benchmark-runner.ts can't see:
|
* Covers CLI wiring that unit tests can't see:
|
||||||
* - --dry-run auth/provider-list resolution
|
* - --dry-run auth/provider-list resolution
|
||||||
* - unknown provider WARN path
|
* - unknown provider WARN path
|
||||||
* - provider default (claude) when --models omitted
|
* - provider default (claude) when --models omitted
|
||||||
@@ -66,11 +66,22 @@ describe('gstack-model-benchmark --dry-run', () => {
|
|||||||
expect(r.stdout).toContain('providers: claude');
|
expect(r.stdout).toContain('providers: claude');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('--timeout-ms and --workdir flags flow through to dry-run report', () => {
|
test('--timeout-ms flows through to dry-run report', () => {
|
||||||
const r = run(['--prompt', 'hi', '--timeout-ms', '9999', '--workdir', '/tmp', '--dry-run']);
|
const r = run(['--prompt', 'hi', '--timeout-ms', '9999', '--dry-run']);
|
||||||
expect(r.status).toBe(0);
|
expect(r.status).toBe(0);
|
||||||
expect(r.stdout).toContain('timeout_ms: 9999');
|
expect(r.stdout).toContain('timeout_ms: 9999');
|
||||||
expect(r.stdout).toContain('workdir: /tmp');
|
});
|
||||||
|
|
||||||
|
test('no prompt falls back to the corpus (not an error)', () => {
|
||||||
|
const r = run(['--models', 'claude', '--dry-run']);
|
||||||
|
expect(r.status).toBe(0);
|
||||||
|
expect(r.stdout).toMatch(/corpus:\s+\d+ case/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('upload is off by default (local only, nothing uploaded)', () => {
|
||||||
|
const r = run(['--prompt', 'hi', '--dry-run'], { env: { BRAINTRUST_API_KEY: '' } });
|
||||||
|
expect(r.status).toBe(0);
|
||||||
|
expect(r.stdout).toContain('local only');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('--judge flag reported in dry-run output', () => {
|
test('--judge flag reported in dry-run output', () => {
|
||||||
@@ -196,9 +207,9 @@ describe('gstack-model-benchmark prompt resolution', () => {
|
|||||||
expect(r.stdout).toContain('treat-me-as-inline');
|
expect(r.stdout).toContain('treat-me-as-inline');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('missing prompt exits non-zero', () => {
|
test('no positional and no --prompt runs the corpus', () => {
|
||||||
const r = run(['--dry-run']);
|
const r = run(['--dry-run']);
|
||||||
expect(r.status).not.toBe(0);
|
expect(r.status).toBe(0);
|
||||||
expect(r.stderr).toContain('specify a prompt');
|
expect(r.stdout).toMatch(/corpus:\s+\d+ case/);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -42,12 +42,6 @@ test('production modules do not import from test directories', () => {
|
|||||||
|
|
||||||
test('former test-helper paths re-export the production benchmark API', async () => {
|
test('former test-helper paths re-export the production benchmark API', async () => {
|
||||||
const [
|
const [
|
||||||
runner,
|
|
||||||
helperRunner,
|
|
||||||
pricing,
|
|
||||||
helperPricing,
|
|
||||||
judge,
|
|
||||||
helperJudge,
|
|
||||||
claude,
|
claude,
|
||||||
helperClaude,
|
helperClaude,
|
||||||
gpt,
|
gpt,
|
||||||
@@ -55,12 +49,6 @@ test('former test-helper paths re-export the production benchmark API', async ()
|
|||||||
gemini,
|
gemini,
|
||||||
helperGemini,
|
helperGemini,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
import('../lib/model-benchmark/runner'),
|
|
||||||
import('./helpers/benchmark-runner'),
|
|
||||||
import('../lib/model-benchmark/pricing'),
|
|
||||||
import('./helpers/pricing'),
|
|
||||||
import('../lib/model-benchmark/judge'),
|
|
||||||
import('./helpers/benchmark-judge'),
|
|
||||||
import('../lib/model-benchmark/providers/claude'),
|
import('../lib/model-benchmark/providers/claude'),
|
||||||
import('./helpers/providers/claude'),
|
import('./helpers/providers/claude'),
|
||||||
import('../lib/model-benchmark/providers/gpt'),
|
import('../lib/model-benchmark/providers/gpt'),
|
||||||
@@ -69,9 +57,6 @@ test('former test-helper paths re-export the production benchmark API', async ()
|
|||||||
import('./helpers/providers/gemini'),
|
import('./helpers/providers/gemini'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(helperRunner.runBenchmark).toBe(runner.runBenchmark);
|
|
||||||
expect(helperPricing.estimateCostUsd).toBe(pricing.estimateCostUsd);
|
|
||||||
expect(helperJudge.judgeEntries).toBe(judge.judgeEntries);
|
|
||||||
expect(helperClaude.ClaudeAdapter).toBe(claude.ClaudeAdapter);
|
expect(helperClaude.ClaudeAdapter).toBe(claude.ClaudeAdapter);
|
||||||
expect(helperGpt.GptAdapter).toBe(gpt.GptAdapter);
|
expect(helperGpt.GptAdapter).toBe(gpt.GptAdapter);
|
||||||
expect(helperGemini.GeminiAdapter).toBe(gemini.GeminiAdapter);
|
expect(helperGemini.GeminiAdapter).toBe(gemini.GeminiAdapter);
|
||||||
|
|||||||
@@ -1,49 +1,14 @@
|
|||||||
/**
|
/**
|
||||||
* Unit tests for the benchmark runner.
|
* Unit tests for benchmark tool-compatibility helpers.
|
||||||
*
|
*
|
||||||
* Mocks adapters to verify:
|
* Orchestration and scoring moved to Braintrust (lib/model-benchmark/braintrust-eval.ts);
|
||||||
* - All adapters run in parallel (Promise.allSettled not serial)
|
* per-provider token/cost tracking was removed with the JSON-schema parsers.
|
||||||
* - Unavailable adapters are skipped or marked depending on flag
|
* Provider capability coverage is what's left to pin here.
|
||||||
* - Per-adapter errors don't abort the batch
|
|
||||||
* - Output formatters (table, json, markdown) produce non-empty strings
|
|
||||||
*
|
|
||||||
* Does NOT exercise live CLIs — see test/providers.e2e.test.ts for those.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { test, expect } from 'bun:test';
|
import { test, expect } from 'bun:test';
|
||||||
import { formatTable, formatJson, formatMarkdown, type BenchmarkReport } from '../lib/model-benchmark/runner';
|
|
||||||
import { estimateCostUsd, PRICING } from '../lib/model-benchmark/pricing';
|
|
||||||
import { missingTools, TOOL_COMPATIBILITY } from './helpers/tool-map';
|
import { missingTools, TOOL_COMPATIBILITY } from './helpers/tool-map';
|
||||||
|
|
||||||
test('estimateCostUsd returns 0 for unknown model (no crash)', () => {
|
|
||||||
const cost = estimateCostUsd({ input: 1000, output: 500 }, 'unknown-model-7b');
|
|
||||||
expect(cost).toBe(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('estimateCostUsd computes correctly for known Claude model', () => {
|
|
||||||
// claude-opus-4-7: $15/MTok input, $75/MTok output
|
|
||||||
// 1M input + 0.5M output = $15 + $37.50 = $52.50
|
|
||||||
const cost = estimateCostUsd({ input: 1_000_000, output: 500_000 }, 'claude-opus-4-7');
|
|
||||||
expect(cost).toBeCloseTo(52.50, 2);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('estimateCostUsd applies cached input discount alongside uncached input', () => {
|
|
||||||
// tokens.input is uncached-only; tokens.cached is disjoint cache-reads at 10%.
|
|
||||||
// 0 uncached input, 1M cached → 10% of 15 = $1.50
|
|
||||||
const cost1 = estimateCostUsd({ input: 0, output: 0, cached: 1_000_000 }, 'claude-opus-4-7');
|
|
||||||
expect(cost1).toBeCloseTo(1.50, 2);
|
|
||||||
// 500K uncached input + 500K cached → $7.50 + $0.75 = $8.25
|
|
||||||
const cost2 = estimateCostUsd({ input: 500_000, output: 0, cached: 500_000 }, 'claude-opus-4-7');
|
|
||||||
expect(cost2).toBeCloseTo(8.25, 2);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('PRICING table covers the key model families', () => {
|
|
||||||
expect(PRICING['claude-opus-4-7']).toBeDefined();
|
|
||||||
expect(PRICING['claude-sonnet-4-6']).toBeDefined();
|
|
||||||
expect(PRICING['gpt-5.4']).toBeDefined();
|
|
||||||
expect(PRICING['gemini-2.5-pro']).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('missingTools reports unsupported tools per provider', () => {
|
test('missingTools reports unsupported tools per provider', () => {
|
||||||
// GPT/Codex doesn't expose Edit, Glob, Grep
|
// GPT/Codex doesn't expose Edit, Glob, Grep
|
||||||
expect(missingTools('gpt', ['Edit', 'Glob', 'Grep'])).toEqual(['Edit', 'Glob', 'Grep']);
|
expect(missingTools('gpt', ['Edit', 'Glob', 'Grep'])).toEqual(['Edit', 'Glob', 'Grep']);
|
||||||
@@ -58,80 +23,3 @@ test('TOOL_COMPATIBILITY is populated for all three families', () => {
|
|||||||
expect(TOOL_COMPATIBILITY.gpt).toBeDefined();
|
expect(TOOL_COMPATIBILITY.gpt).toBeDefined();
|
||||||
expect(TOOL_COMPATIBILITY.gemini).toBeDefined();
|
expect(TOOL_COMPATIBILITY.gemini).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('formatTable handles a report with mixed success/error/unavailable entries', () => {
|
|
||||||
const report: BenchmarkReport = {
|
|
||||||
prompt: 'test prompt',
|
|
||||||
workdir: '/tmp',
|
|
||||||
startedAt: '2026-04-16T20:00:00Z',
|
|
||||||
durationMs: 1500,
|
|
||||||
entries: [
|
|
||||||
{
|
|
||||||
provider: 'claude',
|
|
||||||
family: 'claude',
|
|
||||||
available: true,
|
|
||||||
result: {
|
|
||||||
output: 'ok',
|
|
||||||
tokens: { input: 100, output: 200 },
|
|
||||||
durationMs: 800,
|
|
||||||
toolCalls: 3,
|
|
||||||
modelUsed: 'claude-opus-4-7',
|
|
||||||
},
|
|
||||||
costUsd: 0.0165,
|
|
||||||
qualityScore: 9.2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provider: 'gpt',
|
|
||||||
family: 'gpt',
|
|
||||||
available: true,
|
|
||||||
result: {
|
|
||||||
output: '',
|
|
||||||
tokens: { input: 0, output: 0 },
|
|
||||||
durationMs: 200,
|
|
||||||
toolCalls: 0,
|
|
||||||
modelUsed: 'gpt-5.4',
|
|
||||||
error: { code: 'auth', reason: 'codex login required' },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provider: 'gemini',
|
|
||||||
family: 'gemini',
|
|
||||||
available: false,
|
|
||||||
unavailable_reason: 'gemini CLI not on PATH',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const table = formatTable(report);
|
|
||||||
expect(table).toContain('claude-opus-4-7');
|
|
||||||
expect(table).toContain('ERROR auth');
|
|
||||||
expect(table).toContain('unavailable');
|
|
||||||
expect(table).toContain('9.2/10');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('formatJson produces parseable JSON', () => {
|
|
||||||
const report: BenchmarkReport = {
|
|
||||||
prompt: 'x',
|
|
||||||
workdir: '/tmp',
|
|
||||||
startedAt: '2026-04-16T20:00:00Z',
|
|
||||||
durationMs: 100,
|
|
||||||
entries: [],
|
|
||||||
};
|
|
||||||
const json = formatJson(report);
|
|
||||||
const parsed = JSON.parse(json);
|
|
||||||
expect(parsed.prompt).toBe('x');
|
|
||||||
expect(parsed.entries).toEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('formatMarkdown produces a table header', () => {
|
|
||||||
const report: BenchmarkReport = {
|
|
||||||
prompt: 'x',
|
|
||||||
workdir: '/tmp',
|
|
||||||
startedAt: '2026-04-16T20:00:00Z',
|
|
||||||
durationMs: 100,
|
|
||||||
entries: [],
|
|
||||||
};
|
|
||||||
const md = formatMarkdown(report);
|
|
||||||
expect(md).toContain('# Benchmark report');
|
|
||||||
expect(md).toContain('| Model | Latency |');
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
// Compatibility export for tests and downstream tooling that used the former helper path.
|
|
||||||
export * from '../../lib/model-benchmark/judge';
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
// Compatibility export for tests and downstream tooling that used the former helper path.
|
|
||||||
export * from '../../lib/model-benchmark/runner';
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
// Compatibility export for tests and downstream tooling that used the former helper path.
|
|
||||||
export * from '../../lib/model-benchmark/pricing';
|
|
||||||
@@ -7,22 +7,19 @@
|
|||||||
* to keep cost near $0.001/provider/run.
|
* to keep cost near $0.001/provider/run.
|
||||||
*
|
*
|
||||||
* What this catches that unit tests don't:
|
* What this catches that unit tests don't:
|
||||||
* - CLI output-format drift (the #1 silent breakage path)
|
* - CLI invocation drift (a flag rename or trust-prompt change breaking a run)
|
||||||
* - Token parsing from real provider responses
|
|
||||||
* - Auth-failure vs timeout vs rate-limit error code routing
|
* - Auth-failure vs timeout vs rate-limit error code routing
|
||||||
* - Cost estimation on real token counts
|
* - The adapter terminates without throwing and returns plain-text output
|
||||||
* - Parallel execution via Promise.allSettled — slow provider doesn't block fast
|
|
||||||
*
|
*
|
||||||
* NOT covered here (would need dedicated test files):
|
* NOT covered here (would need dedicated test files):
|
||||||
* - Quality judge integration (benchmark-judge.ts, adds ~$0.05/run)
|
* - Quality judge integration (autoevals ClosedQA, opt-in)
|
||||||
* - Multi-turn tool-using prompts — our single-turn smoke skips `toolCalls > 0`
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
|
import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
|
||||||
import { ClaudeAdapter } from '../lib/model-benchmark/providers/claude';
|
import { ClaudeAdapter } from '../lib/model-benchmark/providers/claude';
|
||||||
import { GptAdapter } from '../lib/model-benchmark/providers/gpt';
|
import { GptAdapter } from '../lib/model-benchmark/providers/gpt';
|
||||||
import { GeminiAdapter } from '../lib/model-benchmark/providers/gemini';
|
import { GeminiAdapter } from '../lib/model-benchmark/providers/gemini';
|
||||||
import { runBenchmark } from '../lib/model-benchmark/runner';
|
import { runProviderBenchmark } from '../lib/model-benchmark/braintrust-eval';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
@@ -91,13 +88,9 @@ describeIfEvals('multi-provider benchmark adapters (live)', () => {
|
|||||||
throw new Error(`claude errored: ${result.error.code} — ${result.error.reason}`);
|
throw new Error(`claude errored: ${result.error.code} — ${result.error.reason}`);
|
||||||
}
|
}
|
||||||
expect(result.output.toLowerCase()).toContain('ok');
|
expect(result.output.toLowerCase()).toContain('ok');
|
||||||
expect(result.tokens.input).toBeGreaterThan(0);
|
|
||||||
expect(result.tokens.output).toBeGreaterThan(0);
|
|
||||||
expect(result.durationMs).toBeGreaterThan(0);
|
expect(result.durationMs).toBeGreaterThan(0);
|
||||||
expect(typeof result.modelUsed).toBe('string');
|
expect(typeof result.modelUsed).toBe('string');
|
||||||
expect(result.modelUsed.length).toBeGreaterThan(0);
|
expect(result.modelUsed.length).toBeGreaterThan(0);
|
||||||
const cost = claude.estimateCost(result.tokens, result.modelUsed);
|
|
||||||
expect(cost).toBeGreaterThan(0);
|
|
||||||
}, 150_000);
|
}, 150_000);
|
||||||
|
|
||||||
test('gpt: trivial prompt produces parseable output', async () => {
|
test('gpt: trivial prompt produces parseable output', async () => {
|
||||||
@@ -111,12 +104,8 @@ describeIfEvals('multi-provider benchmark adapters (live)', () => {
|
|||||||
throw new Error(`gpt errored: ${result.error.code} — ${result.error.reason}`);
|
throw new Error(`gpt errored: ${result.error.code} — ${result.error.reason}`);
|
||||||
}
|
}
|
||||||
expect(result.output.toLowerCase()).toContain('ok');
|
expect(result.output.toLowerCase()).toContain('ok');
|
||||||
expect(result.tokens.input).toBeGreaterThan(0);
|
|
||||||
expect(result.tokens.output).toBeGreaterThan(0);
|
|
||||||
expect(result.durationMs).toBeGreaterThan(0);
|
expect(result.durationMs).toBeGreaterThan(0);
|
||||||
expect(typeof result.modelUsed).toBe('string');
|
expect(typeof result.modelUsed).toBe('string');
|
||||||
const cost = gpt.estimateCost(result.tokens, result.modelUsed);
|
|
||||||
expect(cost).toBeGreaterThan(0);
|
|
||||||
}, 150_000);
|
}, 150_000);
|
||||||
|
|
||||||
test('gemini: trivial prompt produces parseable output', async () => {
|
test('gemini: trivial prompt produces parseable output', async () => {
|
||||||
@@ -129,17 +118,10 @@ describeIfEvals('multi-provider benchmark adapters (live)', () => {
|
|||||||
if (result.error) {
|
if (result.error) {
|
||||||
throw new Error(`gemini errored: ${result.error.code} — ${result.error.reason}`);
|
throw new Error(`gemini errored: ${result.error.code} — ${result.error.reason}`);
|
||||||
}
|
}
|
||||||
// Gemini CLI occasionally returns empty output even on successful runs
|
// Gemini CLI can return empty output on otherwise-successful runs in some
|
||||||
// (model returned content the CLI parser missed, intermittent stream issues).
|
// environments. This smoke is about "did the adapter wire up and terminate
|
||||||
// We assert the adapter ran end-to-end without erroring and reports a non-
|
// without throwing" — assert the shape, not the content.
|
||||||
// empty token count instead of grepping the literal "ok" — that string
|
|
||||||
// assertion was too brittle for a smoke that's really about "did the
|
|
||||||
// adapter wire up and the run terminate successfully?"
|
|
||||||
expect(typeof result.output).toBe('string');
|
expect(typeof result.output).toBe('string');
|
||||||
// Gemini CLI sometimes returns 0 tokens in the result event (older responses);
|
|
||||||
// assert non-negative instead of strictly positive.
|
|
||||||
expect(result.tokens.input).toBeGreaterThanOrEqual(0);
|
|
||||||
expect(result.tokens.output).toBeGreaterThanOrEqual(0);
|
|
||||||
expect(result.durationMs).toBeGreaterThan(0);
|
expect(result.durationMs).toBeGreaterThan(0);
|
||||||
expect(typeof result.modelUsed).toBe('string');
|
expect(typeof result.modelUsed).toBe('string');
|
||||||
}, 150_000);
|
}, 150_000);
|
||||||
@@ -163,30 +145,23 @@ describeIfEvals('multi-provider benchmark adapters (live)', () => {
|
|||||||
expect(result.durationMs).toBeGreaterThan(0);
|
expect(result.durationMs).toBeGreaterThan(0);
|
||||||
}, 30_000);
|
}, 30_000);
|
||||||
|
|
||||||
test('runBenchmark: Promise.allSettled means one unavailable provider does not block others', async () => {
|
test('runProviderBenchmark: an unauthed/failing provider returns a result, never throws', async () => {
|
||||||
// Use the full runner with all three providers — whichever are unauthed should
|
// Braintrust owns orchestration now. The property we care about: a provider
|
||||||
// return entries with available=false and not crash the batch.
|
// that's unavailable or errors comes back as a ProviderBenchmark (score null,
|
||||||
const report = await runBenchmark({
|
// ops carrying the error) instead of throwing and aborting the batch.
|
||||||
prompt: PROMPT,
|
const cases = [{ id: 'smoke', input: PROMPT, required: ['ok'] }];
|
||||||
workdir,
|
const results = await Promise.all(
|
||||||
providers: ['claude', 'gpt', 'gemini'],
|
(['claude', 'gpt', 'gemini'] as const).map(p => runProviderBenchmark(p, cases, { timeoutMs: 120_000 })),
|
||||||
timeoutMs: 120_000,
|
);
|
||||||
skipUnavailable: false,
|
expect(results).toHaveLength(3);
|
||||||
});
|
for (const r of results) {
|
||||||
expect(report.entries).toHaveLength(3);
|
expect(['claude', 'gpt', 'gemini']).toContain(r.provider);
|
||||||
for (const e of report.entries) {
|
expect(r.score === null || (typeof r.score === 'number' && r.score >= 0 && r.score <= 1)).toBe(true);
|
||||||
expect(['claude', 'gpt', 'gemini']).toContain(e.family);
|
expect(Array.isArray(r.ops)).toBe(true);
|
||||||
if (e.available) {
|
|
||||||
expect(e.result).toBeDefined();
|
|
||||||
} else {
|
|
||||||
expect(typeof e.unavailable_reason).toBe('string');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// At least one available provider should have produced a non-error result in a healthy CI env.
|
const hadSuccess = results.some(r => typeof r.score === 'number' && r.ops.some(o => !o.error));
|
||||||
const hadSuccess = report.entries.some(e => e.available && e.result && !e.result.error);
|
|
||||||
// We don't hard-assert this: if NO providers are authed, skip silently.
|
|
||||||
if (!hadSuccess) {
|
if (!hadSuccess) {
|
||||||
process.stderr.write('\nrunBenchmark live: no provider produced a clean result (no auth?)\n');
|
process.stderr.write('\nbenchmark live: no provider produced a clean result (no auth?)\n');
|
||||||
}
|
}
|
||||||
}, 300_000);
|
}, 300_000);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user