@odla-ai/kg 0.3.2 → 0.4.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/dist/chunk-T6SYIVST.js +376 -0
- package/dist/chunk-T6SYIVST.js.map +1 -0
- package/dist/index.cjs +410 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +330 -5
- package/dist/index.d.ts +330 -5
- package/dist/index.js +249 -38
- package/dist/index.js.map +1 -1
- package/dist/lenses-C7Q7Gvnx.d.cts +123 -0
- package/dist/lenses-C7Q7Gvnx.d.ts +123 -0
- package/dist/temporal/index.cjs +1 -689
- package/dist/temporal/index.cjs.map +1 -1
- package/dist/temporal/index.d.cts +4 -565
- package/dist/temporal/index.d.ts +4 -565
- package/dist/temporal/index.js +20 -977
- package/dist/temporal/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-V3N2GRYU.js +0 -73
- package/dist/chunk-V3N2GRYU.js.map +0 -1
- package/dist/sources-nJyD0JUP.d.cts +0 -256
- package/dist/sources-nJyD0JUP.d.ts +0 -256
package/dist/index.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
blendPairs,
|
|
3
|
+
bridgingScores,
|
|
4
|
+
labelPropagation,
|
|
5
|
+
parseDateMs,
|
|
6
|
+
projectBridges,
|
|
7
|
+
reifiedBridgeTargets,
|
|
8
|
+
topBridges
|
|
9
|
+
} from "./chunk-T6SYIVST.js";
|
|
4
10
|
|
|
5
11
|
// src/types.ts
|
|
6
12
|
function emptyUsage() {
|
|
@@ -292,6 +298,235 @@ async function canonicalizeFragment(db, fragment, ontology) {
|
|
|
292
298
|
};
|
|
293
299
|
}
|
|
294
300
|
|
|
301
|
+
// src/graph/connectors.ts
|
|
302
|
+
function findConnectors(fragment, config) {
|
|
303
|
+
const nameOf = /* @__PURE__ */ new Map();
|
|
304
|
+
for (const n of fragment.nodes) {
|
|
305
|
+
const label = n.attrs.name ?? n.attrs.title;
|
|
306
|
+
if (typeof label === "string" && label) nameOf.set(`${n.type}:${n.key}`, label);
|
|
307
|
+
}
|
|
308
|
+
const sets = config.lenses.map((l) => ({
|
|
309
|
+
pairs: projectBridges(reifiedBridgeTargets(fragment, { reified: l.reified, bridgeLabel: l.bridge, targetLabel: l.of }), {
|
|
310
|
+
maxPerBridge: config.maxPerBridge ?? 12
|
|
311
|
+
}),
|
|
312
|
+
weight: l.weight ?? 1
|
|
313
|
+
}));
|
|
314
|
+
const pairs = blendPairs([...sets, ...config.extraPairs ?? []]);
|
|
315
|
+
if (pairs.length === 0) return { connectors: [], communities: 0, nodes: 0 };
|
|
316
|
+
const nodes = [...new Set(pairs.flatMap((p) => [p.a, p.b]))].sort();
|
|
317
|
+
const edges = pairs.map((p) => ({ a: p.a, b: p.b, weight: p.w }));
|
|
318
|
+
const labels = labelPropagation(nodes, edges, { seed: 1 });
|
|
319
|
+
const groupName = /* @__PURE__ */ new Map();
|
|
320
|
+
for (const id of nodes) {
|
|
321
|
+
const g = labels.get(id);
|
|
322
|
+
if (!groupName.has(g)) groupName.set(g, nameOf.get(id) ?? id);
|
|
323
|
+
}
|
|
324
|
+
const bridging = bridgingScores(edges, labels);
|
|
325
|
+
const viaOf = (id) => {
|
|
326
|
+
const names = /* @__PURE__ */ new Set();
|
|
327
|
+
for (const p of pairs) {
|
|
328
|
+
if (p.a !== id && p.b !== id) continue;
|
|
329
|
+
for (const v of p.via) names.add(nameOf.get(v) ?? v.split(":").pop());
|
|
330
|
+
}
|
|
331
|
+
return [...names].slice(0, 6);
|
|
332
|
+
};
|
|
333
|
+
const connectors = topBridges(bridging, config.top ?? 12).map((b) => ({
|
|
334
|
+
ref: b.id,
|
|
335
|
+
name: nameOf.get(b.id) ?? b.id,
|
|
336
|
+
score: b.score,
|
|
337
|
+
groups: b.groups.map((g) => groupName.get(g)),
|
|
338
|
+
// every label has an anchor name
|
|
339
|
+
via: viaOf(b.id)
|
|
340
|
+
}));
|
|
341
|
+
return { connectors, communities: new Set(labels.values()).size, nodes: nodes.length };
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// src/util/identity.ts
|
|
345
|
+
function slugify(input) {
|
|
346
|
+
return input.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60).replace(/-+$/g, "");
|
|
347
|
+
}
|
|
348
|
+
function normalizeUrl(raw) {
|
|
349
|
+
try {
|
|
350
|
+
const u = new URL(raw);
|
|
351
|
+
u.hash = "";
|
|
352
|
+
const drop = [/^utm_/i, /^ref$/i, /^ref_/i, /^fbclid$/i, /^gclid$/i, /^mc_/i];
|
|
353
|
+
for (const key of [...u.searchParams.keys()]) {
|
|
354
|
+
if (drop.some((re) => re.test(key))) u.searchParams.delete(key);
|
|
355
|
+
}
|
|
356
|
+
let s = u.toString();
|
|
357
|
+
s = s.replace(/\/$/, "");
|
|
358
|
+
return s;
|
|
359
|
+
} catch {
|
|
360
|
+
return raw.trim().replace(/#.*$/, "").replace(/\/$/, "");
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
function urlHash(raw) {
|
|
364
|
+
const s = normalizeUrl(raw);
|
|
365
|
+
let h = 2166136261;
|
|
366
|
+
for (let i = 0; i < s.length; i++) {
|
|
367
|
+
h ^= s.charCodeAt(i);
|
|
368
|
+
h = Math.imul(h, 16777619);
|
|
369
|
+
}
|
|
370
|
+
return (h >>> 0).toString(36);
|
|
371
|
+
}
|
|
372
|
+
function isLikelyPersonName(name) {
|
|
373
|
+
const n = name.trim();
|
|
374
|
+
if (!n || /\d/.test(n)) return false;
|
|
375
|
+
const parts = n.split(/\s+/);
|
|
376
|
+
if (parts.length < 2 || parts.length > 4) return false;
|
|
377
|
+
return parts.every((p) => /^[A-Z][a-zA-Z'’.-]*$/.test(p));
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// src/enrich/facts.ts
|
|
381
|
+
var LOCATE_SYSTEM = "You resolve US business locations to counties. For each company you are given a city and/or a free-text region. Name the US county containing that city (for independent cities, use the city's own county-equivalent name) plus its 2-letter state. Use nulls when the location is ambiguous, outside the US, or unknown.";
|
|
382
|
+
var LOCATE_TOOL = {
|
|
383
|
+
name: "resolve_counties",
|
|
384
|
+
description: "County name + state per company",
|
|
385
|
+
parameters: {
|
|
386
|
+
type: "object",
|
|
387
|
+
properties: {
|
|
388
|
+
items: {
|
|
389
|
+
type: "array",
|
|
390
|
+
items: {
|
|
391
|
+
type: "object",
|
|
392
|
+
properties: {
|
|
393
|
+
slug: { type: "string" },
|
|
394
|
+
county: { type: ["string", "null"], description: 'county name WITHOUT the word "County", e.g. "Santa Clara"' },
|
|
395
|
+
state: { type: ["string", "null"], description: "2-letter USPS state" }
|
|
396
|
+
},
|
|
397
|
+
required: ["slug", "county", "state"]
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
},
|
|
401
|
+
required: ["items"]
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
function locatePrompt(batch) {
|
|
405
|
+
return "Companies:\n" + batch.map((c) => `- slug ${c.slug}: city=${c.city ?? "?"} region=${c.region ?? "?"}`).join("\n");
|
|
406
|
+
}
|
|
407
|
+
var FACTS_SEARCH_SYSTEM = "You are a research assistant with web search. Find concrete facts about the company: founding year/date, headquarters city and state, founders and key executives (names, roles), and funding (rounds, dates, amounts, investor names). Prefer official/reputable sources. Report dense factual notes; say 'not found' for anything you cannot establish. Do not speculate.";
|
|
408
|
+
var FACTS_EXTRACT_SYSTEM = "Extract structured facts from the research notes. Use null for anything the notes do not establish.";
|
|
409
|
+
var FACTS_EXTRACT_TOOL = {
|
|
410
|
+
name: "company_facts",
|
|
411
|
+
description: "Structured company facts from research notes",
|
|
412
|
+
parameters: {
|
|
413
|
+
type: "object",
|
|
414
|
+
properties: {
|
|
415
|
+
founded: { type: ["string", "null"], description: "YYYY or YYYY-MM or YYYY-MM-DD" },
|
|
416
|
+
city: { type: ["string", "null"] },
|
|
417
|
+
state: { type: ["string", "null"], description: "2-letter USPS" },
|
|
418
|
+
people: {
|
|
419
|
+
type: "array",
|
|
420
|
+
items: {
|
|
421
|
+
type: "object",
|
|
422
|
+
properties: { name: { type: "string" }, role: { type: "string" } },
|
|
423
|
+
required: ["name", "role"]
|
|
424
|
+
}
|
|
425
|
+
},
|
|
426
|
+
rounds: {
|
|
427
|
+
type: "array",
|
|
428
|
+
items: {
|
|
429
|
+
type: "object",
|
|
430
|
+
properties: {
|
|
431
|
+
round: { type: ["string", "null"] },
|
|
432
|
+
date: { type: ["string", "null"] },
|
|
433
|
+
amount: { type: ["string", "null"] },
|
|
434
|
+
investors: { type: "array", items: { type: "string" } }
|
|
435
|
+
},
|
|
436
|
+
required: ["investors"]
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
},
|
|
440
|
+
required: ["people", "rounds"]
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
var DATE_RE = /^\d{4}(-\d{2}){0,2}$/;
|
|
444
|
+
var isoDate = (v) => !DATE_RE.test(v) ? null : v.length === 4 ? `${v}-01-01` : v.length === 7 ? `${v}-01` : v;
|
|
445
|
+
var countyKey = (name, st) => `${String(name).toLowerCase().replace(/\bcounty\b|\bparish\b|\bborough\b/g, "").replace(/[^a-z]/g, "")}|${String(st).toUpperCase()}`;
|
|
446
|
+
function factsToFragment(company, draft) {
|
|
447
|
+
const nodes = [];
|
|
448
|
+
const edges = [];
|
|
449
|
+
const attrs = {};
|
|
450
|
+
const founded = draft.founded ? isoDate(draft.founded) : null;
|
|
451
|
+
if (founded) attrs.founded = founded;
|
|
452
|
+
if (draft.city && !company.city) attrs.city = String(draft.city).slice(0, 60);
|
|
453
|
+
if (Object.keys(attrs).length > 0) {
|
|
454
|
+
nodes.push({ type: "company", key: company.slug, attrs: { name: company.name, ...attrs } });
|
|
455
|
+
}
|
|
456
|
+
const anchorDate = attrs.founded ?? company.founded ?? company.first_seen ?? "";
|
|
457
|
+
for (const p of (draft.people ?? []).slice(0, 5)) {
|
|
458
|
+
const personSlug = slugify(p.name);
|
|
459
|
+
if (!personSlug || personSlug === company.slug) continue;
|
|
460
|
+
nodes.push({ type: "person", key: personSlug, attrs: { name: p.name } });
|
|
461
|
+
const key = `${personSlug}~${company.slug}`;
|
|
462
|
+
nodes.push({ type: "affiliation", key, attrs: { date: anchorDate, role: String(p.role).slice(0, 60) } });
|
|
463
|
+
edges.push({ from: { type: "affiliation", key }, to: { type: "person", key: personSlug }, label: "of_person" });
|
|
464
|
+
edges.push({ from: { type: "affiliation", key }, to: { type: "company", key: company.slug }, label: "of_company" });
|
|
465
|
+
}
|
|
466
|
+
(draft.rounds ?? []).slice(0, 4).forEach((r, i) => {
|
|
467
|
+
const investors = (r.investors ?? []).map(slugify).filter(Boolean).slice(0, 6);
|
|
468
|
+
const date = r.date ? isoDate(r.date) : null;
|
|
469
|
+
if (investors.length === 0 && !date) return;
|
|
470
|
+
const key = `${company.slug}~e${i}`;
|
|
471
|
+
const rAttrs = { date: date ?? anchorDate };
|
|
472
|
+
if (r.round) rAttrs.round = String(r.round).slice(0, 40);
|
|
473
|
+
if (r.amount) rAttrs.amount = String(r.amount).slice(0, 40);
|
|
474
|
+
nodes.push({ type: "funding_round", key, attrs: rAttrs });
|
|
475
|
+
edges.push({ from: { type: "funding_round", key }, to: { type: "company", key: company.slug }, label: "round_of" });
|
|
476
|
+
for (const [j, invSlug] of investors.entries()) {
|
|
477
|
+
nodes.push({ type: "investor", key: invSlug, attrs: { name: (r.investors ?? [])[j] } });
|
|
478
|
+
edges.push({ from: { type: "funding_round", key }, to: { type: "investor", key: invSlug }, label: "raised_from" });
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
return { nodes, edges };
|
|
482
|
+
}
|
|
483
|
+
function locationFragment(slug, name, fips) {
|
|
484
|
+
return {
|
|
485
|
+
nodes: [{ type: "company", key: slug, attrs: { name, fips } }],
|
|
486
|
+
edges: [{ from: { type: "company", key: slug }, to: { type: "county", key: fips }, label: "located_in" }]
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// src/enrich/run.ts
|
|
491
|
+
async function enrichCompany(ai, models, company) {
|
|
492
|
+
const where = `${company.city ? `, ${company.city}` : ""}${company.region ? ` (${company.region})` : ""}`;
|
|
493
|
+
const { text } = await ai.search({
|
|
494
|
+
model: models.search,
|
|
495
|
+
system: FACTS_SEARCH_SYSTEM,
|
|
496
|
+
user: `Company: ${company.name}${where}. Research its founding, headquarters, key people, and funding.`,
|
|
497
|
+
maxTokens: 900
|
|
498
|
+
});
|
|
499
|
+
const extractOnce = () => ai.extract({
|
|
500
|
+
model: models.extract,
|
|
501
|
+
system: FACTS_EXTRACT_SYSTEM,
|
|
502
|
+
user: `Company: ${company.name}
|
|
503
|
+
|
|
504
|
+
Notes:
|
|
505
|
+
${text}`,
|
|
506
|
+
tool: FACTS_EXTRACT_TOOL,
|
|
507
|
+
maxTokens: 4e3
|
|
508
|
+
// reasoning headroom before the forced tool call
|
|
509
|
+
});
|
|
510
|
+
const { value } = await extractOnce().catch(extractOnce);
|
|
511
|
+
return factsToFragment(company, value);
|
|
512
|
+
}
|
|
513
|
+
async function resolveCounties(ai, model, rows, gazetteer) {
|
|
514
|
+
const { value } = await ai.extract({
|
|
515
|
+
model,
|
|
516
|
+
system: LOCATE_SYSTEM,
|
|
517
|
+
user: locatePrompt(rows),
|
|
518
|
+
tool: LOCATE_TOOL,
|
|
519
|
+
maxTokens: 6e3
|
|
520
|
+
// reasoning models spend tokens before the tool call
|
|
521
|
+
});
|
|
522
|
+
const out = [];
|
|
523
|
+
for (const item of value.items ?? []) {
|
|
524
|
+
const fips = item.county && item.state ? gazetteer.get(countyKey(item.county, item.state)) : void 0;
|
|
525
|
+
if (fips) out.push({ slug: item.slug, fips });
|
|
526
|
+
}
|
|
527
|
+
return out;
|
|
528
|
+
}
|
|
529
|
+
|
|
295
530
|
// src/llm/provider.ts
|
|
296
531
|
import { init, extractText } from "@odla-ai/ai";
|
|
297
532
|
var MODEL_TIERS = {
|
|
@@ -533,42 +768,6 @@ var gdeltConnector = {
|
|
|
533
768
|
}
|
|
534
769
|
};
|
|
535
770
|
|
|
536
|
-
// src/util/identity.ts
|
|
537
|
-
function slugify(input) {
|
|
538
|
-
return input.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60).replace(/-+$/g, "");
|
|
539
|
-
}
|
|
540
|
-
function normalizeUrl(raw) {
|
|
541
|
-
try {
|
|
542
|
-
const u = new URL(raw);
|
|
543
|
-
u.hash = "";
|
|
544
|
-
const drop = [/^utm_/i, /^ref$/i, /^ref_/i, /^fbclid$/i, /^gclid$/i, /^mc_/i];
|
|
545
|
-
for (const key of [...u.searchParams.keys()]) {
|
|
546
|
-
if (drop.some((re) => re.test(key))) u.searchParams.delete(key);
|
|
547
|
-
}
|
|
548
|
-
let s = u.toString();
|
|
549
|
-
s = s.replace(/\/$/, "");
|
|
550
|
-
return s;
|
|
551
|
-
} catch {
|
|
552
|
-
return raw.trim().replace(/#.*$/, "").replace(/\/$/, "");
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
function urlHash(raw) {
|
|
556
|
-
const s = normalizeUrl(raw);
|
|
557
|
-
let h = 2166136261;
|
|
558
|
-
for (let i = 0; i < s.length; i++) {
|
|
559
|
-
h ^= s.charCodeAt(i);
|
|
560
|
-
h = Math.imul(h, 16777619);
|
|
561
|
-
}
|
|
562
|
-
return (h >>> 0).toString(36);
|
|
563
|
-
}
|
|
564
|
-
function isLikelyPersonName(name) {
|
|
565
|
-
const n = name.trim();
|
|
566
|
-
if (!n || /\d/.test(n)) return false;
|
|
567
|
-
const parts = n.split(/\s+/);
|
|
568
|
-
if (parts.length < 2 || parts.length > 4) return false;
|
|
569
|
-
return parts.every((p) => /^[A-Z][a-zA-Z'’.-]*$/.test(p));
|
|
570
|
-
}
|
|
571
|
-
|
|
572
771
|
// src/connectors/fred.ts
|
|
573
772
|
async function fetchSeries(seriesId, apiKey, start) {
|
|
574
773
|
const url = `https://api.stlouisfed.org/fred/series/observations?series_id=${encodeURIComponent(seriesId)}&api_key=${encodeURIComponent(apiKey)}&file_type=json&observation_start=${encodeURIComponent(start)}`;
|
|
@@ -1008,8 +1207,13 @@ async function ingestFresh(step, deps, fresh, kind, resolve = false) {
|
|
|
1008
1207
|
}
|
|
1009
1208
|
export {
|
|
1010
1209
|
COMMIT_STEP_CONFIG,
|
|
1210
|
+
FACTS_EXTRACT_SYSTEM,
|
|
1211
|
+
FACTS_EXTRACT_TOOL,
|
|
1212
|
+
FACTS_SEARCH_SYSTEM,
|
|
1011
1213
|
IO_STEP_CONFIG,
|
|
1012
1214
|
LLM_STEP_CONFIG,
|
|
1215
|
+
LOCATE_SYSTEM,
|
|
1216
|
+
LOCATE_TOOL,
|
|
1013
1217
|
MODEL_TIERS,
|
|
1014
1218
|
Store,
|
|
1015
1219
|
addUsage,
|
|
@@ -1017,14 +1221,18 @@ export {
|
|
|
1017
1221
|
asStepLike,
|
|
1018
1222
|
canonicalizeFragment,
|
|
1019
1223
|
connectorIds,
|
|
1224
|
+
countyKey,
|
|
1020
1225
|
createProvider,
|
|
1021
1226
|
emptyUsage,
|
|
1022
1227
|
enabledSources,
|
|
1228
|
+
enrichCompany,
|
|
1023
1229
|
exportFragment,
|
|
1024
1230
|
extractJson,
|
|
1025
1231
|
extractJsonArray,
|
|
1232
|
+
factsToFragment,
|
|
1026
1233
|
fetchText,
|
|
1027
1234
|
fetchUrl,
|
|
1235
|
+
findConnectors,
|
|
1028
1236
|
fragmentToOps,
|
|
1029
1237
|
getConnector,
|
|
1030
1238
|
getProvider,
|
|
@@ -1033,6 +1241,8 @@ export {
|
|
|
1033
1241
|
ingestFresh,
|
|
1034
1242
|
isLikelyPersonName,
|
|
1035
1243
|
jobsForHour,
|
|
1244
|
+
locatePrompt,
|
|
1245
|
+
locationFragment,
|
|
1036
1246
|
makeContext,
|
|
1037
1247
|
mergeAttrs,
|
|
1038
1248
|
mergeRulesOf,
|
|
@@ -1040,6 +1250,7 @@ export {
|
|
|
1040
1250
|
naturalKeysOf,
|
|
1041
1251
|
normalizeUrl,
|
|
1042
1252
|
resilientStep,
|
|
1253
|
+
resolveCounties,
|
|
1043
1254
|
resolveName,
|
|
1044
1255
|
searchEntities,
|
|
1045
1256
|
slugify,
|