@marinoscar/kvox-eval 0.0.0-bootstrap → 0.1.0-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/LICENSE +21 -0
- package/README.md +166 -0
- package/dist/ask/index.d.ts +6 -0
- package/dist/ask/index.js +28 -0
- package/dist/ask/question-schema.d.ts +121 -0
- package/dist/ask/question-schema.js +167 -0
- package/dist/ask/report.d.ts +88 -0
- package/dist/ask/report.js +170 -0
- package/dist/ask/scorer.d.ts +104 -0
- package/dist/ask/scorer.js +164 -0
- package/dist/ask-run/ask-command.d.ts +62 -0
- package/dist/ask-run/ask-command.js +254 -0
- package/dist/ask-run/host/app.d.ts +65 -0
- package/dist/ask-run/host/app.js +146 -0
- package/dist/ask-run/host/db.d.ts +38 -0
- package/dist/ask-run/host/db.js +126 -0
- package/dist/ask-run/host/ports.d.ts +35 -0
- package/dist/ask-run/host/ports.js +113 -0
- package/dist/ask-run/index.d.ts +13 -0
- package/dist/ask-run/index.js +35 -0
- package/dist/ask-run/replay.d.ts +128 -0
- package/dist/ask-run/replay.js +212 -0
- package/dist/ask-run/run-ask-eval.d.ts +125 -0
- package/dist/ask-run/run-ask-eval.js +193 -0
- package/dist/ask-run/seed-golden-graph.d.ts +64 -0
- package/dist/ask-run/seed-golden-graph.js +495 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +12 -0
- package/dist/cli/kg-command.d.ts +66 -0
- package/dist/cli/kg-command.js +238 -0
- package/dist/cli/main.d.ts +18 -0
- package/dist/cli/main.js +69 -0
- package/dist/fixtures/domains.d.ts +16 -0
- package/dist/fixtures/domains.js +33 -0
- package/dist/fixtures/fixture-schema.d.ts +323 -0
- package/dist/fixtures/fixture-schema.js +199 -0
- package/dist/fixtures/index.d.ts +16 -0
- package/dist/fixtures/index.js +40 -0
- package/dist/fixtures/load.d.ts +73 -0
- package/dist/fixtures/load.js +137 -0
- package/dist/fixtures/questions.d.ts +15 -0
- package/dist/fixtures/questions.js +26 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +36 -0
- package/dist/kg/gold-runner.d.ts +13 -0
- package/dist/kg/gold-runner.js +57 -0
- package/dist/kg/index.d.ts +17 -0
- package/dist/kg/index.js +65 -0
- package/dist/kg/internals.d.ts +7 -0
- package/dist/kg/internals.js +17 -0
- package/dist/kg/prediction-schema.d.ts +245 -0
- package/dist/kg/prediction-schema.js +102 -0
- package/dist/kg/predictions.d.ts +11 -0
- package/dist/kg/predictions.js +49 -0
- package/dist/kg/report.d.ts +84 -0
- package/dist/kg/report.js +125 -0
- package/dist/kg/run-kg-eval.d.ts +77 -0
- package/dist/kg/run-kg-eval.js +137 -0
- package/dist/kg/runner.d.ts +72 -0
- package/dist/kg/runner.js +51 -0
- package/dist/kg/runners/extract-resolve-runner.d.ts +40 -0
- package/dist/kg/runners/extract-resolve-runner.js +199 -0
- package/dist/kg/runners/extract-runner.d.ts +65 -0
- package/dist/kg/runners/extract-runner.js +243 -0
- package/dist/kg/scorer.d.ts +226 -0
- package/dist/kg/scorer.js +463 -0
- package/dist/kg/targets.d.ts +17 -0
- package/dist/kg/targets.js +16 -0
- package/dist/kg/text.d.ts +45 -0
- package/dist/kg/text.js +100 -0
- package/dist/live/adapters.d.ts +16 -0
- package/dist/live/adapters.js +58 -0
- package/dist/live/env.d.ts +39 -0
- package/dist/live/env.js +56 -0
- package/dist/live/index.d.ts +6 -0
- package/dist/live/index.js +16 -0
- package/dist/live/live-ai.d.ts +104 -0
- package/dist/live/live-ai.js +157 -0
- package/dist/parity/constants.d.ts +79 -0
- package/dist/parity/constants.js +104 -0
- package/dist/parity/index.d.ts +20 -0
- package/dist/parity/index.js +38 -0
- package/dist/parity/internals.d.ts +8 -0
- package/dist/parity/internals.js +16 -0
- package/dist/parity/scenario.d.ts +45 -0
- package/dist/parity/scenario.js +245 -0
- package/dist/parity/script.d.ts +44 -0
- package/dist/parity/script.js +136 -0
- package/dist/parity/snapshot.d.ts +43 -0
- package/dist/parity/snapshot.js +71 -0
- package/dist/real-data/index.d.ts +2 -0
- package/dist/real-data/index.js +12 -0
- package/dist/real-data/real-data.d.ts +76 -0
- package/dist/real-data/real-data.js +156 -0
- package/fixtures/kg-golden/README.md +136 -0
- package/fixtures/kg-golden/ask-questions.json +458 -0
- package/fixtures/kg-golden/meetings/m01-northwind-pilot-kickoff.json +792 -0
- package/fixtures/kg-golden/meetings/m02-halden-migration-planning.json +814 -0
- package/fixtures/kg-golden/meetings/m03-larkspur-weekly-sync.json +601 -0
- package/fixtures/kg-golden/meetings/m04-corvid-intake-discovery.json +690 -0
- package/fixtures/kg-golden/meetings/m05-hosting-vendor-selection.json +631 -0
- package/fixtures/kg-golden/meetings/m06-northwind-floor-data-review.json +649 -0
- package/fixtures/kg-golden/meetings/m07-gullwing-intro-notes.json +428 -0
- package/fixtures/kg-golden/meetings/m08-halden-migration-checkpoint.json +672 -0
- package/fixtures/kg-golden/meetings/m09-northwind-platform-org-update.json +711 -0
- package/fixtures/kg-golden/meetings/m10-corvid-prototype-review.json +623 -0
- package/fixtures/kg-golden/meetings/m11-solvane-grid-dashboard-scoping.json +689 -0
- package/fixtures/kg-golden/meetings/m12-northwind-pilot-replan.json +681 -0
- package/fixtures/kg-golden/meetings/m13-halden-cutover-go-no-go.json +721 -0
- package/fixtures/kg-golden/meetings/m14-gullwing-discovery-readout.json +719 -0
- package/fixtures/kg-golden/meetings/m15-halden-handover-ravi-leaving.json +716 -0
- package/fixtures/kg-golden/meetings/m16-varden-claims-portal-intro.json +704 -0
- package/fixtures/kg-golden/meetings/m17-corvid-usability-debrief.json +572 -0
- package/fixtures/kg-golden/meetings/m18-hosting-decision-revisited.json +655 -0
- package/fixtures/kg-golden/meetings/m19-solvane-integration-kickoff.json +726 -0
- package/fixtures/kg-golden/meetings/m20-northwind-go-live-retro.json +673 -0
- package/fixtures/kg-golden/meetings/m21-quarterly-account-review.json +449 -0
- package/fixtures/kg-golden/meetings/m22-halden-intro-call-2020.json +558 -0
- package/fixtures/kg-golden/meetings/m23-gullwing-model-review.json +702 -0
- package/fixtures/kg-golden/meetings/m24-varden-discovery-midpoint.json +643 -0
- package/fixtures/kg-golden/meetings/m25-corvid-rollout-planning.json +642 -0
- package/fixtures/kg-golden/meetings/m26-larkspur-engineering-prep.json +695 -0
- package/fixtures/kg-golden/meetings/m27-solvane-phase-one-demo.json +649 -0
- package/fixtures/kg-golden/meetings/m28-northwind-building-5-scoping.json +715 -0
- package/fixtures/kg-golden/meetings/m29-halden-southern-cutover-review.json +752 -0
- package/fixtures/kg-golden/meetings/m30-varden-portal-build-kickoff.json +697 -0
- package/fixtures/kg-golden/meetings/m31-gullwing-rollout-renewal.json +681 -0
- package/fixtures/kg-golden/meetings/m32-brambleway-weekly-sync.json +472 -0
- package/fixtures/kg-golden/meetings/m33-nina-oliver-one-on-one.json +342 -0
- package/fixtures/kg-golden/meetings/m34-coffee-with-dana.json +249 -0
- package/fixtures/kg-golden/meetings/m35-brambleway-june-sync.json +553 -0
- package/fixtures/kg-golden/meetings/m36-copenhagen-conference-planning.json +296 -0
- package/host/generated/client.d.ts +1 -0
- package/host/generated/client.js +5 -0
- package/host/generated/default.d.ts +1 -0
- package/host/generated/default.js +5 -0
- package/host/generated/edge.d.ts +1 -0
- package/host/generated/edge.js +1658 -0
- package/host/generated/index-browser.js +1684 -0
- package/host/generated/index.d.ts +192335 -0
- package/host/generated/index.js +1658 -0
- package/host/generated/package.json +139 -0
- package/host/generated/query_compiler_fast_bg.js +2 -0
- package/host/generated/query_compiler_fast_bg.wasm +0 -0
- package/host/generated/query_compiler_fast_bg.wasm-base64.js +2 -0
- package/host/generated/runtime/client.d.ts +3433 -0
- package/host/generated/runtime/client.js +110 -0
- package/host/generated/runtime/index-browser.d.ts +90 -0
- package/host/generated/runtime/index-browser.js +6 -0
- package/host/generated/runtime/wasm-compiler-edge.js +100 -0
- package/host/generated/schema.prisma +6312 -0
- package/host/generated/wasm-edge-light-loader.mjs +5 -0
- package/host/generated/wasm-worker-loader.mjs +5 -0
- package/host/prisma/fragments/kvox.ask.prisma +126 -0
- package/host/prisma/fragments/kvox.graph.prisma +1073 -0
- package/host/prisma/fragments/kvox.notes.prisma +787 -0
- package/host/prisma/fragments/kvox.search.prisma +211 -0
- package/host/prisma/fragments/kvox.transcripts.prisma +790 -0
- package/host/prisma/kvox.lock +101 -0
- package/host/prisma/migrations/20240101000000_initial/migration.sql +297 -0
- package/host/prisma/migrations/20240101000001_add_personal_access_tokens/migration.sql +31 -0
- package/host/prisma/migrations/20240101000002_add_credentials/migration.sql +20 -0
- package/host/prisma/migrations/20240101000003_add_notification_deliveries/migration.sql +27 -0
- package/host/prisma/migrations/20240101000004_drop_stale_uuid_defaults/migration.sql +35 -0
- package/host/prisma/migrations/20240101000005_add_notifications/migration.sql +22 -0
- package/host/prisma/migrations/20240101000006_add_push_subscriptions/migration.sql +25 -0
- package/host/prisma/migrations/20240101000007_add_jobs/migration.sql +110 -0
- package/host/prisma/migrations/20240101000008_add_worker_nodes/migration.sql +90 -0
- package/host/prisma/migrations/20240101000009_add_database_backup_runs/migration.sql +104 -0
- package/host/prisma/migrations/20240101000010_add_notification_broadcasts/migration.sql +57 -0
- package/host/prisma/migrations/20240101000011_add_backup_run_job_link/migration.sql +87 -0
- package/host/prisma/migrations/20240101000012_add_job_node_secrets/migration.sql +61 -0
- package/host/prisma/migrations/20240101000013_add_backup_run_pg_dump_version/migration.sql +26 -0
- package/host/prisma/migrations/20240101000014_add_job_claim_token/migration.sql +92 -0
- package/host/prisma/migrations/20240101000015_add_ai_platform/migration.sql +108 -0
- package/host/prisma/migrations/20240101000016_revoke_viewer_ai_use/migration.sql +32 -0
- package/host/prisma/migrations/20240101000017_add_user_credentials/migration.sql +29 -0
- package/host/prisma/migrations/20240101000018_add_device_session_credential_link/migration.sql +52 -0
- package/host/prisma/migrations/20240101000019_add_worker_node_vitals/migration.sql +3 -0
- package/host/prisma/migrations/20240101000020_add_job_trace_context/migration.sql +6 -0
- package/host/prisma/migrations/20240101000021_add_retention_created_at_indexes/migration.sql +13 -0
- package/host/prisma/migrations/20240101000022_add_organizations/migration.sql +142 -0
- package/host/prisma/migrations/20240101000023_split_system_org_roles/migration.sql +131 -0
- package/host/prisma/migrations/20240101000024_org_scoped_rls/migration.sql +215 -0
- package/host/prisma/migrations/20240101000025_add_groups/migration.sql +165 -0
- package/host/prisma/migrations/20240101000026_add_grants/migration.sql +117 -0
- package/host/prisma/migrations/20240101000027_add_org_settings/migration.sql +49 -0
- package/host/prisma/migrations/20240101000028_add_org_credentials/migration.sql +54 -0
- package/host/prisma/migrations/20240101000029_add_jobs_org_id/migration.sql +18 -0
- package/host/prisma/migrations/20240101000030_add_jobs_org_id_status_index/migration.sql +6 -0
- package/host/prisma/migrations/20240101000031_add_broadcast_target_org/migration.sql +21 -0
- package/host/prisma/migrations/20240101000032_add_android_app/migration.sql +60 -0
- package/host/prisma/migrations/20240101000033_open_job_reason/migration.sql +16 -0
- package/host/prisma/migrations/20240101000034_ai_usage_feature/migration.sql +37 -0
- package/host/prisma/migrations/20240101000035_add_app_metadata/migration.sql +49 -0
- package/host/prisma/migrations/20240101000036_open_sharing_vocabularies/migration.sql +97 -0
- package/host/prisma/migrations/20260914130000_kvox_add_transcripts/migration.sql +322 -0
- package/host/prisma/migrations/20260914140000_kvox_add_notes/migration.sql +311 -0
- package/host/prisma/migrations/20260915120000_kvox_add_search_vectors/migration.sql +150 -0
- package/host/prisma/migrations/20260915130000_kvox_add_search_embeddings/migration.sql +362 -0
- package/host/prisma/migrations/20260915210000_kvox_add_note_title_source/migration.sql +30 -0
- package/host/prisma/migrations/20260923120000_kvox_add_note_generation_context_snapshot/migration.sql +24 -0
- package/host/prisma/migrations/20260923130000_kvox_add_user_hidden_note_templates/migration.sql +35 -0
- package/host/prisma/migrations/20260923140000_kvox_add_transcript_speaker_identities/migration.sql +6 -0
- package/host/prisma/migrations/20260923150000_kvox_transcript_name_checks/migration.sql +95 -0
- package/host/prisma/migrations/20260923160000_kvox_add_note_body_format/migration.sql +27 -0
- package/host/prisma/migrations/20260923170000_kvox_add_note_version_body_format/migration.sql +23 -0
- package/host/prisma/migrations/20260926012548_kvox_add_knowledge_graph/migration.sql +680 -0
- package/host/prisma/migrations/20260926030000_kvox_fix_kg_relations_valid_precision_chk/migration.sql +29 -0
- package/host/prisma/migrations/20260926030100_kvox_add_transcript_recorded_at/migration.sql +9 -0
- package/host/prisma/migrations/20260926030200_kvox_kg_evidence_invariant/migration.sql +60 -0
- package/host/prisma/migrations/20260926040000_kvox_add_kg_graph_layouts/migration.sql +30 -0
- package/host/prisma/migrations/20260926050000_kvox_add_ask_conversations/migration.sql +99 -0
- package/host/prisma/migrations/20260926231512_kvox_add_kg_exports/migration.sql +52 -0
- package/host/prisma/migrations/20261016120000_kvox_transcripts_org_id/migration.sql +49 -0
- package/host/prisma/platform.lock +319 -0
- package/host/prisma/schema/app.kvox.ask.prisma +110 -0
- package/host/prisma/schema/app.kvox.graph.prisma +1030 -0
- package/host/prisma/schema/app.kvox.notes.prisma +746 -0
- package/host/prisma/schema/app.kvox.search.prisma +208 -0
- package/host/prisma/schema/app.kvox.transcripts.prisma +726 -0
- package/host/prisma/schema/platform.ai.prisma +419 -0
- package/host/prisma/schema/platform.android-app.prisma +46 -0
- package/host/prisma/schema/platform.base.prisma +15 -0
- package/host/prisma/schema/platform.credentials.prisma +156 -0
- package/host/prisma/schema/platform.db-backup.prisma +359 -0
- package/host/prisma/schema/platform.identity.prisma +638 -0
- package/host/prisma/schema/platform.jobs.prisma +824 -0
- package/host/prisma/schema/platform.notifications.prisma +641 -0
- package/host/prisma/schema/platform.settings.prisma +53 -0
- package/host/prisma/schema/platform.sharing.prisma +208 -0
- package/host/prisma/schema/platform.storage.prisma +118 -0
- package/package.json +112 -1
- package/parity/requests.ask.json +2681 -0
- package/parity/requests.m01.json +1554 -0
- package/parity/requests.m02.json +1554 -0
- package/parity/requests.m03.json +1554 -0
- package/parity/requests.m04.json +1554 -0
- package/parity/requests.m05.json +1554 -0
- package/parity/requests.m06.json +1554 -0
- package/parity/requests.m07.json +1554 -0
- package/parity/requests.m08.json +1554 -0
- package/parity/requests.m09.json +1554 -0
- package/parity/requests.m10.json +1554 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// =============================================================================
|
|
3
|
+
// The Ask evaluation's report: per-category rollup + the two gates (#382)
|
|
4
|
+
// =============================================================================
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.askEvalReportSchema = void 0;
|
|
7
|
+
exports.buildAskEvalReport = buildAskEvalReport;
|
|
8
|
+
exports.formatAskEvalReport = formatAskEvalReport;
|
|
9
|
+
exports.formatAnswersMarkdown = formatAnswersMarkdown;
|
|
10
|
+
const zod_1 = require("zod");
|
|
11
|
+
const ratioSchema = zod_1.z.number().min(0).max(1);
|
|
12
|
+
/**
|
|
13
|
+
* The Ask evaluation report: the per-category rollup, the overall numbers, the two gates
|
|
14
|
+
* (citation validity 1.000, zero sensitive leaks), the answer-match threshold and every
|
|
15
|
+
* question's score. The JSON written as `report.json`.
|
|
16
|
+
*
|
|
17
|
+
* @stability experimental
|
|
18
|
+
*/
|
|
19
|
+
exports.askEvalReportSchema = zod_1.z.object({
|
|
20
|
+
runAt: zod_1.z.string(),
|
|
21
|
+
model: zod_1.z.string().nullable(),
|
|
22
|
+
questions: zod_1.z.number().int(),
|
|
23
|
+
categories: zod_1.z.array(zod_1.z.object({
|
|
24
|
+
category: zod_1.z.string(),
|
|
25
|
+
count: zod_1.z.number().int(),
|
|
26
|
+
answerMatch: ratioSchema,
|
|
27
|
+
cited: ratioSchema,
|
|
28
|
+
entityRecall: ratioSchema,
|
|
29
|
+
meanSteps: zod_1.z.number(),
|
|
30
|
+
meanToolCalls: zod_1.z.number(),
|
|
31
|
+
meanPromptTokens: zod_1.z.number(),
|
|
32
|
+
meanCompletionTokens: zod_1.z.number(),
|
|
33
|
+
meanMs: zod_1.z.number().nullable(),
|
|
34
|
+
})),
|
|
35
|
+
overall: zod_1.z.object({
|
|
36
|
+
answerMatch: ratioSchema,
|
|
37
|
+
citationValidity: ratioSchema,
|
|
38
|
+
sensitiveLeaks: zod_1.z.number().int(),
|
|
39
|
+
}),
|
|
40
|
+
/** `citationValidity === 1` and `sensitiveLeaks === 0` — the two hard gates. */
|
|
41
|
+
gatesPassed: zod_1.z.boolean(),
|
|
42
|
+
/** `overall.answerMatch >= minMatch`. */
|
|
43
|
+
answerMatchPassed: zod_1.z.boolean(),
|
|
44
|
+
minMatch: ratioSchema,
|
|
45
|
+
perQuestion: zod_1.z.array(zod_1.z.object({
|
|
46
|
+
id: zod_1.z.string(),
|
|
47
|
+
category: zod_1.z.string(),
|
|
48
|
+
citationValidity: ratioSchema,
|
|
49
|
+
cited: zod_1.z.boolean(),
|
|
50
|
+
citeFromHit: zod_1.z.boolean(),
|
|
51
|
+
answerMatch: zod_1.z.boolean(),
|
|
52
|
+
entityRecall: ratioSchema,
|
|
53
|
+
sensitiveLeak: zod_1.z.boolean(),
|
|
54
|
+
steps: zod_1.z.number().int(),
|
|
55
|
+
toolCalls: zod_1.z.number().int(),
|
|
56
|
+
promptTokens: zod_1.z.number().int(),
|
|
57
|
+
completionTokens: zod_1.z.number().int(),
|
|
58
|
+
ms: zod_1.z.number().nullable(),
|
|
59
|
+
notes: zod_1.z.array(zod_1.z.string()),
|
|
60
|
+
})),
|
|
61
|
+
});
|
|
62
|
+
const mean = (values) => (values.length === 0 ? 0 : values.reduce((a, b) => a + b, 0) / values.length);
|
|
63
|
+
/**
|
|
64
|
+
* Rolls the per-question scores of one run up into a report.
|
|
65
|
+
*
|
|
66
|
+
* @param scores - one score per answered question.
|
|
67
|
+
* @param meta - the model, the answer-match threshold and (for a test) the run time.
|
|
68
|
+
* @returns the report.
|
|
69
|
+
*
|
|
70
|
+
* @stability experimental
|
|
71
|
+
*/
|
|
72
|
+
function buildAskEvalReport(scores, meta) {
|
|
73
|
+
const byCategory = new Map();
|
|
74
|
+
for (const s of scores)
|
|
75
|
+
byCategory.set(s.category, [...(byCategory.get(s.category) ?? []), s]);
|
|
76
|
+
const categories = [...byCategory.entries()]
|
|
77
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
78
|
+
.map(([category, rows]) => ({
|
|
79
|
+
category,
|
|
80
|
+
count: rows.length,
|
|
81
|
+
answerMatch: mean(rows.map((r) => (r.answerMatch ? 1 : 0))),
|
|
82
|
+
cited: mean(rows.map((r) => (r.cited ? 1 : 0))),
|
|
83
|
+
entityRecall: mean(rows.map((r) => r.entityRecall)),
|
|
84
|
+
meanSteps: mean(rows.map((r) => r.steps)),
|
|
85
|
+
meanToolCalls: mean(rows.map((r) => r.toolCalls)),
|
|
86
|
+
meanPromptTokens: mean(rows.map((r) => r.promptTokens)),
|
|
87
|
+
meanCompletionTokens: mean(rows.map((r) => r.completionTokens)),
|
|
88
|
+
meanMs: rows.every((r) => r.ms === null) ? null : mean(rows.map((r) => r.ms ?? 0)),
|
|
89
|
+
}));
|
|
90
|
+
const citationValidity = mean(scores.map((s) => s.citationValidity));
|
|
91
|
+
const sensitiveLeaks = scores.filter((s) => s.sensitiveLeak).length;
|
|
92
|
+
const answerMatch = mean(scores.map((s) => (s.answerMatch ? 1 : 0)));
|
|
93
|
+
const gatesPassed = citationValidity === 1 && sensitiveLeaks === 0;
|
|
94
|
+
return {
|
|
95
|
+
runAt: (meta.runAt ?? new Date()).toISOString(),
|
|
96
|
+
model: meta.model,
|
|
97
|
+
questions: scores.length,
|
|
98
|
+
categories,
|
|
99
|
+
overall: { answerMatch, citationValidity, sensitiveLeaks },
|
|
100
|
+
gatesPassed,
|
|
101
|
+
answerMatchPassed: answerMatch >= meta.minMatch,
|
|
102
|
+
minMatch: meta.minMatch,
|
|
103
|
+
perQuestion: scores.map((s) => ({
|
|
104
|
+
id: s.questionId,
|
|
105
|
+
category: s.category,
|
|
106
|
+
citationValidity: s.citationValidity,
|
|
107
|
+
cited: s.cited,
|
|
108
|
+
citeFromHit: s.citeFromHit,
|
|
109
|
+
answerMatch: s.answerMatch,
|
|
110
|
+
entityRecall: s.entityRecall,
|
|
111
|
+
sensitiveLeak: s.sensitiveLeak,
|
|
112
|
+
steps: s.steps,
|
|
113
|
+
toolCalls: s.toolCalls,
|
|
114
|
+
promptTokens: s.promptTokens,
|
|
115
|
+
completionTokens: s.completionTokens,
|
|
116
|
+
ms: s.ms,
|
|
117
|
+
notes: s.notes,
|
|
118
|
+
})),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const fmt = (v) => v.toFixed(3);
|
|
122
|
+
const pad = (s, n) => String(s).padEnd(n);
|
|
123
|
+
/**
|
|
124
|
+
* The report as the fixed-column table `ask:eval` prints, then the gates and the failing questions.
|
|
125
|
+
*
|
|
126
|
+
* @param r - the report.
|
|
127
|
+
* @returns the table.
|
|
128
|
+
*
|
|
129
|
+
* @stability experimental
|
|
130
|
+
*/
|
|
131
|
+
function formatAskEvalReport(r) {
|
|
132
|
+
const lines = [];
|
|
133
|
+
lines.push(`ask:eval questions=${r.questions} model=${r.model ?? '-'} ${r.runAt}`);
|
|
134
|
+
lines.push(`${pad('CATEGORY', 14)}${pad('N', 4)}${pad('ANSWER', 8)}${pad('CITED', 8)}${pad('ENT.RECALL', 12)}${pad('STEPS', 8)}${pad('TOKENS(p/c)', 14)}MS`);
|
|
135
|
+
for (const c of r.categories) {
|
|
136
|
+
lines.push(`${pad(c.category, 14)}${pad(c.count, 4)}${pad(fmt(c.answerMatch), 8)}${pad(fmt(c.cited), 8)}${pad(fmt(c.entityRecall), 12)}${pad(c.meanSteps.toFixed(1), 8)}${pad(`${c.meanPromptTokens.toFixed(0)}/${c.meanCompletionTokens.toFixed(0)}`, 14)}${c.meanMs === null ? '-' : c.meanMs.toFixed(0)}`);
|
|
137
|
+
}
|
|
138
|
+
lines.push('');
|
|
139
|
+
lines.push(`OVERALL ANSWER MATCH ${fmt(r.overall.answerMatch)} threshold ≥ ${r.minMatch.toFixed(2)} ${r.answerMatchPassed ? 'PASS' : 'FAIL'}`);
|
|
140
|
+
lines.push(`CITATION VALIDITY ${fmt(r.overall.citationValidity)} required = 1.000 ${r.overall.citationValidity === 1 ? 'PASS' : 'FAIL'}`);
|
|
141
|
+
lines.push(`SENSITIVE LEAKS ${r.overall.sensitiveLeaks} required = 0 ${r.overall.sensitiveLeaks === 0 ? 'PASS' : 'FAIL'}`);
|
|
142
|
+
const failing = r.perQuestion.filter((q) => !q.answerMatch || q.citationValidity < 1 || q.sensitiveLeak);
|
|
143
|
+
if (failing.length > 0) {
|
|
144
|
+
lines.push('');
|
|
145
|
+
lines.push('FAILING QUESTIONS:');
|
|
146
|
+
for (const q of failing) {
|
|
147
|
+
lines.push(` ${q.id} (${q.category}): ${q.notes.join('; ') || (q.citationValidity < 1 ? 'invalid citation' : 'sensitive leak')}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return lines.join('\n');
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* `answers.md`: every question, its answer text and its per-question numbers — for reading, not for scripts.
|
|
154
|
+
*
|
|
155
|
+
* @stability experimental
|
|
156
|
+
*/
|
|
157
|
+
function formatAnswersMarkdown(report, answers) {
|
|
158
|
+
const lines = [`# Ask evaluation answers\n`, `Run at ${report.runAt}, model \`${report.model ?? '-'}\`.\n`];
|
|
159
|
+
for (const q of report.perQuestion) {
|
|
160
|
+
const a = answers.get(q.id);
|
|
161
|
+
lines.push(`## ${q.id} (${q.category})`);
|
|
162
|
+
if (a)
|
|
163
|
+
lines.push(`\n**Q:** ${a.question}\n\n**A:** ${a.content}\n`);
|
|
164
|
+
lines.push(`_citationValidity=${fmt(q.citationValidity)} cited=${q.cited} citeFromHit=${q.citeFromHit} answerMatch=${q.answerMatch} entityRecall=${fmt(q.entityRecall)} sensitiveLeak=${q.sensitiveLeak} steps=${q.steps} toolCalls=${q.toolCalls} tokens=${q.promptTokens}/${q.completionTokens}${q.ms !== null ? ` ms=${q.ms}` : ''}_`);
|
|
165
|
+
if (q.notes.length > 0)
|
|
166
|
+
lines.push(`\n> ${q.notes.join('\n> ')}`);
|
|
167
|
+
lines.push('');
|
|
168
|
+
}
|
|
169
|
+
return lines.join('\n');
|
|
170
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { AskCitation, AskMessageResponse } from '@marinoscar/kvox-contract/ask';
|
|
2
|
+
import type { AskEvalQuestion } from './question-schema.js';
|
|
3
|
+
/**
|
|
4
|
+
* What the scorer needs from the seeder's `idMap` — just the reverse evidence lookup.
|
|
5
|
+
*
|
|
6
|
+
* @stability experimental
|
|
7
|
+
*/
|
|
8
|
+
export type EvidenceSourceMap = ReadonlyMap<string, string>;
|
|
9
|
+
/**
|
|
10
|
+
* One question's score: the gate numbers (citation validity, a sensitive leak), the answer
|
|
11
|
+
* and citation checks, and the turn's cost (steps, tool calls, tokens, time).
|
|
12
|
+
*
|
|
13
|
+
* @stability experimental
|
|
14
|
+
*/
|
|
15
|
+
export interface AskEvalScore {
|
|
16
|
+
questionId: string;
|
|
17
|
+
category: string;
|
|
18
|
+
/** Valid citations / all citation markers. `1` when there were none to check. */
|
|
19
|
+
citationValidity: number;
|
|
20
|
+
/** At least one valid citation (irrelevant for a `notFound` question, which is scored separately). */
|
|
21
|
+
cited: boolean;
|
|
22
|
+
/** Some valid evidence/document citation resolves, via `idMap`, to a listed `citeFrom` source. */
|
|
23
|
+
citeFromHit: boolean;
|
|
24
|
+
/** Every `answerAll` group matched, no `answerNone` string present, and the `notFound` rule (if any) held. */
|
|
25
|
+
answerMatch: boolean;
|
|
26
|
+
/** Expected entities cited (by citation `label`) / expected entity count. `1` when none were expected. */
|
|
27
|
+
entityRecall: number;
|
|
28
|
+
/** An `answerNone` string turned up in a captured prompt or in the final answer. THE LEAK GATE: must be `false` on every question. */
|
|
29
|
+
sensitiveLeak: boolean;
|
|
30
|
+
steps: number;
|
|
31
|
+
toolCalls: number;
|
|
32
|
+
promptTokens: number;
|
|
33
|
+
completionTokens: number;
|
|
34
|
+
/** Wall-clock time the caller measured for this turn, if it did. */
|
|
35
|
+
ms: number | null;
|
|
36
|
+
/** Human-readable reasons `answerMatch`/`citationValidity`/`citeFromHit` failed, for `answers.md`. */
|
|
37
|
+
notes: string[];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Valid citations / all markers. `1` (vacuously satisfied) with zero markers — a question that expects none.
|
|
41
|
+
*
|
|
42
|
+
* @stability experimental
|
|
43
|
+
*/
|
|
44
|
+
export declare function scoreCitationValidity(citations: readonly AskCitation[]): number;
|
|
45
|
+
/**
|
|
46
|
+
* Whether the answer carries at least one valid citation.
|
|
47
|
+
*
|
|
48
|
+
* @param citations - the answer's citations.
|
|
49
|
+
* @returns `true` when one is valid.
|
|
50
|
+
*
|
|
51
|
+
* @stability experimental
|
|
52
|
+
*/
|
|
53
|
+
export declare function scoreCited(citations: readonly AskCitation[]): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Whether a valid evidence citation resolves, through the seeder's map, to one
|
|
56
|
+
* of the question's `citeFrom` sources.
|
|
57
|
+
*
|
|
58
|
+
* @param citeFrom - the expected sources (`mNN-sNNN`, `mNN#note`).
|
|
59
|
+
* @param citations - the answer's citations.
|
|
60
|
+
* @param evidenceSource - evidence id to source, from the seeder.
|
|
61
|
+
* @returns `true` on a hit (vacuously `true` with no `citeFrom`).
|
|
62
|
+
*
|
|
63
|
+
* @stability experimental
|
|
64
|
+
*/
|
|
65
|
+
export declare function scoreCiteFromHit(citeFrom: readonly string[], citations: readonly AskCitation[], evidenceSource: EvidenceSourceMap): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Expected entities cited (by citation label, case-insensitive exact) over the expected count.
|
|
68
|
+
*
|
|
69
|
+
* @param expectedEntities - the labels that must be cited.
|
|
70
|
+
* @param citations - the answer's citations.
|
|
71
|
+
* @returns the ratio (`1` when none were expected).
|
|
72
|
+
*
|
|
73
|
+
* @stability experimental
|
|
74
|
+
*/
|
|
75
|
+
export declare function scoreEntityRecall(expectedEntities: readonly string[], citations: readonly AskCitation[]): number;
|
|
76
|
+
/**
|
|
77
|
+
* THE LEAK GATE: whether an `answerNone` string appears in the answer or in any prompt the
|
|
78
|
+
* provider received.
|
|
79
|
+
*
|
|
80
|
+
* @param answerNone - the strings that must never leave.
|
|
81
|
+
* @param content - the final answer.
|
|
82
|
+
* @param capturedPrompts - every request the provider received, serialized.
|
|
83
|
+
* @returns `true` on a leak.
|
|
84
|
+
*
|
|
85
|
+
* @stability experimental
|
|
86
|
+
*/
|
|
87
|
+
export declare function scoreSensitiveLeak(answerNone: readonly string[], content: string, capturedPrompts: readonly string[]): boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Every `answerAll` group matched, no `answerNone` present, and (for a `notFound` question) no valid citation plus a not-found phrasing.
|
|
90
|
+
*
|
|
91
|
+
* @stability experimental
|
|
92
|
+
*/
|
|
93
|
+
export declare function scoreAnswerMatch(expected: AskEvalQuestion['expected'], content: string, citations: readonly AskCitation[]): {
|
|
94
|
+
matched: boolean;
|
|
95
|
+
reasons: string[];
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* The full score for one question's answered turn.
|
|
99
|
+
*
|
|
100
|
+
* @stability experimental
|
|
101
|
+
*/
|
|
102
|
+
export declare function scoreAnswer(question: AskEvalQuestion, message: Pick<AskMessageResponse, 'content' | 'citations' | 'promptTokens' | 'completionTokens' | 'toolCalls'>, capturedPrompts: readonly string[], evidenceSource: EvidenceSourceMap, timing?: {
|
|
103
|
+
ms?: number;
|
|
104
|
+
}): AskEvalScore;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// =============================================================================
|
|
3
|
+
// The Ask evaluation's scorer (issue #382, epic #348; docs/specs/ontology.md §21)
|
|
4
|
+
// =============================================================================
|
|
5
|
+
//
|
|
6
|
+
// PURE: no Nest, no Prisma, no provider. Takes one question, the assistant
|
|
7
|
+
// message `ask.respond` wrote for it (`AskMessageResponse`, #376's wire
|
|
8
|
+
// contract), every `messages` array the run's provider actually sent (for the
|
|
9
|
+
// leak check), and the seeder's evidence-source map — and returns one
|
|
10
|
+
// `AskEvalScore`. Called by the replay DB spec, the real runner, and its own
|
|
11
|
+
// unit spec, so the three can never disagree about what a number means.
|
|
12
|
+
//
|
|
13
|
+
// Two GATES (spec: 100% citation validity, zero sensitive leaks across the
|
|
14
|
+
// WHOLE run) are per-question booleans/ratios here; the run aggregates them.
|
|
15
|
+
// =============================================================================
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.scoreCitationValidity = scoreCitationValidity;
|
|
18
|
+
exports.scoreCited = scoreCited;
|
|
19
|
+
exports.scoreCiteFromHit = scoreCiteFromHit;
|
|
20
|
+
exports.scoreEntityRecall = scoreEntityRecall;
|
|
21
|
+
exports.scoreSensitiveLeak = scoreSensitiveLeak;
|
|
22
|
+
exports.scoreAnswerMatch = scoreAnswerMatch;
|
|
23
|
+
exports.scoreAnswer = scoreAnswer;
|
|
24
|
+
const NOT_FOUND_PATTERN = /couldn't find|could not find|no (record|information)|not in your|don't have (any )?(record|information)|no results/i;
|
|
25
|
+
function includesCI(haystack, needle) {
|
|
26
|
+
return haystack.toLowerCase().includes(needle.toLowerCase());
|
|
27
|
+
}
|
|
28
|
+
function groupMatched(content, group) {
|
|
29
|
+
return group.some((alt) => includesCI(content, alt));
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Valid citations / all markers. `1` (vacuously satisfied) with zero markers — a question that expects none.
|
|
33
|
+
*
|
|
34
|
+
* @stability experimental
|
|
35
|
+
*/
|
|
36
|
+
function scoreCitationValidity(citations) {
|
|
37
|
+
if (citations.length === 0)
|
|
38
|
+
return 1;
|
|
39
|
+
const valid = citations.filter((c) => c.valid).length;
|
|
40
|
+
return valid / citations.length;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Whether the answer carries at least one valid citation.
|
|
44
|
+
*
|
|
45
|
+
* @param citations - the answer's citations.
|
|
46
|
+
* @returns `true` when one is valid.
|
|
47
|
+
*
|
|
48
|
+
* @stability experimental
|
|
49
|
+
*/
|
|
50
|
+
function scoreCited(citations) {
|
|
51
|
+
return citations.some((c) => c.valid);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Whether a valid evidence citation resolves, through the seeder's map, to one
|
|
55
|
+
* of the question's `citeFrom` sources.
|
|
56
|
+
*
|
|
57
|
+
* @param citeFrom - the expected sources (`mNN-sNNN`, `mNN#note`).
|
|
58
|
+
* @param citations - the answer's citations.
|
|
59
|
+
* @param evidenceSource - evidence id to source, from the seeder.
|
|
60
|
+
* @returns `true` on a hit (vacuously `true` with no `citeFrom`).
|
|
61
|
+
*
|
|
62
|
+
* @stability experimental
|
|
63
|
+
*/
|
|
64
|
+
function scoreCiteFromHit(citeFrom, citations, evidenceSource) {
|
|
65
|
+
if (citeFrom.length === 0)
|
|
66
|
+
return true; // nothing was required (e.g. a negative question)
|
|
67
|
+
return citations.some((c) => {
|
|
68
|
+
if (!c.valid || c.kind !== 'evidence' || !c.id)
|
|
69
|
+
return false;
|
|
70
|
+
const source = evidenceSource.get(c.id);
|
|
71
|
+
return source !== undefined && citeFrom.includes(source);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Expected entities cited (by citation label, case-insensitive exact) over the expected count.
|
|
76
|
+
*
|
|
77
|
+
* @param expectedEntities - the labels that must be cited.
|
|
78
|
+
* @param citations - the answer's citations.
|
|
79
|
+
* @returns the ratio (`1` when none were expected).
|
|
80
|
+
*
|
|
81
|
+
* @stability experimental
|
|
82
|
+
*/
|
|
83
|
+
function scoreEntityRecall(expectedEntities, citations) {
|
|
84
|
+
if (expectedEntities.length === 0)
|
|
85
|
+
return 1;
|
|
86
|
+
const citedLabels = new Set(citations.filter((c) => c.valid && c.label).map((c) => c.label.toLowerCase()));
|
|
87
|
+
const hit = expectedEntities.filter((label) => citedLabels.has(label.toLowerCase())).length;
|
|
88
|
+
return hit / expectedEntities.length;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* THE LEAK GATE: whether an `answerNone` string appears in the answer or in any prompt the
|
|
92
|
+
* provider received.
|
|
93
|
+
*
|
|
94
|
+
* @param answerNone - the strings that must never leave.
|
|
95
|
+
* @param content - the final answer.
|
|
96
|
+
* @param capturedPrompts - every request the provider received, serialized.
|
|
97
|
+
* @returns `true` on a leak.
|
|
98
|
+
*
|
|
99
|
+
* @stability experimental
|
|
100
|
+
*/
|
|
101
|
+
function scoreSensitiveLeak(answerNone, content, capturedPrompts) {
|
|
102
|
+
if (answerNone.length === 0)
|
|
103
|
+
return false;
|
|
104
|
+
const haystacks = [content, ...capturedPrompts];
|
|
105
|
+
return answerNone.some((needle) => haystacks.some((h) => includesCI(h, needle)));
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Every `answerAll` group matched, no `answerNone` present, and (for a `notFound` question) no valid citation plus a not-found phrasing.
|
|
109
|
+
*
|
|
110
|
+
* @stability experimental
|
|
111
|
+
*/
|
|
112
|
+
function scoreAnswerMatch(expected, content, citations) {
|
|
113
|
+
const reasons = [];
|
|
114
|
+
for (const group of expected.answerAll) {
|
|
115
|
+
if (!groupMatched(content, group))
|
|
116
|
+
reasons.push(`missing required phrase (one of: ${group.join(' | ')})`);
|
|
117
|
+
}
|
|
118
|
+
for (const forbidden of expected.answerNone) {
|
|
119
|
+
if (includesCI(content, forbidden))
|
|
120
|
+
reasons.push(`answer contains forbidden phrase "${forbidden}"`);
|
|
121
|
+
}
|
|
122
|
+
if (expected.notFound) {
|
|
123
|
+
if (citations.some((c) => c.valid))
|
|
124
|
+
reasons.push('a `notFound` question cited something valid');
|
|
125
|
+
if (!NOT_FOUND_PATTERN.test(content))
|
|
126
|
+
reasons.push('answer does not read as "could not find it"');
|
|
127
|
+
}
|
|
128
|
+
return { matched: reasons.length === 0, reasons };
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The full score for one question's answered turn.
|
|
132
|
+
*
|
|
133
|
+
* @stability experimental
|
|
134
|
+
*/
|
|
135
|
+
function scoreAnswer(question, message, capturedPrompts, evidenceSource, timing = {}) {
|
|
136
|
+
const citations = message.citations;
|
|
137
|
+
const { matched, reasons } = scoreAnswerMatch(question.expected, message.content, citations);
|
|
138
|
+
const citeFromHit = scoreCiteFromHit(question.expected.citeFrom, citations, evidenceSource);
|
|
139
|
+
const notes = [...reasons];
|
|
140
|
+
if (!citeFromHit && question.expected.citeFrom.length > 0) {
|
|
141
|
+
notes.push(`no valid citation resolved to one of: ${question.expected.citeFrom.join(', ')}`);
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
questionId: question.id,
|
|
145
|
+
category: question.category,
|
|
146
|
+
citationValidity: scoreCitationValidity(citations),
|
|
147
|
+
cited: scoreCited(citations),
|
|
148
|
+
citeFromHit,
|
|
149
|
+
answerMatch: matched && (question.expected.citeFrom.length === 0 || citeFromHit),
|
|
150
|
+
entityRecall: scoreEntityRecall(question.expected.entities, citations),
|
|
151
|
+
sensitiveLeak: scoreSensitiveLeak(question.expected.answerNone, message.content, capturedPrompts),
|
|
152
|
+
// The wire `AskToolCall[]` carries a flat `index` across the whole turn,
|
|
153
|
+
// never which loop iteration ("step") produced it — so the caller passes
|
|
154
|
+
// exactly THIS question's captured provider requests, and one request is
|
|
155
|
+
// one loop iteration by construction (`ask-respond.handler.ts`'s `loop`
|
|
156
|
+
// calls `provider.chat()` exactly once per iteration).
|
|
157
|
+
steps: capturedPrompts.length,
|
|
158
|
+
toolCalls: message.toolCalls.length,
|
|
159
|
+
promptTokens: message.promptTokens ?? 0,
|
|
160
|
+
completionTokens: message.completionTokens ?? 0,
|
|
161
|
+
ms: timing.ms ?? null,
|
|
162
|
+
notes,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the ask command writes, and the environment it reads (the same shape as
|
|
3
|
+
* the kg command's `AskCommandIo`).
|
|
4
|
+
*
|
|
5
|
+
* @stability experimental
|
|
6
|
+
*/
|
|
7
|
+
export interface AskCommandIo {
|
|
8
|
+
/** One line to standard output. */
|
|
9
|
+
out: (line: string) => void;
|
|
10
|
+
/** One line to standard error. */
|
|
11
|
+
err: (line: string) => void;
|
|
12
|
+
/** The environment (`process.env`, or a test's own). */
|
|
13
|
+
env: Readonly<Record<string, string | undefined>>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The exit codes of `kvox-eval ask` (and of the web app's `npm run ask:eval`).
|
|
17
|
+
*
|
|
18
|
+
* @stability experimental
|
|
19
|
+
*/
|
|
20
|
+
export declare const ASK_EVAL_EXIT: Readonly<{
|
|
21
|
+
readonly ok: 0;
|
|
22
|
+
readonly failed: 1;
|
|
23
|
+
readonly config: 2;
|
|
24
|
+
}>;
|
|
25
|
+
/**
|
|
26
|
+
* The usage text of `kvox-eval ask`.
|
|
27
|
+
*
|
|
28
|
+
* @stability experimental
|
|
29
|
+
*/
|
|
30
|
+
export declare const ASK_COMMAND_USAGE = "usage: kvox-eval ask [--provider openai|openai-compatible] [--model <id>] [--base-url <url>]\n [--only q01,q07] [--category <c>] [--out <dir>] [--min-match <0..1>]\n kvox-eval ask --replay the scripted self-test (no model, no key)\n reads the API key from KVOX_EVAL_OPENAI_API_KEY (never a flag); default model gpt-4o-mini";
|
|
31
|
+
/**
|
|
32
|
+
* How a host runs the ask command under its own name.
|
|
33
|
+
*
|
|
34
|
+
* @stability experimental
|
|
35
|
+
*/
|
|
36
|
+
export interface AskCommandOptions {
|
|
37
|
+
/** The prefix of every message (`kvox-eval ask`, `ask:eval`). */
|
|
38
|
+
name?: string;
|
|
39
|
+
/** The usage text printed with a usage error and for `--help`. */
|
|
40
|
+
usage?: string;
|
|
41
|
+
/**
|
|
42
|
+
* The default `--out` root: a run writes `<dir>/runs/<stamp>/` (the web app's
|
|
43
|
+
* git-ignored `apps/api/.ask-eval/`). Default: none, and the output goes under
|
|
44
|
+
* the system temporary directory.
|
|
45
|
+
*/
|
|
46
|
+
syntheticRunsDir?: string | null;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Runs `kvox-eval ask` with the given arguments (everything after `ask`).
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* const code = await runAskCommand(['--replay'], { out: console.log, err: console.error, env: process.env });
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* @param argv - the arguments.
|
|
57
|
+
* @param io - output streams and environment.
|
|
58
|
+
* @param options - the host's name, usage text and runs directory.
|
|
59
|
+
* @returns the exit code ({@link ASK_EVAL_EXIT}).
|
|
60
|
+
* @stability experimental
|
|
61
|
+
*/
|
|
62
|
+
export declare function runAskCommand(argv: string[], io: AskCommandIo, options?: AskCommandOptions): Promise<number>;
|