@polycode-projects/seonix 0.7.1 → 0.8.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 +42 -149
- package/bin/cli.mjs +6 -9
- package/package.json +3 -4
- package/src/ask-browser-entry.mjs +43 -0
- package/src/ask-browser.bundle.js +3040 -0
- package/src/chat-shim.mjs +37 -0
- package/src/server.mjs +6 -6
- package/src/tmct-provider.mjs +37 -0
- package/src/viz.mjs +10 -15
- package/src/walk.mjs +0 -0
- package/src/ask-nlp.mjs +0 -73
- package/src/ask-vocab.mjs +0 -687
- package/src/ask.mjs +0 -2419
- package/src/chat.mjs +0 -850
- package/src/nlp-bundle.mjs +0 -120
- package/src/prose-nlp.mjs +0 -52
package/README.md
CHANGED
|
@@ -7,45 +7,25 @@ bounded **edit digest** you inject once at session start. Your agent edits from
|
|
|
7
7
|
turn 1 without fishing through the repo.
|
|
8
8
|
|
|
9
9
|
> **Headline.** Measured as **mean** input tokens per both-solved task (paired
|
|
10
|
-
> arms, identical model pin). Under the shipped score-gap default
|
|
11
|
-
>
|
|
12
|
-
>
|
|
13
|
-
>
|
|
14
|
-
>
|
|
15
|
-
>
|
|
16
|
-
>
|
|
17
|
-
>
|
|
18
|
-
> task- and model-conditional; full method, numbers and the measurement contract
|
|
19
|
-
> are in `BENCHMARK_017.md`.
|
|
10
|
+
> arms, identical model pin). Under the shipped score-gap default, two cells clear
|
|
11
|
+
> the ≥50% bar, both on exploration-bound complex tasks (a TypeScript middleware
|
|
12
|
+
> task on **Haiku 4.5** and a C# service task on **Sonnet 4.6**), with several more
|
|
13
|
+
> in the 35 to 48% range. The token cut tracks the turn cut: seonix saves tokens by
|
|
14
|
+
> removing exploration turns, so tasks whose edit site is obvious change little.
|
|
15
|
+
> Indexing costs **$0**. The win is task- and model-conditional. Full method,
|
|
16
|
+
> numbers, and the measurement contract are in
|
|
17
|
+
> [`BENCHMARKS.md`](https://gitlab.com/polycode-projects/seonix/-/blob/main/BENCHMARKS.md).
|
|
20
18
|
|
|
21
19
|
- **Website / live demo:** https://seonix.polycode.co.uk
|
|
22
20
|
- **Source + benchmarks:** https://gitlab.com/polycode-projects/seonix
|
|
23
21
|
- **Licence:** AGPL-3.0-only
|
|
24
22
|
|
|
25
|
-
##
|
|
26
|
-
|
|
27
|
-
seonix on the cheaper **Sonnet 4.6**
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
| full task name | otb / Opus 4.8 | solved | seonix / Sonnet 4.6 | solved | token change |
|
|
32
|
-
| --- | --- | --- | --- | --- | --- |
|
|
33
|
-
| ts-hono-utils-helper (simple) | 135k | 3/3 | 117k | 3/3 | +13% |
|
|
34
|
-
| ts-hono-response-time-middleware | 345k | 3/3 | 555k | 3/3 | −61% |
|
|
35
|
-
| ts-hono-json-envelope | 1,102k | 3/3 | 864k | 3/3 | +22% |
|
|
36
|
-
| py-django-truncatelines-filter | 610k | 5/5 | 524k | 5/5 | +14% |
|
|
37
|
-
| cs-eshoponweb-order-service-total | 318k | 5/5 | 154k | 3/3 | +51% |
|
|
38
|
-
| cs-eshoponweb-basket-service-total | 353k | 5/5 | 255k | 3/3 | +28% |
|
|
39
|
-
| js-express-request-id | 330k | 5/5 | 267k | 3/3 | +19% |
|
|
40
|
-
| js-express-json-envelope | 174k | 5/5 | 185k | 3/3 | −7% |
|
|
41
|
-
|
|
42
|
-
seonix on Sonnet used fewer input tokens than otb on Opus on six of these eight
|
|
43
|
-
tasks, at equal correctness. This crosses model and arm together, so
|
|
44
|
-
it is an illustration of practical value, not the paired within-model measurement
|
|
45
|
-
the ≥50% decision rule uses. Across all 13 measured seonix cells the drop in input
|
|
46
|
-
tokens is 0.91-correlated (Pearson) with the drop in turns (slope about 0.71):
|
|
47
|
-
seonix saves tokens by letting the agent skip exploration turns, so where it
|
|
48
|
-
removes no turns there is little to save.
|
|
23
|
+
## A cheaper model, matched
|
|
24
|
+
|
|
25
|
+
seonix on the cheaper **Sonnet 4.6** used fewer input tokens than out-of-the-box
|
|
26
|
+
**Opus 4.8** on six of eight tasks, at equal correctness. The full cross-model
|
|
27
|
+
table and the turns correlation (Pearson 0.91) are in
|
|
28
|
+
[`BENCHMARKS.md`](https://gitlab.com/polycode-projects/seonix/-/blob/main/BENCHMARKS.md).
|
|
49
29
|
|
|
50
30
|
## Install
|
|
51
31
|
|
|
@@ -102,7 +82,10 @@ filesystem root). In the merged graph every module id and label is prefixed with
|
|
|
102
82
|
the repo's directory basename (`mod:<repoName>/<relpath>`); when two repos share a
|
|
103
83
|
basename the first in path sort order keeps the bare name and later ones get `-2`,
|
|
104
84
|
`-3`, … appended. Each repo's git history and `.seonixignore` are handled per repo,
|
|
105
|
-
exactly as in single mode
|
|
85
|
+
exactly as in single mode — a `.seonixignore` at the `multi_root` **parent** is NOT read
|
|
86
|
+
(each sub-repo loads only its own). For an **estate-wide exclude**, put it in a `seonix.toml`
|
|
87
|
+
at the directory you index from (`[index].exclude` / `secret_exclude`); that propagates to
|
|
88
|
+
every sub-repo. Cross-repo call edges only arise where a symbol name
|
|
106
89
|
resolves uniquely across the whole merged graph; ambiguous names are dropped, as
|
|
107
90
|
within one repo. Query a merged graph by passing the merge root as `repo_path`
|
|
108
91
|
(e.g. `seonix cli seonix_search '{"repo_path":"/abs/estate","query":"…"}'`).
|
|
@@ -301,9 +284,8 @@ Flags: `--focus <sym>` (default: highest-degree module), `--depth N` (default 2)
|
|
|
301
284
|
f.html` (default `seon-graph.html`), `--data-out f.json` (split viewer + sidecar
|
|
302
285
|
data file instead of embedding), `--graph <path>` (override the graph.json
|
|
303
286
|
location), `--repo-url <url> --ref <branch>` (source links), `--site-nav`
|
|
304
|
-
(add the site's absolute home entry), `--
|
|
305
|
-
|
|
306
|
-
past the size gate that would otherwise split it out with a message).
|
|
287
|
+
(add the site's absolute home entry), `--force-inline` (embed the full graph inline
|
|
288
|
+
even past the size gate that would otherwise split it out with a message).
|
|
307
289
|
|
|
308
290
|
By default `viz` also writes a `code-browser.html` (Chronograph) and a
|
|
309
291
|
`timeline.html` next to `--out` with a working header nav; override their paths
|
|
@@ -319,15 +301,15 @@ N of M" header note when truncated).
|
|
|
319
301
|
`/timeline.html`; live re-annotates on HEAD change via `/code-browser-version`) —
|
|
320
302
|
port 0 (default) picks a free port.
|
|
321
303
|
|
|
322
|
-
### `chat` — interactive
|
|
304
|
+
### `chat` — interactive tmct chat over the graph
|
|
323
305
|
|
|
324
306
|
```bash
|
|
325
|
-
seonix chat [--repo /abs/path]
|
|
307
|
+
seonix chat [--repo /abs/path]
|
|
326
308
|
```
|
|
327
309
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
310
|
+
Opens tmct's natural-language client over seonix's graph (see [Chat](#chat) below).
|
|
311
|
+
The conversation layer is tmct (`@polycode-projects/the-mechanical-code-talker`);
|
|
312
|
+
seonix supplies the graph through tmct's provider seam.
|
|
331
313
|
|
|
332
314
|
### `SEONIX_STORE` / `SEONIX_GRAPH_FORMAT`
|
|
333
315
|
|
|
@@ -405,114 +387,25 @@ for just the single self-contained graph file.
|
|
|
405
387
|
## Chat
|
|
406
388
|
|
|
407
389
|
```bash
|
|
408
|
-
seonix chat [--repo /abs/path]
|
|
409
|
-
```
|
|
410
|
-
|
|
411
|
-
An interactive client over the typed code-graph (deterministic, no model calls).
|
|
412
|
-
With no `--repo` the target defaults to the **git root** (`git rev-parse
|
|
413
|
-
--show-toplevel`), so running it from a nested package dir still indexes the whole
|
|
414
|
-
repo; pass `--repo <path>` to override. Any **bare line** is a plain-English
|
|
415
|
-
question dispatched through the mechanical `seonix_ask` engine — e.g. `which
|
|
416
|
-
modules import walk.mjs` — and the graph answers. Aggregate questions are answered
|
|
417
|
-
straight off the graph header: `how many classes are there` → `20 classes.`
|
|
418
|
-
(`how many <classes|functions|modules|methods|commits>`, `count the …`, `number of
|
|
419
|
-
…`). A near-miss structural question still gets the precise rephrase hint.
|
|
420
|
-
|
|
421
|
-
**Conversational manners** (deterministic, zero-model, recognised *before* any graph
|
|
422
|
-
dispatch). A small closed set of everyday expressions gets a templated reply rather
|
|
423
|
-
than a grammar hint:
|
|
424
|
-
|
|
425
|
-
| you say | you get |
|
|
426
|
-
| --- | --- |
|
|
427
|
-
| `hi` / `hello` / `hey` / `yo` / `g'day` / `good morning` (and `hello there`, with a small nod) | a short greeting + a nudge to ask or `/help` |
|
|
428
|
-
| `thanks` / `ta` / `cheers` / `ty` | an "any time" acknowledgement |
|
|
429
|
-
| `help` / `what can you do` / `who are you` / `?` | the orientation (the friendly help text; see also `/help`, `/stats`) |
|
|
430
|
-
| `bye` / `goodbye` / `quit` / `see ya` | a farewell **and a clean end of session** (flushes the log, closes — the `/exit` path) |
|
|
431
|
-
| `why` / `how` / `explain` / `say more` / `go on` / `elaborate` | **re-renders the last answer verbosely** — repeats it with the ask engine's traversal receipt and the full matched-entity list the terse render trims (no previous answer → it says so plainly) |
|
|
432
|
-
|
|
433
|
-
These turns resolve no code entity, so — like `/help` — they are recorded as plain
|
|
434
|
-
turns and never add `mgx:asksAbout` edges to the session graph.
|
|
435
|
-
|
|
436
|
-
**Slash-commands** reach every other query tool in one line each: `/find <query>`
|
|
437
|
-
(or `/search`) for a lexical search; `/context <symbol>` for the sized edit bundle
|
|
438
|
-
(start here to change code) and `/snippet <symbol>` for exact source; `/describe`,
|
|
439
|
-
`/signature`, `/members <class>`, `/subclasses <class>`, `/impact <module>`,
|
|
440
|
-
`/callers`, `/callees`, `/tests <symbol>`, `/untested`, `/history <symbol>`,
|
|
441
|
-
`/exports <module>` and `/arch [package]`, plus `/stats` for a one-screen overview
|
|
442
|
-
(entity counts, relationship counts, package totals). An unknown `/command` just
|
|
443
|
-
nudges you to `/help`; a bad symbol prints the tool's own clean error, never a stack.
|
|
444
|
-
|
|
445
|
-
The client tracks a **focus** entity for multi-turn context. A command or an answer
|
|
446
|
-
that resolves a primary entity remembers it (the prompt shows it, `seon(walk.mjs)>`);
|
|
447
|
-
a bare `it`/`this`/`that` in a question then binds to it, and a no-argument entity
|
|
448
|
-
command (`/context`, `/impact`, …) reuses it — so `what calls it` works straight
|
|
449
|
-
after `/describe walk.mjs`. Set it explicitly with `/focus <symbol>`. With no focus
|
|
450
|
-
set the client stays single-shot. `/help` lists the commands and the question
|
|
451
|
-
shapes; `/exit` (or Ctrl+C/Ctrl+D) leaves.
|
|
452
|
-
|
|
453
|
-
Each session is appended per turn to `<repo>/.seonix/session-<uuidv7>.log`, and
|
|
454
|
-
becomes first-class graph data like a commit — **slash-command turns included**: a
|
|
455
|
-
structured sidecar (`.seonix/sessions/*.jsonl`) records each turn's query, the
|
|
456
|
-
command used, and the entities it resolved/answered; the session enters `graph.json`
|
|
457
|
-
as a `Session` individual with `mgx:asksAbout` edges wherever a turn resolved an
|
|
458
|
-
entity (re-attached on every re-index), and it appears on the `seonix viz` timeline.
|
|
459
|
-
|
|
460
|
-
### Opt-in LLM fallback (`--with-claude` / `--with-copilot`)
|
|
461
|
-
|
|
462
|
-
**Default OFF. Chat-only. Never in the benchmark, digest, or MCP path.** With a flag
|
|
463
|
-
set, and *only* when the mechanical engine returns a genuine **miss** on a bare
|
|
464
|
-
question (after the conversational layer and slash-commands have had their turn),
|
|
465
|
-
chat consults an external LLM CLI — spawned in the target repo as cwd, with a
|
|
466
|
-
timeout. To keep answers **graph-grounded** it asks the model to *compile one seonix
|
|
467
|
-
command* (given the tool surface), then runs that command mechanically and shows the
|
|
468
|
-
grounded result, **labelled `[LLM-assisted via …]`**. If the model can't produce a
|
|
469
|
-
valid command, its raw text is shown instead, clearly marked `[answer from external
|
|
470
|
-
model … — NOT the deterministic seonix engine]`; the two are never silently blended.
|
|
471
|
-
|
|
472
|
-
- `--with-claude` → `claude -p "<compile-prompt>" --model haiku` (cheapest model).
|
|
473
|
-
- `--with-copilot` → `gh copilot -- -p "<compile-prompt>" --allow-all-tools --no-color`.
|
|
474
|
-
**Limitation, honestly:** the GitHub Copilot CLI is an *agentic shell assistant*
|
|
475
|
-
(its native verbs are running commands and editing files), not a graph Q&A tool —
|
|
476
|
-
it has no knowledge of the seonix query surface beyond what the prompt hands it, so
|
|
477
|
-
it's a poorer fit than `claude -p` and is wired best-effort. Prefer `--with-claude`.
|
|
478
|
-
|
|
479
|
-
Any failure — tool binary absent, non-zero exit, unparseable output, or timeout —
|
|
480
|
-
degrades to the normal honest-miss hint; the flag never crashes or hangs the chat. If
|
|
481
|
-
the flag is set but the binary is missing, chat says so **once at startup** and
|
|
482
|
-
continues mechanical-only. No LLM call is ever made unless the flag is set **and** the
|
|
483
|
-
mechanical engine missed — the hot path (and every benchmark run) stays zero-model.
|
|
484
|
-
|
|
485
|
-
A real transcript against this repository's own index (verified answers):
|
|
486
|
-
|
|
487
|
-
```text
|
|
488
|
-
seon> where is buildContextBundle defined
|
|
489
|
-
function buildContextBundle() is defined in packages/seonix/src/server.mjs at lines 138-260.
|
|
490
|
-
|
|
491
|
-
seon> what uses codegraph.mjs
|
|
492
|
-
bench/arms.mjs and bench/run.mjs and packages/seonix/src/ask.mjs and
|
|
493
|
-
packages/seonix/src/chat.mjs and packages/seonix/src/extract.mjs and
|
|
494
|
-
packages/seonix/src/server.mjs and packages/seonix/src/viz.mjs and scripts/rank-gate.mjs.
|
|
495
|
-
|
|
496
|
-
seon> when did walk.mjs change
|
|
497
|
-
packages/seonix/src/walk.mjs was last touched by commit a73ac1ba8bfb on 2026-07-01
|
|
498
|
-
("feat(seonix): .seonixignore excludes […]"); 1 earlier commit recorded.
|
|
499
|
-
|
|
500
|
-
seon> which changes touch commit 85073031
|
|
501
|
-
commit 85073031ff05 touched modules packages/seonix/src/viz.mjs and
|
|
502
|
-
packages/seonix/test/viz.test.mjs; functions buildViewerData() and cytoscapeSource().
|
|
503
|
-
|
|
504
|
-
seon> which functions call bulidContextBundle
|
|
505
|
-
assuming you meant buildContextBundle: in packages/seonix/src/server.mjs
|
|
506
|
-
there is function dispatchTool().
|
|
390
|
+
seonix chat [--repo /abs/path]
|
|
507
391
|
```
|
|
508
392
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
393
|
+
`seonix chat` opens an interactive natural-language client over the code graph. The
|
|
394
|
+
conversation engine is **tmct** (`@polycode-projects/the-mechanical-code-talker`), a
|
|
395
|
+
deterministic, no-LLM chatbot that seonix runs over its own graph through tmct's
|
|
396
|
+
provider seam. Every answer is grounded in the graph or an honest miss. With no
|
|
397
|
+
`--repo` the target defaults to the git root, so running it from a nested package dir
|
|
398
|
+
still covers the whole repo.
|
|
399
|
+
|
|
400
|
+
Ask structural questions in plain English — "which modules import walk.mjs", "where
|
|
401
|
+
is buildContextBundle defined", "when did walk.mjs change" — and route slash-commands
|
|
402
|
+
(`/context`, `/impact`, `/callers`, `/arch`, …) to the graph tools. On top of the
|
|
403
|
+
query layer, tmct adds conversational memory, provenance and trust scoring,
|
|
404
|
+
teach-and-recall, and multi-strategy interpretation. See the tmct docs for the full
|
|
405
|
+
command set.
|
|
406
|
+
|
|
407
|
+
seonix supplies the graph; tmct owns the conversation. The old opt-in
|
|
408
|
+
`--with-claude`/`--with-copilot` fallback is gone — tmct is no-LLM by contract.
|
|
516
409
|
|
|
517
410
|
## Languages
|
|
518
411
|
|
package/bin/cli.mjs
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
// seonix viz [--focus <sym>] [--depth N] [--out f.html] [--data-out f.json] [--repo-url <gitlab url> --ref main] [--site-nav] → render a focused sub-graph to HTML (one shared viewer; data embedded, or split out with --data-out). By default also writes code-browser.html + timeline.html next to --out with a working header nav; --graph-only suppresses the siblings
|
|
34
34
|
// seonix viz --browser-out f.html [--browser-data-out f.json] [--timeline-out f.html] [--scope product|<prefixes>] → override the sibling artifact paths (Chronograph code browser: temporal scrub + two-cursor diff + narration + ghost-branch merges + keyboard nav; archive/PLAN_CODE_BROWSER.md)
|
|
35
35
|
// seonix viz --serve [--port N] [--focus <sym>] → serve the same viewer against THIS repo's index (the local equivalent of the site's #viz; code browser at /code-browser.html, live-reannotating on HEAD change via /code-browser-version)
|
|
36
|
-
// seonix chat [--repo <abs>]
|
|
36
|
+
// seonix chat [--repo <abs>] → interactive tmct chat over the graph (@polycode-projects/the-mechanical-code-talker, a deterministic no-LLM chatbot run through tmct's provider seam); /exit to leave
|
|
37
37
|
//
|
|
38
38
|
// The index step is offline and model-free (Python ast + git). It writes the
|
|
39
39
|
// graph artifact to <repo_path>/.seonix/graph.json; the server (started with
|
|
@@ -643,15 +643,12 @@ async function main() {
|
|
|
643
643
|
}
|
|
644
644
|
|
|
645
645
|
if (mode === "chat") {
|
|
646
|
-
|
|
646
|
+
// The chat surface is tmct's now (PLAN_CHAT_EXTRACTION.md — the inversion): run tmct's chat
|
|
647
|
+
// over seonix's native graph via tmct's provider seam. The old --with-claude/--with-copilot
|
|
648
|
+
// LLM fallback does not port (tmct is no-LLM by contract).
|
|
649
|
+
const { runSeonixChat } = await import("../src/chat-shim.mjs");
|
|
647
650
|
const i = process.argv.indexOf("--repo");
|
|
648
|
-
|
|
649
|
-
// path): --with-claude shells `claude -p`, --with-copilot shells `gh copilot -- -p`.
|
|
650
|
-
await runChat({
|
|
651
|
-
repoPath: i !== -1 ? process.argv[i + 1] : undefined,
|
|
652
|
-
withClaude: process.argv.includes("--with-claude"),
|
|
653
|
-
withCopilot: process.argv.includes("--with-copilot"),
|
|
654
|
-
});
|
|
651
|
+
await runSeonixChat({ repoPath: i !== -1 ? process.argv[i + 1] : undefined });
|
|
655
652
|
return;
|
|
656
653
|
}
|
|
657
654
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polycode-projects/seonix",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "SEONIX — a deterministic, offline, $0 code-graph tool that makes a cheap coding agent edit like an expensive one. Indexes a repo with Python ast + git (zero model calls) and renders a bounded edit-digest.",
|
|
@@ -44,14 +44,13 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
47
|
+
"@polycode-projects/the-mechanical-code-talker": "^0.6.0",
|
|
47
48
|
"cytoscape": "^3.30.0",
|
|
48
49
|
"smol-toml": "^1.7.0",
|
|
49
50
|
"tree-sitter": "^0.21.1",
|
|
50
51
|
"tree-sitter-c-sharp": "^0.21.3",
|
|
51
52
|
"tree-sitter-java": "^0.21.0",
|
|
52
|
-
"typescript": "~5.6.2"
|
|
53
|
-
"wink-eng-lite-web-model": "^1.8.1",
|
|
54
|
-
"wink-nlp": "^2.4.0"
|
|
53
|
+
"typescript": "~5.6.2"
|
|
55
54
|
},
|
|
56
55
|
"scripts": {
|
|
57
56
|
"test": "node --test \"test/**/*.test.mjs\"",
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// ask-browser-entry.mjs — the esbuild entry for the website "Ask the graph" panel.
|
|
2
|
+
//
|
|
3
|
+
// PLAN_CHAT_EXTRACTION.md, the inversion: the panel's mechanical NL engine is now tmct's
|
|
4
|
+
// (@polycode-projects/the-mechanical-code-talker), bundled for the browser. It runs adapter-less
|
|
5
|
+
// (no wink model in the browser — tmct's ask degrades to its curated + fuzzy tiers), so the ~4 MB
|
|
6
|
+
// wink bundle is dropped from the site.
|
|
7
|
+
//
|
|
8
|
+
// This entry is bundled by scripts/build-ask-bundle.mjs into src/ask-browser.bundle.js (an IIFE),
|
|
9
|
+
// which viz.mjs inlines into the viewer page. It exposes two globals the panel's inline JS uses:
|
|
10
|
+
// parseEntities(payload) → the graph object tmct's ask consumes
|
|
11
|
+
// ask(graph, query, opts) → { content, seonix_ask } (tmct's tmct_ask, renamed for the panel)
|
|
12
|
+
import { ask as tmctAsk } from "@polycode-projects/the-mechanical-code-talker/ask";
|
|
13
|
+
|
|
14
|
+
// A self-contained parseEntities, byte-identical in output to seonix's and tmct's (the seonix-ask
|
|
15
|
+
// data file is already expanded by source.fetchEntities, so there is no v2 interning to undo here).
|
|
16
|
+
// Kept inline so the browser bundle pulls in nothing from seonix's Node-side codegraph stack.
|
|
17
|
+
function parseEntities(payload) {
|
|
18
|
+
const individuals = Array.isArray(payload?.individuals) ? payload.individuals : [];
|
|
19
|
+
const byId = new Map();
|
|
20
|
+
for (const ind of individuals) if (ind && ind.id) byId.set(ind.id, ind);
|
|
21
|
+
const relations = (Array.isArray(payload?.objectProperties) ? payload.objectProperties : []).map((g) => {
|
|
22
|
+
const edges = (Array.isArray(g?.examples) ? g.examples : []).filter((e) => e && e.subject && e.object);
|
|
23
|
+
return { predicate: String(g?.predicate || ""), prop: g?.prop ?? null, count: Number(g?.count) || edges.length, edges };
|
|
24
|
+
});
|
|
25
|
+
return {
|
|
26
|
+
individuals,
|
|
27
|
+
byId,
|
|
28
|
+
relations,
|
|
29
|
+
truncated: Boolean(payload?.truncated),
|
|
30
|
+
generatedAt: payload?.generated_at || "",
|
|
31
|
+
proseIndex: payload?.proseIndex || {},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Panel-compatible wrapper: tmct's ask returns { content, tmct_ask }; the panel's inline JS reads
|
|
36
|
+
// `seonix_ask.matches` / `.miss`, so rename the envelope. The match/miss shape is shared.
|
|
37
|
+
function ask(graph, query, opts) {
|
|
38
|
+
const r = tmctAsk(graph, query, opts);
|
|
39
|
+
return { content: r.content, seonix_ask: r.tmct_ask };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
globalThis.parseEntities = parseEntities;
|
|
43
|
+
globalThis.ask = ask;
|