@konneal/engine 0.1.0 → 0.1.2
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/README.md +106 -5
- package/dist/admin.d.ts +3 -0
- package/dist/{chunk-MB74PTRM.js → chunk-35ODH64W.js} +23 -4
- package/dist/chunk-CAEHIVG5.js +54 -0
- package/dist/{chunk-WWNCWKKC.js → chunk-EHJEELVB.js} +1 -1
- package/dist/{chunk-WOGQM7DJ.js → chunk-OCNLV7Q7.js} +2 -2
- package/dist/chunk-ROF3Q7UC.js +156 -0
- package/dist/codecs.d.ts +22 -0
- package/dist/context.d.ts +2 -9
- package/dist/modelplane.d.ts +1 -1
- package/dist/pipeline.d.ts +8 -1
- package/dist/profile.gen.d.ts +19 -0
- package/dist/profile2.gen.d.ts +89 -0
- package/dist/prompts/conversational.md +1 -1
- package/dist/prompts/enrichment.md +2 -2
- package/dist/prompts/precision.md +1 -1
- package/dist/prompts/research.md +1 -1
- package/dist/prompts/system.md +3 -3
- package/dist/prompts/understanding.md +4 -4
- package/dist/worker_mcp/src/index.d.ts +10 -0
- package/dist/worker_mcp/src/index.js +153 -0
- package/dist/{config.js → worker_public/src/config.js} +2 -2
- package/dist/{index.js → worker_public/src/index.js} +131 -207
- package/dist/{profile.js → worker_public/src/profile.js} +1 -1
- package/dist/{refusal.js → worker_public/src/refusal.js} +2 -2
- package/dist/worker_public/src/requestScope.js +10 -0
- package/package.json +16 -9
- package/profile/prompts.yaml +11 -0
- package/profile/publisher.yaml +9 -0
- package/profile/retrieval.yaml +4 -0
- package/scripts/gen_profile.mjs +6 -5
- package/workers/worker_internal/src/index.ts +1 -1
- package/workers/worker_mcp/src/index.ts +42 -29
- package/workers/worker_mcp/tsconfig.json +11 -4
- package/workers/worker_public/prompts/conversational.md +1 -1
- package/workers/worker_public/prompts/enrichment.md +2 -2
- package/workers/worker_public/prompts/precision.md +1 -1
- package/workers/worker_public/prompts/research.md +1 -1
- package/workers/worker_public/prompts/system.md +3 -3
- package/workers/worker_public/prompts/understanding.md +4 -4
- package/workers/worker_public/src/admin.ts +15 -3
- package/workers/worker_public/src/ask.ts +13 -10
- package/workers/worker_public/src/bubble.ts +13 -6
- package/workers/worker_public/src/codecs.ts +77 -0
- package/workers/worker_public/src/config.ts +1 -1
- package/workers/worker_public/src/context.ts +6 -26
- package/workers/worker_public/src/graph.ts +2 -3
- package/workers/worker_public/src/index.ts +5 -3
- package/workers/worker_public/src/lib/http.ts +5 -9
- package/workers/worker_public/src/livedata.ts +3 -2
- package/workers/worker_public/src/modelplane.ts +13 -5
- package/workers/worker_public/src/pipeline.ts +28 -13
- package/workers/worker_public/src/profile.gen.ts +23 -4
- package/workers/worker_public/src/profile2.gen.ts +122 -0
- package/workers/worker_public/src/refusal.ts +19 -21
- package/workers/worker_public/src/research.ts +4 -2
- package/workers/worker_public/src/stages/conceptGraph.ts +3 -2
- package/workers/worker_public/src/stages/corpusScope.ts +8 -2
- package/workers/worker_public/src/stages/editionCover.ts +2 -4
- package/workers/worker_public/src/understand.ts +2 -1
- package/dist/chunk-LLWPT2XV.js +0 -49
- package/dist/requestScope.js +0 -10
package/README.md
CHANGED
|
@@ -6,8 +6,109 @@ typed tables/formulas/figures, conformance checking by execution, and
|
|
|
6
6
|
the measurement gates that keep it honest. Konneal builds the databases
|
|
7
7
|
and serves the API; publishers own their content, profile and frontend.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
```
|
|
10
|
+
npm install @konneal/engine
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
BSD-3-Clause. The reference deployment is
|
|
14
|
+
[OIML SMART AI](https://ai.oimlsmart.org).
|
|
15
|
+
|
|
16
|
+
## What a deployment looks like
|
|
17
|
+
|
|
18
|
+
A publisher's worker is ten lines:
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
// workers/worker_public/src/index.ts — the deployment entry
|
|
22
|
+
import worker, { setProfile } from "@konneal/engine";
|
|
23
|
+
import { PROFILE } from "./profile.gen.ts";
|
|
24
|
+
|
|
25
|
+
setProfile(PROFILE);
|
|
26
|
+
|
|
27
|
+
export default worker;
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Everything the publisher owns lives in `profile/*.yaml` — identity,
|
|
31
|
+
datasets, corpora, prompts, thresholds, eval cases. The codegen
|
|
32
|
+
(`gen_profile.mjs`, included) turns the YAML into a committed
|
|
33
|
+
TypeScript module; a drift test keeps both sides honest. The engine
|
|
34
|
+
reads the profile at request time; no publisher fact is hardcoded.
|
|
35
|
+
|
|
36
|
+
The engine also ships the ingest CLI (Python): parse → embed → upsert,
|
|
37
|
+
reading the same profile for its corpora declarations.
|
|
38
|
+
|
|
39
|
+
## The three packages
|
|
40
|
+
|
|
41
|
+
| Package | What it is |
|
|
42
|
+
|---|---|
|
|
43
|
+
| `@konneal/engine` | The serving Worker + the ingest CLI (this package) |
|
|
44
|
+
| `@konneal/client` | The publisher-site contract: wire types, SSE client, citation chips, typed blocks |
|
|
45
|
+
| `@konneal/create-publisher` | `npm create @konneal/publisher` — scaffolds the whole deployment |
|
|
46
|
+
|
|
47
|
+
Start with the scaffolder: `npm create @konneal/publisher my-sdo
|
|
48
|
+
-- --with-site` writes the profile, the worker entry, the Cloudflare
|
|
49
|
+
wiring, the profile codegen, and (with `--with-site`) a minimal
|
|
50
|
+
Astro+Vue frontend consuming `@konneal/client`.
|
|
51
|
+
|
|
52
|
+
## The profile (the publisher's single edit surface)
|
|
53
|
+
|
|
54
|
+
| File | Declares |
|
|
55
|
+
|---|---|
|
|
56
|
+
| `publisher.yaml` | id, name, domains, identity issuer, codec, session cookie, features |
|
|
57
|
+
| `datasets.yaml` | what users can scope to (public vs session-gated), per-corpus prompt notes |
|
|
58
|
+
| `corpora.yaml` | the corpus registry — which corpora exist, which indexes serve them |
|
|
59
|
+
| `sources.yaml` | upstream repos the tooling reads (clean corpus, bibliography, terminology, models) |
|
|
60
|
+
| `ui.yaml` | suggestions, model disclosure, smoke probes |
|
|
61
|
+
| `retrieval.yaml` | steering vocabulary, process-intent notes |
|
|
62
|
+
| `prompts.yaml` | the publisher's prompt voice (identity, refusal, examples) |
|
|
63
|
+
| `evals/` | golden cases, annealment probes, ladder — the promotion-gate data |
|
|
64
|
+
|
|
65
|
+
## Serving architecture
|
|
66
|
+
|
|
67
|
+
- **Retrieval** is a stage registry; the ask path owns composition only
|
|
68
|
+
- **HTTP** is a route table dispatched by both workers
|
|
69
|
+
- Every steering number lives in `THRESHOLDS`; the chunk wire type is
|
|
70
|
+
shared with the Python producer by a contract test
|
|
71
|
+
- **Ports** (`ports/`): ModelRunner, VectorIndex, Kv, Blobs, Runtime —
|
|
72
|
+
the Cloudflare adapters are the only provider-typed module; a purity
|
|
73
|
+
lint enforces it in CI
|
|
74
|
+
- **Publisher purity**: a CI lint fails any publisher string in
|
|
75
|
+
`workers/` outside the codec registry (the identifier grammars)
|
|
76
|
+
- **Feature gates**: `drafts` and `model_plane` are profile-declared,
|
|
77
|
+
off by default
|
|
78
|
+
|
|
79
|
+
## The ingest CLI
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pip install git+https://github.com/konneal/engine.git
|
|
83
|
+
python -m ingest.cli parse # corpora → chunks.jsonl + manifest
|
|
84
|
+
python -m ingest.cli embed # embed via the binding's model
|
|
85
|
+
python -m ingest.cli upsert # push vectors + metadata to Vectorize
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## MCP server
|
|
89
|
+
|
|
90
|
+
The package exports `./mcp` — a ten-line entry per deployment serves
|
|
91
|
+
the publisher's public corpus to MCP clients (agent ecosystems).
|
|
92
|
+
Auth is required: the MCP client presents one of the deployment's API
|
|
93
|
+
keys; the same token rides outbound so spend and quota charge that
|
|
94
|
+
key.
|
|
95
|
+
|
|
96
|
+
## Promotion gates
|
|
97
|
+
|
|
98
|
+
The engine ships the gate harness: a deployment's golden cases and
|
|
99
|
+
annealment probes live in `profile/evals/`; the gate runs them against
|
|
100
|
+
production before any promotion. Answers are witness-checked (the
|
|
101
|
+
answer must contain the expected span, not just score well).
|
|
102
|
+
|
|
103
|
+
## Repository
|
|
104
|
+
|
|
105
|
+
- `workers/worker_public/` — the public-facing Worker
|
|
106
|
+
- `workers/worker_mcp/` — the MCP server Worker
|
|
107
|
+
- `workers/shared/` — the router, chunk wire type, session
|
|
108
|
+
- `ports/` — the provider seam (interfaces + Cloudflare adapters)
|
|
109
|
+
- `profile/` — the engine's fixture profile (a real deployment carries
|
|
110
|
+
its own; this one exists so tests run against declared data)
|
|
111
|
+
- `profile2/` — the reference matrix's second fixture (a different
|
|
112
|
+
publisher shape; CI proves the engine serves declared profiles)
|
|
113
|
+
- `ingest/` — the Python ingest CLI
|
|
114
|
+
- `tests/` — unit suites (plain node, no network)
|
package/dist/admin.d.ts
CHANGED
|
@@ -24,3 +24,6 @@ export declare function handleVectors(env: Env, req: Request): Promise<Response>
|
|
|
24
24
|
export declare function handleJudge(env: Env, req: Request): Promise<Response>;
|
|
25
25
|
export declare function handleCreateKey(env: Env, req: Request): Promise<Response>;
|
|
26
26
|
export declare function handleListKeys(env: Env, req: Request): Promise<Response>;
|
|
27
|
+
/** Revoke an API key (soft: revoked = 1 — the hash row stays for
|
|
28
|
+
* audit; authenticate() already excludes revoked keys). */
|
|
29
|
+
export declare function handleRevokeKey(env: Env, req: Request, id: string): Promise<Response>;
|
|
@@ -7,12 +7,21 @@ var PROFILE = {
|
|
|
7
7
|
"product_name": "Fixture Answers",
|
|
8
8
|
"description": "A minimal publisher profile exercising every declared surface: an open dataset, a permission-gated dataset, production and lane corpora, prompt vars and retrieval vocabulary.",
|
|
9
9
|
"domains": {
|
|
10
|
-
"public": "fixture.example.org"
|
|
10
|
+
"public": "fixture.example.org",
|
|
11
|
+
"origin_suffix": "fixture.example.org"
|
|
11
12
|
},
|
|
12
13
|
"identity": {
|
|
13
14
|
"issuer": "https://id.fixture.example.org"
|
|
14
15
|
},
|
|
15
|
-
"codec": "plain-slug"
|
|
16
|
+
"codec": "plain-slug",
|
|
17
|
+
"session_cookie": "fixture-session",
|
|
18
|
+
"references": {
|
|
19
|
+
"label_prefix": ""
|
|
20
|
+
},
|
|
21
|
+
"features": {
|
|
22
|
+
"drafts": false,
|
|
23
|
+
"model_plane": false
|
|
24
|
+
}
|
|
16
25
|
},
|
|
17
26
|
"datasets": [
|
|
18
27
|
{
|
|
@@ -89,12 +98,22 @@ var PROFILE = {
|
|
|
89
98
|
]
|
|
90
99
|
},
|
|
91
100
|
"retrieval": {
|
|
92
|
-
"process_expansion": " fixture certification system framework application evaluation"
|
|
101
|
+
"process_expansion": " fixture certification system framework application evaluation",
|
|
102
|
+
"process_note": "Retrieval note: these passages come from the fixture certification system documents because they govern application procedures for fixture publications."
|
|
93
103
|
},
|
|
94
104
|
"prompts": {
|
|
95
105
|
"vars": {
|
|
96
106
|
"assistant_identity": "the fixture assistant \u2014 a public service answering questions about the fixture publisher's documents",
|
|
97
|
-
"refusal_sentence": "I don't have information on this in the indexed fixture documents."
|
|
107
|
+
"refusal_sentence": "I don't have information on this in the indexed fixture documents.",
|
|
108
|
+
"account_note_source": "the user's own fixture account",
|
|
109
|
+
"corpus_kind": "a fixture corpus publication",
|
|
110
|
+
"corpus_kind_plural": "fixture corpus publications",
|
|
111
|
+
"cite_example": "FIXTURE 1:2024 \xA72.1",
|
|
112
|
+
"cite_quote_example": 'FIXTURE 1:2024 \xA72.1: "the limit shall not exceed one interval"',
|
|
113
|
+
"parts_example": "FIXTURE 1-1, FIXTURE 1-A",
|
|
114
|
+
"docid_example": "FIXTURE 1-2",
|
|
115
|
+
"spelling_examples": '"f1", "FIXTURE 1"',
|
|
116
|
+
"process_vocab": "the fixture certification system framework"
|
|
98
117
|
}
|
|
99
118
|
}
|
|
100
119
|
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {
|
|
2
|
+
P
|
|
3
|
+
} from "./chunk-35ODH64W.js";
|
|
4
|
+
|
|
5
|
+
// workers/worker_public/src/refusal.ts
|
|
6
|
+
function refusalAnswer() {
|
|
7
|
+
return P().prompts.vars.refusal_sentence;
|
|
8
|
+
}
|
|
9
|
+
function refusalPatterns(publisher) {
|
|
10
|
+
return {
|
|
11
|
+
variant: new RegExp(`^\\s*I don[\u2019']?t have information on .{1,120}? in the indexed ${publisher}(?: \\w+){0,2} (?:publications|passages|documents|corpus)\\.?`, "i"),
|
|
12
|
+
drift: [
|
|
13
|
+
new RegExp(`\\b(can'?t|cannot|couldn'?t|unable)\\b[^.]{0,120}?\\b(indexed )?${publisher}(?: \\w+){0,2} (?:publications|passages|documents|corpus)\\b`, "i"),
|
|
14
|
+
new RegExp(`\\bno real answer to give\\b[^.]{0,120}?\\b${publisher}\\b`, "i"),
|
|
15
|
+
/\b(?:falls|well) outside\b[^.]{0,120}?\b(?:what I can answer|my scope|the scope of)\b/i,
|
|
16
|
+
/\boutside (?:of )?what (?:I|this service) can answer\b/i,
|
|
17
|
+
// "I can't answer that — weather forecasting is outside my scope":
|
|
18
|
+
// requires the refusal verb, so a scope DISCUSSION inside a real answer
|
|
19
|
+
// ("this exemption is outside the scope of R 60") never matches
|
|
20
|
+
/\bI can[’']?t answer\b[^.]{0,100}?\bscope\b/i,
|
|
21
|
+
new RegExp(`^\\s*I don[\u2019']?t have any indexed ${publisher} \\w+(?:s)? (?:covering|about|on)\b`, "im")
|
|
22
|
+
]
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function sentenceStart(answer, i) {
|
|
26
|
+
let s = 0;
|
|
27
|
+
for (const sep of [". ", "! ", "? ", "\n"]) {
|
|
28
|
+
const j = answer.lastIndexOf(sep, i);
|
|
29
|
+
if (j >= 0) s = Math.max(s, j + sep.length);
|
|
30
|
+
}
|
|
31
|
+
return s;
|
|
32
|
+
}
|
|
33
|
+
function sentenceEnd(answer, i) {
|
|
34
|
+
const m = /[.!?\n]/.exec(answer.slice(i));
|
|
35
|
+
return m ? i + m.index + 1 : answer.length;
|
|
36
|
+
}
|
|
37
|
+
function canonicalRefusal(answer) {
|
|
38
|
+
const CANON = refusalAnswer();
|
|
39
|
+
if (answer.includes(CANON)) return answer;
|
|
40
|
+
const { variant: REFUSAL_VARIANT, drift: REFUSAL_DRIFT } = refusalPatterns(P().publisher.name);
|
|
41
|
+
const variant = answer.match(REFUSAL_VARIANT);
|
|
42
|
+
if (variant) return answer.replace(variant[0], CANON);
|
|
43
|
+
for (const drift of REFUSAL_DRIFT) {
|
|
44
|
+
const m = drift.exec(answer);
|
|
45
|
+
if (!m) continue;
|
|
46
|
+
return answer.slice(0, sentenceStart(answer, m.index)) + CANON + answer.slice(sentenceEnd(answer, m.index));
|
|
47
|
+
}
|
|
48
|
+
return answer;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export {
|
|
52
|
+
refusalAnswer,
|
|
53
|
+
canonicalRefusal
|
|
54
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
P
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-35ODH64W.js";
|
|
4
4
|
|
|
5
5
|
// workers/worker_public/src/config.ts
|
|
6
6
|
var MODELS = {
|
|
@@ -159,7 +159,7 @@ function datasetsFor(session) {
|
|
|
159
159
|
label: d.label,
|
|
160
160
|
description: d.description,
|
|
161
161
|
enabled: datasetAllowed(d, session),
|
|
162
|
-
...d.session ? { requires: `the ${d.permission ?? "ai-preview"} permission (
|
|
162
|
+
...d.session ? { requires: `the ${d.permission ?? "ai-preview"} permission (${P().publisher.identity.issuer.replace(/^https?:\/\//, "")})`, authenticated: !!session } : {}
|
|
163
163
|
}));
|
|
164
164
|
}
|
|
165
165
|
function SUGGESTIONS() {
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LIMITS,
|
|
3
|
+
sha256Hex
|
|
4
|
+
} from "./chunk-OCNLV7Q7.js";
|
|
5
|
+
import {
|
|
6
|
+
P
|
|
7
|
+
} from "./chunk-35ODH64W.js";
|
|
8
|
+
|
|
9
|
+
// workers/worker_public/src/bubble.ts
|
|
10
|
+
function isAllowedBubbleOrigin(origin) {
|
|
11
|
+
const d = P().publisher.domains;
|
|
12
|
+
const suffix = d.origin_suffix ?? (d.public ? d.public.replace(/^[^.]+\./, "") : null);
|
|
13
|
+
if (suffix) {
|
|
14
|
+
const host = origin.startsWith("https://") ? origin.slice("https://".length) : "";
|
|
15
|
+
const labels = host.split(".");
|
|
16
|
+
if (host === suffix) return true;
|
|
17
|
+
if (labels.length >= 3 && labels.slice(1).join(".") === suffix) return true;
|
|
18
|
+
}
|
|
19
|
+
if (/^http:\/\/localhost(:\d{1,5})?$/.test(origin)) return true;
|
|
20
|
+
if (/^http:\/\/127\.0\.0\.1(:\d{1,5})?$/.test(origin)) return true;
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
function escapeHtml(s) {
|
|
24
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
25
|
+
}
|
|
26
|
+
function bubbleConfirmPage(opts) {
|
|
27
|
+
const who = escapeHtml(opts.name);
|
|
28
|
+
const host = escapeHtml(new URL(opts.origin).host);
|
|
29
|
+
const jsSafe = (v) => JSON.stringify(v).replace(/</g, "\\u003c");
|
|
30
|
+
const payload = jsSafe({
|
|
31
|
+
type: P().publisher.session_cookie ?? `${P().publisher.id}-session`,
|
|
32
|
+
token: opts.token,
|
|
33
|
+
name: opts.name,
|
|
34
|
+
expiresAt: opts.expiresAt
|
|
35
|
+
});
|
|
36
|
+
return `<!DOCTYPE html>
|
|
37
|
+
<html lang="en">
|
|
38
|
+
<head>
|
|
39
|
+
<meta charset="utf-8" />
|
|
40
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
41
|
+
<meta name="robots" content="noindex" />
|
|
42
|
+
<title>${P().publisher.product_name} \u2014 sign in</title>
|
|
43
|
+
<style>
|
|
44
|
+
:root { color-scheme: light dark; }
|
|
45
|
+
body { font-family: ui-sans-serif, system-ui, sans-serif; margin: 0; padding: 2rem 1.25rem;
|
|
46
|
+
background: #faf6ee; color: #0a1628; line-height: 1.5; }
|
|
47
|
+
main { max-width: 26rem; margin: 0 auto; }
|
|
48
|
+
h1 { font-size: 1.15rem; margin: 0 0 0.75rem; }
|
|
49
|
+
p { margin: 0 0 1rem; font-size: 0.925rem; }
|
|
50
|
+
.who { font-weight: 600; }
|
|
51
|
+
.row { display: flex; gap: 0.75rem; margin-top: 1.25rem; }
|
|
52
|
+
button { flex: 1; min-height: 44px; font: inherit; font-weight: 600; border-radius: 6px; cursor: pointer; }
|
|
53
|
+
.go { background: #004996; color: #fff; border: 1px solid #004996; }
|
|
54
|
+
.no { background: transparent; color: inherit; border: 1px solid #ddd2bd; }
|
|
55
|
+
@media (prefers-color-scheme: dark) {
|
|
56
|
+
body { background: #0a1628; color: #f5efe4; }
|
|
57
|
+
.go { background: #89b4ef; color: #001230; border-color: #89b4ef; }
|
|
58
|
+
.no { border-color: #1f3357; }
|
|
59
|
+
}
|
|
60
|
+
</style>
|
|
61
|
+
</head>
|
|
62
|
+
<body>
|
|
63
|
+
<main>
|
|
64
|
+
<h1>Continue to the ${P().publisher.product_name} assistant?</h1>
|
|
65
|
+
<p>Signed in as <span class="who">${who}</span>. The page at <span class="who">${host}</span>
|
|
66
|
+
asked to connect the assistant to your account, so your conversations sync there.</p>
|
|
67
|
+
<p>The assistant can read the public corpus and your own assistant conversations \u2014 nothing else.</p>
|
|
68
|
+
<div class="row">
|
|
69
|
+
<button type="button" class="no" id="cancel">Cancel</button>
|
|
70
|
+
<button type="button" class="go" id="go">Continue</button>
|
|
71
|
+
</div>
|
|
72
|
+
<p id="done" hidden>You can close this window.</p>
|
|
73
|
+
</main>
|
|
74
|
+
<script>
|
|
75
|
+
var TARGET = ${jsSafe(opts.origin)};
|
|
76
|
+
var PAYLOAD = ${payload};
|
|
77
|
+
var done = document.getElementById("done");
|
|
78
|
+
document.getElementById("cancel").addEventListener("click", function () { window.close(); done.hidden = false; });
|
|
79
|
+
document.getElementById("go").addEventListener("click", function () {
|
|
80
|
+
if (window.opener) {
|
|
81
|
+
window.opener.postMessage(PAYLOAD, TARGET);
|
|
82
|
+
window.close();
|
|
83
|
+
}
|
|
84
|
+
done.hidden = false;
|
|
85
|
+
});
|
|
86
|
+
if (!window.opener) {
|
|
87
|
+
document.querySelector(".row").hidden = true;
|
|
88
|
+
document.querySelector("h1").textContent = "Signed in.";
|
|
89
|
+
done.hidden = false;
|
|
90
|
+
}
|
|
91
|
+
</script>
|
|
92
|
+
</body>
|
|
93
|
+
</html>`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// workers/worker_public/src/lib/http.ts
|
|
97
|
+
var json = (body, status = 200, extra = {}) => new Response(JSON.stringify(body), {
|
|
98
|
+
status,
|
|
99
|
+
headers: { "content-type": "application/json", ...extra }
|
|
100
|
+
});
|
|
101
|
+
var err = (status, code, message) => json({ error: { code, message } }, status);
|
|
102
|
+
function corsHeaders(req) {
|
|
103
|
+
const origin = req.headers.get("origin") ?? "";
|
|
104
|
+
const allowed = isAllowedBubbleOrigin(origin);
|
|
105
|
+
return allowed ? {
|
|
106
|
+
"access-control-allow-origin": origin,
|
|
107
|
+
// PATCH + DELETE: the conversations API speaks them (rename,
|
|
108
|
+
// delete) — the embedded panel preflights cross-origin.
|
|
109
|
+
"access-control-allow-methods": "GET, POST, PATCH, DELETE, OPTIONS",
|
|
110
|
+
"access-control-allow-headers": "authorization, content-type",
|
|
111
|
+
"access-control-max-age": "86400"
|
|
112
|
+
} : {};
|
|
113
|
+
}
|
|
114
|
+
function withCors(res, cors) {
|
|
115
|
+
if (!cors["access-control-allow-origin"]) return res;
|
|
116
|
+
const headers = new Headers(res.headers);
|
|
117
|
+
for (const [k, v] of Object.entries(cors)) headers.set(k, v);
|
|
118
|
+
return new Response(res.body, { status: res.status, statusText: res.statusText, headers });
|
|
119
|
+
}
|
|
120
|
+
async function authenticate(env, req) {
|
|
121
|
+
const auth = req.headers.get("authorization") ?? "";
|
|
122
|
+
const m = auth.match(/^Bearer\s+(.+)$/i);
|
|
123
|
+
if (!m) return null;
|
|
124
|
+
const keyHash = await sha256Hex(m[1].trim());
|
|
125
|
+
const row = await env.DB.prepare(
|
|
126
|
+
"SELECT id, name, day_limit FROM api_keys WHERE key_hash = ?1 AND revoked = 0"
|
|
127
|
+
).bind(keyHash).first();
|
|
128
|
+
return row ?? null;
|
|
129
|
+
}
|
|
130
|
+
async function readJson(req) {
|
|
131
|
+
try {
|
|
132
|
+
const body = await req.json();
|
|
133
|
+
if (!body || typeof body !== "object") return null;
|
|
134
|
+
return body;
|
|
135
|
+
} catch {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function validateQuery(body) {
|
|
140
|
+
const query = typeof body?.query === "string" ? body.query.trim() : "";
|
|
141
|
+
if (!query || query.length > LIMITS.maxInputChars) return null;
|
|
142
|
+
const lang = typeof body?.lang === "string" && /^[a-z]{2}$/.test(body.lang) ? body.lang : void 0;
|
|
143
|
+
return { query, lang };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export {
|
|
147
|
+
isAllowedBubbleOrigin,
|
|
148
|
+
bubbleConfirmPage,
|
|
149
|
+
json,
|
|
150
|
+
err,
|
|
151
|
+
corsHeaders,
|
|
152
|
+
withCors,
|
|
153
|
+
authenticate,
|
|
154
|
+
readJson,
|
|
155
|
+
validateQuery
|
|
156
|
+
};
|
package/dist/codecs.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface DocScope {
|
|
2
|
+
doc_number: string;
|
|
3
|
+
edition?: string;
|
|
4
|
+
label: string;
|
|
5
|
+
}
|
|
6
|
+
export interface RefCodec {
|
|
7
|
+
/** The explicit form: "R 60-1:2021", "urn:…" — null when unparseable. */
|
|
8
|
+
parse(doc: string, edition?: string): DocScope | null;
|
|
9
|
+
/** The gap-tolerant scan over question text — null when nothing names a document. */
|
|
10
|
+
scanQuestion(query: string): DocScope | null;
|
|
11
|
+
/** The graph's node id → document number — null for other shapes. */
|
|
12
|
+
graphDocNumber(nodeId: string): string | null;
|
|
13
|
+
/** A docidentifier's family key ("R-60") for edition steering — null when not of the grammar. */
|
|
14
|
+
familyOf(docidentifier: string): string | null;
|
|
15
|
+
}
|
|
16
|
+
/** OIML's grammar: type letter (R/D/B/G/E) + 1–3 digits, optional part,
|
|
17
|
+
* optional edition year; the URN provenance form; part numbers are
|
|
18
|
+
* significant (R 60-1), the edition is never part of the number. */
|
|
19
|
+
export declare const oimlPubid: RefCodec;
|
|
20
|
+
/** The generic floor: the identifier is whatever string it is. */
|
|
21
|
+
export declare const plainSlug: RefCodec;
|
|
22
|
+
export declare function refCodec(): RefCodec;
|
package/dist/context.d.ts
CHANGED
|
@@ -50,15 +50,8 @@ export declare const NO_CONTEXT: AppliedContext;
|
|
|
50
50
|
* malformed degrades to null (no context), never to a 400 — a context
|
|
51
51
|
* the service can't parse is a context it must not apply. */
|
|
52
52
|
export declare function parseContext(body: any): DeclaredContext | null;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
* an entity's clause provenance spans parts: R 60-1 requirements,
|
|
56
|
-
* R 60-2 tests) */
|
|
57
|
-
doc_number: string;
|
|
58
|
-
edition?: string;
|
|
59
|
-
/** the canonical label form for the echo + the prompt note */
|
|
60
|
-
label: string;
|
|
61
|
-
}
|
|
53
|
+
import { type DocScope } from "./codecs.ts";
|
|
54
|
+
export type { DocScope };
|
|
62
55
|
/** Parse the two reference forms the estate speaks: the URN the SMART
|
|
63
56
|
* models carry as clause provenance (urn:oiml:pub:r:60-1:2021) and the
|
|
64
57
|
* plain docidentifier (OIML R 60-1:2021 / R 60). Part designations
|
package/dist/modelplane.d.ts
CHANGED
|
@@ -58,4 +58,4 @@ export declare function modelEcho(node: BoundModelNode): {
|
|
|
58
58
|
};
|
|
59
59
|
/** The per-corpus guidance note (config.ts's DATASETS pattern — every
|
|
60
60
|
* retrieved model-plane chunk carries it, chip or no chip). */
|
|
61
|
-
export declare
|
|
61
|
+
export declare function modelCorpusNote(): string;
|
package/dist/pipeline.d.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
export { refusalAnswer } from "./refusal";
|
|
2
|
+
/** The interpolation source for every prompt: the profile's declared
|
|
3
|
+
* vars plus the derived publisher tokens. Call sites never build
|
|
4
|
+
* their own var map. */
|
|
5
|
+
export declare function promptVars(extra?: Record<string, string>): Record<string, string>;
|
|
6
|
+
/** Fill {{TOKEN}} placeholders in a prompt data file. Unknown/empty tokens
|
|
7
|
+
* resolve to "" so optional lines vanish cleanly. */
|
|
8
|
+
export declare function fill(template: string, vars: Record<string, string>): string;
|
|
2
9
|
import { QueryFilters } from "./selfquery";
|
|
3
10
|
import type { RetrieveOptions, GlossaryEntry } from "./stages/types";
|
|
4
11
|
export type { ChunkMeta, Hit } from "../../shared/chunk";
|
|
@@ -50,7 +57,7 @@ export declare function citations(hits: Hit[]): {
|
|
|
50
57
|
clause_title: string;
|
|
51
58
|
status: string;
|
|
52
59
|
superseded_by: string | undefined;
|
|
53
|
-
corpus:
|
|
60
|
+
corpus: any;
|
|
54
61
|
url: string | undefined;
|
|
55
62
|
snippet: string;
|
|
56
63
|
score: number;
|
package/dist/profile.gen.d.ts
CHANGED
|
@@ -7,11 +7,20 @@ export declare const PROFILE: {
|
|
|
7
7
|
readonly description: "A minimal publisher profile exercising every declared surface: an open dataset, a permission-gated dataset, production and lane corpora, prompt vars and retrieval vocabulary.";
|
|
8
8
|
readonly domains: {
|
|
9
9
|
readonly public: "fixture.example.org";
|
|
10
|
+
readonly origin_suffix: "fixture.example.org";
|
|
10
11
|
};
|
|
11
12
|
readonly identity: {
|
|
12
13
|
readonly issuer: "https://id.fixture.example.org";
|
|
13
14
|
};
|
|
14
15
|
readonly codec: "plain-slug";
|
|
16
|
+
readonly session_cookie: "fixture-session";
|
|
17
|
+
readonly references: {
|
|
18
|
+
readonly label_prefix: "";
|
|
19
|
+
};
|
|
20
|
+
readonly features: {
|
|
21
|
+
readonly drafts: false;
|
|
22
|
+
readonly model_plane: false;
|
|
23
|
+
};
|
|
15
24
|
};
|
|
16
25
|
readonly datasets: readonly [{
|
|
17
26
|
readonly id: "pub";
|
|
@@ -60,11 +69,21 @@ export declare const PROFILE: {
|
|
|
60
69
|
};
|
|
61
70
|
readonly retrieval: {
|
|
62
71
|
readonly process_expansion: " fixture certification system framework application evaluation";
|
|
72
|
+
readonly process_note: "Retrieval note: these passages come from the fixture certification system documents because they govern application procedures for fixture publications.";
|
|
63
73
|
};
|
|
64
74
|
readonly prompts: {
|
|
65
75
|
readonly vars: {
|
|
66
76
|
readonly assistant_identity: "the fixture assistant — a public service answering questions about the fixture publisher's documents";
|
|
67
77
|
readonly refusal_sentence: "I don't have information on this in the indexed fixture documents.";
|
|
78
|
+
readonly account_note_source: "the user's own fixture account";
|
|
79
|
+
readonly corpus_kind: "a fixture corpus publication";
|
|
80
|
+
readonly corpus_kind_plural: "fixture corpus publications";
|
|
81
|
+
readonly cite_example: "FIXTURE 1:2024 §2.1";
|
|
82
|
+
readonly cite_quote_example: "FIXTURE 1:2024 §2.1: \"the limit shall not exceed one interval\"";
|
|
83
|
+
readonly parts_example: "FIXTURE 1-1, FIXTURE 1-A";
|
|
84
|
+
readonly docid_example: "FIXTURE 1-2";
|
|
85
|
+
readonly spelling_examples: "\"f1\", \"FIXTURE 1\"";
|
|
86
|
+
readonly process_vocab: "the fixture certification system framework";
|
|
68
87
|
};
|
|
69
88
|
};
|
|
70
89
|
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export declare const PROFILE: {
|
|
2
|
+
readonly publisher: {
|
|
3
|
+
readonly id: "atlas";
|
|
4
|
+
readonly name: "Atlas";
|
|
5
|
+
readonly full_name: "The Atlas Standards Institute";
|
|
6
|
+
readonly product_name: "Atlas Answers";
|
|
7
|
+
readonly description: "The second fixture publisher of the reference matrix: a technical-standards institute issuing specifications and errata, with a working-group corpus behind a session permission.";
|
|
8
|
+
readonly domains: {
|
|
9
|
+
readonly public: "answers.atlas.example";
|
|
10
|
+
readonly origin_suffix: "atlas.example";
|
|
11
|
+
};
|
|
12
|
+
readonly identity: {
|
|
13
|
+
readonly issuer: "https://identity.atlas.example";
|
|
14
|
+
};
|
|
15
|
+
readonly codec: "plain-slug";
|
|
16
|
+
readonly session_cookie: "atlas-session";
|
|
17
|
+
readonly references: {
|
|
18
|
+
readonly label_prefix: "ATLAS";
|
|
19
|
+
};
|
|
20
|
+
readonly production: readonly ["spec", "errata", "model"];
|
|
21
|
+
readonly lanes: {
|
|
22
|
+
readonly review: readonly ["review"];
|
|
23
|
+
readonly glossary: readonly ["glossary"];
|
|
24
|
+
};
|
|
25
|
+
readonly features: {
|
|
26
|
+
readonly drafts: false;
|
|
27
|
+
readonly model_plane: false;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
readonly datasets: readonly [{
|
|
31
|
+
readonly id: "spec";
|
|
32
|
+
readonly label: "Atlas Specifications";
|
|
33
|
+
readonly description: "The institute's published specifications and errata";
|
|
34
|
+
readonly corpora: readonly ["spec", "errata"];
|
|
35
|
+
readonly note: "Some passages come from the Atlas errata corpus — cite them the same way as every other passage.";
|
|
36
|
+
}, {
|
|
37
|
+
readonly id: "wg";
|
|
38
|
+
readonly label: "Working-group corpus";
|
|
39
|
+
readonly description: "An access-restricted corpus proving the permission gate";
|
|
40
|
+
readonly session: true;
|
|
41
|
+
readonly permission: "committee";
|
|
42
|
+
readonly corpora: readonly ["wg"];
|
|
43
|
+
}];
|
|
44
|
+
readonly corpora: {
|
|
45
|
+
readonly production: readonly ["spec", "errata", "model"];
|
|
46
|
+
readonly lanes: {
|
|
47
|
+
readonly review: readonly ["review"];
|
|
48
|
+
readonly glossary: readonly ["glossary"];
|
|
49
|
+
};
|
|
50
|
+
readonly corpora: {
|
|
51
|
+
readonly spec: {
|
|
52
|
+
readonly repo: "fixtures/matrix2";
|
|
53
|
+
readonly note: "the second fixture corpus";
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
readonly bibliography: {};
|
|
57
|
+
readonly terminology: {};
|
|
58
|
+
readonly models: {};
|
|
59
|
+
};
|
|
60
|
+
readonly sources: {
|
|
61
|
+
readonly corpora: {};
|
|
62
|
+
readonly bibliography: {};
|
|
63
|
+
readonly terminology: {};
|
|
64
|
+
readonly models: {};
|
|
65
|
+
};
|
|
66
|
+
readonly ui: {
|
|
67
|
+
readonly suggestions: readonly ["What does ATLAS 12 specify?", "Which errata are open?"];
|
|
68
|
+
readonly models_disclosure: readonly [{
|
|
69
|
+
readonly role: "Answers";
|
|
70
|
+
readonly model: "atlas-answer-model";
|
|
71
|
+
}];
|
|
72
|
+
readonly smoke: readonly [{
|
|
73
|
+
readonly label: "sanity";
|
|
74
|
+
readonly query: "What does ATLAS 12 specify?";
|
|
75
|
+
readonly expect: "ATLAS";
|
|
76
|
+
}];
|
|
77
|
+
};
|
|
78
|
+
readonly retrieval: {
|
|
79
|
+
readonly process_expansion: " atlas review procedure errata committee specification";
|
|
80
|
+
readonly process_note: "Retrieval note: these passages come from the Atlas review-procedure documents because they govern the institute's publication process.";
|
|
81
|
+
};
|
|
82
|
+
readonly prompts: {
|
|
83
|
+
readonly vars: {
|
|
84
|
+
readonly assistant_identity: "the Atlas Answers assistant — a public service answering questions about the Atlas Standards Institute's specifications";
|
|
85
|
+
readonly refusal_sentence: "I don't have information on this in the indexed Atlas specifications.";
|
|
86
|
+
readonly account_note_source: "the user's own Atlas Answers account";
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
You are
|
|
1
|
+
You are {{ASSISTANT_IDENTITY}}.
|
|
2
2
|
This turn is conversational — about you, this service, a greeting or small talk — NOT a knowledge question, so there are no context passages.
|
|
3
3
|
Answer naturally in first person, briefly and warmly, in the language of the user's message. Do not cite sources for this turn and never refuse it.
|
|
4
4
|
Facts about this service you may speak from:
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
You write a retrieval context for a passage from
|
|
1
|
+
You write a retrieval context for a passage from {{CORPUS_KIND}}. The context is prepended to the passage before embedding so a semantic search can locate the passage even when the query uses different vocabulary than the passage itself.
|
|
2
2
|
|
|
3
|
-
Write ONE concise sentence (at most 40 words) that situates the passage: name the publication by its exact
|
|
3
|
+
Write ONE concise sentence (at most 40 words) that situates the passage: name the publication by its exact {{PUBLISHER_NAME}} identifier (including part or annex when applicable) and what the passage covers — paraphrasing the topic in words DIFFERENT from the passage's own. Do not copy the passage verbatim, do not add facts that are not derivable from the passage or its header, do not answer or explain the content. Reply with the context sentence only — no quotes, no preamble.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
You judge CONTEXT PRECISION for a retrieval system over
|
|
1
|
+
You judge CONTEXT PRECISION for a retrieval system over {{PUBLISHER_NAME}} publications. Given the question and the ranked passages (in the order they were presented), score the fraction of passages that contain material USEFUL for answering the question: 1.0 = all useful; 0.5 = half; 0.0 = none. Judge each passage on its own content, not its rank. Reply with ONLY: {"score": 0.0-1.0}
|
package/dist/prompts/research.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
You are a sufficiency judge for a research loop over
|
|
1
|
+
You are a sufficiency judge for a research loop over {{PUBLISHER_NAME}} publications. Given the research question and the passages collected so far (across iterations), decide whether the collected evidence is SUFFICIENT to write a complete, well-grounded answer.
|
|
2
2
|
|
|
3
3
|
Reply with ONLY a JSON object:
|
|
4
4
|
{"sufficient": true|false, "missing": "short description of what is still missing (empty string when sufficient)"}
|