@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,77 @@
|
|
|
1
|
+
import type { KvoxAiPort } from '@marinoscar/kvox-api/ports';
|
|
2
|
+
import { type KgEvalPrediction } from './prediction-schema.js';
|
|
3
|
+
import { type KgEvalReport } from './report.js';
|
|
4
|
+
/**
|
|
5
|
+
* What {@link runKgEval} is asked to do.
|
|
6
|
+
*
|
|
7
|
+
* @stability experimental
|
|
8
|
+
*/
|
|
9
|
+
export interface RunKgEvalOptions {
|
|
10
|
+
/**
|
|
11
|
+
* Where predictions come from: `gold` (the default when no `predictionsDir` is
|
|
12
|
+
* given), or a registered live runner (`extract`, `extract-resolve` /
|
|
13
|
+
* `extract+resolve`, or a test's own), which needs `ai` and `model`.
|
|
14
|
+
*/
|
|
15
|
+
runner?: string;
|
|
16
|
+
/** Score the prediction files of this directory instead of running anything. */
|
|
17
|
+
predictionsDir?: string;
|
|
18
|
+
/** The model port a live runner calls. Never needed for `gold` or `predictionsDir`. */
|
|
19
|
+
ai?: KvoxAiPort;
|
|
20
|
+
/** The model a live runner targets (required for one). */
|
|
21
|
+
model?: string;
|
|
22
|
+
/** The provider a live runner targets. Default `openai`. */
|
|
23
|
+
provider?: string;
|
|
24
|
+
/** Fixture ids to keep (`--only`); omitted or empty keeps all. */
|
|
25
|
+
fixtures?: readonly string[];
|
|
26
|
+
/** A tag every kept fixture carries (`--tag`). */
|
|
27
|
+
tag?: string;
|
|
28
|
+
/** Throw {@link KgEvalTargetsError} when a §6 target fails. Default `false` (report only). */
|
|
29
|
+
enforce?: boolean;
|
|
30
|
+
/** The directory the golden set is read from. Default the committed synthetic set. */
|
|
31
|
+
fixturesDir?: string;
|
|
32
|
+
/** Called with every prediction a live runner produced, in fixture order (the CLI writes it to `--out`). */
|
|
33
|
+
onPrediction?: (fixtureId: string, prediction: KgEvalPrediction) => void | Promise<void>;
|
|
34
|
+
/** Called for every fixture a `predictionsDir` has no file for (scored as empty). */
|
|
35
|
+
onMissingPrediction?: (fixtureId: string, dir: string) => void;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* A run asked to enforce the §6 targets missed one. Carries the full report.
|
|
39
|
+
*
|
|
40
|
+
* @stability experimental
|
|
41
|
+
*/
|
|
42
|
+
export declare class KgEvalTargetsError extends Error {
|
|
43
|
+
readonly report: KgEvalReport;
|
|
44
|
+
/**
|
|
45
|
+
* @param report - the run's report (`targetsMet` is `false`).
|
|
46
|
+
*/
|
|
47
|
+
constructor(report: KgEvalReport);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* A run that cannot start: no runner of that name, a live runner without a model
|
|
51
|
+
* or a port, both a runner and a predictions directory, an empty selection. The
|
|
52
|
+
* CLI maps it to exit code 2.
|
|
53
|
+
*
|
|
54
|
+
* @stability experimental
|
|
55
|
+
*/
|
|
56
|
+
export declare class KgEvalUsageError extends Error {
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Runs one knowledge-graph evaluation and returns its report.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```ts
|
|
63
|
+
* const report = await runKgEval({ runner: 'gold' });
|
|
64
|
+
* report.targetsMet; // true: the self-test scores 1.000 everywhere
|
|
65
|
+
*
|
|
66
|
+
* const live = await runKgEval({ runner: 'extract', ai, model: 'gpt-5-mini', fixtures: ['m01'] });
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* @param opts - what to score and how.
|
|
70
|
+
* @returns the report (`formatReport` prints it).
|
|
71
|
+
* @throws {@link KgEvalUsageError} for a run that cannot start; `KgEvalLoadError`
|
|
72
|
+
* for a golden set or prediction file that does not load; {@link KgEvalTargetsError}
|
|
73
|
+
* with `enforce` when a target fails. A live call's failure is the port's
|
|
74
|
+
* `KvoxAiError`, which carries no key.
|
|
75
|
+
* @stability experimental
|
|
76
|
+
*/
|
|
77
|
+
export declare function runKgEval(opts?: RunKgEvalOptions): Promise<KgEvalReport>;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// =============================================================================
|
|
3
|
+
// runKgEval: one knowledge-graph evaluation run, start to report (issue #580)
|
|
4
|
+
// =============================================================================
|
|
5
|
+
//
|
|
6
|
+
// The one code path behind `kvox-eval kg`, `npm run kg:eval` and a host that
|
|
7
|
+
// calls the package directly. Three ways to get the predictions it scores:
|
|
8
|
+
//
|
|
9
|
+
// gold synthesised from the labels (`goldPrediction`): the
|
|
10
|
+
// harness's self-test, 1.000 everywhere or a bug;
|
|
11
|
+
// predictionsDir `<dir>/<fixtureId>.json` written by an earlier run (a
|
|
12
|
+
// missing file is scored as empty, and reported);
|
|
13
|
+
// a live runner `extract` / `extract+resolve` over the `ai` port, one
|
|
14
|
+
// structured call per fixture.
|
|
15
|
+
//
|
|
16
|
+
// PURE OF ENVIRONMENT: nothing here reads `process.env`, prints, or writes a
|
|
17
|
+
// file. A live run is handed a `KvoxAiPort` (the CLI builds one with
|
|
18
|
+
// `createLiveKvoxAi`, which owns the key); the hooks tell the caller about each
|
|
19
|
+
// prediction and each missing file, so the CLI can persist and print them.
|
|
20
|
+
// =============================================================================
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.KgEvalUsageError = exports.KgEvalTargetsError = void 0;
|
|
23
|
+
exports.runKgEval = runKgEval;
|
|
24
|
+
const index_js_1 = require("../fixtures/index.js");
|
|
25
|
+
const gold_runner_js_1 = require("./gold-runner.js");
|
|
26
|
+
const prediction_schema_js_1 = require("./prediction-schema.js");
|
|
27
|
+
const predictions_js_1 = require("./predictions.js");
|
|
28
|
+
const report_js_1 = require("./report.js");
|
|
29
|
+
const runner_js_1 = require("./runner.js");
|
|
30
|
+
const scorer_js_1 = require("./scorer.js");
|
|
31
|
+
/**
|
|
32
|
+
* A run asked to enforce the §6 targets missed one. Carries the full report.
|
|
33
|
+
*
|
|
34
|
+
* @stability experimental
|
|
35
|
+
*/
|
|
36
|
+
class KgEvalTargetsError extends Error {
|
|
37
|
+
report;
|
|
38
|
+
/**
|
|
39
|
+
* @param report - the run's report (`targetsMet` is `false`).
|
|
40
|
+
*/
|
|
41
|
+
constructor(report) {
|
|
42
|
+
super('one or more §6 targets failed (--enforce)');
|
|
43
|
+
this.report = report;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
exports.KgEvalTargetsError = KgEvalTargetsError;
|
|
47
|
+
/**
|
|
48
|
+
* A run that cannot start: no runner of that name, a live runner without a model
|
|
49
|
+
* or a port, both a runner and a predictions directory, an empty selection. The
|
|
50
|
+
* CLI maps it to exit code 2.
|
|
51
|
+
*
|
|
52
|
+
* @stability experimental
|
|
53
|
+
*/
|
|
54
|
+
class KgEvalUsageError extends Error {
|
|
55
|
+
}
|
|
56
|
+
exports.KgEvalUsageError = KgEvalUsageError;
|
|
57
|
+
/**
|
|
58
|
+
* Runs one knowledge-graph evaluation and returns its report.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* const report = await runKgEval({ runner: 'gold' });
|
|
63
|
+
* report.targetsMet; // true: the self-test scores 1.000 everywhere
|
|
64
|
+
*
|
|
65
|
+
* const live = await runKgEval({ runner: 'extract', ai, model: 'gpt-5-mini', fixtures: ['m01'] });
|
|
66
|
+
* ```
|
|
67
|
+
*
|
|
68
|
+
* @param opts - what to score and how.
|
|
69
|
+
* @returns the report (`formatReport` prints it).
|
|
70
|
+
* @throws {@link KgEvalUsageError} for a run that cannot start; `KgEvalLoadError`
|
|
71
|
+
* for a golden set or prediction file that does not load; {@link KgEvalTargetsError}
|
|
72
|
+
* with `enforce` when a target fails. A live call's failure is the port's
|
|
73
|
+
* `KvoxAiError`, which carries no key.
|
|
74
|
+
* @stability experimental
|
|
75
|
+
*/
|
|
76
|
+
async function runKgEval(opts = {}) {
|
|
77
|
+
const predictionsDir = opts.predictionsDir;
|
|
78
|
+
const runnerArg = opts.runner ?? (predictionsDir === undefined ? 'gold' : undefined);
|
|
79
|
+
if (predictionsDir !== undefined && runnerArg !== undefined) {
|
|
80
|
+
throw new KgEvalUsageError('pass either a predictions directory or a runner, not both');
|
|
81
|
+
}
|
|
82
|
+
// An unregistered runner is reported before anything is loaded.
|
|
83
|
+
const live = runnerArg !== undefined && runnerArg !== 'gold' ? (0, runner_js_1.findKgEvalRunner)(runnerArg) : null;
|
|
84
|
+
if (runnerArg !== undefined && runnerArg !== 'gold' && live === null) {
|
|
85
|
+
throw new KgEvalUsageError(`no runner '${runnerArg}' registered (known: ${Object.keys(runner_js_1.KG_EVAL_RUNNERS).join(', ')})`);
|
|
86
|
+
}
|
|
87
|
+
if (live !== null && !opts.model)
|
|
88
|
+
throw new KgEvalUsageError(`the '${live.name}' runner needs a model`);
|
|
89
|
+
if (live !== null && opts.ai === undefined)
|
|
90
|
+
throw new KgEvalUsageError(`the '${live.name}' runner needs a model port (ai)`);
|
|
91
|
+
const fixtures = selectedFixtures(opts);
|
|
92
|
+
const predictions = new Map();
|
|
93
|
+
let runnerLabel;
|
|
94
|
+
let model = null;
|
|
95
|
+
if (live !== null) {
|
|
96
|
+
model = opts.model;
|
|
97
|
+
const runner = await live.factory({ ai: opts.ai });
|
|
98
|
+
runnerLabel = runner.name;
|
|
99
|
+
for (const fixture of fixtures) {
|
|
100
|
+
const pred = prediction_schema_js_1.kgEvalPredictionSchema.parse(await runner.run(fixture, { model, ...(opts.provider !== undefined ? { provider: opts.provider } : {}) }));
|
|
101
|
+
predictions.set(fixture.id, pred);
|
|
102
|
+
await opts.onPrediction?.(fixture.id, pred);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else if (predictionsDir === undefined) {
|
|
106
|
+
runnerLabel = 'gold';
|
|
107
|
+
for (const f of fixtures)
|
|
108
|
+
predictions.set(f.id, (0, gold_runner_js_1.goldPrediction)(f));
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
runnerLabel = 'predictions';
|
|
112
|
+
for (const f of fixtures) {
|
|
113
|
+
const pred = (0, predictions_js_1.loadPredictionFile)(predictionsDir, f.id);
|
|
114
|
+
if (pred === null)
|
|
115
|
+
opts.onMissingPrediction?.(f.id, predictionsDir);
|
|
116
|
+
predictions.set(f.id, pred ?? (0, prediction_schema_js_1.emptyPrediction)(f.id));
|
|
117
|
+
model ??= pred?.model ?? null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const report = (0, report_js_1.buildReport)((0, scorer_js_1.scoreRun)(fixtures, predictions), {
|
|
121
|
+
runner: runnerLabel,
|
|
122
|
+
model,
|
|
123
|
+
fixtureIds: fixtures.map((f) => f.id),
|
|
124
|
+
});
|
|
125
|
+
if (opts.enforce && !report.targetsMet)
|
|
126
|
+
throw new KgEvalTargetsError(report);
|
|
127
|
+
return report;
|
|
128
|
+
}
|
|
129
|
+
/** The golden set narrowed by `fixtures` and `tag`; an empty selection is a usage error. */
|
|
130
|
+
function selectedFixtures(opts) {
|
|
131
|
+
const all = (0, index_js_1.loadGoldenSet)(opts.fixturesDir ?? index_js_1.GOLDEN_MEETINGS_DIR);
|
|
132
|
+
const only = opts.fixtures && opts.fixtures.length > 0 ? [...opts.fixtures] : null;
|
|
133
|
+
const selected = (0, index_js_1.selectFixtures)(all, only, opts.tag ?? null);
|
|
134
|
+
if (selected.length === 0)
|
|
135
|
+
throw new KgEvalUsageError('no fixtures selected');
|
|
136
|
+
return selected;
|
|
137
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { KvoxAiPort } from '@marinoscar/kvox-api/ports';
|
|
2
|
+
import type { GoldenFixture } from '../fixtures/index.js';
|
|
3
|
+
import type { KgEvalPrediction } from './prediction-schema.js';
|
|
4
|
+
/**
|
|
5
|
+
* What one live run is made with.
|
|
6
|
+
*
|
|
7
|
+
* @stability experimental
|
|
8
|
+
*/
|
|
9
|
+
export interface KgEvalRunOptions {
|
|
10
|
+
/** The provider's model id. */
|
|
11
|
+
model: string;
|
|
12
|
+
/** The provider id the call targets (`openai`, `openai-compatible`). Default `openai`. */
|
|
13
|
+
provider?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* What a runner factory is handed.
|
|
17
|
+
*
|
|
18
|
+
* @stability experimental
|
|
19
|
+
*/
|
|
20
|
+
export interface KgEvalRunnerDeps {
|
|
21
|
+
/** The model port every structured call goes through. */
|
|
22
|
+
ai: KvoxAiPort;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Turns one golden fixture into one prediction.
|
|
26
|
+
*
|
|
27
|
+
* @extensionPoint
|
|
28
|
+
* @stability experimental
|
|
29
|
+
*/
|
|
30
|
+
export interface KgEvalRunner {
|
|
31
|
+
/** `extract` (#363), `extract+resolve` (#364): the report's `runner` column. */
|
|
32
|
+
readonly name: string;
|
|
33
|
+
/**
|
|
34
|
+
* One fixture's prediction.
|
|
35
|
+
*
|
|
36
|
+
* @param fixture - the fixture.
|
|
37
|
+
* @param opts - the model (and provider) to run on.
|
|
38
|
+
* @returns the prediction (it carries `model`, never a key).
|
|
39
|
+
*/
|
|
40
|
+
run(fixture: GoldenFixture, opts: KgEvalRunOptions): Promise<KgEvalPrediction>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* A lazy runner factory.
|
|
44
|
+
*
|
|
45
|
+
* @stability experimental
|
|
46
|
+
*/
|
|
47
|
+
export type KgEvalRunnerFactory = (deps: KgEvalRunnerDeps) => Promise<KgEvalRunner>;
|
|
48
|
+
/**
|
|
49
|
+
* The registered runners, by name. Lazy factories, so a `--predictions` run never
|
|
50
|
+
* loads the extraction code. A test may add an entry (and delete it afterwards).
|
|
51
|
+
*
|
|
52
|
+
* @stability experimental
|
|
53
|
+
*/
|
|
54
|
+
export declare const KG_EVAL_RUNNERS: Record<string, KgEvalRunnerFactory>;
|
|
55
|
+
/**
|
|
56
|
+
* Spellings a command line accepts for a registered runner (`extract-resolve`,
|
|
57
|
+
* a shell-friendly `extract+resolve`).
|
|
58
|
+
*
|
|
59
|
+
* @stability experimental
|
|
60
|
+
*/
|
|
61
|
+
export declare const KG_EVAL_RUNNER_ALIASES: Readonly<Record<string, string>>;
|
|
62
|
+
/**
|
|
63
|
+
* The registered runner a name (or an alias) means, or `null`.
|
|
64
|
+
*
|
|
65
|
+
* @param name - a runner name or alias.
|
|
66
|
+
* @returns the factory and the registered name, or `null` when none is registered.
|
|
67
|
+
* @stability experimental
|
|
68
|
+
*/
|
|
69
|
+
export declare function findKgEvalRunner(name: string): {
|
|
70
|
+
name: string;
|
|
71
|
+
factory: KgEvalRunnerFactory;
|
|
72
|
+
} | null;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// =============================================================================
|
|
3
|
+
// The kg:eval runner seam (issue #362; #580 moved it onto the model port).
|
|
4
|
+
//
|
|
5
|
+
// A runner turns one fixture into one prediction. The extraction runner
|
|
6
|
+
// (`extract`) is kg.extract's own pipeline (#363); resolution extends it
|
|
7
|
+
// (`extract+resolve`, #364). A runner registers itself by adding an entry below
|
|
8
|
+
// from its own file (`kg/runners/*.ts`) — a lazy factory, so a `--predictions`
|
|
9
|
+
// run never loads the extraction code.
|
|
10
|
+
//
|
|
11
|
+
// #580: a runner is built over a `KvoxAiPort` (`createKvoxAi` of
|
|
12
|
+
// `@marinoscar/kvox-ai`, or a fake in a test) and never sees a key. Whoever
|
|
13
|
+
// builds the port owns the key (`live/`: the environment, never a flag); a
|
|
14
|
+
// prediction carries `model` alone.
|
|
15
|
+
// =============================================================================
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.KG_EVAL_RUNNER_ALIASES = exports.KG_EVAL_RUNNERS = void 0;
|
|
18
|
+
exports.findKgEvalRunner = findKgEvalRunner;
|
|
19
|
+
/**
|
|
20
|
+
* The registered runners, by name. Lazy factories, so a `--predictions` run never
|
|
21
|
+
* loads the extraction code. A test may add an entry (and delete it afterwards).
|
|
22
|
+
*
|
|
23
|
+
* @stability experimental
|
|
24
|
+
*/
|
|
25
|
+
exports.KG_EVAL_RUNNERS = {
|
|
26
|
+
// `require`, not `import()`: under NodeNext a dynamic import stays a native
|
|
27
|
+
// ESM import, which cannot load a `.ts` file through ts-node or ts-jest.
|
|
28
|
+
extract: async (deps) => require('./runners/extract-runner.js').createExtractRunner(deps),
|
|
29
|
+
// #364: extraction, then in-memory resolution against `knownEntities`.
|
|
30
|
+
'extract+resolve': async (deps) => require('./runners/extract-resolve-runner.js').createExtractResolveRunner(deps),
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Spellings a command line accepts for a registered runner (`extract-resolve`,
|
|
34
|
+
* a shell-friendly `extract+resolve`).
|
|
35
|
+
*
|
|
36
|
+
* @stability experimental
|
|
37
|
+
*/
|
|
38
|
+
exports.KG_EVAL_RUNNER_ALIASES = Object.freeze({ 'extract-resolve': 'extract+resolve' });
|
|
39
|
+
/**
|
|
40
|
+
* The registered runner a name (or an alias) means, or `null`.
|
|
41
|
+
*
|
|
42
|
+
* @param name - a runner name or alias.
|
|
43
|
+
* @returns the factory and the registered name, or `null` when none is registered.
|
|
44
|
+
* @stability experimental
|
|
45
|
+
*/
|
|
46
|
+
function findKgEvalRunner(name) {
|
|
47
|
+
const registered = Object.prototype.hasOwnProperty.call(exports.KG_EVAL_RUNNER_ALIASES, name) ? exports.KG_EVAL_RUNNER_ALIASES[name] : name;
|
|
48
|
+
if (!Object.prototype.hasOwnProperty.call(exports.KG_EVAL_RUNNERS, registered))
|
|
49
|
+
return null;
|
|
50
|
+
return { name: registered, factory: exports.KG_EVAL_RUNNERS[registered] };
|
|
51
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ProposedRow } from '../internals.js';
|
|
2
|
+
import { type GraphPreferences } from '@marinoscar/kvox-api/graph';
|
|
3
|
+
import type { GoldenFixture } from '../../fixtures/index.js';
|
|
4
|
+
import type { KgEvalRunner, KgEvalRunnerDeps } from '../runner.js';
|
|
5
|
+
/**
|
|
6
|
+
* `pg_trgm`'s `similarity(a, b)`: shared trigrams over the union.
|
|
7
|
+
*
|
|
8
|
+
* @stability experimental
|
|
9
|
+
*/
|
|
10
|
+
export declare function trigramSimilarity(a: string, b: string): number;
|
|
11
|
+
/**
|
|
12
|
+
* PURE. Resolve every proposed entity row (in place, on copies) against the
|
|
13
|
+
* fixture's known entities. The Meeting row and anything without a type match
|
|
14
|
+
* keep what extraction decided.
|
|
15
|
+
*
|
|
16
|
+
* @stability experimental
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveRowsInMemory(fixture: GoldenFixture, rows: readonly ProposedRow[], prefs?: GraphPreferences): ProposedRow[];
|
|
19
|
+
/**
|
|
20
|
+
* PURE. #365's relation dedup and closing plan, in memory, against the
|
|
21
|
+
* fixture's prior edges: `known` rows flagged (and so pre-accepted), `overlaps`
|
|
22
|
+
* / `unordered` copied from #353's plan, and closings counted.
|
|
23
|
+
*
|
|
24
|
+
* @stability experimental
|
|
25
|
+
*/
|
|
26
|
+
export declare function applyDedupStagesInMemory(fixture: GoldenFixture, rows: readonly ProposedRow[]): {
|
|
27
|
+
rows: ProposedRow[];
|
|
28
|
+
stats: {
|
|
29
|
+
known: number;
|
|
30
|
+
closings: number;
|
|
31
|
+
overlaps: number;
|
|
32
|
+
unordered: number;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* The `extract+resolve` runner over `deps.ai`.
|
|
37
|
+
*
|
|
38
|
+
* @stability experimental
|
|
39
|
+
*/
|
|
40
|
+
export declare function createExtractResolveRunner(deps: KgEvalRunnerDeps): KgEvalRunner;
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// =============================================================================
|
|
3
|
+
// The `extract+resolve` kg:eval runner (#364; docs/specs/ontology.md §6, §7)
|
|
4
|
+
// =============================================================================
|
|
5
|
+
//
|
|
6
|
+
// `extract` (#363), then resolution of every proposed entity against the
|
|
7
|
+
// fixture's `knownEntities` — so auto-link PRECISION is measured, the number
|
|
8
|
+
// that gates any future lowering of the 0.90 default (#362's target).
|
|
9
|
+
//
|
|
10
|
+
// NO DATABASE: an in-memory implementation of the candidate arms feeds the
|
|
11
|
+
// production `score.ts` and `decide()`:
|
|
12
|
+
//
|
|
13
|
+
// A exact — `normalizeAlias` (#355) over label + aliases, as in SQL
|
|
14
|
+
// B trigram — a JS port of `pg_trgm`'s `similarity()` (lowercased words,
|
|
15
|
+
// padded " w ", trigram-set Jaccard), threshold 0.4
|
|
16
|
+
// C vector — skipped (no embeddings in an eval run)
|
|
17
|
+
//
|
|
18
|
+
// Context: `sameMeeting` for a known Person a fixture speaker is identified as
|
|
19
|
+
// (what #356 produces); the speaker shortcut links such a Person outright, as
|
|
20
|
+
// production does. Adjudication is OFF — the eval measures the deterministic
|
|
21
|
+
// bands; a middle-band row is `possible_duplicate` and never pre-checked.
|
|
22
|
+
//
|
|
23
|
+
// #365: after resolution, the pure cores of `work-item-dedup` (relations) and
|
|
24
|
+
// `temporal-closing` run against the fixture's `knownRelations` through #353's
|
|
25
|
+
// planner, so a restated edge is `known`, an overlap is flagged and a closing
|
|
26
|
+
// is counted (`stats.closings`) exactly as production would propose them.
|
|
27
|
+
// Item dedup needs an embedding and an adjudication call, neither of which an
|
|
28
|
+
// eval run makes; it is covered by the stage's own tests instead.
|
|
29
|
+
//
|
|
30
|
+
// ⚠ The runner never sees a key: the port it is handed owns it (#580).
|
|
31
|
+
// =============================================================================
|
|
32
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
+
exports.trigramSimilarity = trigramSimilarity;
|
|
34
|
+
exports.resolveRowsInMemory = resolveRowsInMemory;
|
|
35
|
+
exports.applyDedupStagesInMemory = applyDedupStagesInMemory;
|
|
36
|
+
exports.createExtractResolveRunner = createExtractResolveRunner;
|
|
37
|
+
const internals_js_1 = require("../internals.js");
|
|
38
|
+
const ontology_1 = require("@marinoscar/kvox-contract/ontology");
|
|
39
|
+
const graph_1 = require("@marinoscar/kvox-api/graph");
|
|
40
|
+
const index_js_1 = require("../../fixtures/index.js");
|
|
41
|
+
const extract_runner_js_1 = require("./extract-runner.js");
|
|
42
|
+
const TRIGRAM_THRESHOLD = 0.4;
|
|
43
|
+
function trigrams(text) {
|
|
44
|
+
const out = new Set();
|
|
45
|
+
for (const word of text.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter(Boolean)) {
|
|
46
|
+
const padded = ` ${word} `;
|
|
47
|
+
for (let i = 0; i + 3 <= padded.length; i += 1)
|
|
48
|
+
out.add(padded.slice(i, i + 3));
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* `pg_trgm`'s `similarity(a, b)`: shared trigrams over the union.
|
|
54
|
+
*
|
|
55
|
+
* @stability experimental
|
|
56
|
+
*/
|
|
57
|
+
function trigramSimilarity(a, b) {
|
|
58
|
+
const x = trigrams(a);
|
|
59
|
+
const y = trigrams(b);
|
|
60
|
+
if (x.size === 0 || y.size === 0)
|
|
61
|
+
return 0;
|
|
62
|
+
let shared = 0;
|
|
63
|
+
for (const t of x)
|
|
64
|
+
if (y.has(t))
|
|
65
|
+
shared += 1;
|
|
66
|
+
return shared / (x.size + y.size - shared);
|
|
67
|
+
}
|
|
68
|
+
function safeNormalize(name) {
|
|
69
|
+
try {
|
|
70
|
+
return (0, graph_1.normalizeAlias)(name);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* PURE. Resolve every proposed entity row (in place, on copies) against the
|
|
78
|
+
* fixture's known entities. The Meeting row and anything without a type match
|
|
79
|
+
* keep what extraction decided.
|
|
80
|
+
*
|
|
81
|
+
* @stability experimental
|
|
82
|
+
*/
|
|
83
|
+
function resolveRowsInMemory(fixture, rows, prefs = graph_1.GRAPH_PREFERENCE_DEFAULTS) {
|
|
84
|
+
const input = (0, extract_runner_js_1.fixtureToInput)(fixture);
|
|
85
|
+
const speakerPersons = new Set(input.speakers.map((s) => s.personEntityId).filter((id) => !!id));
|
|
86
|
+
const personBySpeakerName = new Map(input.speakers.filter((s) => s.personEntityId && s.displayName).map((s) => [(0, internals_js_1.normalizeForMatch)(s.displayName), s.personEntityId]));
|
|
87
|
+
const known = fixture.knownEntities.map((k) => ({
|
|
88
|
+
...k,
|
|
89
|
+
names: [k.label, ...k.aliases],
|
|
90
|
+
normalized: new Set([k.label, ...k.aliases].map(safeNormalize).filter((n) => n !== null)),
|
|
91
|
+
}));
|
|
92
|
+
return rows.map((row) => {
|
|
93
|
+
if (row.kind !== 'entity' || row.payload.ref === 'meeting' || row.resolution?.source === 'meeting')
|
|
94
|
+
return row;
|
|
95
|
+
const p = row.payload;
|
|
96
|
+
if (p.type === 'Person') {
|
|
97
|
+
const speakerPerson = [p.label, ...p.aliases].map((n) => personBySpeakerName.get((0, internals_js_1.normalizeForMatch)(n))).find(Boolean);
|
|
98
|
+
if (speakerPerson) {
|
|
99
|
+
return { ...row, resolution: { ref: speakerPerson, score: 1, source: 'speaker', candidates: [], adjudication: null } };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const mentionNames = [p.label, ...p.aliases].map(safeNormalize).filter((n) => n !== null);
|
|
103
|
+
const scored = known
|
|
104
|
+
.filter((k) => k.type === p.type)
|
|
105
|
+
.map((k) => {
|
|
106
|
+
const aliasExact = mentionNames.some((n) => k.normalized.has(n));
|
|
107
|
+
const best = Math.max(0, ...k.names.map((n) => trigramSimilarity(p.label, n)));
|
|
108
|
+
return {
|
|
109
|
+
entityId: k.id,
|
|
110
|
+
label: k.label,
|
|
111
|
+
type: k.type,
|
|
112
|
+
...(0, internals_js_1.scoreCandidate)({
|
|
113
|
+
aliasExact,
|
|
114
|
+
trigram: best >= TRIGRAM_THRESHOLD ? best : null,
|
|
115
|
+
cosine: null,
|
|
116
|
+
sameMeeting: speakerPersons.has(k.id),
|
|
117
|
+
orgCoMention: false,
|
|
118
|
+
sharedNeighbour: false,
|
|
119
|
+
recent: false,
|
|
120
|
+
}),
|
|
121
|
+
};
|
|
122
|
+
})
|
|
123
|
+
.filter((c) => c.arm !== null);
|
|
124
|
+
const ranked = (0, internals_js_1.rankCandidates)(scored, prefs.resolution);
|
|
125
|
+
const decision = (0, internals_js_1.decide)({ candidates: ranked.candidates, ambiguous: ranked.ambiguous, band: (0, internals_js_1.bandFor)(ranked.candidates[0]?.score ?? null, prefs.resolution) }, prefs.resolution, null);
|
|
126
|
+
return { ...row, resolution: decision.resolution, flags: [...new Set([...row.flags, ...decision.flags])] };
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* PURE. #365's relation dedup and closing plan, in memory, against the
|
|
131
|
+
* fixture's prior edges: `known` rows flagged (and so pre-accepted), `overlaps`
|
|
132
|
+
* / `unordered` copied from #353's plan, and closings counted.
|
|
133
|
+
*
|
|
134
|
+
* @stability experimental
|
|
135
|
+
*/
|
|
136
|
+
function applyDedupStagesInMemory(fixture, rows) {
|
|
137
|
+
const schema = (0, ontology_1.computeEffectiveSchema)({ enabledDomains: (0, index_js_1.fixtureDomains)(fixture), userAttributes: [] });
|
|
138
|
+
const prior = fixture.knownRelations.map((r) => {
|
|
139
|
+
const { range, precision } = (0, internals_js_1.proposedRelationRange)(r, 'state');
|
|
140
|
+
return { id: r.id, type: r.type, fromId: r.from, toId: r.to, props: r.props, valid: range, precision, reviewStatus: 'accepted' };
|
|
141
|
+
});
|
|
142
|
+
const entities = new Map();
|
|
143
|
+
for (const row of rows)
|
|
144
|
+
if (row.kind === 'entity')
|
|
145
|
+
entities.set(row.payload.ref, (0, internals_js_1.entityView)(row.payload, row.resolution));
|
|
146
|
+
const stats = { known: 0, closings: 0, overlaps: 0, unordered: 0 };
|
|
147
|
+
const closed = new Set();
|
|
148
|
+
const out = rows.map((row) => {
|
|
149
|
+
if (row.kind !== 'relation')
|
|
150
|
+
return row;
|
|
151
|
+
const p = row.payload;
|
|
152
|
+
const type = schema.relationType(p.type);
|
|
153
|
+
const fromId = (0, internals_js_1.existingId)((0, internals_js_1.resolveEndpoint)(p.from, entities));
|
|
154
|
+
if (!type || !fromId)
|
|
155
|
+
return row;
|
|
156
|
+
const rule = (0, internals_js_1.ruleForEffectiveRelation)(type);
|
|
157
|
+
const to = (0, internals_js_1.resolveEndpoint)(p.to, entities);
|
|
158
|
+
const toId = (0, internals_js_1.existingId)(to);
|
|
159
|
+
if (toId && (0, internals_js_1.relationKnown)(prior, p, fromId, toId, rule).known) {
|
|
160
|
+
stats.known += 1;
|
|
161
|
+
return { ...row, flags: [...new Set([...row.flags, 'known'])] };
|
|
162
|
+
}
|
|
163
|
+
if (!(0, internals_js_1.isExclusiveTemporal)(rule))
|
|
164
|
+
return row;
|
|
165
|
+
const plan = (0, graph_1.planTemporalInsert)(prior, (0, internals_js_1.candidateEdge)(p, fromId, (0, internals_js_1.placeholderId)(to), rule), rule);
|
|
166
|
+
if (plan.action !== 'create')
|
|
167
|
+
return row;
|
|
168
|
+
for (const c of plan.closes)
|
|
169
|
+
if (!closed.has(c.edgeId))
|
|
170
|
+
closed.add(c.edgeId);
|
|
171
|
+
const flags = plan.flags.filter((f) => f === 'overlaps' || f === 'unordered');
|
|
172
|
+
if (flags.includes('overlaps'))
|
|
173
|
+
stats.overlaps += 1;
|
|
174
|
+
if (flags.includes('unordered'))
|
|
175
|
+
stats.unordered += 1;
|
|
176
|
+
return flags.length > 0 ? { ...row, flags: [...new Set([...row.flags, ...flags])] } : row;
|
|
177
|
+
});
|
|
178
|
+
stats.closings = closed.size;
|
|
179
|
+
return { rows: out, stats };
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* The `extract+resolve` runner over `deps.ai`.
|
|
183
|
+
*
|
|
184
|
+
* @stability experimental
|
|
185
|
+
*/
|
|
186
|
+
function createExtractResolveRunner(deps) {
|
|
187
|
+
return {
|
|
188
|
+
name: 'extract+resolve',
|
|
189
|
+
async run(fixture, opts) {
|
|
190
|
+
const extracted = await (0, extract_runner_js_1.runExtraction)(deps.ai, fixture, opts);
|
|
191
|
+
if (!extracted) {
|
|
192
|
+
return { fixtureId: fixture.id, model: opts.model, entities: [], relations: [], items: [], stats: { invalidOutput: 1 } };
|
|
193
|
+
}
|
|
194
|
+
const resolved = resolveRowsInMemory(fixture, extracted.rows);
|
|
195
|
+
const deduped = applyDedupStagesInMemory(fixture, resolved);
|
|
196
|
+
return (0, extract_runner_js_1.precheckToPrediction)(fixture.id, opts.model, deduped.rows, { ...extracted.stats, ...deduped.stats });
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { type ExtractionInput, type ProposedRow } from '../internals.js';
|
|
2
|
+
import type { KvoxAiPort } from '@marinoscar/kvox-api/ports';
|
|
3
|
+
import type { GoldenFixture } from '../../fixtures/index.js';
|
|
4
|
+
import type { KgEvalPrediction } from '../prediction-schema.js';
|
|
5
|
+
import type { KgEvalRunner, KgEvalRunnerDeps, KgEvalRunOptions } from '../runner.js';
|
|
6
|
+
/**
|
|
7
|
+
* The completion ceiling an eval run uses (a production run takes it from policy).
|
|
8
|
+
*
|
|
9
|
+
* @stability experimental
|
|
10
|
+
*/
|
|
11
|
+
export declare const EVAL_MAX_OUTPUT_TOKENS = 16000;
|
|
12
|
+
/**
|
|
13
|
+
* How long one structured call may take before the run gives up on it.
|
|
14
|
+
*
|
|
15
|
+
* @stability experimental
|
|
16
|
+
*/
|
|
17
|
+
export declare const EVAL_TIMEOUT_MS = 180000;
|
|
18
|
+
/**
|
|
19
|
+
* The context window an eval target reports (the port's target carries one; a direct runtime never reads it).
|
|
20
|
+
*
|
|
21
|
+
* @stability experimental
|
|
22
|
+
*/
|
|
23
|
+
export declare const EVAL_CONTEXT_WINDOW_TOKENS = 128000;
|
|
24
|
+
/**
|
|
25
|
+
* The user id an eval call carries (there is no signed-in user).
|
|
26
|
+
*
|
|
27
|
+
* @stability experimental
|
|
28
|
+
*/
|
|
29
|
+
export declare const EVAL_USER_ID = "kg-eval";
|
|
30
|
+
/**
|
|
31
|
+
* The one fixture → input mapping. Pure.
|
|
32
|
+
*
|
|
33
|
+
* @stability experimental
|
|
34
|
+
*/
|
|
35
|
+
export declare function fixtureToInput(fixture: GoldenFixture): ExtractionInput;
|
|
36
|
+
/**
|
|
37
|
+
* Proposal rows (with their pre-check decisions) → a kg:eval prediction. Pure.
|
|
38
|
+
*
|
|
39
|
+
* @stability experimental
|
|
40
|
+
*/
|
|
41
|
+
export declare function rowsToPrediction(fixtureId: string, model: string | null, rows: ProposedRow[], decisions: string[], stats: Record<string, number>): KgEvalPrediction;
|
|
42
|
+
/**
|
|
43
|
+
* The shared half of every runner: one `generateStructured` call and the
|
|
44
|
+
* validator, returning the proposal rows before any pre-check. `null` when the
|
|
45
|
+
* model's answer fails validation (`invalidOutput`). #364's `extract+resolve`
|
|
46
|
+
* runner resolves these rows before the pre-check runs.
|
|
47
|
+
*
|
|
48
|
+
* @stability experimental
|
|
49
|
+
*/
|
|
50
|
+
export declare function runExtraction(ai: Pick<KvoxAiPort, 'generateStructured'>, fixture: GoldenFixture, opts: KgEvalRunOptions): Promise<{
|
|
51
|
+
rows: ProposedRow[];
|
|
52
|
+
stats: Record<string, number>;
|
|
53
|
+
} | null>;
|
|
54
|
+
/**
|
|
55
|
+
* Pre-check the rows with default preferences and turn them into a prediction.
|
|
56
|
+
*
|
|
57
|
+
* @stability experimental
|
|
58
|
+
*/
|
|
59
|
+
export declare function precheckToPrediction(fixtureId: string, model: string, rows: ProposedRow[], stats: Record<string, number>): KgEvalPrediction;
|
|
60
|
+
/**
|
|
61
|
+
* The `extract` runner over `deps.ai`.
|
|
62
|
+
*
|
|
63
|
+
* @stability experimental
|
|
64
|
+
*/
|
|
65
|
+
export declare function createExtractRunner(deps: KgEvalRunnerDeps): KgEvalRunner;
|