cograph 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +75 -0
- package/dist/cli.js +550 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.js +273 -0
- package/dist/index.js.map +1 -0
- package/package.json +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# cograph
|
|
2
|
+
|
|
3
|
+
Node.js SDK and CLI for [Cograph](https://cograph.cloud) — turn raw data into a queryable knowledge graph.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install cograph
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## SDK
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { Client, CographError } from "cograph";
|
|
15
|
+
|
|
16
|
+
const client = new Client({ apiKey: process.env.COGRAPH_API_KEY });
|
|
17
|
+
|
|
18
|
+
await client.ingest("sales.csv", { kg: "sales" });
|
|
19
|
+
const result = await client.ask("What's the average deal size by region?", { kg: "sales" });
|
|
20
|
+
console.log(result.answer);
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Constructor
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
new Client({
|
|
27
|
+
apiKey?: string, // env: COGRAPH_API_KEY
|
|
28
|
+
baseUrl?: string, // env: COGRAPH_API_URL (default: https://api.cograph.cloud)
|
|
29
|
+
tenant?: string, // env: COGRAPH_TENANT (default: demo-tenant)
|
|
30
|
+
})
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Methods
|
|
34
|
+
|
|
35
|
+
- `ingest(pathOrText, { kg?, contentType? })` — auto-detects CSV by extension and uses two-step schema/rows flow; otherwise sends raw content.
|
|
36
|
+
- `ask(question, { kg? })` — returns `{ answer, sparql?, ... }`.
|
|
37
|
+
- `listKgs()` — list knowledge graphs.
|
|
38
|
+
- `deleteKg(name)` — delete a knowledge graph.
|
|
39
|
+
|
|
40
|
+
All errors throw `CographError`.
|
|
41
|
+
|
|
42
|
+
## CLI
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# List / create / delete knowledge graphs
|
|
46
|
+
npx cograph kg list
|
|
47
|
+
npx cograph kg create my-data --description "demo"
|
|
48
|
+
npx cograph kg delete my-data
|
|
49
|
+
|
|
50
|
+
# Ingest data
|
|
51
|
+
npx cograph ingest data.csv --kg my-data
|
|
52
|
+
npx cograph ingest --text "Alice works at Acme" --kg my-data
|
|
53
|
+
|
|
54
|
+
# Ask questions
|
|
55
|
+
npx cograph ask "How many companies?" --kg my-data
|
|
56
|
+
npx cograph ask "Top 5 deals" --kg my-data --debug
|
|
57
|
+
|
|
58
|
+
# Ontology + clear
|
|
59
|
+
npx cograph ontology types
|
|
60
|
+
npx cograph clear --kg my-data --yes
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Environment
|
|
64
|
+
|
|
65
|
+
- `COGRAPH_API_KEY` — required
|
|
66
|
+
- `COGRAPH_API_URL` — default `https://api.cograph.cloud`
|
|
67
|
+
- `COGRAPH_TENANT` — default `demo-tenant`
|
|
68
|
+
|
|
69
|
+
Legacy `OMNIX_*` vars are also accepted.
|
|
70
|
+
|
|
71
|
+
> PDF ingestion is not yet supported in the Node CLI. Use the Python CLI or POST raw bytes to the API.
|
|
72
|
+
|
|
73
|
+
## License
|
|
74
|
+
|
|
75
|
+
MIT
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import { createInterface } from "readline";
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
|
|
7
|
+
// src/client.ts
|
|
8
|
+
import { existsSync, readFileSync, statSync } from "fs";
|
|
9
|
+
import { extname } from "path";
|
|
10
|
+
var CographError = class extends Error {
|
|
11
|
+
status;
|
|
12
|
+
body;
|
|
13
|
+
constructor(message, opts) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = "CographError";
|
|
16
|
+
this.status = opts?.status;
|
|
17
|
+
this.body = opts?.body;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
function envVar(name, fallback) {
|
|
21
|
+
return process.env[`COGRAPH_${name}`] || process.env[`OMNIX_${name}`] || fallback;
|
|
22
|
+
}
|
|
23
|
+
var EXT_FORMAT = {
|
|
24
|
+
".csv": "csv",
|
|
25
|
+
".json": "json",
|
|
26
|
+
".jsonl": "json",
|
|
27
|
+
".txt": "text"
|
|
28
|
+
};
|
|
29
|
+
function parseCsv(content) {
|
|
30
|
+
const rows = [];
|
|
31
|
+
let cur = [];
|
|
32
|
+
let field = "";
|
|
33
|
+
let inQuotes = false;
|
|
34
|
+
for (let i = 0; i < content.length; i++) {
|
|
35
|
+
const ch = content[i];
|
|
36
|
+
if (inQuotes) {
|
|
37
|
+
if (ch === '"') {
|
|
38
|
+
if (content[i + 1] === '"') {
|
|
39
|
+
field += '"';
|
|
40
|
+
i++;
|
|
41
|
+
} else {
|
|
42
|
+
inQuotes = false;
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
field += ch;
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
if (ch === '"') {
|
|
49
|
+
inQuotes = true;
|
|
50
|
+
} else if (ch === ",") {
|
|
51
|
+
cur.push(field);
|
|
52
|
+
field = "";
|
|
53
|
+
} else if (ch === "\n") {
|
|
54
|
+
cur.push(field);
|
|
55
|
+
rows.push(cur);
|
|
56
|
+
cur = [];
|
|
57
|
+
field = "";
|
|
58
|
+
} else if (ch === "\r") {
|
|
59
|
+
if (content[i + 1] !== "\n") {
|
|
60
|
+
cur.push(field);
|
|
61
|
+
rows.push(cur);
|
|
62
|
+
cur = [];
|
|
63
|
+
field = "";
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
field += ch;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (field.length > 0 || cur.length > 0) {
|
|
71
|
+
cur.push(field);
|
|
72
|
+
rows.push(cur);
|
|
73
|
+
}
|
|
74
|
+
if (rows.length === 0) return [];
|
|
75
|
+
const headers = rows[0].map((h) => h.trim());
|
|
76
|
+
const out = [];
|
|
77
|
+
for (let r = 1; r < rows.length; r++) {
|
|
78
|
+
const row = rows[r];
|
|
79
|
+
if (row.length === 1 && row[0] === "") continue;
|
|
80
|
+
const obj = {};
|
|
81
|
+
for (let c = 0; c < headers.length; c++) {
|
|
82
|
+
obj[headers[c]] = row[c] ?? "";
|
|
83
|
+
}
|
|
84
|
+
out.push(obj);
|
|
85
|
+
}
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
var Client = class {
|
|
89
|
+
apiKey;
|
|
90
|
+
baseUrl;
|
|
91
|
+
tenant;
|
|
92
|
+
constructor(opts = {}) {
|
|
93
|
+
this.apiKey = opts.apiKey ?? envVar("API_KEY");
|
|
94
|
+
const url = opts.baseUrl ?? envVar("API_URL") ?? "https://api.cograph.cloud";
|
|
95
|
+
this.baseUrl = url.replace(/\/+$/, "");
|
|
96
|
+
this.tenant = opts.tenant ?? envVar("TENANT") ?? "demo-tenant";
|
|
97
|
+
}
|
|
98
|
+
headers() {
|
|
99
|
+
const h = { "Content-Type": "application/json" };
|
|
100
|
+
if (this.apiKey) h["X-API-Key"] = this.apiKey;
|
|
101
|
+
return h;
|
|
102
|
+
}
|
|
103
|
+
base() {
|
|
104
|
+
return `${this.baseUrl}/graphs/${this.tenant}`;
|
|
105
|
+
}
|
|
106
|
+
async request(method, url, body, timeoutMs = 12e4) {
|
|
107
|
+
const controller = new AbortController();
|
|
108
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
109
|
+
let res;
|
|
110
|
+
try {
|
|
111
|
+
res = await fetch(url, {
|
|
112
|
+
method,
|
|
113
|
+
headers: this.headers(),
|
|
114
|
+
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
115
|
+
signal: controller.signal
|
|
116
|
+
});
|
|
117
|
+
} catch (err) {
|
|
118
|
+
clearTimeout(timer);
|
|
119
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
120
|
+
throw new CographError(`Request to ${url} timed out after ${timeoutMs}ms`);
|
|
121
|
+
}
|
|
122
|
+
throw new CographError(
|
|
123
|
+
`Network error contacting ${url}: ${err instanceof Error ? err.message : String(err)}`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
clearTimeout(timer);
|
|
127
|
+
if (!res.ok) {
|
|
128
|
+
let text2 = "";
|
|
129
|
+
try {
|
|
130
|
+
text2 = await res.text();
|
|
131
|
+
} catch {
|
|
132
|
+
}
|
|
133
|
+
throw new CographError(`HTTP ${res.status}: ${text2}`, {
|
|
134
|
+
status: res.status,
|
|
135
|
+
body: text2
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
if (res.status === 204) return void 0;
|
|
139
|
+
const ct = res.headers.get("content-type") ?? "";
|
|
140
|
+
if (ct.includes("application/json")) {
|
|
141
|
+
return await res.json();
|
|
142
|
+
}
|
|
143
|
+
const text = await res.text();
|
|
144
|
+
try {
|
|
145
|
+
return JSON.parse(text);
|
|
146
|
+
} catch {
|
|
147
|
+
return text;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Ingest a file path or raw text into a knowledge graph.
|
|
152
|
+
*
|
|
153
|
+
* If `pathOrText` points to an existing file, its contents are read and the
|
|
154
|
+
* format is inferred from the extension (.csv, .json, .txt) unless
|
|
155
|
+
* `contentType` is given. CSV files use the two-step schema-inference + row
|
|
156
|
+
* mapping flow.
|
|
157
|
+
*/
|
|
158
|
+
async ingest(pathOrText, opts = {}) {
|
|
159
|
+
let content;
|
|
160
|
+
let fmt;
|
|
161
|
+
let isFile = false;
|
|
162
|
+
try {
|
|
163
|
+
isFile = existsSync(pathOrText) && statSync(pathOrText).isFile();
|
|
164
|
+
} catch {
|
|
165
|
+
isFile = false;
|
|
166
|
+
}
|
|
167
|
+
if (isFile) {
|
|
168
|
+
const ext = extname(pathOrText).toLowerCase();
|
|
169
|
+
if (ext === ".pdf") {
|
|
170
|
+
throw new CographError(
|
|
171
|
+
"PDF ingest not yet supported in the Node CLI; use the Python CLI or POST raw bytes to the API."
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
content = readFileSync(pathOrText, "utf-8");
|
|
175
|
+
fmt = opts.contentType ?? EXT_FORMAT[ext] ?? "text";
|
|
176
|
+
if (fmt === "csv") {
|
|
177
|
+
return this.ingestCsv(content, opts.kg);
|
|
178
|
+
}
|
|
179
|
+
} else {
|
|
180
|
+
content = pathOrText;
|
|
181
|
+
fmt = opts.contentType ?? "text";
|
|
182
|
+
}
|
|
183
|
+
const body = {
|
|
184
|
+
content,
|
|
185
|
+
content_type: fmt,
|
|
186
|
+
source: "client"
|
|
187
|
+
};
|
|
188
|
+
if (opts.kg) body.kg_name = opts.kg;
|
|
189
|
+
return this.request("POST", `${this.base()}/ingest`, body, 12e4);
|
|
190
|
+
}
|
|
191
|
+
async ingestCsv(content, kgName) {
|
|
192
|
+
const rows = parseCsv(content);
|
|
193
|
+
if (rows.length === 0) throw new CographError("CSV is empty");
|
|
194
|
+
const headers = Object.keys(rows[0]);
|
|
195
|
+
const schemaBody = {
|
|
196
|
+
headers,
|
|
197
|
+
sample_rows: rows.slice(0, 5),
|
|
198
|
+
total_rows: rows.length
|
|
199
|
+
};
|
|
200
|
+
const mapping = await this.request(
|
|
201
|
+
"POST",
|
|
202
|
+
`${this.base()}/ingest/csv/schema`,
|
|
203
|
+
schemaBody,
|
|
204
|
+
3e5
|
|
205
|
+
);
|
|
206
|
+
const batchSize = 50;
|
|
207
|
+
let totalEntities = 0;
|
|
208
|
+
let totalTriples = 0;
|
|
209
|
+
for (let i = 0; i < rows.length; i += batchSize) {
|
|
210
|
+
const batch = rows.slice(i, i + batchSize);
|
|
211
|
+
const body = {
|
|
212
|
+
mapping,
|
|
213
|
+
rows: batch,
|
|
214
|
+
source: "client"
|
|
215
|
+
};
|
|
216
|
+
if (kgName) body.kg_name = kgName;
|
|
217
|
+
const result = await this.request("POST", `${this.base()}/ingest/csv/rows`, body, 3e5);
|
|
218
|
+
totalEntities += result.entities_resolved ?? 0;
|
|
219
|
+
totalTriples += result.triples_inserted ?? 0;
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
entities_resolved: totalEntities,
|
|
223
|
+
triples_inserted: totalTriples,
|
|
224
|
+
mapping
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
/** Ask a natural language question and return the parsed response. */
|
|
228
|
+
async ask(question, opts = {}) {
|
|
229
|
+
const body = { question };
|
|
230
|
+
if (opts.kg) body.kg_name = opts.kg;
|
|
231
|
+
if (opts.model) body.model = opts.model;
|
|
232
|
+
return this.request("POST", `${this.base()}/ask`, body, 6e4);
|
|
233
|
+
}
|
|
234
|
+
/** List all knowledge graphs for the current tenant. */
|
|
235
|
+
async listKgs() {
|
|
236
|
+
const data = await this.request(
|
|
237
|
+
"GET",
|
|
238
|
+
`${this.base()}/kgs`,
|
|
239
|
+
void 0,
|
|
240
|
+
15e3
|
|
241
|
+
);
|
|
242
|
+
if (Array.isArray(data)) return data;
|
|
243
|
+
if (data && typeof data === "object" && "kgs" in data) {
|
|
244
|
+
const kgs = data.kgs;
|
|
245
|
+
if (Array.isArray(kgs)) return kgs;
|
|
246
|
+
}
|
|
247
|
+
return [];
|
|
248
|
+
}
|
|
249
|
+
/** Create a knowledge graph. */
|
|
250
|
+
async createKg(name, description) {
|
|
251
|
+
const body = { name };
|
|
252
|
+
if (description) body.description = description;
|
|
253
|
+
return this.request("POST", `${this.base()}/kgs`, body, 15e3);
|
|
254
|
+
}
|
|
255
|
+
/** Delete a knowledge graph by name. */
|
|
256
|
+
async deleteKg(name) {
|
|
257
|
+
return this.request(
|
|
258
|
+
"DELETE",
|
|
259
|
+
`${this.base()}/kgs/${encodeURIComponent(name)}`,
|
|
260
|
+
void 0,
|
|
261
|
+
3e4
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
/** List ontology types. */
|
|
265
|
+
async ontologyTypes() {
|
|
266
|
+
const data = await this.request(
|
|
267
|
+
"GET",
|
|
268
|
+
`${this.base()}/ontology/types`,
|
|
269
|
+
void 0,
|
|
270
|
+
15e3
|
|
271
|
+
);
|
|
272
|
+
return Array.isArray(data) ? data : [];
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
// src/cli.ts
|
|
277
|
+
function client() {
|
|
278
|
+
return new Client();
|
|
279
|
+
}
|
|
280
|
+
function fail(msg, code = 1) {
|
|
281
|
+
process.stderr.write(msg.endsWith("\n") ? msg : msg + "\n");
|
|
282
|
+
process.exit(code);
|
|
283
|
+
}
|
|
284
|
+
async function withErrors(fn) {
|
|
285
|
+
try {
|
|
286
|
+
return await fn();
|
|
287
|
+
} catch (err) {
|
|
288
|
+
if (err instanceof CographError) {
|
|
289
|
+
fail(`Error: ${err.message}`);
|
|
290
|
+
}
|
|
291
|
+
fail(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
async function confirm(prompt) {
|
|
295
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
296
|
+
return new Promise((resolve) => {
|
|
297
|
+
rl.question(`${prompt} [y/N] `, (ans) => {
|
|
298
|
+
rl.close();
|
|
299
|
+
resolve(ans.trim().toLowerCase() === "y");
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
var program = new Command();
|
|
304
|
+
program.name("cograph").description("Cograph Knowledge Graph CLI").version("0.1.0");
|
|
305
|
+
var kg = program.command("kg").description("Manage knowledge graphs");
|
|
306
|
+
kg.command("list").description("List knowledge graphs").action(async () => {
|
|
307
|
+
await withErrors(async () => {
|
|
308
|
+
const kgs = await client().listKgs();
|
|
309
|
+
if (!kgs.length) {
|
|
310
|
+
process.stdout.write(
|
|
311
|
+
"No knowledge graphs. Create one with: cograph kg create <name>\n"
|
|
312
|
+
);
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
for (const k of kgs) {
|
|
316
|
+
const name = String(k.name ?? "?");
|
|
317
|
+
const triples = Number(k.triple_count ?? 0);
|
|
318
|
+
const desc = k.description ? ` \u2014 ${k.description}` : "";
|
|
319
|
+
const padName = name.padEnd(20, " ");
|
|
320
|
+
const padTriples = String(triples).padStart(6, " ");
|
|
321
|
+
process.stdout.write(` ${padName} ${padTriples} triples${desc}
|
|
322
|
+
`);
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
kg.command("create <name>").description("Create a knowledge graph").option("-d, --description <text>", "Description").action(async (name, opts) => {
|
|
327
|
+
await withErrors(async () => {
|
|
328
|
+
const created = await client().createKg(name, opts.description);
|
|
329
|
+
process.stdout.write(`Created knowledge graph: ${created.name ?? name}
|
|
330
|
+
`);
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
kg.command("delete <name>").description("Delete a knowledge graph").action(async (name) => {
|
|
334
|
+
await withErrors(async () => {
|
|
335
|
+
await client().deleteKg(name);
|
|
336
|
+
process.stdout.write(`Deleted knowledge graph: ${name}
|
|
337
|
+
`);
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
program.command("ingest [file]").description("Ingest data from a file or --text").option("-t, --text <text>", "Inline text to ingest").option("--kg <name>", "Target knowledge graph name").option(
|
|
341
|
+
"-f, --format <fmt>",
|
|
342
|
+
"Override format detection (text|csv|json)"
|
|
343
|
+
).action(
|
|
344
|
+
async (file, opts) => {
|
|
345
|
+
await withErrors(async () => {
|
|
346
|
+
const c = client();
|
|
347
|
+
if (opts.text) {
|
|
348
|
+
process.stdout.write(
|
|
349
|
+
`Ingesting text (${opts.text.length.toLocaleString()} chars)...
|
|
350
|
+
`
|
|
351
|
+
);
|
|
352
|
+
const result2 = await c.ingest(opts.text, {
|
|
353
|
+
kg: opts.kg,
|
|
354
|
+
contentType: opts.format ?? "text"
|
|
355
|
+
});
|
|
356
|
+
printIngestResult(result2);
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
if (!file) {
|
|
360
|
+
fail("Provide a file or --text");
|
|
361
|
+
}
|
|
362
|
+
process.stdout.write(`Ingesting ${file}...
|
|
363
|
+
`);
|
|
364
|
+
const result = await c.ingest(file, {
|
|
365
|
+
kg: opts.kg,
|
|
366
|
+
contentType: opts.format
|
|
367
|
+
});
|
|
368
|
+
printIngestResult(result);
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
);
|
|
372
|
+
function printIngestResult(result) {
|
|
373
|
+
const num = (k) => Number(result[k] ?? 0);
|
|
374
|
+
process.stdout.write(` Entities extracted: ${num("entities_extracted")}
|
|
375
|
+
`);
|
|
376
|
+
process.stdout.write(` Entities resolved: ${num("entities_resolved")}
|
|
377
|
+
`);
|
|
378
|
+
process.stdout.write(` Triples inserted: ${num("triples_inserted")}
|
|
379
|
+
`);
|
|
380
|
+
const types = result.types_created;
|
|
381
|
+
if (Array.isArray(types) && types.length) {
|
|
382
|
+
process.stdout.write(` Types created: ${types.join(", ")}
|
|
383
|
+
`);
|
|
384
|
+
}
|
|
385
|
+
const rejections = result.rejections;
|
|
386
|
+
if (Array.isArray(rejections) && rejections.length) {
|
|
387
|
+
process.stdout.write(` Rejections: ${rejections.length}
|
|
388
|
+
`);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
program.command("ask <question>").description("Ask a natural language question").option("--kg <name>", "Knowledge graph to query").option("-d, --debug", "Show SPARQL and latency breakdown").option("-m, --model <model>", "Override query model").action(
|
|
392
|
+
async (question, opts) => {
|
|
393
|
+
await withErrors(async () => {
|
|
394
|
+
if (opts.model) process.stdout.write(`Model: ${opts.model}
|
|
395
|
+
`);
|
|
396
|
+
process.stdout.write(`Q: ${question}
|
|
397
|
+
`);
|
|
398
|
+
process.stdout.write("Generating answer...\n");
|
|
399
|
+
const t0 = Date.now();
|
|
400
|
+
const result = await client().ask(question, {
|
|
401
|
+
kg: opts.kg,
|
|
402
|
+
model: opts.model
|
|
403
|
+
});
|
|
404
|
+
const roundtripMs = Date.now() - t0;
|
|
405
|
+
process.stdout.write(`
|
|
406
|
+
A: ${result.answer ?? "No answer"}
|
|
407
|
+
`);
|
|
408
|
+
if (opts.debug) {
|
|
409
|
+
process.stdout.write(`
|
|
410
|
+
SPARQL:
|
|
411
|
+
${result.sparql ?? ""}
|
|
412
|
+
`);
|
|
413
|
+
const timing = result.timing ?? {};
|
|
414
|
+
if (Object.keys(timing).length) {
|
|
415
|
+
process.stdout.write(`
|
|
416
|
+
${"\u2500".repeat(40)}
|
|
417
|
+
`);
|
|
418
|
+
process.stdout.write(
|
|
419
|
+
`${"Stage".padEnd(25)} ${"Time".padStart(10)}
|
|
420
|
+
`
|
|
421
|
+
);
|
|
422
|
+
process.stdout.write(`${"\u2500".repeat(40)}
|
|
423
|
+
`);
|
|
424
|
+
for (const [key, val] of Object.entries(timing)) {
|
|
425
|
+
if (key === "attempts") {
|
|
426
|
+
process.stdout.write(
|
|
427
|
+
`${"Attempts".padEnd(25)} ${String(val).padStart(10)}
|
|
428
|
+
`
|
|
429
|
+
);
|
|
430
|
+
} else if (typeof val === "string") {
|
|
431
|
+
const label = key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
432
|
+
process.stdout.write(
|
|
433
|
+
`${label.padEnd(25)} ${val.padStart(10)}
|
|
434
|
+
`
|
|
435
|
+
);
|
|
436
|
+
} else {
|
|
437
|
+
const label = key.replace(/_ms$/, "").replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
438
|
+
const num = typeof val === "number" ? val : Number(val);
|
|
439
|
+
process.stdout.write(
|
|
440
|
+
`${label.padEnd(25)} ${num.toFixed(1).padStart(8)}ms
|
|
441
|
+
`
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
process.stdout.write(`${"\u2500".repeat(40)}
|
|
446
|
+
`);
|
|
447
|
+
process.stdout.write(
|
|
448
|
+
`${"Client roundtrip".padEnd(25)} ${roundtripMs.toFixed(1).padStart(8)}ms
|
|
449
|
+
`
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
);
|
|
456
|
+
var onto = program.command("ontology").description("View ontology");
|
|
457
|
+
onto.command("types").description("List ontology types").action(async () => {
|
|
458
|
+
await withErrors(async () => {
|
|
459
|
+
const types = await client().ontologyTypes();
|
|
460
|
+
if (!types.length) {
|
|
461
|
+
process.stdout.write("No ontology types defined.\n");
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
for (const t of types) {
|
|
465
|
+
const parent = t.parent_type ? ` (subClassOf ${t.parent_type})` : "";
|
|
466
|
+
const desc = t.description ? ` \u2014 ${t.description}` : "";
|
|
467
|
+
process.stdout.write(` ${t.name}${parent}${desc}
|
|
468
|
+
`);
|
|
469
|
+
const attrs = t.attributes ?? [];
|
|
470
|
+
for (const a of attrs) {
|
|
471
|
+
process.stdout.write(
|
|
472
|
+
` .${a.name} (${a.datatype ?? "string"})
|
|
473
|
+
`
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
});
|
|
479
|
+
program.command("clear").description("Clear data").option("--kg <name>", "Clear a specific knowledge graph").option(
|
|
480
|
+
"--include-ontology",
|
|
481
|
+
"Also clear the ontology (only meaningful when --kg is omitted)",
|
|
482
|
+
false
|
|
483
|
+
).option("-y, --yes", "Skip confirmation", false).action(
|
|
484
|
+
async (opts) => {
|
|
485
|
+
await withErrors(async () => {
|
|
486
|
+
let msg;
|
|
487
|
+
if (opts.kg) {
|
|
488
|
+
msg = `Clear KG '${opts.kg}'?`;
|
|
489
|
+
} else if (opts.includeOntology) {
|
|
490
|
+
msg = "Clear EVERYTHING including ontology?";
|
|
491
|
+
} else {
|
|
492
|
+
msg = "Clear all instance data (ontology preserved)?";
|
|
493
|
+
}
|
|
494
|
+
if (!opts.yes) {
|
|
495
|
+
const ok = await confirm(msg);
|
|
496
|
+
if (!ok) {
|
|
497
|
+
process.stdout.write("Cancelled.\n");
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
const c = client();
|
|
502
|
+
if (opts.kg) {
|
|
503
|
+
await c.deleteKg(opts.kg);
|
|
504
|
+
process.stdout.write(`Cleared KG: ${opts.kg}
|
|
505
|
+
`);
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
const tenant = c.tenant;
|
|
509
|
+
const baseUrl = `${c.baseUrl}/graphs/${tenant}`;
|
|
510
|
+
const headers = {
|
|
511
|
+
"Content-Type": "application/json"
|
|
512
|
+
};
|
|
513
|
+
if (c.apiKey) headers["X-API-Key"] = c.apiKey;
|
|
514
|
+
const filters = opts.includeOntology ? "" : `FILTER(CONTAINS(STR(?s), '/entities/') || CONTAINS(STR(?s), '/onto/') || CONTAINS(STR(?s), '/kgs/'))`;
|
|
515
|
+
const query = `SELECT ?s ?p ?o FROM <https://omnix.dev/graphs/${tenant}> WHERE { ?s ?p ?o . ${filters} } LIMIT 1000`;
|
|
516
|
+
process.stdout.write("Clearing...\n");
|
|
517
|
+
let deleted = 0;
|
|
518
|
+
for (let i = 0; i < 50; i++) {
|
|
519
|
+
const fetchRes = await fetch(`${baseUrl}/query`, {
|
|
520
|
+
method: "POST",
|
|
521
|
+
headers,
|
|
522
|
+
body: JSON.stringify({ query })
|
|
523
|
+
});
|
|
524
|
+
if (!fetchRes.ok) break;
|
|
525
|
+
const data = await fetchRes.json();
|
|
526
|
+
const bindings = data.bindings ?? [];
|
|
527
|
+
if (!bindings.length) break;
|
|
528
|
+
const triples = bindings.filter((b) => b.s).map((b) => ({
|
|
529
|
+
subject: b.s,
|
|
530
|
+
predicate: b.p,
|
|
531
|
+
object: b.o
|
|
532
|
+
}));
|
|
533
|
+
for (let j = 0; j < triples.length; j += 100) {
|
|
534
|
+
await fetch(`${baseUrl}/triples`, {
|
|
535
|
+
method: "DELETE",
|
|
536
|
+
headers,
|
|
537
|
+
body: JSON.stringify({ triples: triples.slice(j, j + 100) })
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
deleted += triples.length;
|
|
541
|
+
}
|
|
542
|
+
process.stdout.write(`Deleted ${deleted} triples
|
|
543
|
+
`);
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
);
|
|
547
|
+
program.parseAsync(process.argv).catch((err) => {
|
|
548
|
+
fail(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
549
|
+
});
|
|
550
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/client.ts"],"sourcesContent":["import { createInterface } from \"node:readline\";\nimport { Command } from \"commander\";\nimport { Client, CographError } from \"./client.js\";\n\nfunction client(): Client {\n return new Client();\n}\n\nfunction printJson(data: unknown): void {\n process.stdout.write(JSON.stringify(data, null, 2) + \"\\n\");\n}\n\nfunction fail(msg: string, code = 1): never {\n process.stderr.write(msg.endsWith(\"\\n\") ? msg : msg + \"\\n\");\n process.exit(code);\n}\n\nasync function withErrors<T>(fn: () => Promise<T>): Promise<T | void> {\n try {\n return await fn();\n } catch (err) {\n if (err instanceof CographError) {\n fail(`Error: ${err.message}`);\n }\n fail(`Error: ${err instanceof Error ? err.message : String(err)}`);\n }\n}\n\nasync function confirm(prompt: string): Promise<boolean> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n return new Promise((resolve) => {\n rl.question(`${prompt} [y/N] `, (ans) => {\n rl.close();\n resolve(ans.trim().toLowerCase() === \"y\");\n });\n });\n}\n\nconst program = new Command();\nprogram\n .name(\"cograph\")\n .description(\"Cograph Knowledge Graph CLI\")\n .version(\"0.1.0\");\n\n// ---------------------------------------------------------------------------\n// kg\n// ---------------------------------------------------------------------------\n\nconst kg = program.command(\"kg\").description(\"Manage knowledge graphs\");\n\nkg.command(\"list\")\n .description(\"List knowledge graphs\")\n .action(async () => {\n await withErrors(async () => {\n const kgs = await client().listKgs();\n if (!kgs.length) {\n process.stdout.write(\n \"No knowledge graphs. Create one with: cograph kg create <name>\\n\",\n );\n return;\n }\n for (const k of kgs) {\n const name = String(k.name ?? \"?\");\n const triples = Number(k.triple_count ?? 0);\n const desc = k.description ? ` — ${k.description}` : \"\";\n const padName = name.padEnd(20, \" \");\n const padTriples = String(triples).padStart(6, \" \");\n process.stdout.write(` ${padName} ${padTriples} triples${desc}\\n`);\n }\n });\n });\n\nkg.command(\"create <name>\")\n .description(\"Create a knowledge graph\")\n .option(\"-d, --description <text>\", \"Description\")\n .action(async (name: string, opts: { description?: string }) => {\n await withErrors(async () => {\n const created = await client().createKg(name, opts.description);\n process.stdout.write(`Created knowledge graph: ${created.name ?? name}\\n`);\n });\n });\n\nkg.command(\"delete <name>\")\n .description(\"Delete a knowledge graph\")\n .action(async (name: string) => {\n await withErrors(async () => {\n await client().deleteKg(name);\n process.stdout.write(`Deleted knowledge graph: ${name}\\n`);\n });\n });\n\n// ---------------------------------------------------------------------------\n// ingest\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"ingest [file]\")\n .description(\"Ingest data from a file or --text\")\n .option(\"-t, --text <text>\", \"Inline text to ingest\")\n .option(\"--kg <name>\", \"Target knowledge graph name\")\n .option(\n \"-f, --format <fmt>\",\n \"Override format detection (text|csv|json)\",\n )\n .action(\n async (\n file: string | undefined,\n opts: { text?: string; kg?: string; format?: string },\n ) => {\n await withErrors(async () => {\n const c = client();\n if (opts.text) {\n process.stdout.write(\n `Ingesting text (${opts.text.length.toLocaleString()} chars)...\\n`,\n );\n const result = await c.ingest(opts.text, {\n kg: opts.kg,\n contentType: opts.format ?? \"text\",\n });\n printIngestResult(result);\n return;\n }\n if (!file) {\n fail(\"Provide a file or --text\");\n }\n // ingest() handles file reading + format detection + CSV two-step flow.\n process.stdout.write(`Ingesting ${file}...\\n`);\n const result = await c.ingest(file, {\n kg: opts.kg,\n contentType: opts.format,\n });\n printIngestResult(result);\n });\n },\n );\n\nfunction printIngestResult(result: Record<string, unknown>): void {\n const num = (k: string) => Number(result[k] ?? 0);\n process.stdout.write(` Entities extracted: ${num(\"entities_extracted\")}\\n`);\n process.stdout.write(` Entities resolved: ${num(\"entities_resolved\")}\\n`);\n process.stdout.write(` Triples inserted: ${num(\"triples_inserted\")}\\n`);\n const types = result.types_created;\n if (Array.isArray(types) && types.length) {\n process.stdout.write(` Types created: ${types.join(\", \")}\\n`);\n }\n const rejections = result.rejections;\n if (Array.isArray(rejections) && rejections.length) {\n process.stdout.write(` Rejections: ${rejections.length}\\n`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// ask\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"ask <question>\")\n .description(\"Ask a natural language question\")\n .option(\"--kg <name>\", \"Knowledge graph to query\")\n .option(\"-d, --debug\", \"Show SPARQL and latency breakdown\")\n .option(\"-m, --model <model>\", \"Override query model\")\n .action(\n async (\n question: string,\n opts: { kg?: string; debug?: boolean; model?: string },\n ) => {\n await withErrors(async () => {\n if (opts.model) process.stdout.write(`Model: ${opts.model}\\n`);\n process.stdout.write(`Q: ${question}\\n`);\n process.stdout.write(\"Generating answer...\\n\");\n const t0 = Date.now();\n const result = await client().ask(question, {\n kg: opts.kg,\n model: opts.model,\n });\n const roundtripMs = Date.now() - t0;\n process.stdout.write(`\\nA: ${result.answer ?? \"No answer\"}\\n`);\n if (opts.debug) {\n process.stdout.write(`\\nSPARQL:\\n${result.sparql ?? \"\"}\\n`);\n const timing = (result.timing ?? {}) as Record<string, unknown>;\n if (Object.keys(timing).length) {\n process.stdout.write(`\\n${\"─\".repeat(40)}\\n`);\n process.stdout.write(\n `${\"Stage\".padEnd(25)} ${\"Time\".padStart(10)}\\n`,\n );\n process.stdout.write(`${\"─\".repeat(40)}\\n`);\n for (const [key, val] of Object.entries(timing)) {\n if (key === \"attempts\") {\n process.stdout.write(\n `${\"Attempts\".padEnd(25)} ${String(val).padStart(10)}\\n`,\n );\n } else if (typeof val === \"string\") {\n const label = key\n .replace(/_/g, \" \")\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n process.stdout.write(\n `${label.padEnd(25)} ${val.padStart(10)}\\n`,\n );\n } else {\n const label = key\n .replace(/_ms$/, \"\")\n .replace(/_/g, \" \")\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n const num = typeof val === \"number\" ? val : Number(val);\n process.stdout.write(\n `${label.padEnd(25)} ${num.toFixed(1).padStart(8)}ms\\n`,\n );\n }\n }\n process.stdout.write(`${\"─\".repeat(40)}\\n`);\n process.stdout.write(\n `${\"Client roundtrip\".padEnd(25)} ${roundtripMs.toFixed(1).padStart(8)}ms\\n`,\n );\n }\n }\n });\n },\n );\n\n// ---------------------------------------------------------------------------\n// ontology\n// ---------------------------------------------------------------------------\n\nconst onto = program.command(\"ontology\").description(\"View ontology\");\n\nonto\n .command(\"types\")\n .description(\"List ontology types\")\n .action(async () => {\n await withErrors(async () => {\n const types = await client().ontologyTypes();\n if (!types.length) {\n process.stdout.write(\"No ontology types defined.\\n\");\n return;\n }\n for (const t of types) {\n const parent = t.parent_type\n ? ` (subClassOf ${t.parent_type})`\n : \"\";\n const desc = t.description ? ` — ${t.description}` : \"\";\n process.stdout.write(` ${t.name}${parent}${desc}\\n`);\n const attrs = (t.attributes ?? []) as Array<Record<string, unknown>>;\n for (const a of attrs) {\n process.stdout.write(\n ` .${a.name} (${a.datatype ?? \"string\"})\\n`,\n );\n }\n }\n });\n });\n\n// ---------------------------------------------------------------------------\n// clear\n// ---------------------------------------------------------------------------\n\nprogram\n .command(\"clear\")\n .description(\"Clear data\")\n .option(\"--kg <name>\", \"Clear a specific knowledge graph\")\n .option(\n \"--include-ontology\",\n \"Also clear the ontology (only meaningful when --kg is omitted)\",\n false,\n )\n .option(\"-y, --yes\", \"Skip confirmation\", false)\n .action(\n async (opts: { kg?: string; includeOntology?: boolean; yes?: boolean }) => {\n await withErrors(async () => {\n let msg: string;\n if (opts.kg) {\n msg = `Clear KG '${opts.kg}'?`;\n } else if (opts.includeOntology) {\n msg = \"Clear EVERYTHING including ontology?\";\n } else {\n msg = \"Clear all instance data (ontology preserved)?\";\n }\n\n if (!opts.yes) {\n const ok = await confirm(msg);\n if (!ok) {\n process.stdout.write(\"Cancelled.\\n\");\n return;\n }\n }\n\n const c = client();\n if (opts.kg) {\n await c.deleteKg(opts.kg);\n process.stdout.write(`Cleared KG: ${opts.kg}\\n`);\n return;\n }\n\n // Bulk-clear via /query + DELETE /triples — same loop the Python CLI uses.\n const tenant = c.tenant;\n const baseUrl = `${c.baseUrl}/graphs/${tenant}`;\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n if (c.apiKey) headers[\"X-API-Key\"] = c.apiKey;\n\n const filters = opts.includeOntology\n ? \"\"\n : `FILTER(CONTAINS(STR(?s), '/entities/') || CONTAINS(STR(?s), '/onto/') || CONTAINS(STR(?s), '/kgs/'))`;\n const query = `SELECT ?s ?p ?o FROM <https://omnix.dev/graphs/${tenant}> WHERE { ?s ?p ?o . ${filters} } LIMIT 1000`;\n\n process.stdout.write(\"Clearing...\\n\");\n let deleted = 0;\n for (let i = 0; i < 50; i++) {\n const fetchRes = await fetch(`${baseUrl}/query`, {\n method: \"POST\",\n headers,\n body: JSON.stringify({ query }),\n });\n if (!fetchRes.ok) break;\n const data = (await fetchRes.json()) as {\n bindings?: Array<Record<string, unknown>>;\n };\n const bindings = data.bindings ?? [];\n if (!bindings.length) break;\n const triples = bindings\n .filter((b) => b.s)\n .map((b) => ({\n subject: b.s,\n predicate: b.p,\n object: b.o,\n }));\n for (let j = 0; j < triples.length; j += 100) {\n await fetch(`${baseUrl}/triples`, {\n method: \"DELETE\",\n headers,\n body: JSON.stringify({ triples: triples.slice(j, j + 100) }),\n });\n }\n deleted += triples.length;\n }\n process.stdout.write(`Deleted ${deleted} triples\\n`);\n });\n },\n );\n\n// ---------------------------------------------------------------------------\n\nprogram.parseAsync(process.argv).catch((err) => {\n fail(`Error: ${err instanceof Error ? err.message : String(err)}`);\n});\n\n// silence unused import warning if ever needed\nvoid printJson;\n","import { existsSync, readFileSync, statSync } from \"node:fs\";\nimport { extname } from \"node:path\";\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}\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 this.apiKey = opts.apiKey ?? envVar(\"API_KEY\");\n const url = opts.baseUrl ?? envVar(\"API_URL\") ?? \"https://api.cograph.cloud\";\n this.baseUrl = url.replace(/\\/+$/, \"\");\n this.tenant = opts.tenant ?? envVar(\"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 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.kg);\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 kgName: string | undefined,\n ): Promise<Record<string, unknown>> {\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 const schemaBody = {\n headers,\n sample_rows: rows.slice(0, 5),\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 const batchSize = 50;\n let totalEntities = 0;\n let totalTriples = 0;\n for (let i = 0; i < rows.length; i += batchSize) {\n const batch = rows.slice(i, i + batchSize);\n const body: Record<string, unknown> = {\n mapping,\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 totalEntities += result.entities_resolved ?? 0;\n totalTriples += result.triples_inserted ?? 0;\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 /** 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"],"mappings":"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","names":["text","result"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
declare class CographError extends Error {
|
|
2
|
+
status?: number;
|
|
3
|
+
body?: string;
|
|
4
|
+
constructor(message: string, opts?: {
|
|
5
|
+
status?: number;
|
|
6
|
+
body?: string;
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
interface ClientOptions {
|
|
10
|
+
apiKey?: string;
|
|
11
|
+
baseUrl?: string;
|
|
12
|
+
tenant?: string;
|
|
13
|
+
}
|
|
14
|
+
interface IngestOptions {
|
|
15
|
+
kg?: string;
|
|
16
|
+
contentType?: "text" | "csv" | "json" | string;
|
|
17
|
+
}
|
|
18
|
+
interface AskOptions {
|
|
19
|
+
kg?: string;
|
|
20
|
+
model?: string;
|
|
21
|
+
}
|
|
22
|
+
declare class Client {
|
|
23
|
+
apiKey: string | undefined;
|
|
24
|
+
baseUrl: string;
|
|
25
|
+
tenant: string;
|
|
26
|
+
constructor(opts?: ClientOptions);
|
|
27
|
+
private headers;
|
|
28
|
+
private base;
|
|
29
|
+
private request;
|
|
30
|
+
/**
|
|
31
|
+
* Ingest a file path or raw text into a knowledge graph.
|
|
32
|
+
*
|
|
33
|
+
* If `pathOrText` points to an existing file, its contents are read and the
|
|
34
|
+
* format is inferred from the extension (.csv, .json, .txt) unless
|
|
35
|
+
* `contentType` is given. CSV files use the two-step schema-inference + row
|
|
36
|
+
* mapping flow.
|
|
37
|
+
*/
|
|
38
|
+
ingest(pathOrText: string, opts?: IngestOptions): Promise<Record<string, unknown>>;
|
|
39
|
+
private ingestCsv;
|
|
40
|
+
/** Ask a natural language question and return the parsed response. */
|
|
41
|
+
ask(question: string, opts?: AskOptions): Promise<Record<string, unknown>>;
|
|
42
|
+
/** List all knowledge graphs for the current tenant. */
|
|
43
|
+
listKgs(): Promise<Array<Record<string, unknown>>>;
|
|
44
|
+
/** Create a knowledge graph. */
|
|
45
|
+
createKg(name: string, description?: string): Promise<Record<string, unknown>>;
|
|
46
|
+
/** Delete a knowledge graph by name. */
|
|
47
|
+
deleteKg(name: string): Promise<Record<string, unknown>>;
|
|
48
|
+
/** List ontology types. */
|
|
49
|
+
ontologyTypes(): Promise<Array<Record<string, unknown>>>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export { type AskOptions, Client, type ClientOptions, CographError, type IngestOptions };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
// src/client.ts
|
|
2
|
+
import { existsSync, readFileSync, statSync } from "fs";
|
|
3
|
+
import { extname } from "path";
|
|
4
|
+
var CographError = class extends Error {
|
|
5
|
+
status;
|
|
6
|
+
body;
|
|
7
|
+
constructor(message, opts) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = "CographError";
|
|
10
|
+
this.status = opts?.status;
|
|
11
|
+
this.body = opts?.body;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
function envVar(name, fallback) {
|
|
15
|
+
return process.env[`COGRAPH_${name}`] || process.env[`OMNIX_${name}`] || fallback;
|
|
16
|
+
}
|
|
17
|
+
var EXT_FORMAT = {
|
|
18
|
+
".csv": "csv",
|
|
19
|
+
".json": "json",
|
|
20
|
+
".jsonl": "json",
|
|
21
|
+
".txt": "text"
|
|
22
|
+
};
|
|
23
|
+
function parseCsv(content) {
|
|
24
|
+
const rows = [];
|
|
25
|
+
let cur = [];
|
|
26
|
+
let field = "";
|
|
27
|
+
let inQuotes = false;
|
|
28
|
+
for (let i = 0; i < content.length; i++) {
|
|
29
|
+
const ch = content[i];
|
|
30
|
+
if (inQuotes) {
|
|
31
|
+
if (ch === '"') {
|
|
32
|
+
if (content[i + 1] === '"') {
|
|
33
|
+
field += '"';
|
|
34
|
+
i++;
|
|
35
|
+
} else {
|
|
36
|
+
inQuotes = false;
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
field += ch;
|
|
40
|
+
}
|
|
41
|
+
} else {
|
|
42
|
+
if (ch === '"') {
|
|
43
|
+
inQuotes = true;
|
|
44
|
+
} else if (ch === ",") {
|
|
45
|
+
cur.push(field);
|
|
46
|
+
field = "";
|
|
47
|
+
} else if (ch === "\n") {
|
|
48
|
+
cur.push(field);
|
|
49
|
+
rows.push(cur);
|
|
50
|
+
cur = [];
|
|
51
|
+
field = "";
|
|
52
|
+
} else if (ch === "\r") {
|
|
53
|
+
if (content[i + 1] !== "\n") {
|
|
54
|
+
cur.push(field);
|
|
55
|
+
rows.push(cur);
|
|
56
|
+
cur = [];
|
|
57
|
+
field = "";
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
field += ch;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (field.length > 0 || cur.length > 0) {
|
|
65
|
+
cur.push(field);
|
|
66
|
+
rows.push(cur);
|
|
67
|
+
}
|
|
68
|
+
if (rows.length === 0) return [];
|
|
69
|
+
const headers = rows[0].map((h) => h.trim());
|
|
70
|
+
const out = [];
|
|
71
|
+
for (let r = 1; r < rows.length; r++) {
|
|
72
|
+
const row = rows[r];
|
|
73
|
+
if (row.length === 1 && row[0] === "") continue;
|
|
74
|
+
const obj = {};
|
|
75
|
+
for (let c = 0; c < headers.length; c++) {
|
|
76
|
+
obj[headers[c]] = row[c] ?? "";
|
|
77
|
+
}
|
|
78
|
+
out.push(obj);
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
var Client = class {
|
|
83
|
+
apiKey;
|
|
84
|
+
baseUrl;
|
|
85
|
+
tenant;
|
|
86
|
+
constructor(opts = {}) {
|
|
87
|
+
this.apiKey = opts.apiKey ?? envVar("API_KEY");
|
|
88
|
+
const url = opts.baseUrl ?? envVar("API_URL") ?? "https://api.cograph.cloud";
|
|
89
|
+
this.baseUrl = url.replace(/\/+$/, "");
|
|
90
|
+
this.tenant = opts.tenant ?? envVar("TENANT") ?? "demo-tenant";
|
|
91
|
+
}
|
|
92
|
+
headers() {
|
|
93
|
+
const h = { "Content-Type": "application/json" };
|
|
94
|
+
if (this.apiKey) h["X-API-Key"] = this.apiKey;
|
|
95
|
+
return h;
|
|
96
|
+
}
|
|
97
|
+
base() {
|
|
98
|
+
return `${this.baseUrl}/graphs/${this.tenant}`;
|
|
99
|
+
}
|
|
100
|
+
async request(method, url, body, timeoutMs = 12e4) {
|
|
101
|
+
const controller = new AbortController();
|
|
102
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
103
|
+
let res;
|
|
104
|
+
try {
|
|
105
|
+
res = await fetch(url, {
|
|
106
|
+
method,
|
|
107
|
+
headers: this.headers(),
|
|
108
|
+
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
109
|
+
signal: controller.signal
|
|
110
|
+
});
|
|
111
|
+
} catch (err) {
|
|
112
|
+
clearTimeout(timer);
|
|
113
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
114
|
+
throw new CographError(`Request to ${url} timed out after ${timeoutMs}ms`);
|
|
115
|
+
}
|
|
116
|
+
throw new CographError(
|
|
117
|
+
`Network error contacting ${url}: ${err instanceof Error ? err.message : String(err)}`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
clearTimeout(timer);
|
|
121
|
+
if (!res.ok) {
|
|
122
|
+
let text2 = "";
|
|
123
|
+
try {
|
|
124
|
+
text2 = await res.text();
|
|
125
|
+
} catch {
|
|
126
|
+
}
|
|
127
|
+
throw new CographError(`HTTP ${res.status}: ${text2}`, {
|
|
128
|
+
status: res.status,
|
|
129
|
+
body: text2
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
if (res.status === 204) return void 0;
|
|
133
|
+
const ct = res.headers.get("content-type") ?? "";
|
|
134
|
+
if (ct.includes("application/json")) {
|
|
135
|
+
return await res.json();
|
|
136
|
+
}
|
|
137
|
+
const text = await res.text();
|
|
138
|
+
try {
|
|
139
|
+
return JSON.parse(text);
|
|
140
|
+
} catch {
|
|
141
|
+
return text;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Ingest a file path or raw text into a knowledge graph.
|
|
146
|
+
*
|
|
147
|
+
* If `pathOrText` points to an existing file, its contents are read and the
|
|
148
|
+
* format is inferred from the extension (.csv, .json, .txt) unless
|
|
149
|
+
* `contentType` is given. CSV files use the two-step schema-inference + row
|
|
150
|
+
* mapping flow.
|
|
151
|
+
*/
|
|
152
|
+
async ingest(pathOrText, opts = {}) {
|
|
153
|
+
let content;
|
|
154
|
+
let fmt;
|
|
155
|
+
let isFile = false;
|
|
156
|
+
try {
|
|
157
|
+
isFile = existsSync(pathOrText) && statSync(pathOrText).isFile();
|
|
158
|
+
} catch {
|
|
159
|
+
isFile = false;
|
|
160
|
+
}
|
|
161
|
+
if (isFile) {
|
|
162
|
+
const ext = extname(pathOrText).toLowerCase();
|
|
163
|
+
if (ext === ".pdf") {
|
|
164
|
+
throw new CographError(
|
|
165
|
+
"PDF ingest not yet supported in the Node CLI; use the Python CLI or POST raw bytes to the API."
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
content = readFileSync(pathOrText, "utf-8");
|
|
169
|
+
fmt = opts.contentType ?? EXT_FORMAT[ext] ?? "text";
|
|
170
|
+
if (fmt === "csv") {
|
|
171
|
+
return this.ingestCsv(content, opts.kg);
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
content = pathOrText;
|
|
175
|
+
fmt = opts.contentType ?? "text";
|
|
176
|
+
}
|
|
177
|
+
const body = {
|
|
178
|
+
content,
|
|
179
|
+
content_type: fmt,
|
|
180
|
+
source: "client"
|
|
181
|
+
};
|
|
182
|
+
if (opts.kg) body.kg_name = opts.kg;
|
|
183
|
+
return this.request("POST", `${this.base()}/ingest`, body, 12e4);
|
|
184
|
+
}
|
|
185
|
+
async ingestCsv(content, kgName) {
|
|
186
|
+
const rows = parseCsv(content);
|
|
187
|
+
if (rows.length === 0) throw new CographError("CSV is empty");
|
|
188
|
+
const headers = Object.keys(rows[0]);
|
|
189
|
+
const schemaBody = {
|
|
190
|
+
headers,
|
|
191
|
+
sample_rows: rows.slice(0, 5),
|
|
192
|
+
total_rows: rows.length
|
|
193
|
+
};
|
|
194
|
+
const mapping = await this.request(
|
|
195
|
+
"POST",
|
|
196
|
+
`${this.base()}/ingest/csv/schema`,
|
|
197
|
+
schemaBody,
|
|
198
|
+
3e5
|
|
199
|
+
);
|
|
200
|
+
const batchSize = 50;
|
|
201
|
+
let totalEntities = 0;
|
|
202
|
+
let totalTriples = 0;
|
|
203
|
+
for (let i = 0; i < rows.length; i += batchSize) {
|
|
204
|
+
const batch = rows.slice(i, i + batchSize);
|
|
205
|
+
const body = {
|
|
206
|
+
mapping,
|
|
207
|
+
rows: batch,
|
|
208
|
+
source: "client"
|
|
209
|
+
};
|
|
210
|
+
if (kgName) body.kg_name = kgName;
|
|
211
|
+
const result = await this.request("POST", `${this.base()}/ingest/csv/rows`, body, 3e5);
|
|
212
|
+
totalEntities += result.entities_resolved ?? 0;
|
|
213
|
+
totalTriples += result.triples_inserted ?? 0;
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
entities_resolved: totalEntities,
|
|
217
|
+
triples_inserted: totalTriples,
|
|
218
|
+
mapping
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
/** Ask a natural language question and return the parsed response. */
|
|
222
|
+
async ask(question, opts = {}) {
|
|
223
|
+
const body = { question };
|
|
224
|
+
if (opts.kg) body.kg_name = opts.kg;
|
|
225
|
+
if (opts.model) body.model = opts.model;
|
|
226
|
+
return this.request("POST", `${this.base()}/ask`, body, 6e4);
|
|
227
|
+
}
|
|
228
|
+
/** List all knowledge graphs for the current tenant. */
|
|
229
|
+
async listKgs() {
|
|
230
|
+
const data = await this.request(
|
|
231
|
+
"GET",
|
|
232
|
+
`${this.base()}/kgs`,
|
|
233
|
+
void 0,
|
|
234
|
+
15e3
|
|
235
|
+
);
|
|
236
|
+
if (Array.isArray(data)) return data;
|
|
237
|
+
if (data && typeof data === "object" && "kgs" in data) {
|
|
238
|
+
const kgs = data.kgs;
|
|
239
|
+
if (Array.isArray(kgs)) return kgs;
|
|
240
|
+
}
|
|
241
|
+
return [];
|
|
242
|
+
}
|
|
243
|
+
/** Create a knowledge graph. */
|
|
244
|
+
async createKg(name, description) {
|
|
245
|
+
const body = { name };
|
|
246
|
+
if (description) body.description = description;
|
|
247
|
+
return this.request("POST", `${this.base()}/kgs`, body, 15e3);
|
|
248
|
+
}
|
|
249
|
+
/** Delete a knowledge graph by name. */
|
|
250
|
+
async deleteKg(name) {
|
|
251
|
+
return this.request(
|
|
252
|
+
"DELETE",
|
|
253
|
+
`${this.base()}/kgs/${encodeURIComponent(name)}`,
|
|
254
|
+
void 0,
|
|
255
|
+
3e4
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
/** List ontology types. */
|
|
259
|
+
async ontologyTypes() {
|
|
260
|
+
const data = await this.request(
|
|
261
|
+
"GET",
|
|
262
|
+
`${this.base()}/ontology/types`,
|
|
263
|
+
void 0,
|
|
264
|
+
15e3
|
|
265
|
+
);
|
|
266
|
+
return Array.isArray(data) ? data : [];
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
export {
|
|
270
|
+
Client,
|
|
271
|
+
CographError
|
|
272
|
+
};
|
|
273
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["import { existsSync, readFileSync, statSync } from \"node:fs\";\nimport { extname } from \"node:path\";\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}\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 this.apiKey = opts.apiKey ?? envVar(\"API_KEY\");\n const url = opts.baseUrl ?? envVar(\"API_URL\") ?? \"https://api.cograph.cloud\";\n this.baseUrl = url.replace(/\\/+$/, \"\");\n this.tenant = opts.tenant ?? envVar(\"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 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.kg);\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 kgName: string | undefined,\n ): Promise<Record<string, unknown>> {\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 const schemaBody = {\n headers,\n sample_rows: rows.slice(0, 5),\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 const batchSize = 50;\n let totalEntities = 0;\n let totalTriples = 0;\n for (let i = 0; i < rows.length; i += batchSize) {\n const batch = rows.slice(i, i + batchSize);\n const body: Record<string, unknown> = {\n mapping,\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 totalEntities += result.entities_resolved ?? 0;\n totalTriples += result.triples_inserted ?? 0;\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 /** 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"],"mappings":";AAAA,SAAS,YAAY,cAAc,gBAAgB;AACnD,SAAS,eAAe;AAEjB,IAAM,eAAN,cAA2B,MAAM;AAAA,EACtC;AAAA,EACA;AAAA,EAEA,YAAY,SAAiB,MAA2C;AACtE,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS,MAAM;AACpB,SAAK,OAAO,MAAM;AAAA,EACpB;AACF;AAkBA,SAAS,OAAO,MAAc,UAAuC;AAEnE,SACE,QAAQ,IAAI,WAAW,IAAI,EAAE,KAC7B,QAAQ,IAAI,SAAS,IAAI,EAAE,KAC3B;AAEJ;AAEA,IAAM,aAAqC;AAAA,EACzC,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,UAAU;AAAA,EACV,QAAQ;AACV;AASO,SAAS,SAAS,SAA2C;AAClE,QAAM,OAAmB,CAAC;AAC1B,MAAI,MAAgB,CAAC;AACrB,MAAI,QAAQ;AACZ,MAAI,WAAW;AAEf,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,UAAM,KAAK,QAAQ,CAAC;AACpB,QAAI,UAAU;AACZ,UAAI,OAAO,KAAK;AACd,YAAI,QAAQ,IAAI,CAAC,MAAM,KAAK;AAC1B,mBAAS;AACT;AAAA,QACF,OAAO;AACL,qBAAW;AAAA,QACb;AAAA,MACF,OAAO;AACL,iBAAS;AAAA,MACX;AAAA,IACF,OAAO;AACL,UAAI,OAAO,KAAK;AACd,mBAAW;AAAA,MACb,WAAW,OAAO,KAAK;AACrB,YAAI,KAAK,KAAK;AACd,gBAAQ;AAAA,MACV,WAAW,OAAO,MAAM;AACtB,YAAI,KAAK,KAAK;AACd,aAAK,KAAK,GAAG;AACb,cAAM,CAAC;AACP,gBAAQ;AAAA,MACV,WAAW,OAAO,MAAM;AAEtB,YAAI,QAAQ,IAAI,CAAC,MAAM,MAAM;AAC3B,cAAI,KAAK,KAAK;AACd,eAAK,KAAK,GAAG;AACb,gBAAM,CAAC;AACP,kBAAQ;AAAA,QACV;AAAA,MACF,OAAO;AACL,iBAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAEA,MAAI,MAAM,SAAS,KAAK,IAAI,SAAS,GAAG;AACtC,QAAI,KAAK,KAAK;AACd,SAAK,KAAK,GAAG;AAAA,EACf;AAEA,MAAI,KAAK,WAAW,EAAG,QAAO,CAAC;AAC/B,QAAM,UAAU,KAAK,CAAC,EAAG,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AAC5C,QAAM,MAAgC,CAAC;AACvC,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,MAAM,KAAK,CAAC;AAElB,QAAI,IAAI,WAAW,KAAK,IAAI,CAAC,MAAM,GAAI;AACvC,UAAM,MAA8B,CAAC;AACrC,aAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,UAAI,QAAQ,CAAC,CAAE,IAAI,IAAI,CAAC,KAAK;AAAA,IAC/B;AACA,QAAI,KAAK,GAAG;AAAA,EACd;AACA,SAAO;AACT;AAEO,IAAM,SAAN,MAAa;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EAEA,YAAY,OAAsB,CAAC,GAAG;AACpC,SAAK,SAAS,KAAK,UAAU,OAAO,SAAS;AAC7C,UAAM,MAAM,KAAK,WAAW,OAAO,SAAS,KAAK;AACjD,SAAK,UAAU,IAAI,QAAQ,QAAQ,EAAE;AACrC,SAAK,SAAS,KAAK,UAAU,OAAO,QAAQ,KAAK;AAAA,EACnD;AAAA,EAEQ,UAAkC;AACxC,UAAM,IAA4B,EAAE,gBAAgB,mBAAmB;AACvE,QAAI,KAAK,OAAQ,GAAE,WAAW,IAAI,KAAK;AACvC,WAAO;AAAA,EACT;AAAA,EAEQ,OAAe;AACrB,WAAO,GAAG,KAAK,OAAO,WAAW,KAAK,MAAM;AAAA,EAC9C;AAAA,EAEA,MAAc,QACZ,QACA,KACA,MACA,YAAoB,MACR;AACZ,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,SAAS;AAC5D,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,MAAM,KAAK;AAAA,QACrB;AAAA,QACA,SAAS,KAAK,QAAQ;AAAA,QACtB,MAAM,SAAS,SAAY,SAAY,KAAK,UAAU,IAAI;AAAA,QAC1D,QAAQ,WAAW;AAAA,MACrB,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,mBAAa,KAAK;AAClB,UAAI,eAAe,SAAS,IAAI,SAAS,cAAc;AACrD,cAAM,IAAI,aAAa,cAAc,GAAG,oBAAoB,SAAS,IAAI;AAAA,MAC3E;AACA,YAAM,IAAI;AAAA,QACR,4BAA4B,GAAG,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MACtF;AAAA,IACF;AACA,iBAAa,KAAK;AAElB,QAAI,CAAC,IAAI,IAAI;AACX,UAAIA,QAAO;AACX,UAAI;AACF,QAAAA,QAAO,MAAM,IAAI,KAAK;AAAA,MACxB,QAAQ;AAAA,MAER;AACA,YAAM,IAAI,aAAa,QAAQ,IAAI,MAAM,KAAKA,KAAI,IAAI;AAAA,QACpD,QAAQ,IAAI;AAAA,QACZ,MAAMA;AAAA,MACR,CAAC;AAAA,IACH;AAGA,QAAI,IAAI,WAAW,IAAK,QAAO;AAE/B,UAAM,KAAK,IAAI,QAAQ,IAAI,cAAc,KAAK;AAC9C,QAAI,GAAG,SAAS,kBAAkB,GAAG;AACnC,aAAQ,MAAM,IAAI,KAAK;AAAA,IACzB;AAEA,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,QAAI;AACF,aAAO,KAAK,MAAM,IAAI;AAAA,IACxB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,OACJ,YACA,OAAsB,CAAC,GACW;AAClC,QAAI;AACJ,QAAI;AAEJ,QAAI,SAAS;AACb,QAAI;AACF,eAAS,WAAW,UAAU,KAAK,SAAS,UAAU,EAAE,OAAO;AAAA,IACjE,QAAQ;AACN,eAAS;AAAA,IACX;AAEA,QAAI,QAAQ;AACV,YAAM,MAAM,QAAQ,UAAU,EAAE,YAAY;AAC5C,UAAI,QAAQ,QAAQ;AAClB,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AACA,gBAAU,aAAa,YAAY,OAAO;AAC1C,YAAM,KAAK,eAAe,WAAW,GAAG,KAAK;AAC7C,UAAI,QAAQ,OAAO;AACjB,eAAO,KAAK,UAAU,SAAS,KAAK,EAAE;AAAA,MACxC;AAAA,IACF,OAAO;AACL,gBAAU;AACV,YAAM,KAAK,eAAe;AAAA,IAC5B;AAEA,UAAM,OAAgC;AAAA,MACpC;AAAA,MACA,cAAc;AAAA,MACd,QAAQ;AAAA,IACV;AACA,QAAI,KAAK,GAAI,MAAK,UAAU,KAAK;AACjC,WAAO,KAAK,QAAQ,QAAQ,GAAG,KAAK,KAAK,CAAC,WAAW,MAAM,IAAO;AAAA,EACpE;AAAA,EAEA,MAAc,UACZ,SACA,QACkC;AAClC,UAAM,OAAO,SAAS,OAAO;AAC7B,QAAI,KAAK,WAAW,EAAG,OAAM,IAAI,aAAa,cAAc;AAC5D,UAAM,UAAU,OAAO,KAAK,KAAK,CAAC,CAAE;AAEpC,UAAM,aAAa;AAAA,MACjB;AAAA,MACA,aAAa,KAAK,MAAM,GAAG,CAAC;AAAA,MAC5B,YAAY,KAAK;AAAA,IACnB;AACA,UAAM,UAAU,MAAM,KAAK;AAAA,MACzB;AAAA,MACA,GAAG,KAAK,KAAK,CAAC;AAAA,MACd;AAAA,MACA;AAAA,IACF;AAEA,UAAM,YAAY;AAClB,QAAI,gBAAgB;AACpB,QAAI,eAAe;AACnB,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,WAAW;AAC/C,YAAM,QAAQ,KAAK,MAAM,GAAG,IAAI,SAAS;AACzC,YAAM,OAAgC;AAAA,QACpC;AAAA,QACA,MAAM;AAAA,QACN,QAAQ;AAAA,MACV;AACA,UAAI,OAAQ,MAAK,UAAU;AAC3B,YAAM,SAAS,MAAM,KAAK,QAGvB,QAAQ,GAAG,KAAK,KAAK,CAAC,oBAAoB,MAAM,GAAO;AAC1D,uBAAiB,OAAO,qBAAqB;AAC7C,sBAAgB,OAAO,oBAAoB;AAAA,IAC7C;AAEA,WAAO;AAAA,MACL,mBAAmB;AAAA,MACnB,kBAAkB;AAAA,MAClB;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,IACJ,UACA,OAAmB,CAAC,GACc;AAClC,UAAM,OAAgC,EAAE,SAAS;AACjD,QAAI,KAAK,GAAI,MAAK,UAAU,KAAK;AACjC,QAAI,KAAK,MAAO,MAAK,QAAQ,KAAK;AAClC,WAAO,KAAK,QAAQ,QAAQ,GAAG,KAAK,KAAK,CAAC,QAAQ,MAAM,GAAM;AAAA,EAChE;AAAA;AAAA,EAGA,MAAM,UAAmD;AACvD,UAAM,OAAO,MAAM,KAAK;AAAA,MACtB;AAAA,MACA,GAAG,KAAK,KAAK,CAAC;AAAA,MACd;AAAA,MACA;AAAA,IACF;AACA,QAAI,MAAM,QAAQ,IAAI,EAAG,QAAO;AAChC,QAAI,QAAQ,OAAO,SAAS,YAAY,SAAS,MAAM;AACrD,YAAM,MAAO,KAA2B;AACxC,UAAI,MAAM,QAAQ,GAAG,EAAG,QAAO;AAAA,IACjC;AACA,WAAO,CAAC;AAAA,EACV;AAAA;AAAA,EAGA,MAAM,SACJ,MACA,aACkC;AAClC,UAAM,OAAgC,EAAE,KAAK;AAC7C,QAAI,YAAa,MAAK,cAAc;AACpC,WAAO,KAAK,QAAQ,QAAQ,GAAG,KAAK,KAAK,CAAC,QAAQ,MAAM,IAAM;AAAA,EAChE;AAAA;AAAA,EAGA,MAAM,SAAS,MAAgD;AAC7D,WAAO,KAAK;AAAA,MACV;AAAA,MACA,GAAG,KAAK,KAAK,CAAC,QAAQ,mBAAmB,IAAI,CAAC;AAAA,MAC9C;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,gBAAyD;AAC7D,UAAM,OAAO,MAAM,KAAK;AAAA,MACtB;AAAA,MACA,GAAG,KAAK,KAAK,CAAC;AAAA,MACd;AAAA,MACA;AAAA,IACF;AACA,WAAO,MAAM,QAAQ,IAAI,IAAK,OAA0C,CAAC;AAAA,EAC3E;AACF;","names":["text"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cograph",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Cograph SDK and CLI — knowledge graph platform for structured data",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"cograph": "./dist/cli.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup",
|
|
23
|
+
"prepack": "node ../../scripts/chmod-bin.js packages/cograph/dist/cli.js"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"knowledge-graph",
|
|
27
|
+
"cograph",
|
|
28
|
+
"sdk",
|
|
29
|
+
"cli",
|
|
30
|
+
"rdf",
|
|
31
|
+
"sparql",
|
|
32
|
+
"ontology",
|
|
33
|
+
"ai",
|
|
34
|
+
"llm"
|
|
35
|
+
],
|
|
36
|
+
"author": "Cograph",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"homepage": "https://cograph.cloud",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/git-moeen/cograph-oss.git",
|
|
42
|
+
"directory": "packages/cograph"
|
|
43
|
+
},
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/git-moeen/cograph-oss/issues"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"commander": "^12.1.0"
|
|
49
|
+
},
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=20"
|
|
52
|
+
}
|
|
53
|
+
}
|