cograph 0.1.25 → 0.1.27
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 +32 -0
- package/dist/chunk-PE2EOIGT.js +978 -0
- package/dist/chunk-PE2EOIGT.js.map +1 -0
- package/dist/cli.js +3 -3
- package/dist/index.d.ts +220 -1
- package/dist/index.js +423 -2
- package/dist/index.js.map +1 -1
- package/dist/{shell-ILZ322LT.js → shell-A5UU33YS.js} +2 -2
- package/package.json +6 -3
- package/dist/chunk-O3L4FCRY.js +0 -558
- package/dist/chunk-O3L4FCRY.js.map +0 -1
- /package/dist/{shell-ILZ322LT.js.map → shell-A5UU33YS.js.map} +0 -0
package/dist/chunk-O3L4FCRY.js
DELETED
|
@@ -1,558 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
readConfig
|
|
4
|
-
} from "./chunk-7VVBEUZQ.js";
|
|
5
|
-
|
|
6
|
-
// src/client.ts
|
|
7
|
-
import { existsSync, readFileSync, statSync } from "fs";
|
|
8
|
-
import { extname } from "path";
|
|
9
|
-
var CographError = class extends Error {
|
|
10
|
-
status;
|
|
11
|
-
body;
|
|
12
|
-
constructor(message, opts) {
|
|
13
|
-
super(message);
|
|
14
|
-
this.name = "CographError";
|
|
15
|
-
this.status = opts?.status;
|
|
16
|
-
this.body = opts?.body;
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
|
-
var SCHEMA_SAMPLE_CAP = 5e3;
|
|
20
|
-
function stridedSample(rows, cap = SCHEMA_SAMPLE_CAP) {
|
|
21
|
-
if (rows.length <= cap) return rows;
|
|
22
|
-
const out = [];
|
|
23
|
-
for (let i = 0; i < cap; i++) out.push(rows[Math.floor(i * rows.length / cap)]);
|
|
24
|
-
return out;
|
|
25
|
-
}
|
|
26
|
-
function envVar(name, fallback) {
|
|
27
|
-
return process.env[`COGRAPH_${name}`] || process.env[`OMNIX_${name}`] || fallback;
|
|
28
|
-
}
|
|
29
|
-
var EXT_FORMAT = {
|
|
30
|
-
".csv": "csv",
|
|
31
|
-
".json": "json",
|
|
32
|
-
".jsonl": "json",
|
|
33
|
-
".txt": "text"
|
|
34
|
-
};
|
|
35
|
-
function parseCsv(content) {
|
|
36
|
-
const rows = [];
|
|
37
|
-
let cur = [];
|
|
38
|
-
let field = "";
|
|
39
|
-
let inQuotes = false;
|
|
40
|
-
for (let i = 0; i < content.length; i++) {
|
|
41
|
-
const ch = content[i];
|
|
42
|
-
if (inQuotes) {
|
|
43
|
-
if (ch === '"') {
|
|
44
|
-
if (content[i + 1] === '"') {
|
|
45
|
-
field += '"';
|
|
46
|
-
i++;
|
|
47
|
-
} else {
|
|
48
|
-
inQuotes = false;
|
|
49
|
-
}
|
|
50
|
-
} else {
|
|
51
|
-
field += ch;
|
|
52
|
-
}
|
|
53
|
-
} else {
|
|
54
|
-
if (ch === '"') {
|
|
55
|
-
inQuotes = true;
|
|
56
|
-
} else if (ch === ",") {
|
|
57
|
-
cur.push(field);
|
|
58
|
-
field = "";
|
|
59
|
-
} else if (ch === "\n") {
|
|
60
|
-
cur.push(field);
|
|
61
|
-
rows.push(cur);
|
|
62
|
-
cur = [];
|
|
63
|
-
field = "";
|
|
64
|
-
} else if (ch === "\r") {
|
|
65
|
-
if (content[i + 1] !== "\n") {
|
|
66
|
-
cur.push(field);
|
|
67
|
-
rows.push(cur);
|
|
68
|
-
cur = [];
|
|
69
|
-
field = "";
|
|
70
|
-
}
|
|
71
|
-
} else {
|
|
72
|
-
field += ch;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
if (field.length > 0 || cur.length > 0) {
|
|
77
|
-
cur.push(field);
|
|
78
|
-
rows.push(cur);
|
|
79
|
-
}
|
|
80
|
-
if (rows.length === 0) return [];
|
|
81
|
-
const headers = rows[0].map((h) => h.trim());
|
|
82
|
-
const out = [];
|
|
83
|
-
for (let r = 1; r < rows.length; r++) {
|
|
84
|
-
const row = rows[r];
|
|
85
|
-
if (row.length === 1 && row[0] === "") continue;
|
|
86
|
-
const obj = {};
|
|
87
|
-
for (let c = 0; c < headers.length; c++) {
|
|
88
|
-
obj[headers[c]] = row[c] ?? "";
|
|
89
|
-
}
|
|
90
|
-
out.push(obj);
|
|
91
|
-
}
|
|
92
|
-
return out;
|
|
93
|
-
}
|
|
94
|
-
var Client = class {
|
|
95
|
-
apiKey;
|
|
96
|
-
baseUrl;
|
|
97
|
-
tenant;
|
|
98
|
-
constructor(opts = {}) {
|
|
99
|
-
const cfg = readConfig();
|
|
100
|
-
this.apiKey = opts.apiKey ?? envVar("API_KEY") ?? cfg.apiKey;
|
|
101
|
-
const url = opts.baseUrl ?? envVar("API_URL") ?? cfg.apiUrl ?? "https://api.cograph.cloud";
|
|
102
|
-
this.baseUrl = url.replace(/\/+$/, "");
|
|
103
|
-
this.tenant = opts.tenant ?? envVar("TENANT") ?? cfg.tenant ?? "demo-tenant";
|
|
104
|
-
}
|
|
105
|
-
headers() {
|
|
106
|
-
const h = { "Content-Type": "application/json" };
|
|
107
|
-
if (this.apiKey) h["X-API-Key"] = this.apiKey;
|
|
108
|
-
return h;
|
|
109
|
-
}
|
|
110
|
-
base() {
|
|
111
|
-
return `${this.baseUrl}/graphs/${this.tenant}`;
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Probe the backend to determine reachability and whether endpoints
|
|
115
|
-
* require an X-API-Key header. Used at shell startup to distinguish
|
|
116
|
-
* cloud (auth required) from self-hosted open-access deployments.
|
|
117
|
-
*/
|
|
118
|
-
async healthCheck() {
|
|
119
|
-
const healthUrl = `${this.baseUrl}/health`;
|
|
120
|
-
try {
|
|
121
|
-
const res = await fetch(healthUrl, {
|
|
122
|
-
signal: AbortSignal.timeout(5e3)
|
|
123
|
-
});
|
|
124
|
-
if (!res.ok) return { ok: false, requiresAuth: false, url: this.baseUrl };
|
|
125
|
-
} catch {
|
|
126
|
-
return { ok: false, requiresAuth: false, url: this.baseUrl };
|
|
127
|
-
}
|
|
128
|
-
try {
|
|
129
|
-
const res = await fetch(`${this.base()}/kgs`, {
|
|
130
|
-
headers: { "Content-Type": "application/json" },
|
|
131
|
-
signal: AbortSignal.timeout(5e3)
|
|
132
|
-
});
|
|
133
|
-
return {
|
|
134
|
-
ok: true,
|
|
135
|
-
requiresAuth: res.status === 401,
|
|
136
|
-
url: this.baseUrl
|
|
137
|
-
};
|
|
138
|
-
} catch {
|
|
139
|
-
return { ok: true, requiresAuth: true, url: this.baseUrl };
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
async request(method, url, body, timeoutMs = 12e4) {
|
|
143
|
-
const controller = new AbortController();
|
|
144
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
145
|
-
let res;
|
|
146
|
-
try {
|
|
147
|
-
res = await fetch(url, {
|
|
148
|
-
method,
|
|
149
|
-
headers: this.headers(),
|
|
150
|
-
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
151
|
-
signal: controller.signal
|
|
152
|
-
});
|
|
153
|
-
} catch (err) {
|
|
154
|
-
clearTimeout(timer);
|
|
155
|
-
if (err instanceof Error && err.name === "AbortError") {
|
|
156
|
-
throw new CographError(`Request to ${url} timed out after ${timeoutMs}ms`);
|
|
157
|
-
}
|
|
158
|
-
throw new CographError(
|
|
159
|
-
`Network error contacting ${url}: ${err instanceof Error ? err.message : String(err)}`
|
|
160
|
-
);
|
|
161
|
-
}
|
|
162
|
-
clearTimeout(timer);
|
|
163
|
-
if (!res.ok) {
|
|
164
|
-
let text2 = "";
|
|
165
|
-
try {
|
|
166
|
-
text2 = await res.text();
|
|
167
|
-
} catch {
|
|
168
|
-
}
|
|
169
|
-
throw new CographError(`HTTP ${res.status}: ${text2}`, {
|
|
170
|
-
status: res.status,
|
|
171
|
-
body: text2
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
if (res.status === 204) return void 0;
|
|
175
|
-
const ct = res.headers.get("content-type") ?? "";
|
|
176
|
-
if (ct.includes("application/json")) {
|
|
177
|
-
return await res.json();
|
|
178
|
-
}
|
|
179
|
-
const text = await res.text();
|
|
180
|
-
try {
|
|
181
|
-
return JSON.parse(text);
|
|
182
|
-
} catch {
|
|
183
|
-
return text;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
/**
|
|
187
|
-
* Ingest a file path or raw text into a knowledge graph.
|
|
188
|
-
*
|
|
189
|
-
* If `pathOrText` points to an existing file, its contents are read and the
|
|
190
|
-
* format is inferred from the extension (.csv, .json, .txt) unless
|
|
191
|
-
* `contentType` is given. CSV files use the two-step schema-inference + row
|
|
192
|
-
* mapping flow.
|
|
193
|
-
*/
|
|
194
|
-
async ingest(pathOrText, opts = {}) {
|
|
195
|
-
let content;
|
|
196
|
-
let fmt;
|
|
197
|
-
let isFile = false;
|
|
198
|
-
try {
|
|
199
|
-
isFile = existsSync(pathOrText) && statSync(pathOrText).isFile();
|
|
200
|
-
} catch {
|
|
201
|
-
isFile = false;
|
|
202
|
-
}
|
|
203
|
-
if (isFile) {
|
|
204
|
-
const ext = extname(pathOrText).toLowerCase();
|
|
205
|
-
if (ext === ".pdf") {
|
|
206
|
-
throw new CographError(
|
|
207
|
-
"PDF ingest not yet supported in the Node CLI; use the Python CLI or POST raw bytes to the API."
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
content = readFileSync(pathOrText, "utf-8");
|
|
211
|
-
fmt = opts.contentType ?? EXT_FORMAT[ext] ?? "text";
|
|
212
|
-
if (fmt === "csv") {
|
|
213
|
-
return this.ingestCsv(content, opts);
|
|
214
|
-
}
|
|
215
|
-
} else {
|
|
216
|
-
content = pathOrText;
|
|
217
|
-
fmt = opts.contentType ?? "text";
|
|
218
|
-
}
|
|
219
|
-
const body = {
|
|
220
|
-
content,
|
|
221
|
-
content_type: fmt,
|
|
222
|
-
source: "client"
|
|
223
|
-
};
|
|
224
|
-
if (opts.kg) body.kg_name = opts.kg;
|
|
225
|
-
return this.request("POST", `${this.base()}/ingest`, body, 12e4);
|
|
226
|
-
}
|
|
227
|
-
async ingestCsv(content, opts) {
|
|
228
|
-
const kgName = opts.kg;
|
|
229
|
-
const batchSize = opts.batchSize ?? 200;
|
|
230
|
-
const concurrency = opts.concurrency ?? 4;
|
|
231
|
-
const rows = parseCsv(content);
|
|
232
|
-
if (rows.length === 0) throw new CographError("CSV is empty");
|
|
233
|
-
const headers = Object.keys(rows[0]);
|
|
234
|
-
const sampleRows = stridedSample(rows);
|
|
235
|
-
const schemaBody = {
|
|
236
|
-
headers,
|
|
237
|
-
sample_rows: sampleRows,
|
|
238
|
-
total_rows: rows.length
|
|
239
|
-
};
|
|
240
|
-
const mapping = await this.request(
|
|
241
|
-
"POST",
|
|
242
|
-
`${this.base()}/ingest/csv/schema`,
|
|
243
|
-
schemaBody,
|
|
244
|
-
3e5
|
|
245
|
-
);
|
|
246
|
-
let mappingToPost = mapping;
|
|
247
|
-
if (opts.onSchemaInferred) {
|
|
248
|
-
const reviewed = await opts.onSchemaInferred(mapping, {
|
|
249
|
-
totalRows: rows.length,
|
|
250
|
-
rowsProfiled: sampleRows.length
|
|
251
|
-
});
|
|
252
|
-
if (reviewed == null) {
|
|
253
|
-
return { cancelled: true, message: "Ingest cancelled before any rows were written." };
|
|
254
|
-
}
|
|
255
|
-
mappingToPost = reviewed;
|
|
256
|
-
}
|
|
257
|
-
const batches = [];
|
|
258
|
-
for (let i = 0; i < rows.length; i += batchSize) {
|
|
259
|
-
batches.push(rows.slice(i, i + batchSize));
|
|
260
|
-
}
|
|
261
|
-
let totalEntities = 0;
|
|
262
|
-
let totalTriples = 0;
|
|
263
|
-
let rowsProcessed = 0;
|
|
264
|
-
let nextBatch = 0;
|
|
265
|
-
const postBatch = async (batch) => {
|
|
266
|
-
const body = {
|
|
267
|
-
mapping: mappingToPost,
|
|
268
|
-
rows: batch,
|
|
269
|
-
source: "client"
|
|
270
|
-
};
|
|
271
|
-
if (kgName) body.kg_name = kgName;
|
|
272
|
-
const result = await this.request("POST", `${this.base()}/ingest/csv/rows`, body, 3e5);
|
|
273
|
-
return {
|
|
274
|
-
entities: result.entities_resolved ?? 0,
|
|
275
|
-
triples: result.triples_inserted ?? 0,
|
|
276
|
-
size: batch.length
|
|
277
|
-
};
|
|
278
|
-
};
|
|
279
|
-
const worker = async () => {
|
|
280
|
-
while (true) {
|
|
281
|
-
const idx = nextBatch++;
|
|
282
|
-
if (idx >= batches.length) return;
|
|
283
|
-
const r = await postBatch(batches[idx]);
|
|
284
|
-
totalEntities += r.entities;
|
|
285
|
-
totalTriples += r.triples;
|
|
286
|
-
rowsProcessed += r.size;
|
|
287
|
-
opts.onProgress?.({
|
|
288
|
-
rowsProcessed,
|
|
289
|
-
totalRows: rows.length,
|
|
290
|
-
entitiesResolved: totalEntities,
|
|
291
|
-
triplesInserted: totalTriples
|
|
292
|
-
});
|
|
293
|
-
}
|
|
294
|
-
};
|
|
295
|
-
const workers = [];
|
|
296
|
-
for (let i = 0; i < Math.min(concurrency, batches.length); i++) {
|
|
297
|
-
workers.push(worker());
|
|
298
|
-
}
|
|
299
|
-
await Promise.all(workers);
|
|
300
|
-
if (kgName) {
|
|
301
|
-
try {
|
|
302
|
-
await this.request(
|
|
303
|
-
"POST",
|
|
304
|
-
`${this.base()}/explore/kgs/${encodeURIComponent(kgName)}/recompute-stats`,
|
|
305
|
-
{},
|
|
306
|
-
15e3
|
|
307
|
-
);
|
|
308
|
-
} catch {
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
return {
|
|
312
|
-
entities_resolved: totalEntities,
|
|
313
|
-
triples_inserted: totalTriples,
|
|
314
|
-
mapping
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
/** Ask a natural language question and return the parsed response. */
|
|
318
|
-
async ask(question, opts = {}) {
|
|
319
|
-
const body = { question };
|
|
320
|
-
if (opts.kg) body.kg_name = opts.kg;
|
|
321
|
-
if (opts.model) body.model = opts.model;
|
|
322
|
-
return this.request("POST", `${this.base()}/ask`, body, 6e4);
|
|
323
|
-
}
|
|
324
|
-
/**
|
|
325
|
-
* One turn of the unified Ask-AI agent — the SINGLE conversational surface
|
|
326
|
-
* (`POST /graphs/{tenant}/agent`, COG-118). Mirrors the HTTP contract exactly:
|
|
327
|
-
*
|
|
328
|
-
* - `confirmPlanId` set → the server runs `execute_plan` (the only mutating
|
|
329
|
-
* path) and returns `{kind:"result", steps}`.
|
|
330
|
-
* - otherwise → the server runs `planner.handle(message)` and returns one of
|
|
331
|
-
* `{kind:"answer"}` / `{kind:"clarify"}` / `{kind:"plan"}`.
|
|
332
|
-
*
|
|
333
|
-
* The agent classifies intent server-side and drives the underlying engines
|
|
334
|
-
* through its capability registry — the client never talks to `/ask`,
|
|
335
|
-
* `/enrich/*` etc. for an agent turn. ENTITLEMENT for any paid step a plan
|
|
336
|
-
* contains is enforced server-side at execute time (the same authorization the
|
|
337
|
-
* direct paid routes apply), so confirming a plan here cannot bypass a gate the
|
|
338
|
-
* direct path enforces — the gate lives behind the endpoint, not in this client.
|
|
339
|
-
*/
|
|
340
|
-
async agent(opts) {
|
|
341
|
-
const body = {
|
|
342
|
-
message: opts.message ?? "",
|
|
343
|
-
context: {
|
|
344
|
-
kg_name: opts.kgName ?? "",
|
|
345
|
-
type_name: opts.typeName ?? null
|
|
346
|
-
}
|
|
347
|
-
};
|
|
348
|
-
if (opts.sessionId) body.session_id = opts.sessionId;
|
|
349
|
-
if (opts.confirmPlanId) body.confirm = { plan_id: opts.confirmPlanId };
|
|
350
|
-
return this.request(
|
|
351
|
-
"POST",
|
|
352
|
-
`${this.base()}/agent`,
|
|
353
|
-
body,
|
|
354
|
-
// Generous: a confirmed plan can kick off enrichment/dedup work, and a
|
|
355
|
-
// question turn runs an LLM round-trip server-side.
|
|
356
|
-
12e4
|
|
357
|
-
);
|
|
358
|
-
}
|
|
359
|
-
/** List the tenants the authenticated user can access (GET /v1/me/tenants).
|
|
360
|
-
* Keyed by the API key (X-API-Key → user), so it's independent of the active
|
|
361
|
-
* tenant. Throws CographError with status 501 on deployments without a tenant
|
|
362
|
-
* provider (e.g. OSS-only). */
|
|
363
|
-
async listTenants() {
|
|
364
|
-
return this.request(
|
|
365
|
-
"GET",
|
|
366
|
-
`${this.baseUrl}/v1/me/tenants`,
|
|
367
|
-
void 0,
|
|
368
|
-
15e3
|
|
369
|
-
);
|
|
370
|
-
}
|
|
371
|
-
/** List all knowledge graphs for the current tenant. */
|
|
372
|
-
async listKgs() {
|
|
373
|
-
const data = await this.request(
|
|
374
|
-
"GET",
|
|
375
|
-
`${this.base()}/kgs`,
|
|
376
|
-
void 0,
|
|
377
|
-
15e3
|
|
378
|
-
);
|
|
379
|
-
if (Array.isArray(data)) return data;
|
|
380
|
-
if (data && typeof data === "object" && "kgs" in data) {
|
|
381
|
-
const kgs = data.kgs;
|
|
382
|
-
if (Array.isArray(kgs)) return kgs;
|
|
383
|
-
}
|
|
384
|
-
return [];
|
|
385
|
-
}
|
|
386
|
-
/** Create a knowledge graph. */
|
|
387
|
-
async createKg(name, description) {
|
|
388
|
-
const body = { name };
|
|
389
|
-
if (description) body.description = description;
|
|
390
|
-
return this.request("POST", `${this.base()}/kgs`, body, 15e3);
|
|
391
|
-
}
|
|
392
|
-
/** Delete a knowledge graph by name. */
|
|
393
|
-
async deleteKg(name) {
|
|
394
|
-
return this.request(
|
|
395
|
-
"DELETE",
|
|
396
|
-
`${this.base()}/kgs/${encodeURIComponent(name)}`,
|
|
397
|
-
void 0,
|
|
398
|
-
3e4
|
|
399
|
-
);
|
|
400
|
-
}
|
|
401
|
-
/** List ontology types. */
|
|
402
|
-
async ontologyTypes() {
|
|
403
|
-
const data = await this.request(
|
|
404
|
-
"GET",
|
|
405
|
-
`${this.base()}/ontology/types`,
|
|
406
|
-
void 0,
|
|
407
|
-
15e3
|
|
408
|
-
);
|
|
409
|
-
return Array.isArray(data) ? data : [];
|
|
410
|
-
}
|
|
411
|
-
/**
|
|
412
|
-
* Resolve a natural-language ontology change against the existing ontology.
|
|
413
|
-
* The caller does not need to know exact type/attribute/relationship names —
|
|
414
|
-
* the server matches the plain-language `ask` to the current schema and
|
|
415
|
-
* returns auto-applied changes plus proposals that need confirmation.
|
|
416
|
-
*/
|
|
417
|
-
async ontologyResolve(ask, opts = {}) {
|
|
418
|
-
const body = { ask };
|
|
419
|
-
if (opts.knowledge_graph) body.knowledge_graph = opts.knowledge_graph;
|
|
420
|
-
return this.request(
|
|
421
|
-
"POST",
|
|
422
|
-
`${this.base()}/ontology/resolve`,
|
|
423
|
-
body,
|
|
424
|
-
6e4
|
|
425
|
-
);
|
|
426
|
-
}
|
|
427
|
-
/**
|
|
428
|
-
* Apply a single resolved ontology change — one of the `proposals` returned
|
|
429
|
-
* by {@link ontologyResolve}. Pass the proposal object through unchanged.
|
|
430
|
-
*/
|
|
431
|
-
async ontologyApply(proposal) {
|
|
432
|
-
return this.request(
|
|
433
|
-
"POST",
|
|
434
|
-
`${this.base()}/ontology/apply`,
|
|
435
|
-
proposal,
|
|
436
|
-
6e4
|
|
437
|
-
);
|
|
438
|
-
}
|
|
439
|
-
/**
|
|
440
|
-
* Second-pass entity resolution: re-run ER over an already-ingested KG to
|
|
441
|
-
* collapse intra-batch fragments. Synchronous on the server; returns a
|
|
442
|
-
* per-type before/after report. Generous timeout — it rewrites triples.
|
|
443
|
-
*/
|
|
444
|
-
async erRebuild(kg) {
|
|
445
|
-
return this.request(
|
|
446
|
-
"POST",
|
|
447
|
-
`${this.base()}/explore/kgs/${encodeURIComponent(kg)}/er-rebuild`,
|
|
448
|
-
{},
|
|
449
|
-
3e5
|
|
450
|
-
);
|
|
451
|
-
}
|
|
452
|
-
/** Per-KG type counts: every type with ≥1 instance, sorted desc. */
|
|
453
|
-
async typeCounts(kg) {
|
|
454
|
-
const data = await this.request(
|
|
455
|
-
"GET",
|
|
456
|
-
`${this.base()}/kgs/${encodeURIComponent(kg)}/type-counts`,
|
|
457
|
-
void 0,
|
|
458
|
-
3e4
|
|
459
|
-
);
|
|
460
|
-
return Array.isArray(data) ? data : [];
|
|
461
|
-
}
|
|
462
|
-
/** Plan + run an enrichment job. Returns immediately with the job id. */
|
|
463
|
-
async enrichRun(req) {
|
|
464
|
-
return this.request(
|
|
465
|
-
"POST",
|
|
466
|
-
`${this.base()}/enrich/jobs`,
|
|
467
|
-
req,
|
|
468
|
-
3e4
|
|
469
|
-
);
|
|
470
|
-
}
|
|
471
|
-
/** List recent enrichment jobs for the current tenant. */
|
|
472
|
-
async enrichJobs() {
|
|
473
|
-
const data = await this.request(
|
|
474
|
-
"GET",
|
|
475
|
-
`${this.base()}/enrich/jobs`,
|
|
476
|
-
void 0,
|
|
477
|
-
15e3
|
|
478
|
-
);
|
|
479
|
-
return Array.isArray(data) ? data : [];
|
|
480
|
-
}
|
|
481
|
-
/** Fetch a single enrichment job (with truncated results). */
|
|
482
|
-
async enrichJob(jobId) {
|
|
483
|
-
return this.request(
|
|
484
|
-
"GET",
|
|
485
|
-
`${this.base()}/enrich/jobs/${encodeURIComponent(jobId)}`,
|
|
486
|
-
void 0,
|
|
487
|
-
15e3
|
|
488
|
-
);
|
|
489
|
-
}
|
|
490
|
-
/** Fetch the conflict review queue for a job. */
|
|
491
|
-
async enrichConflicts(jobId) {
|
|
492
|
-
const data = await this.request(
|
|
493
|
-
"GET",
|
|
494
|
-
`${this.base()}/enrich/jobs/${encodeURIComponent(jobId)}/conflicts`,
|
|
495
|
-
void 0,
|
|
496
|
-
3e4
|
|
497
|
-
);
|
|
498
|
-
return Array.isArray(data) ? data : [];
|
|
499
|
-
}
|
|
500
|
-
/** Apply a set of conflict review decisions to a job. */
|
|
501
|
-
async enrichApply(jobId, decisions) {
|
|
502
|
-
return this.request(
|
|
503
|
-
"POST",
|
|
504
|
-
`${this.base()}/enrich/jobs/${encodeURIComponent(jobId)}/apply`,
|
|
505
|
-
{ decisions },
|
|
506
|
-
6e4
|
|
507
|
-
);
|
|
508
|
-
}
|
|
509
|
-
/** Cancel an enrichment job. */
|
|
510
|
-
async enrichCancel(jobId) {
|
|
511
|
-
await this.request(
|
|
512
|
-
"DELETE",
|
|
513
|
-
`${this.base()}/enrich/jobs/${encodeURIComponent(jobId)}`,
|
|
514
|
-
void 0,
|
|
515
|
-
15e3
|
|
516
|
-
);
|
|
517
|
-
}
|
|
518
|
-
/** Per-type breakdown for one type in one KG: definition + counts + samples.
|
|
519
|
-
*
|
|
520
|
-
* System predicates (rdfs:label, ingested_at, source) are hidden by default
|
|
521
|
-
* — they're attached to every entity at 100% and drown out the columns the
|
|
522
|
-
* user cares about. Pass `includeSystem: true` to see them. */
|
|
523
|
-
async typeUsage(kg, typeName, opts = {}) {
|
|
524
|
-
const qs = opts.includeSystem ? "?include_system=true" : "";
|
|
525
|
-
return this.request(
|
|
526
|
-
"GET",
|
|
527
|
-
`${this.base()}/kgs/${encodeURIComponent(kg)}/types/${encodeURIComponent(typeName)}/usage${qs}`,
|
|
528
|
-
void 0,
|
|
529
|
-
3e4
|
|
530
|
-
);
|
|
531
|
-
}
|
|
532
|
-
/** Explorer summary for a type — like typeUsage but adds coverage_pct + avg_degree. */
|
|
533
|
-
async typeSummary(kg, typeName) {
|
|
534
|
-
return this.request(
|
|
535
|
-
"GET",
|
|
536
|
-
`${this.base()}/explore/kgs/${encodeURIComponent(kg)}/types/${encodeURIComponent(typeName)}/summary`,
|
|
537
|
-
void 0,
|
|
538
|
-
3e4
|
|
539
|
-
);
|
|
540
|
-
}
|
|
541
|
-
/** Search types or attributes by name substring within a KG. */
|
|
542
|
-
async exploreSearch(kg, q, kind = "type") {
|
|
543
|
-
const qs = new URLSearchParams({ kg, q, kind }).toString();
|
|
544
|
-
const data = await this.request(
|
|
545
|
-
"GET",
|
|
546
|
-
`${this.base()}/explore/search?${qs}`,
|
|
547
|
-
void 0,
|
|
548
|
-
15e3
|
|
549
|
-
);
|
|
550
|
-
return Array.isArray(data) ? data : [];
|
|
551
|
-
}
|
|
552
|
-
};
|
|
553
|
-
|
|
554
|
-
export {
|
|
555
|
-
CographError,
|
|
556
|
-
Client
|
|
557
|
-
};
|
|
558
|
-
//# sourceMappingURL=chunk-O3L4FCRY.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["import { existsSync, readFileSync, statSync } from \"node:fs\";\nimport { extname } from \"node:path\";\nimport { readConfig } from \"./config.js\";\n\nexport class CographError extends Error {\n status?: number;\n body?: string;\n\n constructor(message: string, opts?: { status?: number; body?: string }) {\n super(message);\n this.name = \"CographError\";\n this.status = opts?.status;\n this.body = opts?.body;\n }\n}\n\nexport interface ClientOptions {\n apiKey?: string;\n baseUrl?: string;\n tenant?: string;\n}\n\nexport interface IngestOptions {\n kg?: string;\n contentType?: \"text\" | \"csv\" | \"json\" | string;\n /** Rows per batch for CSV ingest. Default 200. Larger = fewer round-trips\n * but higher per-request memory; 200 is a good balance for typical KGs. */\n batchSize?: number;\n /** Max number of batches in flight at once. Default 4. Higher saturates\n * the backend faster but risks 429s on large ingests. */\n concurrency?: number;\n /** Called after each batch completes during CSV ingest, in batch order.\n * Use for progress UI. Not invoked for text/json ingest. */\n onProgress?: (progress: IngestProgress) => void;\n /** CSV only. Called once after schema inference and BEFORE any rows are\n * written, with the inferred mapping. Return the (possibly edited/approved)\n * mapping to ingest, or `null` to cancel without writing anything. When\n * omitted the inferred mapping is applied as-is (non-interactive). This is\n * the same confirm/override gate the Explorer surfaces in its review step. */\n onSchemaInferred?: (\n mapping: Record<string, unknown>,\n info: { totalRows: number; rowsProfiled: number },\n ) => Promise<Record<string, unknown> | null>;\n}\n\nexport interface IngestProgress {\n rowsProcessed: number;\n totalRows: number;\n entitiesResolved: number;\n triplesInserted: number;\n}\n\n/** Rows sent to schema inference. Profile fidelity = decision quality, so we\n * send the whole file up to this cap, evenly strided across it (never the\n * head — head-of-file bias is exactly what evidence-grounded inference fixes).\n * Matches the Explorer's SCHEMA_SAMPLE_CAP. */\nexport const SCHEMA_SAMPLE_CAP = 5000;\n\nfunction stridedSample<T>(rows: T[], cap: number = SCHEMA_SAMPLE_CAP): T[] {\n if (rows.length <= cap) return rows;\n const out: T[] = [];\n for (let i = 0; i < cap; i++) out.push(rows[Math.floor((i * rows.length) / cap)]!);\n return out;\n}\n\nexport interface AskOptions {\n kg?: string;\n model?: string;\n}\n\nfunction envVar(name: string, fallback?: string): string | undefined {\n // Prefer COGRAPH_, fall back to OMNIX_ so old configs keep working.\n return (\n process.env[`COGRAPH_${name}`] ||\n process.env[`OMNIX_${name}`] ||\n fallback\n );\n}\n\nconst EXT_FORMAT: Record<string, string> = {\n \".csv\": \"csv\",\n \".json\": \"json\",\n \".jsonl\": \"json\",\n \".txt\": \"text\",\n};\n\n/**\n * Parse a CSV string into an array of row objects.\n *\n * Minimal RFC-4180-ish parser: handles quoted fields with commas, escaped\n * quotes (`\"\"`), CRLF/LF line endings. Does not handle BOM stripping or\n * encoding detection — we assume UTF-8 text in.\n */\nexport function parseCsv(content: string): Record<string, string>[] {\n const rows: string[][] = [];\n let cur: string[] = [];\n let field = \"\";\n let inQuotes = false;\n\n for (let i = 0; i < content.length; i++) {\n const ch = content[i];\n if (inQuotes) {\n if (ch === '\"') {\n if (content[i + 1] === '\"') {\n field += '\"';\n i++;\n } else {\n inQuotes = false;\n }\n } else {\n field += ch;\n }\n } else {\n if (ch === '\"') {\n inQuotes = true;\n } else if (ch === \",\") {\n cur.push(field);\n field = \"\";\n } else if (ch === \"\\n\") {\n cur.push(field);\n rows.push(cur);\n cur = [];\n field = \"\";\n } else if (ch === \"\\r\") {\n // swallow; handled by the following \\n in CRLF, or treat lone \\r as line end\n if (content[i + 1] !== \"\\n\") {\n cur.push(field);\n rows.push(cur);\n cur = [];\n field = \"\";\n }\n } else {\n field += ch;\n }\n }\n }\n // flush trailing field/row\n if (field.length > 0 || cur.length > 0) {\n cur.push(field);\n rows.push(cur);\n }\n\n if (rows.length === 0) return [];\n const headers = rows[0]!.map((h) => h.trim());\n const out: Record<string, string>[] = [];\n for (let r = 1; r < rows.length; r++) {\n const row = rows[r]!;\n // skip blank trailing lines\n if (row.length === 1 && row[0] === \"\") continue;\n const obj: Record<string, string> = {};\n for (let c = 0; c < headers.length; c++) {\n obj[headers[c]!] = row[c] ?? \"\";\n }\n out.push(obj);\n }\n return out;\n}\n\nexport class Client {\n apiKey: string | undefined;\n baseUrl: string;\n tenant: string;\n\n constructor(opts: ClientOptions = {}) {\n // Resolution order for each field: explicit opts → env var → ~/.cograph/config.json\n // (written by `cograph login`) → built-in default. Reading the config eagerly\n // is cheap (small JSON file) and lets users skip env vars entirely after login.\n const cfg = readConfig();\n this.apiKey = opts.apiKey ?? envVar(\"API_KEY\") ?? cfg.apiKey;\n const url =\n opts.baseUrl ?? envVar(\"API_URL\") ?? cfg.apiUrl ?? \"https://api.cograph.cloud\";\n this.baseUrl = url.replace(/\\/+$/, \"\");\n this.tenant = opts.tenant ?? envVar(\"TENANT\") ?? cfg.tenant ?? \"demo-tenant\";\n }\n\n private headers(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (this.apiKey) h[\"X-API-Key\"] = this.apiKey;\n return h;\n }\n\n private base(): string {\n return `${this.baseUrl}/graphs/${this.tenant}`;\n }\n\n /**\n * Probe the backend to determine reachability and whether endpoints\n * require an X-API-Key header. Used at shell startup to distinguish\n * cloud (auth required) from self-hosted open-access deployments.\n */\n async healthCheck(): Promise<{\n ok: boolean;\n requiresAuth: boolean;\n url: string;\n }> {\n const healthUrl = `${this.baseUrl}/health`;\n try {\n const res = await fetch(healthUrl, {\n signal: AbortSignal.timeout(5000),\n });\n if (!res.ok) return { ok: false, requiresAuth: false, url: this.baseUrl };\n } catch {\n return { ok: false, requiresAuth: false, url: this.baseUrl };\n }\n // Probe whether endpoints require auth by hitting /kgs without X-API-Key.\n // 401 = requires auth; 200/empty = open access; anything else = treat as\n // auth-required to be safe.\n try {\n const res = await fetch(`${this.base()}/kgs`, {\n headers: { \"Content-Type\": \"application/json\" },\n signal: AbortSignal.timeout(5000),\n });\n return {\n ok: true,\n requiresAuth: res.status === 401,\n url: this.baseUrl,\n };\n } catch {\n return { ok: true, requiresAuth: true, url: this.baseUrl };\n }\n }\n\n private async request<T = unknown>(\n method: string,\n url: string,\n body?: unknown,\n timeoutMs: number = 120_000,\n ): Promise<T> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n let res: Response;\n try {\n res = await fetch(url, {\n method,\n headers: this.headers(),\n body: body === undefined ? undefined : JSON.stringify(body),\n signal: controller.signal,\n });\n } catch (err) {\n clearTimeout(timer);\n if (err instanceof Error && err.name === \"AbortError\") {\n throw new CographError(`Request to ${url} timed out after ${timeoutMs}ms`);\n }\n throw new CographError(\n `Network error contacting ${url}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n clearTimeout(timer);\n\n if (!res.ok) {\n let text = \"\";\n try {\n text = await res.text();\n } catch {\n // ignore\n }\n throw new CographError(`HTTP ${res.status}: ${text}`, {\n status: res.status,\n body: text,\n });\n }\n\n // 204 No Content\n if (res.status === 204) return undefined as T;\n\n const ct = res.headers.get(\"content-type\") ?? \"\";\n if (ct.includes(\"application/json\")) {\n return (await res.json()) as T;\n }\n // fall back to text\n const text = await res.text();\n try {\n return JSON.parse(text) as T;\n } catch {\n return text as unknown as T;\n }\n }\n\n /**\n * Ingest a file path or raw text into a knowledge graph.\n *\n * If `pathOrText` points to an existing file, its contents are read and the\n * format is inferred from the extension (.csv, .json, .txt) unless\n * `contentType` is given. CSV files use the two-step schema-inference + row\n * mapping flow.\n */\n async ingest(\n pathOrText: string,\n opts: IngestOptions = {},\n ): Promise<Record<string, unknown>> {\n let content: string;\n let fmt: string;\n\n let isFile = false;\n try {\n isFile = existsSync(pathOrText) && statSync(pathOrText).isFile();\n } catch {\n isFile = false;\n }\n\n if (isFile) {\n const ext = extname(pathOrText).toLowerCase();\n if (ext === \".pdf\") {\n throw new CographError(\n \"PDF ingest not yet supported in the Node CLI; use the Python CLI or POST raw bytes to the API.\",\n );\n }\n content = readFileSync(pathOrText, \"utf-8\");\n fmt = opts.contentType ?? EXT_FORMAT[ext] ?? \"text\";\n if (fmt === \"csv\") {\n return this.ingestCsv(content, opts);\n }\n } else {\n content = pathOrText;\n fmt = opts.contentType ?? \"text\";\n }\n\n const body: Record<string, unknown> = {\n content,\n content_type: fmt,\n source: \"client\",\n };\n if (opts.kg) body.kg_name = opts.kg;\n return this.request(\"POST\", `${this.base()}/ingest`, body, 120_000);\n }\n\n private async ingestCsv(\n content: string,\n opts: IngestOptions,\n ): Promise<Record<string, unknown>> {\n const kgName = opts.kg;\n const batchSize = opts.batchSize ?? 200;\n const concurrency = opts.concurrency ?? 4;\n\n const rows = parseCsv(content);\n if (rows.length === 0) throw new CographError(\"CSV is empty\");\n const headers = Object.keys(rows[0]!);\n\n // Send the whole file to the profiler, evenly strided across it (never the\n // head — head-of-file bias, e.g. a key column that goes sparse later, is\n // exactly what evidence-grounded inference fixes). Profile fidelity =\n // decision quality. Mirrors the Explorer's upload flow.\n const sampleRows = stridedSample(rows);\n\n const schemaBody = {\n headers,\n sample_rows: sampleRows,\n total_rows: rows.length,\n };\n const mapping = await this.request<Record<string, unknown>>(\n \"POST\",\n `${this.base()}/ingest/csv/schema`,\n schemaBody,\n 300_000,\n );\n\n // Confirm/override gate (same contract as the Explorer's review step): the\n // caller inspects the inferred mapping and returns what to ingest, or null\n // to cancel before any rows are written. /ingest/csv/rows applies exactly\n // what we post back. When no hook is given, apply the inference as-is.\n let mappingToPost: Record<string, unknown> = mapping;\n if (opts.onSchemaInferred) {\n const reviewed = await opts.onSchemaInferred(mapping, {\n totalRows: rows.length,\n rowsProfiled: sampleRows.length,\n });\n if (reviewed == null) {\n return { cancelled: true, message: \"Ingest cancelled before any rows were written.\" };\n }\n mappingToPost = reviewed;\n }\n\n // Slice rows into batches up front so we can fire them off in a\n // bounded worker pool. Sequential 50-row batches over 891 rows took\n // ~60s end-to-end (18 round-trips); 200-row batches × 4 in flight\n // brings that to ~5s on the same backend.\n const batches: Array<Record<string, string>[]> = [];\n for (let i = 0; i < rows.length; i += batchSize) {\n batches.push(rows.slice(i, i + batchSize));\n }\n\n let totalEntities = 0;\n let totalTriples = 0;\n let rowsProcessed = 0;\n let nextBatch = 0;\n\n const postBatch = async (batch: Record<string, string>[]) => {\n const body: Record<string, unknown> = {\n mapping: mappingToPost,\n rows: batch,\n source: \"client\",\n };\n if (kgName) body.kg_name = kgName;\n const result = await this.request<{\n entities_resolved?: number;\n triples_inserted?: number;\n }>(\"POST\", `${this.base()}/ingest/csv/rows`, body, 300_000);\n return {\n entities: result.entities_resolved ?? 0,\n triples: result.triples_inserted ?? 0,\n size: batch.length,\n };\n };\n\n const worker = async (): Promise<void> => {\n while (true) {\n const idx = nextBatch++;\n if (idx >= batches.length) return;\n const r = await postBatch(batches[idx]!);\n totalEntities += r.entities;\n totalTriples += r.triples;\n rowsProcessed += r.size;\n opts.onProgress?.({\n rowsProcessed,\n totalRows: rows.length,\n entitiesResolved: totalEntities,\n triplesInserted: totalTriples,\n });\n }\n };\n\n const workers: Array<Promise<void>> = [];\n for (let i = 0; i < Math.min(concurrency, batches.length); i++) {\n workers.push(worker());\n }\n await Promise.all(workers);\n\n // All batches are in — kick off a background recompute of the Explorer\n // type-stats for this KG so type-detail views load instantly. The endpoint\n // returns immediately (the scan runs server-side in the background); this\n // is best-effort and never fails the ingest.\n if (kgName) {\n try {\n await this.request(\n \"POST\",\n `${this.base()}/explore/kgs/${encodeURIComponent(kgName)}/recompute-stats`,\n {},\n 15_000,\n );\n } catch {\n // non-fatal — stats fall back to a live scan until the next recompute\n }\n }\n\n return {\n entities_resolved: totalEntities,\n triples_inserted: totalTriples,\n mapping,\n };\n }\n\n /** Ask a natural language question and return the parsed response. */\n async ask(\n question: string,\n opts: AskOptions = {},\n ): Promise<Record<string, unknown>> {\n const body: Record<string, unknown> = { question };\n if (opts.kg) body.kg_name = opts.kg;\n if (opts.model) body.model = opts.model;\n return this.request(\"POST\", `${this.base()}/ask`, body, 60_000);\n }\n\n /**\n * One turn of the unified Ask-AI agent — the SINGLE conversational surface\n * (`POST /graphs/{tenant}/agent`, COG-118). Mirrors the HTTP contract exactly:\n *\n * - `confirmPlanId` set → the server runs `execute_plan` (the only mutating\n * path) and returns `{kind:\"result\", steps}`.\n * - otherwise → the server runs `planner.handle(message)` and returns one of\n * `{kind:\"answer\"}` / `{kind:\"clarify\"}` / `{kind:\"plan\"}`.\n *\n * The agent classifies intent server-side and drives the underlying engines\n * through its capability registry — the client never talks to `/ask`,\n * `/enrich/*` etc. for an agent turn. ENTITLEMENT for any paid step a plan\n * contains is enforced server-side at execute time (the same authorization the\n * direct paid routes apply), so confirming a plan here cannot bypass a gate the\n * direct path enforces — the gate lives behind the endpoint, not in this client.\n */\n async agent(opts: AgentTurnOptions): Promise<AgentResult> {\n const body: Record<string, unknown> = {\n message: opts.message ?? \"\",\n context: {\n kg_name: opts.kgName ?? \"\",\n type_name: opts.typeName ?? null,\n },\n };\n if (opts.sessionId) body.session_id = opts.sessionId;\n // confirm.plan_id present → the server routes to execute_plan (mutating).\n if (opts.confirmPlanId) body.confirm = { plan_id: opts.confirmPlanId };\n return this.request<AgentResult>(\n \"POST\",\n `${this.base()}/agent`,\n body,\n // Generous: a confirmed plan can kick off enrichment/dedup work, and a\n // question turn runs an LLM round-trip server-side.\n 120_000,\n );\n }\n\n /** List the tenants the authenticated user can access (GET /v1/me/tenants).\n * Keyed by the API key (X-API-Key → user), so it's independent of the active\n * tenant. Throws CographError with status 501 on deployments without a tenant\n * provider (e.g. OSS-only). */\n async listTenants(): Promise<Array<{ id: string; label: string }>> {\n return this.request<Array<{ id: string; label: string }>>(\n \"GET\",\n `${this.baseUrl}/v1/me/tenants`,\n undefined,\n 15_000,\n );\n }\n\n /** List all knowledge graphs for the current tenant. */\n async listKgs(): Promise<Array<Record<string, unknown>>> {\n const data = await this.request<unknown>(\n \"GET\",\n `${this.base()}/kgs`,\n undefined,\n 15_000,\n );\n if (Array.isArray(data)) return data as Array<Record<string, unknown>>;\n if (data && typeof data === \"object\" && \"kgs\" in data) {\n const kgs = (data as { kgs?: unknown }).kgs;\n if (Array.isArray(kgs)) return kgs as Array<Record<string, unknown>>;\n }\n return [];\n }\n\n /** Create a knowledge graph. */\n async createKg(\n name: string,\n description?: string,\n ): Promise<Record<string, unknown>> {\n const body: Record<string, unknown> = { name };\n if (description) body.description = description;\n return this.request(\"POST\", `${this.base()}/kgs`, body, 15_000);\n }\n\n /** Delete a knowledge graph by name. */\n async deleteKg(name: string): Promise<Record<string, unknown>> {\n return this.request(\n \"DELETE\",\n `${this.base()}/kgs/${encodeURIComponent(name)}`,\n undefined,\n 30_000,\n );\n }\n\n /** List ontology types. */\n async ontologyTypes(): Promise<Array<Record<string, unknown>>> {\n const data = await this.request<unknown>(\n \"GET\",\n `${this.base()}/ontology/types`,\n undefined,\n 15_000,\n );\n return Array.isArray(data) ? (data as Array<Record<string, unknown>>) : [];\n }\n\n /**\n * Resolve a natural-language ontology change against the existing ontology.\n * The caller does not need to know exact type/attribute/relationship names —\n * the server matches the plain-language `ask` to the current schema and\n * returns auto-applied changes plus proposals that need confirmation.\n */\n async ontologyResolve(\n ask: string,\n opts: { knowledge_graph?: string } = {},\n ): Promise<OntologyResolveResult> {\n const body: Record<string, unknown> = { ask };\n if (opts.knowledge_graph) body.knowledge_graph = opts.knowledge_graph;\n return this.request<OntologyResolveResult>(\n \"POST\",\n `${this.base()}/ontology/resolve`,\n body,\n 60_000,\n );\n }\n\n /**\n * Apply a single resolved ontology change — one of the `proposals` returned\n * by {@link ontologyResolve}. Pass the proposal object through unchanged.\n */\n async ontologyApply(\n proposal: ResolvedChange,\n ): Promise<OntologyApplyResult> {\n return this.request<OntologyApplyResult>(\n \"POST\",\n `${this.base()}/ontology/apply`,\n proposal,\n 60_000,\n );\n }\n\n /**\n * Second-pass entity resolution: re-run ER over an already-ingested KG to\n * collapse intra-batch fragments. Synchronous on the server; returns a\n * per-type before/after report. Generous timeout — it rewrites triples.\n */\n async erRebuild(kg: string): Promise<Record<string, unknown>> {\n return this.request<Record<string, unknown>>(\n \"POST\",\n `${this.base()}/explore/kgs/${encodeURIComponent(kg)}/er-rebuild`,\n {},\n 300_000,\n );\n }\n\n /** Per-KG type counts: every type with ≥1 instance, sorted desc. */\n async typeCounts(kg: string): Promise<TypeCount[]> {\n const data = await this.request<unknown>(\n \"GET\",\n `${this.base()}/kgs/${encodeURIComponent(kg)}/type-counts`,\n undefined,\n 30_000,\n );\n return Array.isArray(data) ? (data as TypeCount[]) : [];\n }\n\n /** Plan + run an enrichment job. Returns immediately with the job id. */\n async enrichRun(req: EnrichRequest): Promise<EnrichJobCreate> {\n return this.request<EnrichJobCreate>(\n \"POST\",\n `${this.base()}/enrich/jobs`,\n req,\n 30_000,\n );\n }\n\n /** List recent enrichment jobs for the current tenant. */\n async enrichJobs(): Promise<JobSummary[]> {\n const data = await this.request<unknown>(\n \"GET\",\n `${this.base()}/enrich/jobs`,\n undefined,\n 15_000,\n );\n return Array.isArray(data) ? (data as JobSummary[]) : [];\n }\n\n /** Fetch a single enrichment job (with truncated results). */\n async enrichJob(jobId: string): Promise<EnrichJob> {\n return this.request<EnrichJob>(\n \"GET\",\n `${this.base()}/enrich/jobs/${encodeURIComponent(jobId)}`,\n undefined,\n 15_000,\n );\n }\n\n /** Fetch the conflict review queue for a job. */\n async enrichConflicts(jobId: string): Promise<ConflictReview[]> {\n const data = await this.request<unknown>(\n \"GET\",\n `${this.base()}/enrich/jobs/${encodeURIComponent(jobId)}/conflicts`,\n undefined,\n 30_000,\n );\n return Array.isArray(data) ? (data as ConflictReview[]) : [];\n }\n\n /** Apply a set of conflict review decisions to a job. */\n async enrichApply(\n jobId: string,\n decisions: ConflictReview[],\n ): Promise<{ applied: number }> {\n return this.request<{ applied: number }>(\n \"POST\",\n `${this.base()}/enrich/jobs/${encodeURIComponent(jobId)}/apply`,\n { decisions },\n 60_000,\n );\n }\n\n /** Cancel an enrichment job. */\n async enrichCancel(jobId: string): Promise<void> {\n await this.request<void>(\n \"DELETE\",\n `${this.base()}/enrich/jobs/${encodeURIComponent(jobId)}`,\n undefined,\n 15_000,\n );\n }\n\n /** Per-type breakdown for one type in one KG: definition + counts + samples.\n *\n * System predicates (rdfs:label, ingested_at, source) are hidden by default\n * — they're attached to every entity at 100% and drown out the columns the\n * user cares about. Pass `includeSystem: true` to see them. */\n async typeUsage(\n kg: string,\n typeName: string,\n opts: { includeSystem?: boolean } = {},\n ): Promise<TypeUsage> {\n const qs = opts.includeSystem ? \"?include_system=true\" : \"\";\n return this.request<TypeUsage>(\n \"GET\",\n `${this.base()}/kgs/${encodeURIComponent(kg)}/types/${encodeURIComponent(typeName)}/usage${qs}`,\n undefined,\n 30_000,\n );\n }\n\n /** Explorer summary for a type — like typeUsage but adds coverage_pct + avg_degree. */\n async typeSummary(kg: string, typeName: string): Promise<TypeSummary> {\n return this.request<TypeSummary>(\n \"GET\",\n `${this.base()}/explore/kgs/${encodeURIComponent(kg)}/types/${encodeURIComponent(typeName)}/summary`,\n undefined,\n 30_000,\n );\n }\n\n /** Search types or attributes by name substring within a KG. */\n async exploreSearch(\n kg: string,\n q: string,\n kind: \"type\" | \"attr\" = \"type\",\n ): Promise<Array<Record<string, unknown>>> {\n const qs = new URLSearchParams({ kg, q, kind }).toString();\n const data = await this.request<unknown>(\n \"GET\",\n `${this.base()}/explore/search?${qs}`,\n undefined,\n 15_000,\n );\n return Array.isArray(data) ? (data as Array<Record<string, unknown>>) : [];\n }\n}\n\n/**\n * A single ontology change resolved against the existing schema. The same\n * shape is returned under `applied`/`proposals` by {@link Client.ontologyResolve}\n * and accepted as the request body by {@link Client.ontologyApply}.\n */\nexport interface ResolvedChange {\n kind: \"attribute\" | \"relationship\";\n subject_type: string;\n name: string;\n datatype_or_target: string;\n action: \"reuse\" | \"extend\" | \"create\";\n confidence: number;\n reason: string;\n}\n\nexport interface OntologyResolveResult {\n applied: ResolvedChange[];\n proposals: ResolvedChange[];\n summary: string;\n}\n\nexport interface OntologyApplyResult {\n applied: ResolvedChange;\n operations: number;\n summary: string;\n}\n\nexport interface TypeCount {\n name: string;\n entity_count: number;\n}\n\nexport interface AttributeUsage {\n name: string;\n datatype: string;\n count: number;\n}\n\nexport interface RelationshipUsage {\n name: string;\n target_type: string | null;\n count: number;\n}\n\nexport interface EntitySample {\n uri: string;\n label: string;\n}\n\nexport interface TypeUsage {\n name: string;\n description: string;\n parent_type: string | null;\n entity_count: number;\n attributes: AttributeUsage[];\n relationships: RelationshipUsage[];\n samples: EntitySample[];\n}\n\nexport interface AttributeSummary {\n name: string;\n predicate_uri: string;\n datatype: string;\n count: number;\n coverage_pct: number;\n}\n\nexport interface RelationshipSummary {\n name: string;\n predicate_uri: string;\n target_type: string | null;\n count: number;\n coverage_pct: number;\n avg_degree: number;\n}\n\nexport interface TypeSummary {\n name: string;\n description: string;\n parent_type: string | null;\n entity_count: number;\n attributes: AttributeSummary[];\n relationships: RelationshipSummary[];\n}\n\nexport type EnrichmentTier = \"lite\" | \"base\" | \"core\" | \"pro\";\nexport type JobStatus =\n | \"queued\"\n | \"running\"\n | \"review\"\n | \"applied\"\n | \"cancelled\"\n | \"failed\";\nexport type ConflictPolicy = \"skip\" | \"verify\" | \"overwrite\" | \"stage\";\nexport type RowAction =\n | \"filled\"\n | \"verified\"\n | \"conflict\"\n | \"skipped\"\n | \"no_match\";\nexport type ReviewDecision = \"accept\" | \"reject\" | \"skip\";\n\nexport interface EnrichRequest {\n type_name: string;\n attributes: string[];\n tier?: EnrichmentTier;\n kg_name: string;\n conflict_policy?: ConflictPolicy;\n confidence_min?: number;\n limit?: number;\n}\n\nexport interface EnrichJobCreate {\n job_id: string;\n status: JobStatus;\n estimated_cost_usd: number;\n total_entities: number;\n}\n\nexport interface Verdict {\n value: string;\n confidence: number;\n source: string;\n source_url?: string | null;\n reasoning?: string | null;\n}\n\nexport interface JobProgress {\n total: number;\n processed: number;\n filled: number;\n verified: number;\n conflicts: number;\n skipped: number;\n cache_hits: number;\n}\n\nexport interface RowResult {\n entity_uri: string;\n attribute: string;\n existing_value: string | null;\n verdict: Verdict | null;\n action: RowAction;\n}\n\nexport interface JobSummary {\n id: string;\n tenant_id: string;\n kg_name: string;\n type_name: string;\n attributes: string[];\n tier: EnrichmentTier;\n status: JobStatus;\n progress: JobProgress;\n created_at: string;\n started_at?: string | null;\n completed_at?: string | null;\n conflict_policy: ConflictPolicy;\n confidence_min: number;\n error?: string | null;\n}\n\nexport interface EnrichJob extends JobSummary {\n results?: RowResult[];\n limit?: number | null;\n}\n\nexport interface ConflictReview {\n entity_uri: string;\n attribute: string;\n existing_value: string;\n proposed: Verdict;\n decision?: ReviewDecision | null;\n}\n\n// --- Unified Ask-AI agent (COG-118 / COG-125) -------------------------------- #\n\n/** Inputs to {@link Client.agent} — mirror the `/agent` HTTP body. */\nexport interface AgentTurnOptions {\n /** The user's natural-language message. Optional when `confirmPlanId` is set\n * (a confirm turn carries no new message). */\n message?: string;\n /** Knowledge graph the turn operates within. */\n kgName?: string;\n /** Optional active type scope (needed for enrich/clean/dedup planning). */\n typeName?: string;\n /** Optional conversation/session id for multi-turn continuity. */\n sessionId?: string;\n /** When set, the server CONFIRMS + EXECUTES this previously-proposed plan\n * (the only mutating path) instead of classifying a new message. */\n confirmPlanId?: string;\n}\n\n/**\n * The kind-tagged result of one agent turn. The server returns exactly one of:\n * - `answer` — a read-only answer (questions; an ontology INSPECT) with SPARQL.\n * - `clarify` — the agent needs more detail; ask the user `question`.\n * - `plan` — a proposed (un-executed) plan with `plan_id` + `steps`; confirm\n * by calling `agent({ confirmPlanId: plan_id })`.\n * - `result` — the outcome of executing a confirmed plan, per-step.\n * - `error` — e.g. an unknown/expired plan_id on confirm.\n * Extra fields vary by kind (answer/sparql/rows; question; plan_id/steps;\n * steps), so this is intentionally open beyond the discriminant.\n */\nexport interface AgentResult {\n kind: \"answer\" | \"clarify\" | \"plan\" | \"result\" | \"error\";\n [key: string]: unknown;\n}\n"],"mappings":"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","names":["text"]}
|
|
File without changes
|