@hasna/knowledge 0.2.26 → 0.2.28
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 +61 -0
- package/bin/open-knowledge-mcp.js +85 -9
- package/bin/open-knowledge.js +86 -86
- package/dist/agent.d.ts +35 -0
- package/dist/artifact-store.d.ts +63 -0
- package/dist/auth.d.ts +35 -0
- package/dist/embeddings.d.ts +77 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +5709 -0
- package/dist/knowledge-db.d.ts +27 -0
- package/dist/manifest-ingest.d.ts +35 -0
- package/dist/outbox-consume.d.ts +25 -0
- package/dist/provenance.d.ts +50 -0
- package/dist/providers.d.ts +89 -0
- package/dist/reindex.d.ts +37 -0
- package/dist/remote-client.d.ts +108 -0
- package/dist/retrieval.d.ts +71 -0
- package/dist/safety.d.ts +70 -0
- package/dist/sdk.d.ts +72 -0
- package/dist/search.d.ts +65 -0
- package/dist/service.d.ts +117 -0
- package/dist/source-ingest.d.ts +18 -0
- package/dist/source-ref.d.ts +30 -0
- package/dist/source-resolver.d.ts +92 -0
- package/dist/storage-contract.d.ts +106 -0
- package/dist/web-search.d.ts +40 -0
- package/dist/wiki-compiler.d.ts +67 -0
- package/dist/wiki-layout.d.ts +23 -0
- package/dist/workspace.d.ts +111 -0
- package/docs/architecture/ai-native-knowledge-base.md +24 -0
- package/docs/architecture/hosted-wrapper-responsibilities.md +8 -0
- package/docs/canonical-secrets-bootstrap-2026-06-08.md +127 -0
- package/package.json +15 -7
- package/src/agent.ts +0 -367
- package/src/artifact-store.ts +0 -184
- package/src/auth.ts +0 -123
- package/src/cli.ts +0 -1181
- package/src/embeddings.ts +0 -516
- package/src/knowledge-db.ts +0 -354
- package/src/manifest-ingest.ts +0 -515
- package/src/mcp-http.js +0 -110
- package/src/mcp.js +0 -1503
- package/src/outbox-consume.ts +0 -463
- package/src/provenance.ts +0 -93
- package/src/providers.ts +0 -308
- package/src/reindex.ts +0 -260
- package/src/remote-client.ts +0 -268
- package/src/retrieval.ts +0 -326
- package/src/safety.ts +0 -265
- package/src/schema.js +0 -25
- package/src/search.ts +0 -510
- package/src/service.ts +0 -432
- package/src/source-ingest.ts +0 -268
- package/src/source-ref.ts +0 -104
- package/src/source-resolver.ts +0 -436
- package/src/storage-contract.ts +0 -293
- package/src/store.ts +0 -113
- package/src/web-search.ts +0 -330
- package/src/wiki-compiler.ts +0 -711
- package/src/wiki-layout.ts +0 -251
- package/src/workspace.ts +0 -213
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Canonical Secrets Bootstrap Evidence: 2026-06-08
|
|
2
|
+
|
|
3
|
+
Scope: canonical Hasna XYZ app secret paths in account `hasna-xyz-infra`
|
|
4
|
+
(`789877399345`), region `us-east-1`, mirrored into the local `spark02`
|
|
5
|
+
`secrets` vault.
|
|
6
|
+
|
|
7
|
+
This note records names and migration intent only. It intentionally does not
|
|
8
|
+
include secret payloads, passwords, API keys, connection strings, or `.env`
|
|
9
|
+
contents.
|
|
10
|
+
|
|
11
|
+
## Ownership Rule
|
|
12
|
+
|
|
13
|
+
App-owned runtime/config secrets use:
|
|
14
|
+
|
|
15
|
+
```txt
|
|
16
|
+
hasna/xyz/{app_type}/{app}/prod/{component}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Shared infra/admin pointers use an infra-owned path:
|
|
20
|
+
|
|
21
|
+
```txt
|
|
22
|
+
hasna/xyz/infra/{resource_group}/prod/{component}/{role}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Legacy master credentials copied for migration are suffixed with
|
|
26
|
+
`legacy-master`. They are deprecation inputs for migration jobs, not the clean
|
|
27
|
+
runtime secret names new app code should read.
|
|
28
|
+
|
|
29
|
+
## Open Files
|
|
30
|
+
|
|
31
|
+
Created or verified in AWS Secrets Manager and the local vault:
|
|
32
|
+
|
|
33
|
+
```txt
|
|
34
|
+
hasna/xyz/opensource/files/prod/env
|
|
35
|
+
hasna/xyz/opensource/files/prod/aws
|
|
36
|
+
hasna/xyz/opensource/files/prod/s3
|
|
37
|
+
hasna/xyz/opensource/files/prod/rds
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Meaning:
|
|
41
|
+
|
|
42
|
+
- `env`: app environment configuration.
|
|
43
|
+
- `aws`: AWS account/profile/region and related app config metadata.
|
|
44
|
+
- `s3`: canonical app storage bucket metadata for
|
|
45
|
+
`hasna-xyz-opensource-files-prod`.
|
|
46
|
+
- `rds`: app runtime database connection fields for database `files` and role
|
|
47
|
+
`files_app`.
|
|
48
|
+
|
|
49
|
+
The `aws` and `s3` entries are metadata-only. They do not contain access keys or
|
|
50
|
+
tokens.
|
|
51
|
+
|
|
52
|
+
## Open Knowledge
|
|
53
|
+
|
|
54
|
+
Created or verified in AWS Secrets Manager and the local vault:
|
|
55
|
+
|
|
56
|
+
```txt
|
|
57
|
+
hasna/xyz/opensource/knowledge/prod/env
|
|
58
|
+
hasna/xyz/opensource/knowledge/prod/aws
|
|
59
|
+
hasna/xyz/opensource/knowledge/prod/s3
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Meaning:
|
|
63
|
+
|
|
64
|
+
- `env`: open-knowledge production app config metadata.
|
|
65
|
+
- `aws`: AWS account/profile/region and related app config metadata.
|
|
66
|
+
- `s3`: canonical app storage bucket metadata for
|
|
67
|
+
`hasna-xyz-opensource-knowledge-prod`.
|
|
68
|
+
|
|
69
|
+
No `hasna/xyz/opensource/knowledge/prod/rds` secret was created in this pass.
|
|
70
|
+
The OSS package is local-first and currently uses SQLite for local knowledge
|
|
71
|
+
state. If a hosted wrapper later provisions an app database, the app-owned
|
|
72
|
+
runtime secret should use:
|
|
73
|
+
|
|
74
|
+
```txt
|
|
75
|
+
hasna/xyz/opensource/knowledge/prod/rds
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Legacy Secret Mapping
|
|
79
|
+
|
|
80
|
+
Mapped legacy AWS Secrets Manager names to canonical ownership paths:
|
|
81
|
+
|
|
82
|
+
| Legacy name | Canonical path | Use |
|
|
83
|
+
| --- | --- | --- |
|
|
84
|
+
| `prod/microservice/rds/master` | `hasna/xyz/opensource/microservices/prod/rds/legacy-master` | Migration-only legacy master alias. |
|
|
85
|
+
| `prod/connect/rds/master` | `hasna/xyz/opensource/connectors/prod/rds/legacy-master` | Migration-only legacy master alias. |
|
|
86
|
+
| `internalapps/prod/rds/master` | `hasna/xyz/infra/apps/prod/postgres/legacy-internalapps-master` | Migration-only legacy shared/admin alias. |
|
|
87
|
+
| `internalapps/prod/iapp-news/env` | `hasna/xyz/internalapp/news/prod/env` | Canonical app env path for internalapp `news`. |
|
|
88
|
+
|
|
89
|
+
The three RDS aliases preserve old master credential payloads under explicit
|
|
90
|
+
legacy names so migration jobs can read them without relying on noncanonical
|
|
91
|
+
paths. They should not be used as the final runtime credentials for new app
|
|
92
|
+
code. Clean app runtime database credentials should be provisioned under
|
|
93
|
+
app-owned paths such as:
|
|
94
|
+
|
|
95
|
+
```txt
|
|
96
|
+
hasna/xyz/opensource/files/prod/rds
|
|
97
|
+
hasna/xyz/internalapp/news/prod/rds
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The shared canonical Postgres admin pointer remains:
|
|
101
|
+
|
|
102
|
+
```txt
|
|
103
|
+
hasna/xyz/infra/apps/prod/postgres/master
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Verification
|
|
107
|
+
|
|
108
|
+
AWS Secrets Manager name-only verification returned these canonical entries:
|
|
109
|
+
|
|
110
|
+
```txt
|
|
111
|
+
hasna/xyz/infra/apps/prod/postgres/legacy-internalapps-master
|
|
112
|
+
hasna/xyz/infra/apps/prod/postgres/master
|
|
113
|
+
hasna/xyz/internalapp/news/prod/env
|
|
114
|
+
hasna/xyz/opensource/connectors/prod/rds/legacy-master
|
|
115
|
+
hasna/xyz/opensource/files/prod/aws
|
|
116
|
+
hasna/xyz/opensource/files/prod/env
|
|
117
|
+
hasna/xyz/opensource/files/prod/rds
|
|
118
|
+
hasna/xyz/opensource/files/prod/s3
|
|
119
|
+
hasna/xyz/opensource/knowledge/prod/aws
|
|
120
|
+
hasna/xyz/opensource/knowledge/prod/env
|
|
121
|
+
hasna/xyz/opensource/knowledge/prod/s3
|
|
122
|
+
hasna/xyz/opensource/microservices/prod/rds/legacy-master
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Local `secrets list` verification returned the same names with redacted values.
|
|
126
|
+
|
|
127
|
+
No secret values were printed during creation or verification.
|
package/package.json
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/knowledge",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.28",
|
|
4
4
|
"description": "Agent-friendly local knowledge CLI with JSON output, pagination, and safe destructive actions",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"import": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"main": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
6
14
|
"bin": {
|
|
7
15
|
"knowledge": "bin/open-knowledge.js",
|
|
8
16
|
"open-knowledge": "bin/open-knowledge.js",
|
|
@@ -10,7 +18,7 @@
|
|
|
10
18
|
},
|
|
11
19
|
"files": [
|
|
12
20
|
"bin",
|
|
13
|
-
"
|
|
21
|
+
"dist",
|
|
14
22
|
"docs",
|
|
15
23
|
"LICENSE",
|
|
16
24
|
"README.md"
|
|
@@ -18,9 +26,8 @@
|
|
|
18
26
|
"scripts": {
|
|
19
27
|
"test": "bun test",
|
|
20
28
|
"test:cli": "bun test tests/cli.test.ts",
|
|
21
|
-
"build": "bun build --target=bun --outfile=bin/open-knowledge.js --minify --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/cli.ts && bun build --target=bun --outfile=bin/open-knowledge-mcp.js --external @modelcontextprotocol/sdk --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/mcp.js",
|
|
22
|
-
"prepublishOnly": "bun run build"
|
|
23
|
-
"postinstall": "bun run build"
|
|
29
|
+
"build": "rm -rf dist && bun build --target=bun --outfile=bin/open-knowledge.js --minify --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/cli.ts && bun build --target=bun --outfile=bin/open-knowledge-mcp.js --external @modelcontextprotocol/sdk --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/mcp.js && bun build ./src/index.ts --outdir ./dist --target bun --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek && bunx tsc -p tsconfig.build.json",
|
|
30
|
+
"prepublishOnly": "bun run build"
|
|
24
31
|
},
|
|
25
32
|
"keywords": [
|
|
26
33
|
"knowledge",
|
|
@@ -49,12 +56,13 @@
|
|
|
49
56
|
"node": ">=18"
|
|
50
57
|
},
|
|
51
58
|
"dependencies": {
|
|
52
|
-
"@aws-sdk/client-s3": "^3.1063.0",
|
|
53
|
-
"@aws-sdk/credential-providers": "^3.1063.0",
|
|
54
59
|
"@ai-sdk/anthropic": "^3.0.81",
|
|
55
60
|
"@ai-sdk/deepseek": "^2.0.35",
|
|
56
61
|
"@ai-sdk/openai": "^3.0.68",
|
|
62
|
+
"@aws-sdk/client-s3": "^3.1063.0",
|
|
63
|
+
"@aws-sdk/credential-providers": "^3.1063.0",
|
|
57
64
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
65
|
+
"@types/json-schema": "^7.0.15",
|
|
58
66
|
"ai": "^6.0.197",
|
|
59
67
|
"zod": "^4.3.6"
|
|
60
68
|
},
|
package/src/agent.ts
DELETED
|
@@ -1,367 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { migrateKnowledgeDb, openKnowledgeDb } from './knowledge-db';
|
|
3
|
-
import { languageModelFor, normalizeAiSdkUsage, parseModelRef, recordProviderUsage, resolveModelRef } from './providers';
|
|
4
|
-
import { retrieveKnowledgeContext, type KnowledgeContextPack, type RetrievalOptions } from './retrieval';
|
|
5
|
-
import type { KnowledgeConfig } from './workspace';
|
|
6
|
-
|
|
7
|
-
export interface KnowledgePromptOptions extends Omit<RetrievalOptions, 'query'> {
|
|
8
|
-
prompt: string;
|
|
9
|
-
generate?: boolean;
|
|
10
|
-
approveWrite?: boolean;
|
|
11
|
-
now?: Date;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface KnowledgePromptResult {
|
|
15
|
-
run_id: string;
|
|
16
|
-
prompt: string;
|
|
17
|
-
generated: boolean;
|
|
18
|
-
provider: string;
|
|
19
|
-
model: string;
|
|
20
|
-
answer: string;
|
|
21
|
-
context: KnowledgeContextPack;
|
|
22
|
-
citations: KnowledgeContextPack['citations'];
|
|
23
|
-
proposed_wiki_updates: Array<{
|
|
24
|
-
kind: 'answer_note';
|
|
25
|
-
title: string;
|
|
26
|
-
citations: string[];
|
|
27
|
-
requires_approval: boolean;
|
|
28
|
-
}>;
|
|
29
|
-
write_policy: {
|
|
30
|
-
approved: boolean;
|
|
31
|
-
durable_writes_performed: false;
|
|
32
|
-
reason: string;
|
|
33
|
-
};
|
|
34
|
-
usage: {
|
|
35
|
-
input_tokens: number;
|
|
36
|
-
output_tokens: number;
|
|
37
|
-
cost_usd: number;
|
|
38
|
-
};
|
|
39
|
-
warnings: string[];
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function estimateTokens(text: string): number {
|
|
43
|
-
const words = text.trim().split(/\s+/).filter(Boolean).length;
|
|
44
|
-
return Math.max(1, Math.ceil(words * 1.25));
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function citationLabel(index: number): string {
|
|
48
|
-
return `C${index + 1}`;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function localAnswer(prompt: string, context: KnowledgeContextPack): string {
|
|
52
|
-
if (context.excerpts.length === 0) {
|
|
53
|
-
return `No indexed knowledge matched the prompt: ${prompt}`;
|
|
54
|
-
}
|
|
55
|
-
const lines = [
|
|
56
|
-
`Found ${context.excerpts.length} relevant knowledge excerpt(s) for: ${prompt}`,
|
|
57
|
-
'',
|
|
58
|
-
...context.excerpts.slice(0, 5).map((excerpt, index) => {
|
|
59
|
-
const citation = context.citations.find((entry) => entry.id === excerpt.citation_id);
|
|
60
|
-
const ref = citation?.source_ref ?? citation?.source_uri ?? citation?.artifact_path ?? citation?.artifact_uri ?? 'unknown source';
|
|
61
|
-
return `[${citationLabel(index)}] ${excerpt.text} (${ref})`;
|
|
62
|
-
}),
|
|
63
|
-
];
|
|
64
|
-
return lines.join('\n');
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function promptForModel(prompt: string, context: KnowledgeContextPack): string {
|
|
68
|
-
const citations = context.citations.map((citation, index) => ({
|
|
69
|
-
id: citationLabel(index),
|
|
70
|
-
source_ref: citation.source_ref,
|
|
71
|
-
source_uri: citation.source_uri,
|
|
72
|
-
artifact_path: citation.artifact_path,
|
|
73
|
-
revision: citation.revision,
|
|
74
|
-
hash: citation.hash,
|
|
75
|
-
quote: citation.quote,
|
|
76
|
-
}));
|
|
77
|
-
const excerpts = context.excerpts.map((excerpt, index) => ({
|
|
78
|
-
id: citationLabel(index),
|
|
79
|
-
kind: excerpt.kind,
|
|
80
|
-
text: excerpt.text,
|
|
81
|
-
score: excerpt.score,
|
|
82
|
-
}));
|
|
83
|
-
return [
|
|
84
|
-
`Prompt: ${prompt}`,
|
|
85
|
-
'',
|
|
86
|
-
'Use only the provided context. Cite claims with citation ids like [C1]. If context is insufficient, say what is missing.',
|
|
87
|
-
'',
|
|
88
|
-
`Context excerpts:\n${JSON.stringify(excerpts, null, 2)}`,
|
|
89
|
-
'',
|
|
90
|
-
`Citations:\n${JSON.stringify(citations, null, 2)}`,
|
|
91
|
-
].join('\n');
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function proposedUpdates(prompt: string, context: KnowledgeContextPack): KnowledgePromptResult['proposed_wiki_updates'] {
|
|
95
|
-
if (context.citations.length === 0) return [];
|
|
96
|
-
return [{
|
|
97
|
-
kind: 'answer_note',
|
|
98
|
-
title: prompt.length > 80 ? `${prompt.slice(0, 77)}...` : prompt,
|
|
99
|
-
citations: context.citations.map((citation) => citation.id),
|
|
100
|
-
requires_approval: true,
|
|
101
|
-
}];
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function insertRun(dbPath: string, input: {
|
|
105
|
-
runId: string;
|
|
106
|
-
prompt: string;
|
|
107
|
-
status: string;
|
|
108
|
-
provider: string;
|
|
109
|
-
model: string;
|
|
110
|
-
metadata: Record<string, unknown>;
|
|
111
|
-
now: string;
|
|
112
|
-
}): void {
|
|
113
|
-
const db = openKnowledgeDb(dbPath);
|
|
114
|
-
try {
|
|
115
|
-
db.run(
|
|
116
|
-
`INSERT INTO runs (id, type, prompt, status, provider, model, metadata_json, created_at, updated_at)
|
|
117
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
118
|
-
[
|
|
119
|
-
input.runId,
|
|
120
|
-
'knowledge-prompt',
|
|
121
|
-
input.prompt,
|
|
122
|
-
input.status,
|
|
123
|
-
input.provider,
|
|
124
|
-
input.model,
|
|
125
|
-
JSON.stringify(input.metadata),
|
|
126
|
-
input.now,
|
|
127
|
-
input.now,
|
|
128
|
-
],
|
|
129
|
-
);
|
|
130
|
-
} finally {
|
|
131
|
-
db.close();
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function addRunEvent(dbPath: string, input: {
|
|
136
|
-
runId: string;
|
|
137
|
-
level: 'info' | 'warn' | 'error';
|
|
138
|
-
event: string;
|
|
139
|
-
metadata: Record<string, unknown>;
|
|
140
|
-
now: string;
|
|
141
|
-
}): void {
|
|
142
|
-
const db = openKnowledgeDb(dbPath);
|
|
143
|
-
try {
|
|
144
|
-
db.run(
|
|
145
|
-
`INSERT INTO run_events (id, run_id, level, event, metadata_json, created_at)
|
|
146
|
-
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
147
|
-
[
|
|
148
|
-
`evt_${randomUUID()}`,
|
|
149
|
-
input.runId,
|
|
150
|
-
input.level,
|
|
151
|
-
input.event,
|
|
152
|
-
JSON.stringify(input.metadata),
|
|
153
|
-
input.now,
|
|
154
|
-
],
|
|
155
|
-
);
|
|
156
|
-
} finally {
|
|
157
|
-
db.close();
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function updateRun(dbPath: string, input: {
|
|
162
|
-
runId: string;
|
|
163
|
-
status: string;
|
|
164
|
-
provider: string;
|
|
165
|
-
model: string;
|
|
166
|
-
metadata: Record<string, unknown>;
|
|
167
|
-
now: string;
|
|
168
|
-
}): void {
|
|
169
|
-
const db = openKnowledgeDb(dbPath);
|
|
170
|
-
try {
|
|
171
|
-
db.run(
|
|
172
|
-
`UPDATE runs
|
|
173
|
-
SET status = ?, provider = ?, model = ?, metadata_json = ?, updated_at = ?
|
|
174
|
-
WHERE id = ?`,
|
|
175
|
-
[
|
|
176
|
-
input.status,
|
|
177
|
-
input.provider,
|
|
178
|
-
input.model,
|
|
179
|
-
JSON.stringify(input.metadata),
|
|
180
|
-
input.now,
|
|
181
|
-
input.runId,
|
|
182
|
-
],
|
|
183
|
-
);
|
|
184
|
-
} finally {
|
|
185
|
-
db.close();
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function recordUsage(dbPath: string, runId: string, usage: KnowledgePromptResult['usage'], provider: string, model: string, now: string, metadata: Record<string, unknown> = {}): void {
|
|
190
|
-
const db = openKnowledgeDb(dbPath);
|
|
191
|
-
try {
|
|
192
|
-
recordProviderUsage(db, {
|
|
193
|
-
run_id: runId,
|
|
194
|
-
provider,
|
|
195
|
-
model,
|
|
196
|
-
input_tokens: usage.input_tokens,
|
|
197
|
-
output_tokens: usage.output_tokens,
|
|
198
|
-
cost_usd: usage.cost_usd,
|
|
199
|
-
metadata,
|
|
200
|
-
created_at: now,
|
|
201
|
-
});
|
|
202
|
-
} finally {
|
|
203
|
-
db.close();
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
export async function runKnowledgePrompt(options: KnowledgePromptOptions): Promise<KnowledgePromptResult> {
|
|
208
|
-
const prompt = options.prompt.trim();
|
|
209
|
-
if (!prompt) throw new Error('Knowledge prompt is required.');
|
|
210
|
-
const now = (options.now ?? new Date()).toISOString();
|
|
211
|
-
const runId = `run_${randomUUID()}`;
|
|
212
|
-
const modelRef = resolveModelRef(options.modelRef ?? 'default', options.config);
|
|
213
|
-
const parsed = parseModelRef(modelRef);
|
|
214
|
-
|
|
215
|
-
migrateKnowledgeDb(options.dbPath);
|
|
216
|
-
insertRun(options.dbPath, {
|
|
217
|
-
runId,
|
|
218
|
-
prompt,
|
|
219
|
-
status: options.generate ? 'running' : 'dry_run',
|
|
220
|
-
provider: options.generate ? parsed.provider : 'local',
|
|
221
|
-
model: options.generate ? parsed.model : 'context-draft',
|
|
222
|
-
metadata: {
|
|
223
|
-
semantic: options.semantic === true || options.fake === true || Boolean(options.modelRef),
|
|
224
|
-
approve_write: options.approveWrite === true,
|
|
225
|
-
generated: options.generate === true,
|
|
226
|
-
},
|
|
227
|
-
now,
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
const { prompt: _prompt, generate: _generate, approveWrite: _approveWrite, now: _now, ...retrievalOptions } = options;
|
|
231
|
-
const context = await retrieveKnowledgeContext({
|
|
232
|
-
...retrievalOptions,
|
|
233
|
-
query: prompt,
|
|
234
|
-
});
|
|
235
|
-
addRunEvent(options.dbPath, {
|
|
236
|
-
runId,
|
|
237
|
-
level: 'info',
|
|
238
|
-
event: 'context_retrieved',
|
|
239
|
-
metadata: {
|
|
240
|
-
results: context.results.length,
|
|
241
|
-
citations: context.citations.length,
|
|
242
|
-
warnings: context.warnings,
|
|
243
|
-
},
|
|
244
|
-
now,
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
let answer = localAnswer(prompt, context);
|
|
248
|
-
let generated = false;
|
|
249
|
-
let provider = 'local';
|
|
250
|
-
let model = 'context-draft';
|
|
251
|
-
let usage = {
|
|
252
|
-
input_tokens: estimateTokens(prompt) + context.excerpts.reduce((sum, excerpt) => sum + estimateTokens(excerpt.text), 0),
|
|
253
|
-
output_tokens: estimateTokens(answer),
|
|
254
|
-
cost_usd: 0,
|
|
255
|
-
};
|
|
256
|
-
const warnings = [...context.warnings];
|
|
257
|
-
|
|
258
|
-
if (options.generate) {
|
|
259
|
-
try {
|
|
260
|
-
if (options.fake) {
|
|
261
|
-
generated = true;
|
|
262
|
-
provider = parsed.provider;
|
|
263
|
-
model = parsed.model;
|
|
264
|
-
answer = `Fake generated answer for: ${prompt}\n\n${answer}`;
|
|
265
|
-
} else {
|
|
266
|
-
const { generateText } = await import('ai');
|
|
267
|
-
const languageModel = await languageModelFor(modelRef, {
|
|
268
|
-
config: options.config,
|
|
269
|
-
env: options.env,
|
|
270
|
-
});
|
|
271
|
-
const result = await generateText({
|
|
272
|
-
model: languageModel as never,
|
|
273
|
-
system: 'You answer company knowledge-base prompts using only provided context and citation ids.',
|
|
274
|
-
prompt: promptForModel(prompt, context),
|
|
275
|
-
});
|
|
276
|
-
generated = true;
|
|
277
|
-
provider = parsed.provider;
|
|
278
|
-
model = parsed.model;
|
|
279
|
-
answer = result.text;
|
|
280
|
-
const normalized = normalizeAiSdkUsage({
|
|
281
|
-
provider,
|
|
282
|
-
model,
|
|
283
|
-
usage: result.usage as Record<string, unknown> | undefined,
|
|
284
|
-
providerMetadata: result.providerMetadata as Record<string, unknown> | undefined,
|
|
285
|
-
});
|
|
286
|
-
usage = {
|
|
287
|
-
input_tokens: normalized.input_tokens,
|
|
288
|
-
output_tokens: normalized.output_tokens,
|
|
289
|
-
cost_usd: normalized.cost_usd,
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
} catch (error) {
|
|
293
|
-
addRunEvent(options.dbPath, {
|
|
294
|
-
runId,
|
|
295
|
-
level: 'error',
|
|
296
|
-
event: 'answer_generation_failed',
|
|
297
|
-
metadata: { message: error instanceof Error ? error.message : String(error) },
|
|
298
|
-
now,
|
|
299
|
-
});
|
|
300
|
-
updateRun(options.dbPath, {
|
|
301
|
-
runId,
|
|
302
|
-
status: 'failed',
|
|
303
|
-
provider: parsed.provider,
|
|
304
|
-
model: parsed.model,
|
|
305
|
-
metadata: {
|
|
306
|
-
generated: false,
|
|
307
|
-
error: error instanceof Error ? error.message : String(error),
|
|
308
|
-
},
|
|
309
|
-
now,
|
|
310
|
-
});
|
|
311
|
-
throw error;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
const updates = proposedUpdates(prompt, context);
|
|
316
|
-
const writePolicy = {
|
|
317
|
-
approved: options.approveWrite === true,
|
|
318
|
-
durable_writes_performed: false as const,
|
|
319
|
-
reason: options.approveWrite
|
|
320
|
-
? 'Approval flag recorded; durable wiki writing is deferred to the wiki compile task.'
|
|
321
|
-
: 'Dry-run mode: proposed wiki updates require approval before durable writes.',
|
|
322
|
-
};
|
|
323
|
-
addRunEvent(options.dbPath, {
|
|
324
|
-
runId,
|
|
325
|
-
level: 'info',
|
|
326
|
-
event: generated ? 'answer_generated' : 'answer_drafted',
|
|
327
|
-
metadata: {
|
|
328
|
-
provider,
|
|
329
|
-
model,
|
|
330
|
-
proposed_updates: updates.length,
|
|
331
|
-
durable_writes_performed: false,
|
|
332
|
-
},
|
|
333
|
-
now,
|
|
334
|
-
});
|
|
335
|
-
recordUsage(options.dbPath, runId, usage, provider, model, now, {
|
|
336
|
-
generated,
|
|
337
|
-
citations: context.citations.length,
|
|
338
|
-
});
|
|
339
|
-
updateRun(options.dbPath, {
|
|
340
|
-
runId,
|
|
341
|
-
status: generated ? 'completed' : 'dry_run',
|
|
342
|
-
provider,
|
|
343
|
-
model,
|
|
344
|
-
metadata: {
|
|
345
|
-
generated,
|
|
346
|
-
citations: context.citations.length,
|
|
347
|
-
proposed_updates: updates.length,
|
|
348
|
-
approve_write: options.approveWrite === true,
|
|
349
|
-
},
|
|
350
|
-
now,
|
|
351
|
-
});
|
|
352
|
-
|
|
353
|
-
return {
|
|
354
|
-
run_id: runId,
|
|
355
|
-
prompt,
|
|
356
|
-
generated,
|
|
357
|
-
provider,
|
|
358
|
-
model,
|
|
359
|
-
answer,
|
|
360
|
-
context,
|
|
361
|
-
citations: context.citations,
|
|
362
|
-
proposed_wiki_updates: updates,
|
|
363
|
-
write_policy: writePolicy,
|
|
364
|
-
usage,
|
|
365
|
-
warnings,
|
|
366
|
-
};
|
|
367
|
-
}
|