@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.
Files changed (245) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/LICENSE +21 -0
  3. package/README.md +166 -0
  4. package/dist/ask/index.d.ts +6 -0
  5. package/dist/ask/index.js +28 -0
  6. package/dist/ask/question-schema.d.ts +121 -0
  7. package/dist/ask/question-schema.js +167 -0
  8. package/dist/ask/report.d.ts +88 -0
  9. package/dist/ask/report.js +170 -0
  10. package/dist/ask/scorer.d.ts +104 -0
  11. package/dist/ask/scorer.js +164 -0
  12. package/dist/ask-run/ask-command.d.ts +62 -0
  13. package/dist/ask-run/ask-command.js +254 -0
  14. package/dist/ask-run/host/app.d.ts +65 -0
  15. package/dist/ask-run/host/app.js +146 -0
  16. package/dist/ask-run/host/db.d.ts +38 -0
  17. package/dist/ask-run/host/db.js +126 -0
  18. package/dist/ask-run/host/ports.d.ts +35 -0
  19. package/dist/ask-run/host/ports.js +113 -0
  20. package/dist/ask-run/index.d.ts +13 -0
  21. package/dist/ask-run/index.js +35 -0
  22. package/dist/ask-run/replay.d.ts +128 -0
  23. package/dist/ask-run/replay.js +212 -0
  24. package/dist/ask-run/run-ask-eval.d.ts +125 -0
  25. package/dist/ask-run/run-ask-eval.js +193 -0
  26. package/dist/ask-run/seed-golden-graph.d.ts +64 -0
  27. package/dist/ask-run/seed-golden-graph.js +495 -0
  28. package/dist/cli/index.d.ts +3 -0
  29. package/dist/cli/index.js +12 -0
  30. package/dist/cli/kg-command.d.ts +66 -0
  31. package/dist/cli/kg-command.js +238 -0
  32. package/dist/cli/main.d.ts +18 -0
  33. package/dist/cli/main.js +69 -0
  34. package/dist/fixtures/domains.d.ts +16 -0
  35. package/dist/fixtures/domains.js +33 -0
  36. package/dist/fixtures/fixture-schema.d.ts +323 -0
  37. package/dist/fixtures/fixture-schema.js +199 -0
  38. package/dist/fixtures/index.d.ts +16 -0
  39. package/dist/fixtures/index.js +40 -0
  40. package/dist/fixtures/load.d.ts +73 -0
  41. package/dist/fixtures/load.js +137 -0
  42. package/dist/fixtures/questions.d.ts +15 -0
  43. package/dist/fixtures/questions.js +26 -0
  44. package/dist/index.d.ts +20 -0
  45. package/dist/index.js +36 -0
  46. package/dist/kg/gold-runner.d.ts +13 -0
  47. package/dist/kg/gold-runner.js +57 -0
  48. package/dist/kg/index.d.ts +17 -0
  49. package/dist/kg/index.js +65 -0
  50. package/dist/kg/internals.d.ts +7 -0
  51. package/dist/kg/internals.js +17 -0
  52. package/dist/kg/prediction-schema.d.ts +245 -0
  53. package/dist/kg/prediction-schema.js +102 -0
  54. package/dist/kg/predictions.d.ts +11 -0
  55. package/dist/kg/predictions.js +49 -0
  56. package/dist/kg/report.d.ts +84 -0
  57. package/dist/kg/report.js +125 -0
  58. package/dist/kg/run-kg-eval.d.ts +77 -0
  59. package/dist/kg/run-kg-eval.js +137 -0
  60. package/dist/kg/runner.d.ts +72 -0
  61. package/dist/kg/runner.js +51 -0
  62. package/dist/kg/runners/extract-resolve-runner.d.ts +40 -0
  63. package/dist/kg/runners/extract-resolve-runner.js +199 -0
  64. package/dist/kg/runners/extract-runner.d.ts +65 -0
  65. package/dist/kg/runners/extract-runner.js +243 -0
  66. package/dist/kg/scorer.d.ts +226 -0
  67. package/dist/kg/scorer.js +463 -0
  68. package/dist/kg/targets.d.ts +17 -0
  69. package/dist/kg/targets.js +16 -0
  70. package/dist/kg/text.d.ts +45 -0
  71. package/dist/kg/text.js +100 -0
  72. package/dist/live/adapters.d.ts +16 -0
  73. package/dist/live/adapters.js +58 -0
  74. package/dist/live/env.d.ts +39 -0
  75. package/dist/live/env.js +56 -0
  76. package/dist/live/index.d.ts +6 -0
  77. package/dist/live/index.js +16 -0
  78. package/dist/live/live-ai.d.ts +104 -0
  79. package/dist/live/live-ai.js +157 -0
  80. package/dist/parity/constants.d.ts +79 -0
  81. package/dist/parity/constants.js +104 -0
  82. package/dist/parity/index.d.ts +20 -0
  83. package/dist/parity/index.js +38 -0
  84. package/dist/parity/internals.d.ts +8 -0
  85. package/dist/parity/internals.js +16 -0
  86. package/dist/parity/scenario.d.ts +45 -0
  87. package/dist/parity/scenario.js +245 -0
  88. package/dist/parity/script.d.ts +44 -0
  89. package/dist/parity/script.js +136 -0
  90. package/dist/parity/snapshot.d.ts +43 -0
  91. package/dist/parity/snapshot.js +71 -0
  92. package/dist/real-data/index.d.ts +2 -0
  93. package/dist/real-data/index.js +12 -0
  94. package/dist/real-data/real-data.d.ts +76 -0
  95. package/dist/real-data/real-data.js +156 -0
  96. package/fixtures/kg-golden/README.md +136 -0
  97. package/fixtures/kg-golden/ask-questions.json +458 -0
  98. package/fixtures/kg-golden/meetings/m01-northwind-pilot-kickoff.json +792 -0
  99. package/fixtures/kg-golden/meetings/m02-halden-migration-planning.json +814 -0
  100. package/fixtures/kg-golden/meetings/m03-larkspur-weekly-sync.json +601 -0
  101. package/fixtures/kg-golden/meetings/m04-corvid-intake-discovery.json +690 -0
  102. package/fixtures/kg-golden/meetings/m05-hosting-vendor-selection.json +631 -0
  103. package/fixtures/kg-golden/meetings/m06-northwind-floor-data-review.json +649 -0
  104. package/fixtures/kg-golden/meetings/m07-gullwing-intro-notes.json +428 -0
  105. package/fixtures/kg-golden/meetings/m08-halden-migration-checkpoint.json +672 -0
  106. package/fixtures/kg-golden/meetings/m09-northwind-platform-org-update.json +711 -0
  107. package/fixtures/kg-golden/meetings/m10-corvid-prototype-review.json +623 -0
  108. package/fixtures/kg-golden/meetings/m11-solvane-grid-dashboard-scoping.json +689 -0
  109. package/fixtures/kg-golden/meetings/m12-northwind-pilot-replan.json +681 -0
  110. package/fixtures/kg-golden/meetings/m13-halden-cutover-go-no-go.json +721 -0
  111. package/fixtures/kg-golden/meetings/m14-gullwing-discovery-readout.json +719 -0
  112. package/fixtures/kg-golden/meetings/m15-halden-handover-ravi-leaving.json +716 -0
  113. package/fixtures/kg-golden/meetings/m16-varden-claims-portal-intro.json +704 -0
  114. package/fixtures/kg-golden/meetings/m17-corvid-usability-debrief.json +572 -0
  115. package/fixtures/kg-golden/meetings/m18-hosting-decision-revisited.json +655 -0
  116. package/fixtures/kg-golden/meetings/m19-solvane-integration-kickoff.json +726 -0
  117. package/fixtures/kg-golden/meetings/m20-northwind-go-live-retro.json +673 -0
  118. package/fixtures/kg-golden/meetings/m21-quarterly-account-review.json +449 -0
  119. package/fixtures/kg-golden/meetings/m22-halden-intro-call-2020.json +558 -0
  120. package/fixtures/kg-golden/meetings/m23-gullwing-model-review.json +702 -0
  121. package/fixtures/kg-golden/meetings/m24-varden-discovery-midpoint.json +643 -0
  122. package/fixtures/kg-golden/meetings/m25-corvid-rollout-planning.json +642 -0
  123. package/fixtures/kg-golden/meetings/m26-larkspur-engineering-prep.json +695 -0
  124. package/fixtures/kg-golden/meetings/m27-solvane-phase-one-demo.json +649 -0
  125. package/fixtures/kg-golden/meetings/m28-northwind-building-5-scoping.json +715 -0
  126. package/fixtures/kg-golden/meetings/m29-halden-southern-cutover-review.json +752 -0
  127. package/fixtures/kg-golden/meetings/m30-varden-portal-build-kickoff.json +697 -0
  128. package/fixtures/kg-golden/meetings/m31-gullwing-rollout-renewal.json +681 -0
  129. package/fixtures/kg-golden/meetings/m32-brambleway-weekly-sync.json +472 -0
  130. package/fixtures/kg-golden/meetings/m33-nina-oliver-one-on-one.json +342 -0
  131. package/fixtures/kg-golden/meetings/m34-coffee-with-dana.json +249 -0
  132. package/fixtures/kg-golden/meetings/m35-brambleway-june-sync.json +553 -0
  133. package/fixtures/kg-golden/meetings/m36-copenhagen-conference-planning.json +296 -0
  134. package/host/generated/client.d.ts +1 -0
  135. package/host/generated/client.js +5 -0
  136. package/host/generated/default.d.ts +1 -0
  137. package/host/generated/default.js +5 -0
  138. package/host/generated/edge.d.ts +1 -0
  139. package/host/generated/edge.js +1658 -0
  140. package/host/generated/index-browser.js +1684 -0
  141. package/host/generated/index.d.ts +192335 -0
  142. package/host/generated/index.js +1658 -0
  143. package/host/generated/package.json +139 -0
  144. package/host/generated/query_compiler_fast_bg.js +2 -0
  145. package/host/generated/query_compiler_fast_bg.wasm +0 -0
  146. package/host/generated/query_compiler_fast_bg.wasm-base64.js +2 -0
  147. package/host/generated/runtime/client.d.ts +3433 -0
  148. package/host/generated/runtime/client.js +110 -0
  149. package/host/generated/runtime/index-browser.d.ts +90 -0
  150. package/host/generated/runtime/index-browser.js +6 -0
  151. package/host/generated/runtime/wasm-compiler-edge.js +100 -0
  152. package/host/generated/schema.prisma +6312 -0
  153. package/host/generated/wasm-edge-light-loader.mjs +5 -0
  154. package/host/generated/wasm-worker-loader.mjs +5 -0
  155. package/host/prisma/fragments/kvox.ask.prisma +126 -0
  156. package/host/prisma/fragments/kvox.graph.prisma +1073 -0
  157. package/host/prisma/fragments/kvox.notes.prisma +787 -0
  158. package/host/prisma/fragments/kvox.search.prisma +211 -0
  159. package/host/prisma/fragments/kvox.transcripts.prisma +790 -0
  160. package/host/prisma/kvox.lock +101 -0
  161. package/host/prisma/migrations/20240101000000_initial/migration.sql +297 -0
  162. package/host/prisma/migrations/20240101000001_add_personal_access_tokens/migration.sql +31 -0
  163. package/host/prisma/migrations/20240101000002_add_credentials/migration.sql +20 -0
  164. package/host/prisma/migrations/20240101000003_add_notification_deliveries/migration.sql +27 -0
  165. package/host/prisma/migrations/20240101000004_drop_stale_uuid_defaults/migration.sql +35 -0
  166. package/host/prisma/migrations/20240101000005_add_notifications/migration.sql +22 -0
  167. package/host/prisma/migrations/20240101000006_add_push_subscriptions/migration.sql +25 -0
  168. package/host/prisma/migrations/20240101000007_add_jobs/migration.sql +110 -0
  169. package/host/prisma/migrations/20240101000008_add_worker_nodes/migration.sql +90 -0
  170. package/host/prisma/migrations/20240101000009_add_database_backup_runs/migration.sql +104 -0
  171. package/host/prisma/migrations/20240101000010_add_notification_broadcasts/migration.sql +57 -0
  172. package/host/prisma/migrations/20240101000011_add_backup_run_job_link/migration.sql +87 -0
  173. package/host/prisma/migrations/20240101000012_add_job_node_secrets/migration.sql +61 -0
  174. package/host/prisma/migrations/20240101000013_add_backup_run_pg_dump_version/migration.sql +26 -0
  175. package/host/prisma/migrations/20240101000014_add_job_claim_token/migration.sql +92 -0
  176. package/host/prisma/migrations/20240101000015_add_ai_platform/migration.sql +108 -0
  177. package/host/prisma/migrations/20240101000016_revoke_viewer_ai_use/migration.sql +32 -0
  178. package/host/prisma/migrations/20240101000017_add_user_credentials/migration.sql +29 -0
  179. package/host/prisma/migrations/20240101000018_add_device_session_credential_link/migration.sql +52 -0
  180. package/host/prisma/migrations/20240101000019_add_worker_node_vitals/migration.sql +3 -0
  181. package/host/prisma/migrations/20240101000020_add_job_trace_context/migration.sql +6 -0
  182. package/host/prisma/migrations/20240101000021_add_retention_created_at_indexes/migration.sql +13 -0
  183. package/host/prisma/migrations/20240101000022_add_organizations/migration.sql +142 -0
  184. package/host/prisma/migrations/20240101000023_split_system_org_roles/migration.sql +131 -0
  185. package/host/prisma/migrations/20240101000024_org_scoped_rls/migration.sql +215 -0
  186. package/host/prisma/migrations/20240101000025_add_groups/migration.sql +165 -0
  187. package/host/prisma/migrations/20240101000026_add_grants/migration.sql +117 -0
  188. package/host/prisma/migrations/20240101000027_add_org_settings/migration.sql +49 -0
  189. package/host/prisma/migrations/20240101000028_add_org_credentials/migration.sql +54 -0
  190. package/host/prisma/migrations/20240101000029_add_jobs_org_id/migration.sql +18 -0
  191. package/host/prisma/migrations/20240101000030_add_jobs_org_id_status_index/migration.sql +6 -0
  192. package/host/prisma/migrations/20240101000031_add_broadcast_target_org/migration.sql +21 -0
  193. package/host/prisma/migrations/20240101000032_add_android_app/migration.sql +60 -0
  194. package/host/prisma/migrations/20240101000033_open_job_reason/migration.sql +16 -0
  195. package/host/prisma/migrations/20240101000034_ai_usage_feature/migration.sql +37 -0
  196. package/host/prisma/migrations/20240101000035_add_app_metadata/migration.sql +49 -0
  197. package/host/prisma/migrations/20240101000036_open_sharing_vocabularies/migration.sql +97 -0
  198. package/host/prisma/migrations/20260914130000_kvox_add_transcripts/migration.sql +322 -0
  199. package/host/prisma/migrations/20260914140000_kvox_add_notes/migration.sql +311 -0
  200. package/host/prisma/migrations/20260915120000_kvox_add_search_vectors/migration.sql +150 -0
  201. package/host/prisma/migrations/20260915130000_kvox_add_search_embeddings/migration.sql +362 -0
  202. package/host/prisma/migrations/20260915210000_kvox_add_note_title_source/migration.sql +30 -0
  203. package/host/prisma/migrations/20260923120000_kvox_add_note_generation_context_snapshot/migration.sql +24 -0
  204. package/host/prisma/migrations/20260923130000_kvox_add_user_hidden_note_templates/migration.sql +35 -0
  205. package/host/prisma/migrations/20260923140000_kvox_add_transcript_speaker_identities/migration.sql +6 -0
  206. package/host/prisma/migrations/20260923150000_kvox_transcript_name_checks/migration.sql +95 -0
  207. package/host/prisma/migrations/20260923160000_kvox_add_note_body_format/migration.sql +27 -0
  208. package/host/prisma/migrations/20260923170000_kvox_add_note_version_body_format/migration.sql +23 -0
  209. package/host/prisma/migrations/20260926012548_kvox_add_knowledge_graph/migration.sql +680 -0
  210. package/host/prisma/migrations/20260926030000_kvox_fix_kg_relations_valid_precision_chk/migration.sql +29 -0
  211. package/host/prisma/migrations/20260926030100_kvox_add_transcript_recorded_at/migration.sql +9 -0
  212. package/host/prisma/migrations/20260926030200_kvox_kg_evidence_invariant/migration.sql +60 -0
  213. package/host/prisma/migrations/20260926040000_kvox_add_kg_graph_layouts/migration.sql +30 -0
  214. package/host/prisma/migrations/20260926050000_kvox_add_ask_conversations/migration.sql +99 -0
  215. package/host/prisma/migrations/20260926231512_kvox_add_kg_exports/migration.sql +52 -0
  216. package/host/prisma/migrations/20261016120000_kvox_transcripts_org_id/migration.sql +49 -0
  217. package/host/prisma/platform.lock +319 -0
  218. package/host/prisma/schema/app.kvox.ask.prisma +110 -0
  219. package/host/prisma/schema/app.kvox.graph.prisma +1030 -0
  220. package/host/prisma/schema/app.kvox.notes.prisma +746 -0
  221. package/host/prisma/schema/app.kvox.search.prisma +208 -0
  222. package/host/prisma/schema/app.kvox.transcripts.prisma +726 -0
  223. package/host/prisma/schema/platform.ai.prisma +419 -0
  224. package/host/prisma/schema/platform.android-app.prisma +46 -0
  225. package/host/prisma/schema/platform.base.prisma +15 -0
  226. package/host/prisma/schema/platform.credentials.prisma +156 -0
  227. package/host/prisma/schema/platform.db-backup.prisma +359 -0
  228. package/host/prisma/schema/platform.identity.prisma +638 -0
  229. package/host/prisma/schema/platform.jobs.prisma +824 -0
  230. package/host/prisma/schema/platform.notifications.prisma +641 -0
  231. package/host/prisma/schema/platform.settings.prisma +53 -0
  232. package/host/prisma/schema/platform.sharing.prisma +208 -0
  233. package/host/prisma/schema/platform.storage.prisma +118 -0
  234. package/package.json +112 -1
  235. package/parity/requests.ask.json +2681 -0
  236. package/parity/requests.m01.json +1554 -0
  237. package/parity/requests.m02.json +1554 -0
  238. package/parity/requests.m03.json +1554 -0
  239. package/parity/requests.m04.json +1554 -0
  240. package/parity/requests.m05.json +1554 -0
  241. package/parity/requests.m06.json +1554 -0
  242. package/parity/requests.m07.json +1554 -0
  243. package/parity/requests.m08.json +1554 -0
  244. package/parity/requests.m09.json +1554 -0
  245. package/parity/requests.m10.json +1554 -0
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ // =============================================================================
3
+ // The ports the headless Ask evaluation binds (issue #608)
4
+ // =============================================================================
5
+ //
6
+ // Ten ports, most of them the in-memory fakes of `@marinoscar/kvox-api/testing`.
7
+ // The four that are not:
8
+ //
9
+ // prisma the PGlite client (`db.ts`).
10
+ // ai the port under test (live: `createLiveKvoxAi`; replay: a scripted
11
+ // runtime under `createKvoxAi`), handed in by the caller.
12
+ // settings in-memory, with the system `ai` namespace REGISTERED here: the
13
+ // graph and Ask slices read it and none of them declares it (in the
14
+ // web edition it is the platform AI slice's; the bare host declares
15
+ // it in `bootstrap.ts`). The graph switch is ON, the caps are null
16
+ // (the model's own limits govern), no reasoning parameter.
17
+ // jobs the fake queue, except `enqueueWithin`: `ask_messages.job_id` is a
18
+ // foreign key to `jobs`, so the turn's job must be a REAL row written
19
+ // in the caller's transaction (never on the pool: one connection).
20
+ // Nothing claims it; the runner calls the handler itself.
21
+ // identity one principal, the evaluation user, holding the three read
22
+ // permissions the Ask handler probes (graph, transcripts, notes).
23
+ // =============================================================================
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.ASK_EVAL_PERMISSIONS = void 0;
26
+ exports.createAskEvalSettings = createAskEvalSettings;
27
+ exports.createAskEvalJobs = createAskEvalJobs;
28
+ exports.createAskEvalIdentity = createAskEvalIdentity;
29
+ const zod_1 = require("zod");
30
+ const graph_1 = require("@marinoscar/kvox-api/graph");
31
+ const notes_1 = require("@marinoscar/kvox-api/notes");
32
+ const testing_1 = require("@marinoscar/kvox-api/testing");
33
+ const transcripts_1 = require("@marinoscar/kvox-api/transcripts");
34
+ /**
35
+ * The permissions the evaluation user holds: what `ask.respond` probes for its
36
+ * actor. Read-only on purpose; the agent writes nothing.
37
+ *
38
+ * @stability experimental
39
+ */
40
+ exports.ASK_EVAL_PERMISSIONS = Object.freeze([
41
+ graph_1.GRAPH_READ_PERMISSION,
42
+ transcripts_1.TRANSCRIPTS_READ_PERMISSION,
43
+ notes_1.NOTES_READ_PERMISSION,
44
+ ]);
45
+ /** The `ai` system namespace as the slices read it (the bare host declares the same shape). */
46
+ const aiNamespaceSchema = zod_1.z.object({
47
+ maxInputTokens: zod_1.z.number().int().positive().nullable().default(null),
48
+ maxOutputTokens: zod_1.z.number().int().positive().nullable().default(null),
49
+ maxDocumentBytes: zod_1.z.number().int().positive().default(10 * 1024 * 1024),
50
+ graphEnabled: zod_1.z.boolean().default(true),
51
+ reasoningEffort: zod_1.z.enum(['none', 'low', 'medium', 'high', 'xhigh']).default('none'),
52
+ });
53
+ /**
54
+ * In-memory settings with the system `ai` namespace declared and the graph switch on.
55
+ *
56
+ * @returns the settings port.
57
+ * @stability experimental
58
+ */
59
+ function createAskEvalSettings() {
60
+ const settings = (0, testing_1.createInMemoryKvoxSettings)();
61
+ settings.registerNamespace({
62
+ scope: 'system',
63
+ name: 'ai',
64
+ schema: aiNamespaceSchema,
65
+ defaults: {
66
+ maxInputTokens: null,
67
+ maxOutputTokens: null,
68
+ maxDocumentBytes: 10 * 1024 * 1024,
69
+ graphEnabled: true,
70
+ reasoningEffort: 'none',
71
+ },
72
+ });
73
+ return settings;
74
+ }
75
+ /**
76
+ * The fake queue, with `enqueueWithin` writing a real `jobs` row in the caller's
77
+ * transaction (the turn's `ask_messages.job_id` references it). Nothing claims
78
+ * the row: the runner hands it to the handler directly.
79
+ *
80
+ * @param orgId - the organization every job row is stamped with (the default one).
81
+ * @returns the jobs port.
82
+ * @stability experimental
83
+ */
84
+ function createAskEvalJobs(orgId) {
85
+ const jobs = (0, testing_1.createFakeKvoxJobs)();
86
+ // Replaced in place: the fake's getters (`jobs`, `handlers`) stay live.
87
+ jobs.enqueueWithin = async (tx, input) => {
88
+ const row = await tx.job.create({
89
+ data: {
90
+ type: input.type,
91
+ reason: input.reason ?? 'upload',
92
+ subjectType: input.subjectType ?? null,
93
+ subjectId: input.subjectId ?? null,
94
+ priority: input.priority ?? 0,
95
+ payload: input.payload,
96
+ orgId,
97
+ },
98
+ });
99
+ return { id: row.id, deduplicated: false };
100
+ };
101
+ return jobs;
102
+ }
103
+ /**
104
+ * One principal: the evaluation user with {@link ASK_EVAL_PERMISSIONS}.
105
+ *
106
+ * @param userId - the evaluation user's id.
107
+ * @param orgId - the default organization's id.
108
+ * @returns the identity port.
109
+ * @stability experimental
110
+ */
111
+ function createAskEvalIdentity(userId, orgId) {
112
+ return (0, testing_1.createFakeKvoxIdentity)({ principal: { userId, orgId, permissions: exports.ASK_EVAL_PERMISSIONS } });
113
+ }
@@ -0,0 +1,13 @@
1
+ export { ASK_EVAL_USER_EMAIL, startAskEvalHost } from './host/app.js';
2
+ export type { AskEvalHost, StartAskEvalHostOptions } from './host/app.js';
3
+ export { ASK_EVAL_HOST_DIR, startAskEvalDatabase } from './host/db.js';
4
+ export type { AskEvalDatabase, AskEvalPrismaClient } from './host/db.js';
5
+ export { ASK_EVAL_PERMISSIONS, createAskEvalIdentity, createAskEvalJobs, createAskEvalSettings } from './host/ports.js';
6
+ export { seedGoldenGraph } from './seed-golden-graph.js';
7
+ export type { AskEvalIdMap, AskEvalSeedRefusal, SeededGoldenGraph } from './seed-golden-graph.js';
8
+ export { DEFAULT_ASK_EVAL_MODEL, runAskEval, startAskEvalSession } from './run-ask-eval.js';
9
+ export type { AskEvalRunResult, AskEvalSession, AskQuestionRunResult, RunAskEvalOptions } from './run-ask-eval.js';
10
+ export { ASK_REPLAY_MODEL, ASK_REPLAY_SCENARIOS, ASK_REPLAY_SENSITIVE_TEXT, askReplayStepEvents, runAskReplay, } from './replay.js';
11
+ export type { AskReplayResult, AskReplayRun, AskReplayScenario, AskReplayStep } from './replay.js';
12
+ export { ASK_COMMAND_USAGE, ASK_EVAL_EXIT, runAskCommand } from './ask-command.js';
13
+ export type { AskCommandIo, AskCommandOptions } from './ask-command.js';
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runAskCommand = exports.ASK_EVAL_EXIT = exports.ASK_COMMAND_USAGE = exports.runAskReplay = exports.askReplayStepEvents = exports.ASK_REPLAY_SENSITIVE_TEXT = exports.ASK_REPLAY_SCENARIOS = exports.ASK_REPLAY_MODEL = exports.startAskEvalSession = exports.runAskEval = exports.DEFAULT_ASK_EVAL_MODEL = exports.seedGoldenGraph = exports.createAskEvalSettings = exports.createAskEvalJobs = exports.createAskEvalIdentity = exports.ASK_EVAL_PERMISSIONS = exports.startAskEvalDatabase = exports.ASK_EVAL_HOST_DIR = exports.startAskEvalHost = exports.ASK_EVAL_USER_EMAIL = void 0;
4
+ // The ask-run slice's public surface: the `@marinoscar/kvox-eval/ask-run` entry
5
+ // (kept off the package root, which stays light for the pure scorer): the
6
+ // headless Ask evaluation. An in-memory PostgreSQL (PGlite) migrated with the
7
+ // platform and domain history, the golden graph seeded through the graph write
8
+ // service, the real `ask.respond` handler on the Ask slice, over any model port.
9
+ var app_js_1 = require("./host/app.js");
10
+ Object.defineProperty(exports, "ASK_EVAL_USER_EMAIL", { enumerable: true, get: function () { return app_js_1.ASK_EVAL_USER_EMAIL; } });
11
+ Object.defineProperty(exports, "startAskEvalHost", { enumerable: true, get: function () { return app_js_1.startAskEvalHost; } });
12
+ var db_js_1 = require("./host/db.js");
13
+ Object.defineProperty(exports, "ASK_EVAL_HOST_DIR", { enumerable: true, get: function () { return db_js_1.ASK_EVAL_HOST_DIR; } });
14
+ Object.defineProperty(exports, "startAskEvalDatabase", { enumerable: true, get: function () { return db_js_1.startAskEvalDatabase; } });
15
+ var ports_js_1 = require("./host/ports.js");
16
+ Object.defineProperty(exports, "ASK_EVAL_PERMISSIONS", { enumerable: true, get: function () { return ports_js_1.ASK_EVAL_PERMISSIONS; } });
17
+ Object.defineProperty(exports, "createAskEvalIdentity", { enumerable: true, get: function () { return ports_js_1.createAskEvalIdentity; } });
18
+ Object.defineProperty(exports, "createAskEvalJobs", { enumerable: true, get: function () { return ports_js_1.createAskEvalJobs; } });
19
+ Object.defineProperty(exports, "createAskEvalSettings", { enumerable: true, get: function () { return ports_js_1.createAskEvalSettings; } });
20
+ var seed_golden_graph_js_1 = require("./seed-golden-graph.js");
21
+ Object.defineProperty(exports, "seedGoldenGraph", { enumerable: true, get: function () { return seed_golden_graph_js_1.seedGoldenGraph; } });
22
+ var run_ask_eval_js_1 = require("./run-ask-eval.js");
23
+ Object.defineProperty(exports, "DEFAULT_ASK_EVAL_MODEL", { enumerable: true, get: function () { return run_ask_eval_js_1.DEFAULT_ASK_EVAL_MODEL; } });
24
+ Object.defineProperty(exports, "runAskEval", { enumerable: true, get: function () { return run_ask_eval_js_1.runAskEval; } });
25
+ Object.defineProperty(exports, "startAskEvalSession", { enumerable: true, get: function () { return run_ask_eval_js_1.startAskEvalSession; } });
26
+ var replay_js_1 = require("./replay.js");
27
+ Object.defineProperty(exports, "ASK_REPLAY_MODEL", { enumerable: true, get: function () { return replay_js_1.ASK_REPLAY_MODEL; } });
28
+ Object.defineProperty(exports, "ASK_REPLAY_SCENARIOS", { enumerable: true, get: function () { return replay_js_1.ASK_REPLAY_SCENARIOS; } });
29
+ Object.defineProperty(exports, "ASK_REPLAY_SENSITIVE_TEXT", { enumerable: true, get: function () { return replay_js_1.ASK_REPLAY_SENSITIVE_TEXT; } });
30
+ Object.defineProperty(exports, "askReplayStepEvents", { enumerable: true, get: function () { return replay_js_1.askReplayStepEvents; } });
31
+ Object.defineProperty(exports, "runAskReplay", { enumerable: true, get: function () { return replay_js_1.runAskReplay; } });
32
+ var ask_command_js_1 = require("./ask-command.js");
33
+ Object.defineProperty(exports, "ASK_COMMAND_USAGE", { enumerable: true, get: function () { return ask_command_js_1.ASK_COMMAND_USAGE; } });
34
+ Object.defineProperty(exports, "ASK_EVAL_EXIT", { enumerable: true, get: function () { return ask_command_js_1.ASK_EVAL_EXIT; } });
35
+ Object.defineProperty(exports, "runAskCommand", { enumerable: true, get: function () { return ask_command_js_1.runAskCommand; } });
@@ -0,0 +1,128 @@
1
+ import type { AskMessageResponse } from '@marinoscar/kvox-contract/ask';
2
+ import type { AiStreamEvent } from '@marinoscar/platform-api/ai';
3
+ /**
4
+ * The provider and model a replay names (the scripted runtime answers for them).
5
+ *
6
+ * @stability experimental
7
+ */
8
+ export declare const ASK_REPLAY_MODEL: Readonly<{
9
+ readonly provider: "openai";
10
+ readonly model: "gpt-replay";
11
+ }>;
12
+ /**
13
+ * The `sensitive` PersonFact text (m13) the leak gate looks for: it must never
14
+ * appear in a captured request or an answer.
15
+ *
16
+ * @stability experimental
17
+ */
18
+ export declare const ASK_REPLAY_SENSITIVE_TEXT = "shellfish allergy";
19
+ /**
20
+ * One scripted model call: tool calls (the model asking for tools) or the final answer.
21
+ *
22
+ * @stability experimental
23
+ */
24
+ export type AskReplayStep = {
25
+ /** The calls, in order: id, tool name, arguments as the JSON text the provider streams. */
26
+ toolCalls: readonly {
27
+ id: string;
28
+ name: string;
29
+ arguments: string;
30
+ }[];
31
+ } | {
32
+ /** The answer text, citation markers included. */
33
+ answer: string;
34
+ };
35
+ /**
36
+ * One scripted question and what its answer must show.
37
+ *
38
+ * @stability experimental
39
+ */
40
+ export interface AskReplayScenario {
41
+ /** A short id (`r1` .. `r6`). */
42
+ id: string;
43
+ /** What the test case proves. */
44
+ title: string;
45
+ /** The question. */
46
+ question: string;
47
+ /** The model calls, in order. */
48
+ steps: readonly AskReplayStep[];
49
+ /** What the finished message must show. */
50
+ expected: {
51
+ /** Every citation valid (citation validity 1.0), or at least one invalid (below 1). */
52
+ allValid: boolean;
53
+ /** Markers that must be present and invalid (with no resolved id). */
54
+ invalidMarkers: readonly string[];
55
+ /** The exact number of citations, when the scenario pins it. */
56
+ citationCount?: number;
57
+ /** A valid citation that must be present, by marker and kind. */
58
+ validCitation?: {
59
+ marker: string;
60
+ kind: string;
61
+ };
62
+ };
63
+ }
64
+ /**
65
+ * The six scripted questions of the replay, as data.
66
+ *
67
+ * @stability experimental
68
+ */
69
+ export declare const ASK_REPLAY_SCENARIOS: readonly AskReplayScenario[];
70
+ /**
71
+ * The provider events of one scripted model call: the function calls (finish
72
+ * reason `tool_calls`) or the answer's text (finish reason `stop`), then the
73
+ * completed response with fixed usage.
74
+ *
75
+ * @param step - the step.
76
+ * @returns the events, in order.
77
+ * @stability experimental
78
+ */
79
+ export declare function askReplayStepEvents(step: AskReplayStep): AiStreamEvent[];
80
+ /**
81
+ * One replayed question: the finished message and what it was checked for.
82
+ *
83
+ * @stability experimental
84
+ */
85
+ export interface AskReplayResult {
86
+ /** The scenario. */
87
+ scenario: AskReplayScenario;
88
+ /** The finished assistant message. */
89
+ message: AskMessageResponse;
90
+ /** Every `chat()` request of this turn, serialized. */
91
+ capturedPrompts: string[];
92
+ /** Citation validity (`scoreCitationValidity`). */
93
+ citationValidity: number;
94
+ /** Why the scenario failed its expectation; empty when it held. */
95
+ failures: string[];
96
+ }
97
+ /**
98
+ * The outcome of {@link runAskReplay}.
99
+ *
100
+ * @stability experimental
101
+ */
102
+ export interface AskReplayRun {
103
+ /** One per scenario, in order. */
104
+ results: AskReplayResult[];
105
+ /** The leak gate: no captured request and no answer contains {@link ASK_REPLAY_SENSITIVE_TEXT}. */
106
+ leakFree: boolean;
107
+ /** Every scenario held and the leak gate passed. */
108
+ passed: boolean;
109
+ }
110
+ /**
111
+ * Runs the replay: the whole golden graph seeded, the six scripted questions
112
+ * through the real handler, each checked, and the leak gate over every request
113
+ * and answer.
114
+ *
115
+ * @example
116
+ * ```ts
117
+ * const run = await runAskReplay();
118
+ * if (!run.passed) process.exitCode = 1;
119
+ * ```
120
+ *
121
+ * @param options - the scenarios (default {@link ASK_REPLAY_SCENARIOS}) and a progress sink.
122
+ * @returns the results and the gates.
123
+ * @stability experimental
124
+ */
125
+ export declare function runAskReplay(options?: {
126
+ scenarios?: readonly AskReplayScenario[];
127
+ log?: (line: string) => void;
128
+ }): Promise<AskReplayRun>;
@@ -0,0 +1,212 @@
1
+ "use strict";
2
+ // =============================================================================
3
+ // The Ask evaluation's replay: the harness's self-test, no model, no key (#608)
4
+ // =============================================================================
5
+ //
6
+ // Seeds the ENTIRE golden graph (exactly as a live run does), then drives the
7
+ // REAL `AskRespondHandler` (real toolset, real database, real handle registry
8
+ // and citation mapper, and the real `createKvoxAi` event mapping) through six
9
+ // representative questions with a SCRIPTED runtime (`createScriptedRuntime` of
10
+ // `@marinoscar/kvox-ai/testing`): a fixed sequence of tool calls per question,
11
+ // using handles the real tools actually returned, then a canned answer that
12
+ // cites them, plus one answer that cites an unissued `[^ev99]`.
13
+ //
14
+ // It is the same role `kvox-eval kg --predictions gold` plays for extraction:
15
+ // it proves the seeder, the tools, the handler and the scorer agree end to end
16
+ // WITHOUT a real model or a paid key. The scripts are the ones of the web app's
17
+ // `apps/api/test/ask/ask-eval.replay.db.spec.ts` (same tool calls, same
18
+ // arguments, same answers), exported as DATA so a test and `kvox-eval ask
19
+ // --replay` share them.
20
+ //
21
+ // What it checks: citation validity < 1 for the planted answer (with `ev99`
22
+ // invalid) and 1.0 for the other five, every turn `complete`, and the leak
23
+ // gate: the graph's `sensitive` PersonFact (Marcus Webb's shellfish allergy)
24
+ // never reaches a captured request or an answer.
25
+ // =============================================================================
26
+ Object.defineProperty(exports, "__esModule", { value: true });
27
+ exports.ASK_REPLAY_SCENARIOS = exports.ASK_REPLAY_SENSITIVE_TEXT = exports.ASK_REPLAY_MODEL = void 0;
28
+ exports.askReplayStepEvents = askReplayStepEvents;
29
+ exports.runAskReplay = runAskReplay;
30
+ const kvox_ai_1 = require("@marinoscar/kvox-ai");
31
+ const testing_1 = require("@marinoscar/kvox-ai/testing");
32
+ const index_js_1 = require("../ask/index.js");
33
+ const index_js_2 = require("../live/index.js");
34
+ const run_ask_eval_js_1 = require("./run-ask-eval.js");
35
+ /**
36
+ * The provider and model a replay names (the scripted runtime answers for them).
37
+ *
38
+ * @stability experimental
39
+ */
40
+ exports.ASK_REPLAY_MODEL = Object.freeze({ provider: 'openai', model: 'gpt-replay' });
41
+ /**
42
+ * The `sensitive` PersonFact text (m13) the leak gate looks for: it must never
43
+ * appear in a captured request or an answer.
44
+ *
45
+ * @stability experimental
46
+ */
47
+ exports.ASK_REPLAY_SENSITIVE_TEXT = 'shellfish allergy';
48
+ const call = (id, name, args) => ({ id, name, arguments: JSON.stringify(args) });
49
+ /**
50
+ * The six scripted questions of the replay, as data.
51
+ *
52
+ * @stability experimental
53
+ */
54
+ exports.ASK_REPLAY_SCENARIOS = Object.freeze([
55
+ {
56
+ id: 'r1',
57
+ title: 'search → get_entity → cites [^ent1]: valid, citation validity 1.0',
58
+ question: 'Who does Sarah Chen work for?',
59
+ steps: [
60
+ { toolCalls: [call('c1', 'search', { query: 'Sarah Chen', scope: 'entities', limit: null })] },
61
+ { toolCalls: [call('c2', 'get_entity', { entity: 'ent1' })] },
62
+ { answer: 'Sarah Chen works for Northwind Robotics.[^ent1]' },
63
+ ],
64
+ expected: { allValid: true, invalidMarkers: [] },
65
+ },
66
+ {
67
+ id: 'r2',
68
+ title: 'search → evidence(ent1) → cites [^ev1]: valid, citation validity 1.0',
69
+ question: 'Where is the pick-path pilot?',
70
+ steps: [
71
+ { toolCalls: [call('c1', 'search', { query: 'pick-path pilot', scope: 'entities', limit: null })] },
72
+ { toolCalls: [call('c2', 'evidence', { subject: 'ent1', limit: 5 })] },
73
+ { answer: 'Here is what was said about it.[^ev1]' },
74
+ ],
75
+ expected: { allValid: true, invalidMarkers: [], citationCount: 1, validCitation: { marker: 'ev1', kind: 'evidence' } },
76
+ },
77
+ {
78
+ id: 'r3',
79
+ title: 'search → timeline → evidence(itm1) → cites [^ev1]: valid, citation validity 1.0',
80
+ question: 'What has happened with Halden Freight?',
81
+ steps: [
82
+ { toolCalls: [call('c1', 'search', { query: 'Halden Freight', scope: 'entities', limit: null })] },
83
+ { toolCalls: [call('c2', 'timeline', { entity: 'ent1', asOf: null, kinds: null, limit: 5 })] },
84
+ { toolCalls: [call('c3', 'evidence', { subject: 'itm1', limit: 3 })] },
85
+ { answer: 'Several things happened.[^ev1]' },
86
+ ],
87
+ expected: { allValid: true, invalidMarkers: [] },
88
+ },
89
+ {
90
+ id: 'r4',
91
+ title: 'a not-found question: search returns nothing, the answer cites nothing (citation validity vacuously 1.0)',
92
+ question: 'What did Jamie Fontaine decide about the Meridian project?',
93
+ steps: [
94
+ { toolCalls: [call('c1', 'search', { query: 'Jamie Fontaine', scope: 'all', limit: null })] },
95
+ { answer: "I couldn't find Jamie Fontaine or a Meridian project in your graph." },
96
+ ],
97
+ expected: { allValid: true, invalidMarkers: [], citationCount: 0 },
98
+ },
99
+ {
100
+ id: 'r5',
101
+ title: 'cites entity + evidence together, all valid: citation validity 1.0',
102
+ question: 'Who runs the northern depots cutover, and what is the evidence?',
103
+ steps: [
104
+ { toolCalls: [call('c1', 'search', { query: 'Marcus Webb', scope: 'entities', limit: null })] },
105
+ { toolCalls: [call('c2', 'get_entity', { entity: 'ent1' })] },
106
+ { toolCalls: [call('c3', 'evidence', { subject: 'ent1', limit: 3 })] },
107
+ { answer: 'Marcus Webb is involved.[^ent1] See here.[^ev1]' },
108
+ ],
109
+ expected: { allValid: true, invalidMarkers: [] },
110
+ },
111
+ {
112
+ id: 'r6',
113
+ title: 'THE PLANTED BAD ANSWER: cites an unissued [^ev99] alongside a real one: citation validity < 1',
114
+ question: 'What is going on with the Corvid Health project?',
115
+ steps: [
116
+ { toolCalls: [call('c1', 'search', { query: 'Corvid Health', scope: 'entities', limit: null })] },
117
+ { toolCalls: [call('c2', 'get_entity', { entity: 'ent1' })] },
118
+ { answer: 'Corvid Health is a client.[^ent1] Also this, which nobody ever returned.[^ev99]' },
119
+ ],
120
+ expected: { allValid: false, invalidMarkers: ['ev99'] },
121
+ },
122
+ ]);
123
+ /**
124
+ * The provider events of one scripted model call: the function calls (finish
125
+ * reason `tool_calls`) or the answer's text (finish reason `stop`), then the
126
+ * completed response with fixed usage.
127
+ *
128
+ * @param step - the step.
129
+ * @returns the events, in order.
130
+ * @stability experimental
131
+ */
132
+ function askReplayStepEvents(step) {
133
+ const completed = (finishReason) => (0, testing_1.completedEvent)({
134
+ id: 'resp_replay',
135
+ provider: exports.ASK_REPLAY_MODEL.provider,
136
+ model: exports.ASK_REPLAY_MODEL.model,
137
+ usage: { inputTokens: 100, outputTokens: 20 },
138
+ finishReason,
139
+ });
140
+ if ('answer' in step)
141
+ return [(0, testing_1.textDeltaEvent)(step.answer), completed('stop')];
142
+ return [...step.toolCalls.map((c) => (0, testing_1.functionCallEvent)(c.id, c.name, c.arguments)), completed('tool_calls')];
143
+ }
144
+ /** What one finished message failed of its scenario's expectation. */
145
+ function checkScenario(scenario, message) {
146
+ const failures = [];
147
+ const { expected } = scenario;
148
+ if (message.status !== 'complete')
149
+ failures.push(`status is '${message.status}', not 'complete'`);
150
+ const validity = (0, index_js_1.scoreCitationValidity)(message.citations);
151
+ if (expected.allValid && validity !== 1)
152
+ failures.push(`citation validity ${validity}, expected 1`);
153
+ if (!expected.allValid && !(validity < 1))
154
+ failures.push(`citation validity ${validity}, expected < 1`);
155
+ for (const marker of expected.invalidMarkers) {
156
+ const citation = message.citations.find((c) => c.marker === marker);
157
+ if (!citation || citation.valid || citation.id !== null)
158
+ failures.push(`[^${marker}] is not present as an invalid citation`);
159
+ }
160
+ if (expected.citationCount !== undefined && message.citations.length !== expected.citationCount) {
161
+ failures.push(`${message.citations.length} citation(s), expected ${expected.citationCount}`);
162
+ }
163
+ if (expected.validCitation) {
164
+ const { marker, kind } = expected.validCitation;
165
+ if (!message.citations.some((c) => c.marker === marker && c.kind === kind && c.valid)) {
166
+ failures.push(`no valid ${kind} citation [^${marker}]`);
167
+ }
168
+ }
169
+ return failures;
170
+ }
171
+ /**
172
+ * Runs the replay: the whole golden graph seeded, the six scripted questions
173
+ * through the real handler, each checked, and the leak gate over every request
174
+ * and answer.
175
+ *
176
+ * @example
177
+ * ```ts
178
+ * const run = await runAskReplay();
179
+ * if (!run.passed) process.exitCode = 1;
180
+ * ```
181
+ *
182
+ * @param options - the scenarios (default {@link ASK_REPLAY_SCENARIOS}) and a progress sink.
183
+ * @returns the results and the gates.
184
+ * @stability experimental
185
+ */
186
+ async function runAskReplay(options = {}) {
187
+ const scenarios = options.scenarios ?? exports.ASK_REPLAY_SCENARIOS;
188
+ const runtime = (0, testing_1.createScriptedRuntime)();
189
+ const ai = (0, kvox_ai_1.createKvoxAi)({ runtime, taskModels: (0, index_js_2.staticTaskModelSource)(exports.ASK_REPLAY_MODEL.provider, exports.ASK_REPLAY_MODEL.model) });
190
+ const session = await (0, run_ask_eval_js_1.startAskEvalSession)({ ai, ...(options.log ? { log: options.log } : {}) });
191
+ try {
192
+ const results = [];
193
+ for (const scenario of scenarios) {
194
+ for (const step of scenario.steps)
195
+ runtime.queueStream({ events: askReplayStepEvents(step) });
196
+ const { message, capturedPrompts } = await session.ask(scenario.question, exports.ASK_REPLAY_MODEL.model);
197
+ const failures = checkScenario(scenario, message);
198
+ // Every scripted call played, and no more: a leftover would leak into the next question.
199
+ const played = runtime.calls.filter((c) => c.kind === 'stream').length;
200
+ if (played !== scenario.steps.length)
201
+ failures.push(`${played} model call(s), the script has ${scenario.steps.length}`);
202
+ runtime.reset();
203
+ results.push({ scenario, message, capturedPrompts, citationValidity: (0, index_js_1.scoreCitationValidity)(message.citations), failures });
204
+ }
205
+ const leakFree = results.every((r) => !r.capturedPrompts.some((p) => p.toLowerCase().includes(exports.ASK_REPLAY_SENSITIVE_TEXT)) &&
206
+ !(0, index_js_1.scoreSensitiveLeak)([exports.ASK_REPLAY_SENSITIVE_TEXT], r.message.content, []));
207
+ return { results, leakFree, passed: leakFree && results.every((r) => r.failures.length === 0) };
208
+ }
209
+ finally {
210
+ await session.close();
211
+ }
212
+ }
@@ -0,0 +1,125 @@
1
+ import type { AskMessageResponse } from '@marinoscar/kvox-contract/ask';
2
+ import { type KvoxAiPort } from '@marinoscar/kvox-api/ports';
3
+ import { type AskEvalQuestion, type AskEvalReport, type AskEvalScore } from '../ask/index.js';
4
+ import { type GoldenFixture } from '../fixtures/index.js';
5
+ import { type AskEvalHost } from './host/app.js';
6
+ import { type AskEvalIdMap, type SeededGoldenGraph } from './seed-golden-graph.js';
7
+ /**
8
+ * The model a live run uses when none is named.
9
+ *
10
+ * @stability experimental
11
+ */
12
+ export declare const DEFAULT_ASK_EVAL_MODEL = "gpt-4o-mini";
13
+ /**
14
+ * One answered question.
15
+ *
16
+ * @stability experimental
17
+ */
18
+ export interface AskQuestionRunResult {
19
+ /** The question as asked. */
20
+ question: AskEvalQuestion;
21
+ /** The finished assistant message, as the API would return it. */
22
+ message: AskMessageResponse;
23
+ /** Every `chat()` request the port received while answering THIS question, serialized (signal removed). */
24
+ capturedPrompts: string[];
25
+ /** Wall-clock time of the turn, in milliseconds. */
26
+ ms: number;
27
+ /**
28
+ * The model call that failed the turn, by the port's error class (`auth`,
29
+ * `unavailable`, ...): the handler marked the message `failed` and the run went
30
+ * on. Absent when the turn ran to its end.
31
+ */
32
+ modelError?: {
33
+ errorClass: string;
34
+ };
35
+ }
36
+ /**
37
+ * The scored outcome of a run.
38
+ *
39
+ * @stability experimental
40
+ */
41
+ export interface AskEvalRunResult {
42
+ /** One per question, in the order asked. */
43
+ results: AskQuestionRunResult[];
44
+ /** Fixture ids ↔ row ids (`evidenceSource` is what `citeFrom` resolves against). */
45
+ idMap: AskEvalIdMap;
46
+ /** What the seeder reported (refusals, warnings, counts). */
47
+ seed: Pick<SeededGoldenGraph, 'refusals' | 'warnings' | 'counts'>;
48
+ /** One score per result. */
49
+ scores: AskEvalScore[];
50
+ /** The aggregated report. */
51
+ report: AskEvalReport;
52
+ }
53
+ /**
54
+ * What {@link runAskEval} runs.
55
+ *
56
+ * @stability experimental
57
+ */
58
+ export interface RunAskEvalOptions {
59
+ /** The model port under test. */
60
+ ai: KvoxAiPort;
61
+ /** The model id each turn names (and the report records). */
62
+ model: string;
63
+ /** The questions, or ids of the committed ones. Default: every committed question. */
64
+ questions?: readonly AskEvalQuestion[] | readonly string[];
65
+ /** The golden meetings to seed. Default: `loadGoldenSet()`. */
66
+ fixtures?: readonly GoldenFixture[];
67
+ /** The answer-match gate of the report (0..1). Default 0.8. */
68
+ minMatch?: number;
69
+ /** Receives progress lines (steps, counts, question ids; never a question's text or an answer). */
70
+ log?: (line: string) => void;
71
+ }
72
+ /**
73
+ * A seeded host that answers one question at a time: what {@link runAskEval}
74
+ * and the replay drive.
75
+ *
76
+ * @stability experimental
77
+ */
78
+ export interface AskEvalSession {
79
+ /** The running host. */
80
+ readonly host: AskEvalHost;
81
+ /** What the seeder wrote. */
82
+ readonly seeded: SeededGoldenGraph;
83
+ /**
84
+ * Asks one question in a new conversation and runs its `ask.respond` job to the end.
85
+ *
86
+ * @param question - the question text.
87
+ * @param model - the model id the turn names.
88
+ * @returns the finished message and this turn's captured requests.
89
+ */
90
+ ask(question: string, model: string): Promise<Omit<AskQuestionRunResult, 'question'>>;
91
+ /** Restores the port's `chat()` and closes the host. Safe to call twice. */
92
+ close(): Promise<void>;
93
+ }
94
+ /**
95
+ * Starts the host on `ai`, seeds the golden graph and returns a session that asks
96
+ * questions through the real Ask services and handler.
97
+ *
98
+ * @param options - the port, the fixtures (default: the whole golden set) and a progress sink.
99
+ * @returns the session; call `close()` when done (in a `finally`).
100
+ * @stability experimental
101
+ */
102
+ export declare function startAskEvalSession(options: {
103
+ ai: KvoxAiPort;
104
+ fixtures?: readonly GoldenFixture[];
105
+ log?: (line: string) => void;
106
+ }): Promise<AskEvalSession>;
107
+ /**
108
+ * Runs the Ask evaluation headless: an in-memory PostgreSQL, the golden graph
109
+ * seeded through the graph write service, every question through the real
110
+ * `ask.respond` handler on `ai`, each answer scored.
111
+ *
112
+ * @example
113
+ * ```ts
114
+ * const key = readEvalApiKey(process.env);
115
+ * const { ai } = createLiveKvoxAi({ provider: 'openai', model: 'gpt-4o-mini', apiKey: key?.apiKey ?? null });
116
+ * const run = await runAskEval({ ai, model: 'gpt-4o-mini', questions: ['q01', 'q07'] });
117
+ * console.log(formatAskEvalReport(run.report));
118
+ * ```
119
+ *
120
+ * @param options - the port, the model, the questions and the gate.
121
+ * @returns the answers, the id map, the scores and the report.
122
+ * @throws `AskEvalQuestionLoadError` for an unknown question id; `KgEvalLoadError` for a malformed fixture.
123
+ * @stability experimental
124
+ */
125
+ export declare function runAskEval(options: RunAskEvalOptions): Promise<AskEvalRunResult>;