@exellix/graph-composer 2.0.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/.env.example +66 -0
- package/LICENSE +21 -0
- package/README.md +329 -0
- package/dist/aiTaskProfile.d.ts +66 -0
- package/dist/aiTaskProfile.d.ts.map +1 -0
- package/dist/aiTaskProfile.js +179 -0
- package/dist/canonicalGraphDocument.d.ts +8 -0
- package/dist/canonicalGraphDocument.d.ts.map +1 -0
- package/dist/canonicalGraphDocument.js +344 -0
- package/dist/canonicalGraphWarnings.d.ts +6 -0
- package/dist/canonicalGraphWarnings.d.ts.map +1 -0
- package/dist/canonicalGraphWarnings.js +140 -0
- package/dist/catalogMatchAssist.d.ts +20 -0
- package/dist/catalogMatchAssist.d.ts.map +1 -0
- package/dist/catalogMatchAssist.js +203 -0
- package/dist/cataloxCatalogBridge.d.ts +103 -0
- package/dist/cataloxCatalogBridge.d.ts.map +1 -0
- package/dist/cataloxCatalogBridge.js +222 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +43 -0
- package/dist/composeInstructions.d.ts +11 -0
- package/dist/composeInstructions.d.ts.map +1 -0
- package/dist/composeInstructions.js +39 -0
- package/dist/defaultUtilitySkills.d.ts +4 -0
- package/dist/defaultUtilitySkills.d.ts.map +1 -0
- package/dist/defaultUtilitySkills.js +5 -0
- package/dist/exampleGeneration.d.ts +15 -0
- package/dist/exampleGeneration.d.ts.map +1 -0
- package/dist/exampleGeneration.js +72 -0
- package/dist/exampleInputs.d.ts +12 -0
- package/dist/exampleInputs.d.ts.map +1 -0
- package/dist/exampleInputs.js +181 -0
- package/dist/graphComposerActions.d.ts +22 -0
- package/dist/graphComposerActions.d.ts.map +1 -0
- package/dist/graphComposerActions.js +168 -0
- package/dist/graphComposerAgent.d.ts +26 -0
- package/dist/graphComposerAgent.d.ts.map +1 -0
- package/dist/graphComposerAgent.js +175 -0
- package/dist/graphComposerOutputValidation.d.ts +23 -0
- package/dist/graphComposerOutputValidation.d.ts.map +1 -0
- package/dist/graphComposerOutputValidation.js +709 -0
- package/dist/graphConceptPatchMerge.d.ts +10 -0
- package/dist/graphConceptPatchMerge.d.ts.map +1 -0
- package/dist/graphConceptPatchMerge.js +40 -0
- package/dist/graphEngineBridge.d.ts +7 -0
- package/dist/graphEngineBridge.d.ts.map +1 -0
- package/dist/graphEngineBridge.js +5 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/openRouterConnectTimeout.d.ts +3 -0
- package/dist/openRouterConnectTimeout.d.ts.map +1 -0
- package/dist/openRouterConnectTimeout.js +48 -0
- package/dist/packDir.d.ts +7 -0
- package/dist/packDir.d.ts.map +1 -0
- package/dist/packDir.js +23 -0
- package/dist/parseGraphConceptStory.d.ts +21 -0
- package/dist/parseGraphConceptStory.d.ts.map +1 -0
- package/dist/parseGraphConceptStory.js +105 -0
- package/dist/redactForLog.d.ts +2 -0
- package/dist/redactForLog.d.ts.map +1 -0
- package/dist/redactForLog.js +37 -0
- package/dist/runGraphComposer.d.ts +54 -0
- package/dist/runGraphComposer.d.ts.map +1 -0
- package/dist/runGraphComposer.js +444 -0
- package/dist/scopingCatalogHostTypes.d.ts +28 -0
- package/dist/scopingCatalogHostTypes.d.ts.map +1 -0
- package/dist/scopingCatalogHostTypes.js +6 -0
- package/dist/scopingNeedMatchAssist.d.ts +14 -0
- package/dist/scopingNeedMatchAssist.d.ts.map +1 -0
- package/dist/scopingNeedMatchAssist.js +58 -0
- package/dist/taskNodeTaskVariable.d.ts +44 -0
- package/dist/taskNodeTaskVariable.d.ts.map +1 -0
- package/dist/taskNodeTaskVariable.js +347 -0
- package/dist/types.d.ts +174 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/examples/network-vuln-subnet-triage.v2.json +389 -0
- package/functions/graph-composer/meta.json +607 -0
- package/functions/graph-composer/prompts/README.md +46 -0
- package/functions/graph-composer/prompts/action-create.md +51 -0
- package/functions/graph-composer/prompts/action-explain.md +26 -0
- package/functions/graph-composer/prompts/action-modify.md +32 -0
- package/functions/graph-composer/prompts/action-review-concept.md +97 -0
- package/functions/graph-composer/prompts/action-suggest-catalog-creations.md +31 -0
- package/functions/graph-composer/prompts/action-suggest-catalog-resolution.md +42 -0
- package/functions/graph-composer/prompts/action-suggest-concept-objective.md +38 -0
- package/functions/graph-composer/prompts/action-suggest-scoping-map-creation.md +31 -0
- package/functions/graph-composer/prompts/action-suggest-scoping-need-match.md +25 -0
- package/functions/graph-composer/prompts/default-utility-skills.json +22 -0
- package/functions/graph-composer/prompts/judge-rules.md +30 -0
- package/functions/graph-composer/prompts/orchestrator-system.md +21 -0
- package/functions/graph-composer/prompts/shared/graph-format.md +124 -0
- package/functions/graph-composer/prompts/shared/request-context.md +12 -0
- package/functions/graph-composer/prompts/shared/skill-selection.md +6 -0
- package/functions/graph-composer/prompts/shared/structural-validation.md +19 -0
- package/functions/graph-composer/prompts/skill-catalog-ai-header.md +3 -0
- package/functions/graph-composer/prompts/skill-catalog-utility-header.md +3 -0
- package/functions/graph-composer/prompts/skill-mode-extensible.md +7 -0
- package/functions/graph-composer/prompts/skill-mode-locked.md +7 -0
- package/functions/graph-composer/test-cases.json +52 -0
- package/package.json +86 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Client } from "@x12i/funcx";
|
|
2
|
+
import type { LlmMode } from "@x12i/funcx/functions";
|
|
3
|
+
import type { GraphComposerInput, SkillDescriptor } from "./types.js";
|
|
4
|
+
export type CatalogMatchAssistOptions = {
|
|
5
|
+
client: Client;
|
|
6
|
+
mode?: LlmMode;
|
|
7
|
+
model?: string;
|
|
8
|
+
temperature?: number;
|
|
9
|
+
maxTokens?: number;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* When `catalogCandidates` is set and lists are non-empty, runs `matchLists` per dimension
|
|
13
|
+
* and returns a compact object to inject into the worker prompt as `catalogMatchHints`.
|
|
14
|
+
*/
|
|
15
|
+
export declare function buildCatalogMatchHints(input: GraphComposerInput & {
|
|
16
|
+
skillMode: "locked" | "extensible";
|
|
17
|
+
aiSkills: SkillDescriptor[];
|
|
18
|
+
utilitySkills: SkillDescriptor[];
|
|
19
|
+
}, opts: CatalogMatchAssistOptions): Promise<Record<string, unknown> | undefined>;
|
|
20
|
+
//# sourceMappingURL=catalogMatchAssist.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalogMatchAssist.d.ts","sourceRoot":"","sources":["../src/catalogMatchAssist.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAEV,kBAAkB,EAGlB,eAAe,EAChB,MAAM,YAAY,CAAC;AAgKpB,MAAM,MAAM,yBAAyB,GAAG;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,kBAAkB,GAAG;IAC1B,SAAS,EAAE,QAAQ,GAAG,YAAY,CAAC;IACnC,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,aAAa,EAAE,eAAe,EAAE,CAAC;CAClC,EACD,IAAI,EAAE,yBAAyB,GAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,CAqF9C"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { matchLists } from "@x12i/funcx/functions";
|
|
2
|
+
function mergeBySkillKey(a, b) {
|
|
3
|
+
if (!b?.length)
|
|
4
|
+
return [...a];
|
|
5
|
+
const seen = new Set(a.map((s) => s.skillKey));
|
|
6
|
+
const out = [...a];
|
|
7
|
+
for (const s of b) {
|
|
8
|
+
if (!seen.has(s.skillKey)) {
|
|
9
|
+
seen.add(s.skillKey);
|
|
10
|
+
out.push(s);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return out;
|
|
14
|
+
}
|
|
15
|
+
function taskNodesFromGraph(graph) {
|
|
16
|
+
const g = graph;
|
|
17
|
+
const nodes = g.nodes;
|
|
18
|
+
if (!Array.isArray(nodes))
|
|
19
|
+
return [];
|
|
20
|
+
return nodes.filter((n) => n !== null && typeof n === "object" && !Array.isArray(n) && n.type === "task");
|
|
21
|
+
}
|
|
22
|
+
function nodeSkillSummary(node) {
|
|
23
|
+
const id = typeof node.id === "string" ? node.id : "(no-id)";
|
|
24
|
+
const skillKey = typeof node.skillKey === "string" ? node.skillKey : undefined;
|
|
25
|
+
const meta = node.metadata !== null &&
|
|
26
|
+
node.metadata !== undefined &&
|
|
27
|
+
typeof node.metadata === "object" &&
|
|
28
|
+
!Array.isArray(node.metadata)
|
|
29
|
+
? node.metadata
|
|
30
|
+
: undefined;
|
|
31
|
+
const gr = meta?.graphReadability !== null &&
|
|
32
|
+
meta?.graphReadability !== undefined &&
|
|
33
|
+
typeof meta.graphReadability === "object" &&
|
|
34
|
+
!Array.isArray(meta.graphReadability)
|
|
35
|
+
? meta.graphReadability
|
|
36
|
+
: undefined;
|
|
37
|
+
const title = typeof gr?.title === "string" ? gr.title : undefined;
|
|
38
|
+
const asks = typeof gr?.asks === "string" ? gr.asks : undefined;
|
|
39
|
+
const kind = typeof gr?.kind === "string" ? gr.kind : undefined;
|
|
40
|
+
return {
|
|
41
|
+
nodeId: id,
|
|
42
|
+
currentSkillKey: skillKey,
|
|
43
|
+
title,
|
|
44
|
+
asks,
|
|
45
|
+
graphReadabilityKind: kind,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function scopingNodeSummary(node) {
|
|
49
|
+
const meta = node.metadata !== null &&
|
|
50
|
+
node.metadata !== undefined &&
|
|
51
|
+
typeof node.metadata === "object" &&
|
|
52
|
+
!Array.isArray(node.metadata)
|
|
53
|
+
? node.metadata
|
|
54
|
+
: undefined;
|
|
55
|
+
const skillKey = typeof node.skillKey === "string" ? node.skillKey : undefined;
|
|
56
|
+
const usesScoped = skillKey === "scoped-data-reader" ||
|
|
57
|
+
skillKey === "scoped-answer-writer" ||
|
|
58
|
+
typeof meta?.scopingMapId === "string" ||
|
|
59
|
+
typeof meta?.questionId === "string";
|
|
60
|
+
if (!usesScoped)
|
|
61
|
+
return null;
|
|
62
|
+
const id = typeof node.id === "string" ? node.id : "(no-id)";
|
|
63
|
+
const gr = meta?.graphReadability !== null &&
|
|
64
|
+
meta?.graphReadability !== undefined &&
|
|
65
|
+
typeof meta.graphReadability === "object" &&
|
|
66
|
+
!Array.isArray(meta.graphReadability)
|
|
67
|
+
? meta.graphReadability
|
|
68
|
+
: undefined;
|
|
69
|
+
return {
|
|
70
|
+
nodeId: id,
|
|
71
|
+
skillKey,
|
|
72
|
+
title: typeof gr?.title === "string" ? gr.title : undefined,
|
|
73
|
+
asks: typeof gr?.asks === "string" ? gr.asks : undefined,
|
|
74
|
+
scopingMapId: typeof meta?.scopingMapId === "string" ? meta.scopingMapId : undefined,
|
|
75
|
+
questionId: typeof meta?.questionId === "string" ? meta.questionId : undefined,
|
|
76
|
+
entityIdPath: typeof meta?.entityIdPath === "string" ? meta.entityIdPath : undefined,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function narrixNodeSummary(node) {
|
|
80
|
+
const meta = node.metadata !== null &&
|
|
81
|
+
node.metadata !== undefined &&
|
|
82
|
+
typeof node.metadata === "object" &&
|
|
83
|
+
!Array.isArray(node.metadata)
|
|
84
|
+
? node.metadata
|
|
85
|
+
: undefined;
|
|
86
|
+
if (!meta?.narrix)
|
|
87
|
+
return null;
|
|
88
|
+
const narrix = meta.narrix;
|
|
89
|
+
if (narrix === null ||
|
|
90
|
+
typeof narrix !== "object" ||
|
|
91
|
+
Array.isArray(narrix)) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
const n = narrix;
|
|
95
|
+
const id = typeof node.id === "string" ? node.id : "(no-id)";
|
|
96
|
+
const skillKey = typeof node.skillKey === "string" ? node.skillKey : undefined;
|
|
97
|
+
return {
|
|
98
|
+
nodeId: id,
|
|
99
|
+
skillKey,
|
|
100
|
+
narrix: {
|
|
101
|
+
datasetId: n.datasetId,
|
|
102
|
+
layer: n.layer,
|
|
103
|
+
enableWebScope: n.enableWebScope,
|
|
104
|
+
narrativeTypeIds: n.narrativeTypeIds,
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function skillCatalogItems(skills) {
|
|
109
|
+
return skills.map((s) => ({
|
|
110
|
+
skillKey: s.skillKey,
|
|
111
|
+
description: s.description,
|
|
112
|
+
isLocal: s.isLocal ?? false,
|
|
113
|
+
tags: s.tags ?? [],
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
function scopingCatalogItems(maps) {
|
|
117
|
+
return maps.map((m) => ({
|
|
118
|
+
scopingMapId: m.scopingMapId,
|
|
119
|
+
questionId: m.questionId,
|
|
120
|
+
title: m.title,
|
|
121
|
+
description: m.description,
|
|
122
|
+
tags: m.tags ?? [],
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
125
|
+
function narrixCatalogItems(templates) {
|
|
126
|
+
return templates.map((t) => ({
|
|
127
|
+
id: t.id,
|
|
128
|
+
title: t.title,
|
|
129
|
+
description: t.description,
|
|
130
|
+
datasetId: t.datasetId,
|
|
131
|
+
layer: t.layer,
|
|
132
|
+
narrativeTypeIds: t.narrativeTypeIds ?? [],
|
|
133
|
+
enableWebScope: t.enableWebScope ?? false,
|
|
134
|
+
tags: t.tags ?? [],
|
|
135
|
+
}));
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* When `catalogCandidates` is set and lists are non-empty, runs `matchLists` per dimension
|
|
139
|
+
* and returns a compact object to inject into the worker prompt as `catalogMatchHints`.
|
|
140
|
+
*/
|
|
141
|
+
export async function buildCatalogMatchHints(input, opts) {
|
|
142
|
+
const cc = input.catalogCandidates;
|
|
143
|
+
if (!cc ||
|
|
144
|
+
(input.intent.action !== "suggestCatalogResolution" &&
|
|
145
|
+
input.intent.action !== "suggestCatalogCreations")) {
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
148
|
+
const existingGraph = input.existingGraph;
|
|
149
|
+
if (existingGraph === undefined ||
|
|
150
|
+
existingGraph === null ||
|
|
151
|
+
typeof existingGraph !== "object") {
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
|
154
|
+
const hints = {};
|
|
155
|
+
const { client, mode, model } = opts;
|
|
156
|
+
const aiMerged = mergeBySkillKey(input.aiSkills, cc.aiSkills);
|
|
157
|
+
const utilMerged = mergeBySkillKey(input.utilitySkills, cc.utilitySkills);
|
|
158
|
+
const allSkills = [...utilMerged, ...aiMerged];
|
|
159
|
+
const taskNodes = taskNodesFromGraph(existingGraph);
|
|
160
|
+
const nodeSummaries = taskNodes.map(nodeSkillSummary);
|
|
161
|
+
if (nodeSummaries.length > 0 && allSkills.length > 0) {
|
|
162
|
+
const r = await matchLists({
|
|
163
|
+
list1: nodeSummaries,
|
|
164
|
+
list2: skillCatalogItems(allSkills),
|
|
165
|
+
guidance: "Match each graph task node to the single best catalog skillKey by operational role (data read, rules, LLM, scoped IO, assembly). Prefer local/utility skills for deterministic work. Use reason briefly.",
|
|
166
|
+
mode,
|
|
167
|
+
client,
|
|
168
|
+
model,
|
|
169
|
+
}, { rules: [] });
|
|
170
|
+
hints.skills = { matches: r.matches, unmatched: r.unmatched };
|
|
171
|
+
}
|
|
172
|
+
const scopingNodes = taskNodes
|
|
173
|
+
.map(scopingNodeSummary)
|
|
174
|
+
.filter((x) => x !== null);
|
|
175
|
+
const scopingMaps = cc.scopingMaps ?? [];
|
|
176
|
+
if (scopingNodes.length > 0 && scopingMaps.length > 0) {
|
|
177
|
+
const r = await matchLists({
|
|
178
|
+
list1: scopingNodes,
|
|
179
|
+
list2: scopingCatalogItems(scopingMaps),
|
|
180
|
+
guidance: "Match each scoped-data or scoping-related node to the best scopingMapId or questionId candidate by what the node reads/asks and entity scope. reason briefly.",
|
|
181
|
+
mode,
|
|
182
|
+
client,
|
|
183
|
+
model,
|
|
184
|
+
}, { rules: [] });
|
|
185
|
+
hints.scoping = { matches: r.matches, unmatched: r.unmatched };
|
|
186
|
+
}
|
|
187
|
+
const narrixNodes = taskNodes
|
|
188
|
+
.map(narrixNodeSummary)
|
|
189
|
+
.filter((x) => x !== null);
|
|
190
|
+
const narrixTemplates = cc.narrixTemplates ?? [];
|
|
191
|
+
if (narrixNodes.length > 0 && narrixTemplates.length > 0) {
|
|
192
|
+
const r = await matchLists({
|
|
193
|
+
list1: narrixNodes,
|
|
194
|
+
list2: narrixCatalogItems(narrixTemplates),
|
|
195
|
+
guidance: "Match each node that has metadata.narrix to the best narrix template candidate (layer, datasetId, narrativeTypeIds, web scope). reason briefly.",
|
|
196
|
+
mode,
|
|
197
|
+
client,
|
|
198
|
+
model,
|
|
199
|
+
}, { rules: [] });
|
|
200
|
+
hints.narrix = { matches: r.matches, unmatched: r.unmatched };
|
|
201
|
+
}
|
|
202
|
+
return Object.keys(hints).length > 0 ? hints : undefined;
|
|
203
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Load {@link CatalogCandidates} from Catalox (`listCatalogItems`, or optional
|
|
3
|
+
* {@link CataloxCatalogBridgeOptions.aiItemMatch}: pool rows via Catalox, then rank by relevance with **funcx** `rank` — no Catalox search API).
|
|
4
|
+
* Hosts construct {@link Catalox} via `createCatalox`
|
|
5
|
+
* from `@x12i/catalox/embedder` and pass `CataloxContext` (with **@x12i/catalox** ≥ 3.4.6,
|
|
6
|
+
* `appId` may be omitted for catalog-id-first flows when your deployment allows it).
|
|
7
|
+
*
|
|
8
|
+
* **Catalog IDs are caller-supplied** — this package does not hardcode your
|
|
9
|
+
* Firestore `catalogId` strings. Use {@link CataloxCatalogBridgeCatalogIds} to
|
|
10
|
+
* map your Catalox catalogs into the four `CatalogCandidates` slices (see
|
|
11
|
+
* `docs/catalog-metadb-end-state-contract.md` for a reference mapping table).
|
|
12
|
+
*/
|
|
13
|
+
import type { Catalox } from "@x12i/catalox/embedder";
|
|
14
|
+
import type { CataloxContext, CatalogQueryOptions, UnifiedCatalogItem } from "@x12i/catalox/embedder";
|
|
15
|
+
import type { Client } from "@x12i/funcx";
|
|
16
|
+
import type { LlmMode } from "@x12i/funcx/functions";
|
|
17
|
+
import type { CatalogCandidates, NarrixTemplateCandidate, ScopingMapCandidate, SkillDescriptor } from "./types.js";
|
|
18
|
+
/**
|
|
19
|
+
* Which Catalox `catalogId` to read for each optional fetch. Several keys
|
|
20
|
+
* merge into the same `CatalogCandidates` slice (e.g. pre/post strategy catalogs
|
|
21
|
+
* both become `aiSkills`).
|
|
22
|
+
*/
|
|
23
|
+
export type CataloxCatalogBridgeCatalogIds = {
|
|
24
|
+
/** e.g. `ai-skills` → {@link CatalogCandidates.aiSkills} */
|
|
25
|
+
aiSkills?: string;
|
|
26
|
+
/** e.g. `ai-tasks-strategies-pre` → merged into `aiSkills` after `aiSkills`. */
|
|
27
|
+
aiTasksStrategiesPre?: string;
|
|
28
|
+
/** e.g. `ai-tasks-strategies-post` → merged into `aiSkills` after pre. */
|
|
29
|
+
aiTasksStrategiesPost?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Synthesis **mode** catalog (PRE-style rows) → merged into `aiSkills` after post.
|
|
32
|
+
* Node `metadata.aiTaskProfile.inputSynthesis.catalogId` should match this id when using that catalog.
|
|
33
|
+
*/
|
|
34
|
+
inputSynthesisModes?: string;
|
|
35
|
+
utilitySkills?: string;
|
|
36
|
+
/** Legacy single scoping catalog; merged first into `scopingMaps`. */
|
|
37
|
+
scopingMaps?: string;
|
|
38
|
+
/** e.g. `xmemory.scopingQuestions` → merged into `scopingMaps`. */
|
|
39
|
+
xmemoryScopingQuestions?: string;
|
|
40
|
+
/** e.g. `xmemory.entity-collections` → merged into `scopingMaps`. */
|
|
41
|
+
xmemoryEntityCollections?: string;
|
|
42
|
+
narrixTemplates?: string;
|
|
43
|
+
/** Alias for Catalox catalog id `narratives` (same slice as `narrixTemplates`). */
|
|
44
|
+
narratives?: string;
|
|
45
|
+
};
|
|
46
|
+
export type CataloxCatalogBridgeListOptions = Partial<Record<keyof CataloxCatalogBridgeCatalogIds, CatalogQueryOptions | undefined>>;
|
|
47
|
+
/**
|
|
48
|
+
* Semantic narrowing: Catalox supplies a **candidate pool** (`listCatalogItems` with `poolLimit`),
|
|
49
|
+
* then **@x12i/funcx** `rank` scores items against `query`. Catalox search APIs are not used.
|
|
50
|
+
*
|
|
51
|
+
* `temperature`, `maxTokens`, and `timeoutMs` are reserved for future funcx wiring; `rank` currently uses its defaults.
|
|
52
|
+
*/
|
|
53
|
+
export type CataloxCatalogBridgeAiItemMatch = {
|
|
54
|
+
query: string;
|
|
55
|
+
maxResultsPerSlice?: number;
|
|
56
|
+
/** Drop ranked rows below this score (0–1). If every row fails, the slice is empty. */
|
|
57
|
+
minScore?: number;
|
|
58
|
+
/** Max rows pulled from Catalox before ranking (default 200). */
|
|
59
|
+
poolLimit?: number;
|
|
60
|
+
mode?: LlmMode;
|
|
61
|
+
/** Required: OpenRouter-backed funcx client (same as graph-composer workers). */
|
|
62
|
+
client: Client;
|
|
63
|
+
model?: string;
|
|
64
|
+
temperature?: number;
|
|
65
|
+
maxTokens?: number;
|
|
66
|
+
timeoutMs?: number;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Optional **per-bridge-key** Catalox context for multi-app deployments: each
|
|
70
|
+
* `catalogId` may be bound under a different `appId` than your default service app.
|
|
71
|
+
* When omitted for a key, the **base** `context` passed to {@link loadCatalogCandidatesFromCatalox} is used.
|
|
72
|
+
*/
|
|
73
|
+
export type CataloxCatalogBridgeContextByKey = Partial<Record<keyof CataloxCatalogBridgeCatalogIds, CataloxContext>>;
|
|
74
|
+
export type CataloxCatalogBridgeOptions = {
|
|
75
|
+
catalogIds: CataloxCatalogBridgeCatalogIds;
|
|
76
|
+
listOptions?: CataloxCatalogBridgeListOptions;
|
|
77
|
+
/** When set, lists a capped pool per catalog, then ranks with funcx using {@link CataloxCatalogBridgeAiItemMatch.query}. */
|
|
78
|
+
aiItemMatch?: CataloxCatalogBridgeAiItemMatch;
|
|
79
|
+
/**
|
|
80
|
+
* Pin `CataloxContext` per bridge fetch (e.g. `aiSkills` under the `ai-skills` app,
|
|
81
|
+
* PRE/POST under `ai-tasks`). See `docs/catalox-multi-app-context.md`.
|
|
82
|
+
*/
|
|
83
|
+
contextByBridgeKey?: CataloxCatalogBridgeContextByKey;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Map a Catalox row to {@link SkillDescriptor} (AI or utility catalog).
|
|
87
|
+
* Reads `data.skillKey` / `data.key` / `itemId`, then description from `data.description`, subtitle, or title.
|
|
88
|
+
*/
|
|
89
|
+
export declare function unifiedCatalogItemToSkillDescriptor(item: UnifiedCatalogItem): SkillDescriptor;
|
|
90
|
+
/**
|
|
91
|
+
* Map a Catalox row to {@link ScopingMapCandidate} (expects `data` fields aligned with your descriptor).
|
|
92
|
+
*/
|
|
93
|
+
export declare function unifiedCatalogItemToScopingMapCandidate(item: UnifiedCatalogItem): ScopingMapCandidate;
|
|
94
|
+
/**
|
|
95
|
+
* Map a Catalox row to {@link NarrixTemplateCandidate}.
|
|
96
|
+
*/
|
|
97
|
+
export declare function unifiedCatalogItemToNarrixTemplateCandidate(item: UnifiedCatalogItem): NarrixTemplateCandidate;
|
|
98
|
+
/**
|
|
99
|
+
* Fetches configured Catalox catalogs and returns a {@link CatalogCandidates} object
|
|
100
|
+
* suitable for `GraphComposerInput.catalogCandidates`.
|
|
101
|
+
*/
|
|
102
|
+
export declare function loadCatalogCandidatesFromCatalox(catalox: Catalox, context: CataloxContext, options: CataloxCatalogBridgeOptions): Promise<CatalogCandidates>;
|
|
103
|
+
//# sourceMappingURL=cataloxCatalogBridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cataloxCatalogBridge.d.ts","sourceRoot":"","sources":["../src/cataloxCatalogBridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,KAAK,EACV,cAAc,EACd,mBAAmB,EACnB,kBAAkB,EACnB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EACV,iBAAiB,EACjB,uBAAuB,EACvB,mBAAmB,EACnB,eAAe,EAChB,MAAM,YAAY,CAAC;AAEpB;;;;GAIG;AACH,MAAM,MAAM,8BAA8B,GAAG;IAC3C,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,0EAA0E;IAC1E,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mEAAmE;IACnE,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,qEAAqE;IACrE,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mFAAmF;IACnF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG,OAAO,CACnD,MAAM,CAAC,MAAM,8BAA8B,EAAE,mBAAmB,GAAG,SAAS,CAAC,CAC9E,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,+BAA+B,GAAG;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,uFAAuF;IACvF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,iFAAiF;IACjF,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,gCAAgC,GAAG,OAAO,CACpD,MAAM,CAAC,MAAM,8BAA8B,EAAE,cAAc,CAAC,CAC7D,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,UAAU,EAAE,8BAA8B,CAAC;IAC3C,WAAW,CAAC,EAAE,+BAA+B,CAAC;IAC9C,4HAA4H;IAC5H,WAAW,CAAC,EAAE,+BAA+B,CAAC;IAC9C;;;OAGG;IACH,kBAAkB,CAAC,EAAE,gCAAgC,CAAC;CACvD,CAAC;AA2FF;;;GAGG;AACH,wBAAgB,mCAAmC,CACjD,IAAI,EAAE,kBAAkB,GACvB,eAAe,CAwBjB;AAED;;GAEG;AACH,wBAAgB,uCAAuC,CACrD,IAAI,EAAE,kBAAkB,GACvB,mBAAmB,CASrB;AAED;;GAEG;AACH,wBAAgB,2CAA2C,CACzD,IAAI,EAAE,kBAAkB,GACvB,uBAAuB,CAYzB;AA2CD;;;GAGG;AACH,wBAAsB,gCAAgC,CACpD,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,iBAAiB,CAAC,CAyF5B"}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { rank } from "@x12i/funcx/functions";
|
|
2
|
+
function resolveBridgeContext(bridgeKey, baseContext, options) {
|
|
3
|
+
const pin = options.contextByBridgeKey?.[bridgeKey];
|
|
4
|
+
return pin ?? baseContext;
|
|
5
|
+
}
|
|
6
|
+
function pickStr(v) {
|
|
7
|
+
return typeof v === "string" && v.trim().length > 0 ? v.trim() : undefined;
|
|
8
|
+
}
|
|
9
|
+
function pickStrArray(v) {
|
|
10
|
+
if (!Array.isArray(v))
|
|
11
|
+
return undefined;
|
|
12
|
+
const out = v.filter((x) => typeof x === "string" && x.trim().length > 0);
|
|
13
|
+
return out.length > 0 ? out : undefined;
|
|
14
|
+
}
|
|
15
|
+
function pickBool(v) {
|
|
16
|
+
return typeof v === "boolean" ? v : undefined;
|
|
17
|
+
}
|
|
18
|
+
const DEFAULT_AI_POOL_LIMIT = 200;
|
|
19
|
+
function compactUnifiedItemForRank(item) {
|
|
20
|
+
const d = item.data ?? {};
|
|
21
|
+
return {
|
|
22
|
+
itemId: String(item.itemId),
|
|
23
|
+
title: item.title,
|
|
24
|
+
subtitle: item.subtitle,
|
|
25
|
+
skillKey: pickStr(d.skillKey) ?? pickStr(d.key),
|
|
26
|
+
description: pickStr(d.description),
|
|
27
|
+
id: pickStr(d.id),
|
|
28
|
+
scopingMapId: pickStr(d.scopingMapId) ?? pickStr(d.scoping_map_id),
|
|
29
|
+
questionId: pickStr(d.questionId) ?? pickStr(d.question_id),
|
|
30
|
+
datasetId: pickStr(d.datasetId) ?? pickStr(d.dataset_id),
|
|
31
|
+
layer: pickStr(d.layer),
|
|
32
|
+
tags: pickStrArray(d.tags),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
async function rankCatalogPoolWithFuncx(pool, ai) {
|
|
36
|
+
if (pool.length === 0)
|
|
37
|
+
return [];
|
|
38
|
+
const payloads = pool.map((it) => compactUnifiedItemForRank(it));
|
|
39
|
+
const ranked = await rank({
|
|
40
|
+
items: payloads,
|
|
41
|
+
query: ai.query,
|
|
42
|
+
mode: ai.mode ?? "normal",
|
|
43
|
+
client: ai.client,
|
|
44
|
+
model: ai.model,
|
|
45
|
+
}, { rules: [] });
|
|
46
|
+
const byId = new Map(pool.map((it) => [String(it.itemId), it]));
|
|
47
|
+
const raw = ranked.rankedItems;
|
|
48
|
+
if (!Array.isArray(raw))
|
|
49
|
+
return [];
|
|
50
|
+
const scored = raw
|
|
51
|
+
.map((row) => {
|
|
52
|
+
const payload = row?.item;
|
|
53
|
+
const id = payload && typeof payload.itemId === "string" ? payload.itemId : undefined;
|
|
54
|
+
const score = typeof row?.score === "number" ? row.score : 0;
|
|
55
|
+
return id !== undefined ? { id, score } : null;
|
|
56
|
+
})
|
|
57
|
+
.filter((x) => x !== null)
|
|
58
|
+
.sort((a, b) => b.score - a.score);
|
|
59
|
+
const maxN = ai.maxResultsPerSlice ?? 50;
|
|
60
|
+
const min = ai.minScore;
|
|
61
|
+
const out = [];
|
|
62
|
+
const seen = new Set();
|
|
63
|
+
for (const row of scored) {
|
|
64
|
+
if (out.length >= maxN)
|
|
65
|
+
break;
|
|
66
|
+
if (min !== undefined && row.score < min)
|
|
67
|
+
continue;
|
|
68
|
+
const full = byId.get(row.id);
|
|
69
|
+
if (full && !seen.has(row.id)) {
|
|
70
|
+
seen.add(row.id);
|
|
71
|
+
out.push(full);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Map a Catalox row to {@link SkillDescriptor} (AI or utility catalog).
|
|
78
|
+
* Reads `data.skillKey` / `data.key` / `itemId`, then description from `data.description`, subtitle, or title.
|
|
79
|
+
*/
|
|
80
|
+
export function unifiedCatalogItemToSkillDescriptor(item) {
|
|
81
|
+
const d = item.data ?? {};
|
|
82
|
+
const skillKey = pickStr(d.skillKey) ??
|
|
83
|
+
pickStr(d.key) ??
|
|
84
|
+
pickStr(d.id) ??
|
|
85
|
+
String(item.itemId);
|
|
86
|
+
const description = pickStr(d.description) ??
|
|
87
|
+
item.subtitle ??
|
|
88
|
+
item.title ??
|
|
89
|
+
skillKey;
|
|
90
|
+
const isLocal = pickBool(d.isLocal);
|
|
91
|
+
const tags = pickStrArray(d.tags);
|
|
92
|
+
const out = { skillKey, description };
|
|
93
|
+
if (isLocal !== undefined)
|
|
94
|
+
out.isLocal = isLocal;
|
|
95
|
+
if (tags !== undefined)
|
|
96
|
+
out.tags = tags;
|
|
97
|
+
if (typeof d.inputSchema === "object" && d.inputSchema !== null && !Array.isArray(d.inputSchema)) {
|
|
98
|
+
out.inputSchema = d.inputSchema;
|
|
99
|
+
}
|
|
100
|
+
if (typeof d.outputSchema === "object" && d.outputSchema !== null && !Array.isArray(d.outputSchema)) {
|
|
101
|
+
out.outputSchema = d.outputSchema;
|
|
102
|
+
}
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Map a Catalox row to {@link ScopingMapCandidate} (expects `data` fields aligned with your descriptor).
|
|
107
|
+
*/
|
|
108
|
+
export function unifiedCatalogItemToScopingMapCandidate(item) {
|
|
109
|
+
const d = item.data ?? {};
|
|
110
|
+
return {
|
|
111
|
+
scopingMapId: pickStr(d.scopingMapId) ?? pickStr(d.scoping_map_id),
|
|
112
|
+
questionId: pickStr(d.questionId) ?? pickStr(d.question_id),
|
|
113
|
+
title: item.title ?? pickStr(d.title),
|
|
114
|
+
description: pickStr(d.description) ?? item.subtitle,
|
|
115
|
+
tags: pickStrArray(d.tags),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Map a Catalox row to {@link NarrixTemplateCandidate}.
|
|
120
|
+
*/
|
|
121
|
+
export function unifiedCatalogItemToNarrixTemplateCandidate(item) {
|
|
122
|
+
const d = item.data ?? {};
|
|
123
|
+
return {
|
|
124
|
+
id: pickStr(d.id) ?? String(item.itemId),
|
|
125
|
+
title: item.title ?? pickStr(d.title),
|
|
126
|
+
description: pickStr(d.description) ?? item.subtitle,
|
|
127
|
+
datasetId: pickStr(d.datasetId) ?? pickStr(d.dataset_id),
|
|
128
|
+
layer: pickStr(d.layer),
|
|
129
|
+
narrativeTypeIds: pickStrArray(d.narrativeTypeIds) ?? pickStrArray(d.narrative_type_ids),
|
|
130
|
+
enableWebScope: pickBool(d.enableWebScope) ?? pickBool(d.enable_web_scope),
|
|
131
|
+
tags: pickStrArray(d.tags),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
async function listItemsForCatalog(catalox, context, catalogId, listOptions, ai) {
|
|
135
|
+
if (ai !== undefined) {
|
|
136
|
+
const poolLimit = ai.poolLimit ?? listOptions?.limit ?? DEFAULT_AI_POOL_LIMIT;
|
|
137
|
+
const poolOpts = {
|
|
138
|
+
...listOptions,
|
|
139
|
+
limit: poolLimit,
|
|
140
|
+
};
|
|
141
|
+
const list = await catalox.listCatalogItems(context, catalogId, poolOpts);
|
|
142
|
+
if (list.listOutcome === "mapping_blocked") {
|
|
143
|
+
const msg = (list.issues ?? [])
|
|
144
|
+
.map((i) => i.message ?? String(i))
|
|
145
|
+
.join("; ");
|
|
146
|
+
throw new Error(`Catalox listCatalogItems mapping_blocked for catalog "${catalogId}" (AI pool)${msg ? `: ${msg}` : ""}`);
|
|
147
|
+
}
|
|
148
|
+
return rankCatalogPoolWithFuncx(list.items, ai);
|
|
149
|
+
}
|
|
150
|
+
const list = await catalox.listCatalogItems(context, catalogId, listOptions);
|
|
151
|
+
if (list.listOutcome === "mapping_blocked") {
|
|
152
|
+
const msg = (list.issues ?? [])
|
|
153
|
+
.map((i) => i.message ?? String(i))
|
|
154
|
+
.join("; ");
|
|
155
|
+
throw new Error(`Catalox listCatalogItems mapping_blocked for catalog "${catalogId}"${msg ? `: ${msg}` : ""}`);
|
|
156
|
+
}
|
|
157
|
+
return list.items;
|
|
158
|
+
}
|
|
159
|
+
function nonemptyCatalogId(id) {
|
|
160
|
+
return id !== undefined && id !== "";
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Fetches configured Catalox catalogs and returns a {@link CatalogCandidates} object
|
|
164
|
+
* suitable for `GraphComposerInput.catalogCandidates`.
|
|
165
|
+
*/
|
|
166
|
+
export async function loadCatalogCandidatesFromCatalox(catalox, context, options) {
|
|
167
|
+
const { catalogIds, listOptions, aiItemMatch } = options;
|
|
168
|
+
const out = {};
|
|
169
|
+
const aiSkillCatalogs = [
|
|
170
|
+
"aiSkills",
|
|
171
|
+
"aiTasksStrategiesPre",
|
|
172
|
+
"aiTasksStrategiesPost",
|
|
173
|
+
"inputSynthesisModes",
|
|
174
|
+
];
|
|
175
|
+
const skillRows = [];
|
|
176
|
+
for (const key of aiSkillCatalogs) {
|
|
177
|
+
const id = catalogIds[key];
|
|
178
|
+
if (!nonemptyCatalogId(id))
|
|
179
|
+
continue;
|
|
180
|
+
const items = await listItemsForCatalog(catalox, resolveBridgeContext(key, context, options), id, listOptions?.[key], aiItemMatch);
|
|
181
|
+
skillRows.push(...items.map((i) => unifiedCatalogItemToSkillDescriptor(i)));
|
|
182
|
+
}
|
|
183
|
+
if (skillRows.length > 0) {
|
|
184
|
+
out.aiSkills = skillRows;
|
|
185
|
+
}
|
|
186
|
+
if (nonemptyCatalogId(catalogIds.utilitySkills)) {
|
|
187
|
+
const key = "utilitySkills";
|
|
188
|
+
const items = await listItemsForCatalog(catalox, resolveBridgeContext(key, context, options), catalogIds.utilitySkills, listOptions?.utilitySkills, aiItemMatch);
|
|
189
|
+
out.utilitySkills = items.map((i) => unifiedCatalogItemToSkillDescriptor(i));
|
|
190
|
+
}
|
|
191
|
+
const scopingCatalogs = [
|
|
192
|
+
"scopingMaps",
|
|
193
|
+
"xmemoryScopingQuestions",
|
|
194
|
+
"xmemoryEntityCollections",
|
|
195
|
+
];
|
|
196
|
+
const scopingRows = [];
|
|
197
|
+
for (const key of scopingCatalogs) {
|
|
198
|
+
const id = catalogIds[key];
|
|
199
|
+
if (!nonemptyCatalogId(id))
|
|
200
|
+
continue;
|
|
201
|
+
const items = await listItemsForCatalog(catalox, resolveBridgeContext(key, context, options), id, listOptions?.[key], aiItemMatch);
|
|
202
|
+
scopingRows.push(...items.map((i) => unifiedCatalogItemToScopingMapCandidate(i)));
|
|
203
|
+
}
|
|
204
|
+
if (scopingRows.length > 0) {
|
|
205
|
+
out.scopingMaps = scopingRows;
|
|
206
|
+
}
|
|
207
|
+
const narrixParts = [];
|
|
208
|
+
if (nonemptyCatalogId(catalogIds.narrixTemplates)) {
|
|
209
|
+
const key = "narrixTemplates";
|
|
210
|
+
const items = await listItemsForCatalog(catalox, resolveBridgeContext(key, context, options), catalogIds.narrixTemplates, listOptions?.narrixTemplates, aiItemMatch);
|
|
211
|
+
narrixParts.push(items.map((i) => unifiedCatalogItemToNarrixTemplateCandidate(i)));
|
|
212
|
+
}
|
|
213
|
+
if (nonemptyCatalogId(catalogIds.narratives)) {
|
|
214
|
+
const key = "narratives";
|
|
215
|
+
const items = await listItemsForCatalog(catalox, resolveBridgeContext(key, context, options), catalogIds.narratives, listOptions?.narratives, aiItemMatch);
|
|
216
|
+
narrixParts.push(items.map((i) => unifiedCatalogItemToNarrixTemplateCandidate(i)));
|
|
217
|
+
}
|
|
218
|
+
if (narrixParts.length > 0) {
|
|
219
|
+
out.narrixTemplates = narrixParts.flat();
|
|
220
|
+
}
|
|
221
|
+
return out;
|
|
222
|
+
}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI: runs one graph-composer case against OpenRouter.
|
|
4
|
+
* - Positional args `network-vuln` / `network-vuln-intent` are **demo smoke** entrypoints (bundled sample graph), not the core library API.
|
|
5
|
+
* - Prefer constructing `GraphComposerInput` in application code or using `test-cases.json` ids for fixtures.
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
import { runGraphComposer } from "./runGraphComposer.js";
|
|
11
|
+
import { inputExplainNetworkVulnSubnet, inputSuggestConceptObjectiveNetworkVulnSubnet, } from "./exampleInputs.js";
|
|
12
|
+
const repoRoot = path.join(fileURLToPath(new URL("..", import.meta.url)));
|
|
13
|
+
const testCasesPath = path.join(repoRoot, "functions", "graph-composer", "test-cases.json");
|
|
14
|
+
function loadTestCase(id) {
|
|
15
|
+
const raw = readFileSync(testCasesPath, "utf-8");
|
|
16
|
+
const cases = JSON.parse(raw);
|
|
17
|
+
const found = cases.find((c) => c.id === id);
|
|
18
|
+
if (!found) {
|
|
19
|
+
const ids = cases.map((c) => c.id).join(", ");
|
|
20
|
+
throw new Error(`Unknown test case "${id}". Known: ${ids}`);
|
|
21
|
+
}
|
|
22
|
+
return found.input;
|
|
23
|
+
}
|
|
24
|
+
const arg = process.argv[2] ?? process.env.GRAPH_COMPOSER_TEST_CASE ?? "explain-basic";
|
|
25
|
+
const input = arg === "network-vuln"
|
|
26
|
+
? inputExplainNetworkVulnSubnet()
|
|
27
|
+
: arg === "network-vuln-intent"
|
|
28
|
+
? inputSuggestConceptObjectiveNetworkVulnSubnet()
|
|
29
|
+
: loadTestCase(arg);
|
|
30
|
+
const result = await runGraphComposer(input, arg === "network-vuln"
|
|
31
|
+
? {
|
|
32
|
+
askTimeoutMs: 180_000,
|
|
33
|
+
connectTimeoutMs: 60_000,
|
|
34
|
+
maxTokens: 16384,
|
|
35
|
+
}
|
|
36
|
+
: arg === "network-vuln-intent"
|
|
37
|
+
? {
|
|
38
|
+
askTimeoutMs: 180_000,
|
|
39
|
+
connectTimeoutMs: 60_000,
|
|
40
|
+
maxTokens: 8192,
|
|
41
|
+
}
|
|
42
|
+
: { askTimeoutMs: 180_000, connectTimeoutMs: 60_000 });
|
|
43
|
+
console.log(JSON.stringify(result, null, 2));
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SkillDescriptor } from "./types.js";
|
|
2
|
+
import type { GraphComposerAction } from "./graphComposerActions.js";
|
|
3
|
+
/** Loaded from `functions/graph-composer/prompts/skill-mode-locked.md`. */
|
|
4
|
+
export declare const LOCKED_GATE_TEXT: string;
|
|
5
|
+
/** Loaded from `functions/graph-composer/prompts/skill-mode-extensible.md`. */
|
|
6
|
+
export declare const EXTENSIBLE_GATE_TEXT: string;
|
|
7
|
+
export declare function formatSkillList(catalogSectionHeader: string, skills: SkillDescriptor[]): string;
|
|
8
|
+
export declare function composeInstructions(baseInstructions: string, aiSkills: SkillDescriptor[], utilitySkills: SkillDescriptor[], skillMode: "locked" | "extensible"): string;
|
|
9
|
+
/** Per-action worker system prompt: shared fragments + action file + catalogs + skill mode. */
|
|
10
|
+
export declare function composeWorkerInstructions(action: GraphComposerAction, aiSkills: SkillDescriptor[], utilitySkills: SkillDescriptor[], skillMode: "locked" | "extensible"): string;
|
|
11
|
+
//# sourceMappingURL=composeInstructions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composeInstructions.d.ts","sourceRoot":"","sources":["../src/composeInstructions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAGrE,2EAA2E;AAC3E,eAAO,MAAM,gBAAgB,QAE5B,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,oBAAoB,QAEhC,CAAC;AAgBF,wBAAgB,eAAe,CAC7B,oBAAoB,EAAE,MAAM,EAC5B,MAAM,EAAE,eAAe,EAAE,GACxB,MAAM,CAaR;AAED,wBAAgB,mBAAmB,CACjC,gBAAgB,EAAE,MAAM,EACxB,QAAQ,EAAE,eAAe,EAAE,EAC3B,aAAa,EAAE,eAAe,EAAE,EAChC,SAAS,EAAE,QAAQ,GAAG,YAAY,GACjC,MAAM,CASR;AAED,+FAA+F;AAC/F,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,mBAAmB,EAC3B,QAAQ,EAAE,eAAe,EAAE,EAC3B,aAAa,EAAE,eAAe,EAAE,EAChC,SAAS,EAAE,QAAQ,GAAG,YAAY,GACjC,MAAM,CAOR"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { readGraphComposerPromptFile } from "./packDir.js";
|
|
2
|
+
import { loadWorkerBaseMarkdown } from "./graphComposerActions.js";
|
|
3
|
+
/** Loaded from `functions/graph-composer/prompts/skill-mode-locked.md`. */
|
|
4
|
+
export const LOCKED_GATE_TEXT = readGraphComposerPromptFile("skill-mode-locked.md");
|
|
5
|
+
/** Loaded from `functions/graph-composer/prompts/skill-mode-extensible.md`. */
|
|
6
|
+
export const EXTENSIBLE_GATE_TEXT = readGraphComposerPromptFile("skill-mode-extensible.md");
|
|
7
|
+
const SKILL_CATALOG_AI_HEADER = readGraphComposerPromptFile("skill-catalog-ai-header.md").trimEnd();
|
|
8
|
+
const SKILL_CATALOG_UTILITY_HEADER = readGraphComposerPromptFile("skill-catalog-utility-header.md").trimEnd();
|
|
9
|
+
function schemaSummary(schema) {
|
|
10
|
+
if (schema === undefined || schema === null)
|
|
11
|
+
return "see description";
|
|
12
|
+
if (typeof schema === "object")
|
|
13
|
+
return JSON.stringify(schema);
|
|
14
|
+
return String(schema);
|
|
15
|
+
}
|
|
16
|
+
export function formatSkillList(catalogSectionHeader, skills) {
|
|
17
|
+
const blocks = skills.map((s) => {
|
|
18
|
+
const tags = s.tags?.length ? s.tags.join(", ") : "(none)";
|
|
19
|
+
return [
|
|
20
|
+
`- \`skillKey\`: ${s.skillKey}`,
|
|
21
|
+
` Description: ${s.description}`,
|
|
22
|
+
` Input: ${schemaSummary(s.inputSchema)}`,
|
|
23
|
+
` Output: ${schemaSummary(s.outputSchema)}`,
|
|
24
|
+
` Tags: ${tags}`,
|
|
25
|
+
].join("\n");
|
|
26
|
+
});
|
|
27
|
+
const body = blocks.length ? blocks.join("\n\n") : "(none)";
|
|
28
|
+
return `${catalogSectionHeader}\n\n${body}`;
|
|
29
|
+
}
|
|
30
|
+
export function composeInstructions(baseInstructions, aiSkills, utilitySkills, skillMode) {
|
|
31
|
+
const aiSection = formatSkillList(SKILL_CATALOG_AI_HEADER, aiSkills);
|
|
32
|
+
const utilSection = formatSkillList(SKILL_CATALOG_UTILITY_HEADER, utilitySkills);
|
|
33
|
+
const modeSection = skillMode === "locked" ? LOCKED_GATE_TEXT : EXTENSIBLE_GATE_TEXT;
|
|
34
|
+
return [baseInstructions, aiSection, utilSection, modeSection].join("\n\n");
|
|
35
|
+
}
|
|
36
|
+
/** Per-action worker system prompt: shared fragments + action file + catalogs + skill mode. */
|
|
37
|
+
export function composeWorkerInstructions(action, aiSkills, utilitySkills, skillMode) {
|
|
38
|
+
return composeInstructions(loadWorkerBaseMarkdown(action), aiSkills, utilitySkills, skillMode);
|
|
39
|
+
}
|