diff --git a/.claude/ralph-loop.local.md b/.claude/ralph-loop.local.md new file mode 100644 index 00000000..fa15fd74 --- /dev/null +++ b/.claude/ralph-loop.local.md @@ -0,0 +1,18 @@ +--- +active: false +iteration: 3 +session_id: qa-fix-loop-20260412 +max_iterations: 20 +completion_promise: "ALL_CLEAR" +started_at: "2026-04-12T08:00:00Z" +completed_at: "2026-04-12T08:20:00Z" +--- + +ALL_CLEAR — All three chat modes (Graph RAG, Doc RAG, Agent) return substantive answers with grounded data. Agent mode now forwards explainability graph from graph-rag pipeline. No stuck spinners. No console errors. + +Fixes applied: +1. Graph-rag service: send answer + explain data in single message (agent was getting empty explain event as first response) +2. Doc RAG pipeline: fixed types, added content to Qdrant payload, seeded 10 document chunks +3. Agent service: forward explain events from KnowledgeQuery tool calls +4. Client: handle explain events embedded in answer message (Graph RAG) and as separate chunks (Agent) +5. Gateway: added "agent" to TERM_BEARING_RESPONSE_SERVICES for triple format translation diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..9a897b64 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/GitLink.xml b/.idea/GitLink.xml new file mode 100644 index 00000000..009597cc --- /dev/null +++ b/.idea/GitLink.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 00000000..6b327b41 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/discord.xml b/.idea/discord.xml new file mode 100644 index 00000000..30bab2ab --- /dev/null +++ b/.idea/discord.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000..379858e1 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,130 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..babc8cb3 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/trustgraph.iml b/.idea/trustgraph.iml new file mode 100644 index 00000000..c956989b --- /dev/null +++ b/.idea/trustgraph.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..35eb1ddf --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.playwright-mcp/console-2026-04-12T05-27-22-237Z.log b/.playwright-mcp/console-2026-04-12T05-27-22-237Z.log new file mode 100644 index 00000000..df833967 --- /dev/null +++ b/.playwright-mcp/console-2026-04-12T05-27-22-237Z.log @@ -0,0 +1,18 @@ +[ 188ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://localhost:5173/node_modules/.vite/deps/react-dom_client.js?v=714749ff:20102 +[ 232ms] [LOG] SOCKET: opening socket... without auth user: user @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:40 +[ 232ms] [LOG] SOCKET: connecting to /api/socket @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:135 +[ 232ms] [LOG] SOCKET: socket opened @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:42 +[ 234ms] [WARNING] WebSocket connection to 'ws://localhost:5173/api/socket' failed: WebSocket is closed before the connection is established. @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:270 +[ 234ms] [LOG] SOCKET: opening socket... without auth user: user @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:40 +[ 235ms] [LOG] SOCKET: connecting to /api/socket @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:135 +[ 235ms] [LOG] SOCKET: socket opened @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:42 +[ 370ms] [ERROR] WebSocket connection to 'ws://localhost:5173/api/socket' failed: Connection closed before receiving a handshake response @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:137 +[ 379ms] [ERROR] [socket error] Event @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:202 +[ 392ms] [LOG] [socket close] 1006 @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:176 +[ 392ms] [LOG] [socket] Reconnecting in 2304.210165117683ms (attempt 1/10) @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:235 +[ 2696ms] [LOG] [socket reopen] @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:245 +[ 2697ms] [LOG] SOCKET: connecting to /api/socket @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:135 +[ 2698ms] [ERROR] WebSocket connection to 'ws://localhost:5173/api/socket' failed: Connection closed before receiving a handshake response @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:137 +[ 2698ms] [ERROR] [socket error] Event @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:202 +[ 2711ms] [LOG] [socket close] 1006 @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:176 +[ 2711ms] [LOG] [socket] Reconnection already in progress, skipping @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:212 diff --git a/.playwright-mcp/console-2026-04-12T05-27-32-160Z.log b/.playwright-mcp/console-2026-04-12T05-27-32-160Z.log new file mode 100644 index 00000000..62cd3567 --- /dev/null +++ b/.playwright-mcp/console-2026-04-12T05-27-32-160Z.log @@ -0,0 +1,32 @@ +[ 80ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://localhost:5173/node_modules/.vite/deps/react-dom_client.js?v=714749ff:20102 +[ 128ms] [LOG] SOCKET: opening socket... without auth user: user @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:40 +[ 128ms] [LOG] SOCKET: connecting to /api/socket @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:135 +[ 128ms] [LOG] SOCKET: socket opened @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:42 +[ 129ms] [WARNING] WebSocket connection to 'ws://localhost:5173/api/socket' failed: WebSocket is closed before the connection is established. @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:270 +[ 129ms] [LOG] SOCKET: opening socket... without auth user: user @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:40 +[ 129ms] [LOG] SOCKET: connecting to /api/socket @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:135 +[ 129ms] [LOG] SOCKET: socket opened @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:42 +[ 184ms] [LOG] Request 3vyu2yhjsasom22g-1 waiting for socket reconnection... @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/service-call.js:174 +[ 185ms] [LOG] Request 3vyu2yhjsasom22g-2 waiting for socket reconnection... @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/service-call.js:174 +[ 223ms] [ERROR] WebSocket connection to 'ws://localhost:5173/api/socket' failed: Connection closed before receiving a handshake response @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:137 +[ 223ms] [ERROR] [socket error] Event @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:202 +[ 245ms] [LOG] [socket close] 1006 @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:176 +[ 245ms] [LOG] [socket] Reconnecting in 2122.8106630994694ms (attempt 1/10) @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:235 +[ 266ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://localhost:5173/settings:0 +[ 2368ms] [LOG] [socket reopen] @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:245 +[ 2368ms] [LOG] SOCKET: connecting to /api/socket @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:135 +[ 2373ms] [ERROR] WebSocket connection to 'ws://localhost:5173/api/socket' failed: Connection closed before receiving a handshake response @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:137 +[ 2373ms] [ERROR] [socket error] Event @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:202 +[ 2391ms] [LOG] [socket close] 1006 @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:176 +[ 2391ms] [LOG] [socket] Reconnection already in progress, skipping @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/trustgraph-socket.js:212 +[ 4734ms] [LOG] Request 3vyu2yhjsasom22g-2 waiting for socket reconnection... @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/service-call.js:174 +[ 5123ms] [LOG] Request 3vyu2yhjsasom22g-1 waiting for socket reconnection... @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/service-call.js:174 +[ 12903ms] [LOG] Request 3vyu2yhjsasom22g-2 waiting for socket reconnection... @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/service-call.js:174 +[ 13362ms] [LOG] Request 3vyu2yhjsasom22g-1 waiting for socket reconnection... @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/service-call.js:174 +[ 29608ms] [LOG] Request 3vyu2yhjsasom22g-1 ran out of retries @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/service-call.js:147 +[ 29803ms] [LOG] Request 3vyu2yhjsasom22g-2 ran out of retries @ http://localhost:5173/@fs/home/elpresidank/YeeBois/dev/trustgraph/ts/packages/client/dist/socket/service-call.js:147 +[ 69887ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://localhost:5173/mcp-tools:0 +[ 142680ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://localhost:5173/mcp-tools:0 +[ 219289ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://localhost:5173/mcp-tools:0 +[ 238347ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://localhost:5173/mcp-tools:0 +[ 255087ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://localhost:5173/mcp-tools:0 diff --git a/.playwright-mcp/page-2026-04-12T05-27-22-648Z.yml b/.playwright-mcp/page-2026-04-12T05-27-22-648Z.yml new file mode 100644 index 00000000..22a563ff --- /dev/null +++ b/.playwright-mcp/page-2026-04-12T05-27-22-648Z.yml @@ -0,0 +1,77 @@ +- generic [ref=e3]: + - link "Skip to content" [ref=e4] [cursor=pointer]: + - /url: "#main-content" + - complementary "Sidebar" [ref=e5]: + - generic [ref=e6]: + - img [ref=e7] + - generic [ref=e10]: TrustGraph + - generic [ref=e13]: + - generic [ref=e14]: + - generic [ref=e15]: + - img [ref=e16] + - text: Flow + - generic [ref=e20]: + - combobox "Flow" [ref=e21]: + - option "default" [selected] + - img + - generic [ref=e22]: + - img [ref=e23] + - generic [ref=e27]: default + - navigation "Main navigation" [ref=e29]: + - link "Chat" [ref=e30] [cursor=pointer]: + - /url: /chat + - generic [ref=e31]: + - img [ref=e32] + - generic [ref=e34]: Chat + - link "Library" [ref=e35] [cursor=pointer]: + - /url: /library + - generic [ref=e36]: + - img [ref=e37] + - generic [ref=e40]: Library + - link "Graph" [ref=e41] [cursor=pointer]: + - /url: /graph + - generic [ref=e42]: + - img [ref=e43] + - generic [ref=e47]: Graph + - link "Prompts" [ref=e48] [cursor=pointer]: + - /url: /prompts + - generic [ref=e49]: + - img [ref=e50] + - generic [ref=e54]: Prompts + - link "Token Cost" [ref=e55] [cursor=pointer]: + - /url: /token-cost + - generic [ref=e56]: + - img [ref=e57] + - generic [ref=e62]: Token Cost + - link "Knowledge Cores" [ref=e63] [cursor=pointer]: + - /url: /knowledge-cores + - generic [ref=e64]: + - img [ref=e65] + - generic [ref=e77]: Knowledge Cores + - link "Flows" [ref=e78] [cursor=pointer]: + - /url: /flows + - generic [ref=e79]: + - img [ref=e80] + - generic [ref=e84]: Flows + - link "Settings" [ref=e85] [cursor=pointer]: + - /url: /settings + - generic [ref=e86]: + - img [ref=e87] + - generic [ref=e90]: Settings + - generic [ref=e92]: + - img [ref=e94] + - generic [ref=e101]: connecting + - generic [ref=e102]: + - banner [ref=e103]: + - generic [ref=e104]: + - generic [ref=e105]: + - img [ref=e106] + - text: default + - generic [ref=e110]: + - img [ref=e111] + - text: default + - main [ref=e115]: + - generic [ref=e116]: + - img [ref=e117] + - paragraph [ref=e119]: MCP Tools is not enabled. + - paragraph [ref=e120]: Enable it in Settings → Feature Switches → MCP Tools. \ No newline at end of file diff --git a/.playwright-mcp/page-2026-04-12T05-27-32-384Z.yml b/.playwright-mcp/page-2026-04-12T05-27-32-384Z.yml new file mode 100644 index 00000000..07a512c8 --- /dev/null +++ b/.playwright-mcp/page-2026-04-12T05-27-32-384Z.yml @@ -0,0 +1,176 @@ +- generic [ref=e3]: + - link "Skip to content" [ref=e4] [cursor=pointer]: + - /url: "#main-content" + - complementary "Sidebar" [ref=e5]: + - generic [ref=e6]: + - img [ref=e7] + - generic [ref=e10]: TrustGraph + - generic [ref=e13]: + - generic [ref=e14]: + - generic [ref=e15]: + - img [ref=e16] + - text: Flow + - generic [ref=e20]: + - combobox "Flow" [ref=e21]: + - option "default" [selected] + - img + - generic [ref=e22]: + - img [ref=e23] + - generic [ref=e27]: default + - navigation "Main navigation" [ref=e29]: + - link "Chat" [ref=e30] [cursor=pointer]: + - /url: /chat + - generic [ref=e31]: + - img [ref=e32] + - generic [ref=e34]: Chat + - link "Library" [ref=e35] [cursor=pointer]: + - /url: /library + - generic [ref=e36]: + - img [ref=e37] + - generic [ref=e40]: Library + - link "Graph" [ref=e41] [cursor=pointer]: + - /url: /graph + - generic [ref=e42]: + - img [ref=e43] + - generic [ref=e47]: Graph + - link "Prompts" [ref=e48] [cursor=pointer]: + - /url: /prompts + - generic [ref=e49]: + - img [ref=e50] + - generic [ref=e54]: Prompts + - link "Token Cost" [ref=e55] [cursor=pointer]: + - /url: /token-cost + - generic [ref=e56]: + - img [ref=e57] + - generic [ref=e62]: Token Cost + - link "Knowledge Cores" [ref=e63] [cursor=pointer]: + - /url: /knowledge-cores + - generic [ref=e64]: + - img [ref=e65] + - generic [ref=e77]: Knowledge Cores + - link "Flows" [ref=e78] [cursor=pointer]: + - /url: /flows + - generic [ref=e79]: + - img [ref=e80] + - generic [ref=e84]: Flows + - link "Settings" [ref=e85] [cursor=pointer]: + - /url: /settings + - generic [ref=e86]: + - img [ref=e87] + - generic [ref=e90]: Settings + - generic [ref=e92]: + - img [ref=e94] + - generic [ref=e101]: connecting + - generic [ref=e102]: + - banner [ref=e103]: + - generic [ref=e104]: + - generic [ref=e105]: + - img [ref=e106] + - text: default + - generic [ref=e110]: + - img [ref=e111] + - text: default + - main [ref=e115]: + - generic [ref=e116]: + - generic [ref=e117]: + - img [ref=e118] + - heading "Settings" [level=1] [ref=e121] + - generic [ref=e122]: + - generic [ref=e123]: + - heading "Connection" [level=2] [ref=e124]: + - img [ref=e125] + - text: Connection + - generic [ref=e129]: + - generic [ref=e130]: + - generic [ref=e131]: "Status:" + - generic [ref=e132]: + - img [ref=e133] + - text: connecting + - generic [ref=e140]: + - generic [ref=e141]: Gateway URL + - textbox "Gateway URL" [ref=e142]: + - /placeholder: Leave blank to use the default proxy + - paragraph [ref=e143]: The WebSocket URL for the TrustGraph gateway. + - generic [ref=e144]: + - generic [ref=e145]: User ID + - textbox "User ID" [ref=e146]: user + - generic [ref=e147]: + - heading "Authentication" [level=2] [ref=e148]: + - img [ref=e149] + - text: Authentication + - generic [ref=e154]: + - generic [ref=e155]: API Key + - generic [ref=e156]: + - textbox "API Key" [ref=e157]: + - /placeholder: Leave blank for unauthenticated access + - button "Show API key" [ref=e158]: + - img [ref=e159] + - paragraph [ref=e162]: Changing the API key will reconnect the WebSocket. + - generic [ref=e163]: + - heading "Collection" [level=2] [ref=e164]: + - img [ref=e165] + - text: Collection + - generic [ref=e170]: + - generic [ref=e171]: Active Collection + - generic [ref=e172]: + - img [ref=e173] + - text: Loading collections... + - generic [ref=e175]: + - heading "Active Flow" [level=2] [ref=e176]: + - img [ref=e177] + - text: Active Flow + - generic [ref=e182]: + - generic [ref=e183]: Flow + - textbox "Flow" [ref=e184]: + - /placeholder: default + - text: default + - paragraph [ref=e185]: The flow ID used for chat, graph queries, and document processing. + - generic [ref=e186]: + - heading "Appearance" [level=2] [ref=e187]: + - img [ref=e188] + - text: Appearance + - generic [ref=e191]: + - generic [ref=e192]: + - paragraph [ref=e193]: Theme + - paragraph [ref=e194]: Toggle between dark and light mode. + - paragraph [ref=e195]: Currently using dark mode. + - switch "Dark mode" [checked] [ref=e196] + - generic [ref=e198]: + - heading "Feature Switches" [level=2] [ref=e199]: + - img [ref=e200] + - text: Feature Switches + - generic [ref=e203]: + - generic [ref=e204]: + - paragraph [ref=e206]: Flow Classes + - switch "Flow Classes" [ref=e207] + - generic [ref=e209]: + - paragraph [ref=e211]: Submissions + - switch "Submissions" [ref=e212] + - generic [ref=e214]: + - paragraph [ref=e216]: Token Cost + - switch "Token Cost" [ref=e217] + - generic [ref=e219]: + - paragraph [ref=e221]: Schemas + - switch "Schemas" [ref=e222] + - generic [ref=e224]: + - paragraph [ref=e226]: Structured Query + - switch "Structured Query" [ref=e227] + - generic [ref=e229]: + - paragraph [ref=e231]: Ontology Editor + - switch "Ontology Editor" [ref=e232] + - generic [ref=e234]: + - paragraph [ref=e236]: Agent Tools + - switch "Agent Tools" [ref=e237] + - generic [ref=e239]: + - paragraph [ref=e241]: MCP Tools + - switch "MCP Tools" [ref=e242] + - generic [ref=e244]: + - paragraph [ref=e246]: LLM Models + - switch "LLM Models" [ref=e247] + - generic [ref=e249]: + - heading "About" [level=2] [ref=e250]: + - img [ref=e251] + - text: About + - generic [ref=e254]: + - paragraph [ref=e255]: TrustGraph Workbench v0.1.0 + - paragraph [ref=e256]: A web-based interface for interacting with the TrustGraph knowledge-graph system. \ No newline at end of file diff --git a/.playwright-mcp/page-2026-04-12T05-27-56-501Z.yml b/.playwright-mcp/page-2026-04-12T05-27-56-501Z.yml new file mode 100644 index 00000000..b9c32352 --- /dev/null +++ b/.playwright-mcp/page-2026-04-12T05-27-56-501Z.yml @@ -0,0 +1,184 @@ +- generic [ref=e3]: + - link "Skip to content" [ref=e4] [cursor=pointer]: + - /url: "#main-content" + - complementary "Sidebar" [ref=e5]: + - generic [ref=e6]: + - img [ref=e7] + - generic [ref=e10]: TrustGraph + - generic [ref=e13]: + - generic [ref=e14]: + - generic [ref=e15]: + - img [ref=e16] + - text: Flow + - generic [ref=e20]: + - combobox "Flow" [ref=e21]: + - option "default" [selected] + - img + - generic [ref=e22]: + - img [ref=e23] + - generic [ref=e27]: default + - navigation "Main navigation" [ref=e29]: + - link "Chat" [ref=e30] [cursor=pointer]: + - /url: /chat + - generic [ref=e31]: + - img [ref=e32] + - generic [ref=e34]: Chat + - link "Library" [ref=e35] [cursor=pointer]: + - /url: /library + - generic [ref=e36]: + - img [ref=e37] + - generic [ref=e40]: Library + - link "Graph" [ref=e41] [cursor=pointer]: + - /url: /graph + - generic [ref=e42]: + - img [ref=e43] + - generic [ref=e47]: Graph + - link "Prompts" [ref=e48] [cursor=pointer]: + - /url: /prompts + - generic [ref=e49]: + - img [ref=e50] + - generic [ref=e54]: Prompts + - link "Token Cost" [ref=e55] [cursor=pointer]: + - /url: /token-cost + - generic [ref=e56]: + - img [ref=e57] + - generic [ref=e62]: Token Cost + - link "Knowledge Cores" [ref=e63] [cursor=pointer]: + - /url: /knowledge-cores + - generic [ref=e64]: + - img [ref=e65] + - generic [ref=e77]: Knowledge Cores + - link "Flows" [ref=e78] [cursor=pointer]: + - /url: /flows + - generic [ref=e79]: + - img [ref=e80] + - generic [ref=e84]: Flows + - link "MCP Tools" [ref=e266] [cursor=pointer]: + - /url: /mcp-tools + - generic [ref=e267]: + - img [ref=e268] + - generic [ref=e270]: MCP Tools + - link "Settings" [ref=e85] [cursor=pointer]: + - /url: /settings + - generic [ref=e86]: + - img [ref=e87] + - generic [ref=e90]: Settings + - generic [ref=e92]: + - img [ref=e94] + - generic [ref=e101]: reconnecting + - generic [ref=e102]: + - banner [ref=e103]: + - generic [ref=e104]: + - generic [ref=e105]: + - img [ref=e106] + - text: default + - generic [ref=e110]: + - img [ref=e111] + - text: default + - alert [ref=e257]: + - img [ref=e258] + - generic [ref=e265]: Connection lost. Attempting to reconnect... + - main [ref=e115]: + - generic [ref=e116]: + - generic [ref=e117]: + - img [ref=e118] + - heading "Settings" [level=1] [ref=e121] + - generic [ref=e122]: + - generic [ref=e123]: + - heading "Connection" [level=2] [ref=e124]: + - img [ref=e125] + - text: Connection + - generic [ref=e129]: + - generic [ref=e130]: + - generic [ref=e131]: "Status:" + - generic [ref=e132]: + - img [ref=e133] + - text: reconnecting + - generic [ref=e140]: + - generic [ref=e141]: Gateway URL + - textbox "Gateway URL" [ref=e142]: + - /placeholder: Leave blank to use the default proxy + - paragraph [ref=e143]: The WebSocket URL for the TrustGraph gateway. + - generic [ref=e144]: + - generic [ref=e145]: User ID + - textbox "User ID" [ref=e146]: user + - generic [ref=e147]: + - heading "Authentication" [level=2] [ref=e148]: + - img [ref=e149] + - text: Authentication + - generic [ref=e154]: + - generic [ref=e155]: API Key + - generic [ref=e156]: + - textbox "API Key" [ref=e157]: + - /placeholder: Leave blank for unauthenticated access + - button "Show API key" [ref=e158]: + - img [ref=e159] + - paragraph [ref=e162]: Changing the API key will reconnect the WebSocket. + - generic [ref=e163]: + - heading "Collection" [level=2] [ref=e164]: + - img [ref=e165] + - text: Collection + - generic [ref=e170]: + - generic [ref=e171]: Active Collection + - generic [ref=e172]: + - img [ref=e173] + - text: Loading collections... + - generic [ref=e175]: + - heading "Active Flow" [level=2] [ref=e176]: + - img [ref=e177] + - text: Active Flow + - generic [ref=e182]: + - generic [ref=e183]: Flow + - textbox "Flow" [ref=e184]: + - /placeholder: default + - text: default + - paragraph [ref=e185]: The flow ID used for chat, graph queries, and document processing. + - generic [ref=e186]: + - heading "Appearance" [level=2] [ref=e187]: + - img [ref=e188] + - text: Appearance + - generic [ref=e191]: + - generic [ref=e192]: + - paragraph [ref=e193]: Theme + - paragraph [ref=e194]: Toggle between dark and light mode. + - paragraph [ref=e195]: Currently using dark mode. + - switch "Dark mode" [checked] [ref=e196] + - generic [ref=e198]: + - heading "Feature Switches" [level=2] [ref=e199]: + - img [ref=e200] + - text: Feature Switches + - generic [ref=e203]: + - generic [ref=e204]: + - paragraph [ref=e206]: Flow Classes + - switch "Flow Classes" [ref=e207] + - generic [ref=e209]: + - paragraph [ref=e211]: Submissions + - switch "Submissions" [ref=e212] + - generic [ref=e214]: + - paragraph [ref=e216]: Token Cost + - switch "Token Cost" [ref=e217] + - generic [ref=e219]: + - paragraph [ref=e221]: Schemas + - switch "Schemas" [ref=e222] + - generic [ref=e224]: + - paragraph [ref=e226]: Structured Query + - switch "Structured Query" [ref=e227] + - generic [ref=e229]: + - paragraph [ref=e231]: Ontology Editor + - switch "Ontology Editor" [ref=e232] + - generic [ref=e234]: + - paragraph [ref=e236]: Agent Tools + - switch "Agent Tools" [ref=e237] + - generic [ref=e239]: + - paragraph [ref=e241]: MCP Tools + - switch "MCP Tools" [checked] [active] [ref=e242] + - generic [ref=e244]: + - paragraph [ref=e246]: LLM Models + - switch "LLM Models" [ref=e247] + - generic [ref=e249]: + - heading "About" [level=2] [ref=e250]: + - img [ref=e251] + - text: About + - generic [ref=e254]: + - paragraph [ref=e255]: TrustGraph Workbench v0.1.0 + - paragraph [ref=e256]: A web-based interface for interacting with the TrustGraph knowledge-graph system. \ No newline at end of file diff --git a/.playwright-mcp/page-2026-04-12T05-28-05-234Z.yml b/.playwright-mcp/page-2026-04-12T05-28-05-234Z.yml new file mode 100644 index 00000000..4af701d2 --- /dev/null +++ b/.playwright-mcp/page-2026-04-12T05-28-05-234Z.yml @@ -0,0 +1,106 @@ +- generic [ref=e3]: + - link "Skip to content" [ref=e4] [cursor=pointer]: + - /url: "#main-content" + - complementary "Sidebar" [ref=e5]: + - generic [ref=e6]: + - img [ref=e7] + - generic [ref=e10]: TrustGraph + - generic [ref=e13]: + - generic [ref=e14]: + - generic [ref=e15]: + - img [ref=e16] + - text: Flow + - generic [ref=e20]: + - combobox "Flow" [ref=e21]: + - option "default" [selected] + - img + - generic [ref=e22]: + - img [ref=e23] + - generic [ref=e27]: default + - navigation "Main navigation" [ref=e29]: + - link "Chat" [ref=e30] [cursor=pointer]: + - /url: /chat + - generic [ref=e31]: + - img [ref=e32] + - generic [ref=e34]: Chat + - link "Library" [ref=e35] [cursor=pointer]: + - /url: /library + - generic [ref=e36]: + - img [ref=e37] + - generic [ref=e40]: Library + - link "Graph" [ref=e41] [cursor=pointer]: + - /url: /graph + - generic [ref=e42]: + - img [ref=e43] + - generic [ref=e47]: Graph + - link "Prompts" [ref=e48] [cursor=pointer]: + - /url: /prompts + - generic [ref=e49]: + - img [ref=e50] + - generic [ref=e54]: Prompts + - link "Token Cost" [ref=e55] [cursor=pointer]: + - /url: /token-cost + - generic [ref=e56]: + - img [ref=e57] + - generic [ref=e62]: Token Cost + - link "Knowledge Cores" [ref=e63] [cursor=pointer]: + - /url: /knowledge-cores + - generic [ref=e64]: + - img [ref=e65] + - generic [ref=e77]: Knowledge Cores + - link "Flows" [ref=e78] [cursor=pointer]: + - /url: /flows + - generic [ref=e79]: + - img [ref=e80] + - generic [ref=e84]: Flows + - link "MCP Tools" [active] [ref=e266] [cursor=pointer]: + - /url: /mcp-tools + - generic [ref=e267]: + - img [ref=e268] + - generic [ref=e270]: MCP Tools + - link "Settings" [ref=e85] [cursor=pointer]: + - /url: /settings + - generic [ref=e86]: + - img [ref=e87] + - generic [ref=e90]: Settings + - generic [ref=e92]: + - img [ref=e94] + - generic [ref=e101]: reconnecting + - generic [ref=e102]: + - banner [ref=e103]: + - generic [ref=e104]: + - generic [ref=e105]: + - img [ref=e106] + - text: default + - generic [ref=e110]: + - img [ref=e111] + - text: default + - alert [ref=e257]: + - img [ref=e258] + - generic [ref=e265]: Connection lost. Attempting to reconnect... + - main [ref=e115]: + - generic [ref=e271]: + - generic [ref=e272]: + - generic [ref=e273]: + - img [ref=e274] + - heading "MCP Tools" [level=1] [ref=e276] + - generic [ref=e277]: 0 servers, 0 tools + - generic [ref=e278]: + - button "Refresh" [ref=e279]: + - img [ref=e280] + - text: Refresh + - button "Add Server" [ref=e285]: + - img [ref=e286] + - text: Add Server + - tablist "MCP sections" [ref=e287]: + - tab "Servers" [selected] [ref=e288]: + - img [ref=e289] + - text: Servers + - tab "Tools" [ref=e292]: + - img [ref=e293] + - text: Tools + - tabpanel "Servers" [ref=e295]: + - generic [ref=e296]: + - img [ref=e297] + - paragraph [ref=e300]: No MCP servers configured. + - paragraph [ref=e301]: Click "Add Server" to connect an external MCP server. \ No newline at end of file diff --git a/.playwright-mcp/page-2026-04-12T05-28-17-352Z.yml b/.playwright-mcp/page-2026-04-12T05-28-17-352Z.yml new file mode 100644 index 00000000..cb222d0a --- /dev/null +++ b/.playwright-mcp/page-2026-04-12T05-28-17-352Z.yml @@ -0,0 +1,106 @@ +- generic [ref=e3]: + - link "Skip to content" [ref=e4] [cursor=pointer]: + - /url: "#main-content" + - complementary "Sidebar" [ref=e5]: + - generic [ref=e6]: + - img [ref=e7] + - generic [ref=e10]: TrustGraph + - generic [ref=e13]: + - generic [ref=e14]: + - generic [ref=e15]: + - img [ref=e16] + - text: Flow + - generic [ref=e20]: + - combobox "Flow" [ref=e21]: + - option "default" [selected] + - img + - generic [ref=e22]: + - img [ref=e23] + - generic [ref=e27]: default + - navigation "Main navigation" [ref=e29]: + - link "Chat" [ref=e30] [cursor=pointer]: + - /url: /chat + - generic [ref=e31]: + - img [ref=e32] + - generic [ref=e34]: Chat + - link "Library" [ref=e35] [cursor=pointer]: + - /url: /library + - generic [ref=e36]: + - img [ref=e37] + - generic [ref=e40]: Library + - link "Graph" [ref=e41] [cursor=pointer]: + - /url: /graph + - generic [ref=e42]: + - img [ref=e43] + - generic [ref=e47]: Graph + - link "Prompts" [ref=e48] [cursor=pointer]: + - /url: /prompts + - generic [ref=e49]: + - img [ref=e50] + - generic [ref=e54]: Prompts + - link "Token Cost" [ref=e55] [cursor=pointer]: + - /url: /token-cost + - generic [ref=e56]: + - img [ref=e57] + - generic [ref=e62]: Token Cost + - link "Knowledge Cores" [ref=e63] [cursor=pointer]: + - /url: /knowledge-cores + - generic [ref=e64]: + - img [ref=e65] + - generic [ref=e77]: Knowledge Cores + - link "Flows" [ref=e78] [cursor=pointer]: + - /url: /flows + - generic [ref=e79]: + - img [ref=e80] + - generic [ref=e84]: Flows + - link "MCP Tools" [ref=e266] [cursor=pointer]: + - /url: /mcp-tools + - generic [ref=e267]: + - img [ref=e268] + - generic [ref=e270]: MCP Tools + - link "Settings" [ref=e85] [cursor=pointer]: + - /url: /settings + - generic [ref=e86]: + - img [ref=e87] + - generic [ref=e90]: Settings + - generic [ref=e92]: + - img [ref=e94] + - generic [ref=e101]: reconnecting + - generic [ref=e102]: + - banner [ref=e103]: + - generic [ref=e104]: + - generic [ref=e105]: + - img [ref=e106] + - text: default + - generic [ref=e110]: + - img [ref=e111] + - text: default + - alert [ref=e257]: + - img [ref=e258] + - generic [ref=e265]: Connection lost. Attempting to reconnect... + - main [ref=e115]: + - generic [ref=e271]: + - generic [ref=e272]: + - generic [ref=e273]: + - img [ref=e274] + - heading "MCP Tools" [level=1] [ref=e276] + - generic [ref=e277]: 0 servers, 0 tools + - generic [ref=e278]: + - button "Refresh" [ref=e279]: + - img [ref=e280] + - text: Refresh + - button "Add Tool" [ref=e302]: + - img [ref=e286] + - text: Add Tool + - tablist "MCP sections" [ref=e287]: + - tab "Servers" [ref=e288]: + - img [ref=e289] + - text: Servers + - tab "Tools" [active] [selected] [ref=e292]: + - img [ref=e293] + - text: Tools + - tabpanel "Tools" [ref=e303]: + - generic [ref=e304]: + - img [ref=e305] + - paragraph [ref=e307]: No tools configured. + - paragraph [ref=e308]: Click "Add Tool" to create an MCP tool. \ No newline at end of file diff --git a/.playwright-mcp/page-2026-04-12T05-28-36-861Z.yml b/.playwright-mcp/page-2026-04-12T05-28-36-861Z.yml new file mode 100644 index 00000000..ee26e755 --- /dev/null +++ b/.playwright-mcp/page-2026-04-12T05-28-36-861Z.yml @@ -0,0 +1,106 @@ +- generic [ref=e3]: + - link "Skip to content" [ref=e4] [cursor=pointer]: + - /url: "#main-content" + - complementary "Sidebar" [ref=e5]: + - generic [ref=e6]: + - img [ref=e7] + - generic [ref=e10]: TrustGraph + - generic [ref=e13]: + - generic [ref=e14]: + - generic [ref=e15]: + - img [ref=e16] + - text: Flow + - generic [ref=e20]: + - combobox "Flow" [ref=e21]: + - option "default" [selected] + - img + - generic [ref=e22]: + - img [ref=e23] + - generic [ref=e27]: default + - navigation "Main navigation" [ref=e29]: + - link "Chat" [ref=e30] [cursor=pointer]: + - /url: /chat + - generic [ref=e31]: + - img [ref=e32] + - generic [ref=e34]: Chat + - link "Library" [ref=e35] [cursor=pointer]: + - /url: /library + - generic [ref=e36]: + - img [ref=e37] + - generic [ref=e40]: Library + - link "Graph" [ref=e41] [cursor=pointer]: + - /url: /graph + - generic [ref=e42]: + - img [ref=e43] + - generic [ref=e47]: Graph + - link "Prompts" [ref=e48] [cursor=pointer]: + - /url: /prompts + - generic [ref=e49]: + - img [ref=e50] + - generic [ref=e54]: Prompts + - link "Token Cost" [ref=e55] [cursor=pointer]: + - /url: /token-cost + - generic [ref=e56]: + - img [ref=e57] + - generic [ref=e62]: Token Cost + - link "Knowledge Cores" [ref=e63] [cursor=pointer]: + - /url: /knowledge-cores + - generic [ref=e64]: + - img [ref=e65] + - generic [ref=e77]: Knowledge Cores + - link "Flows" [ref=e78] [cursor=pointer]: + - /url: /flows + - generic [ref=e79]: + - img [ref=e80] + - generic [ref=e84]: Flows + - link "MCP Tools" [ref=e266] [cursor=pointer]: + - /url: /mcp-tools + - generic [ref=e267]: + - img [ref=e268] + - generic [ref=e270]: MCP Tools + - link "Settings" [ref=e85] [cursor=pointer]: + - /url: /settings + - generic [ref=e86]: + - img [ref=e87] + - generic [ref=e90]: Settings + - generic [ref=e92]: + - img [ref=e94] + - generic [ref=e101]: reconnecting + - generic [ref=e102]: + - banner [ref=e103]: + - generic [ref=e104]: + - generic [ref=e105]: + - img [ref=e106] + - text: default + - generic [ref=e110]: + - img [ref=e111] + - text: default + - alert [ref=e257]: + - img [ref=e258] + - generic [ref=e265]: Connection lost. Attempting to reconnect... + - main [ref=e115]: + - generic [ref=e271]: + - generic [ref=e272]: + - generic [ref=e273]: + - img [ref=e274] + - heading "MCP Tools" [level=1] [ref=e276] + - generic [ref=e277]: 0 servers, 0 tools + - generic [ref=e278]: + - button "Refresh" [ref=e279]: + - img [ref=e280] + - text: Refresh + - button "Add Server" [ref=e309]: + - img [ref=e286] + - text: Add Server + - tablist "MCP sections" [ref=e287]: + - tab "Servers" [active] [selected] [ref=e288]: + - img [ref=e289] + - text: Servers + - tab "Tools" [ref=e292]: + - img [ref=e293] + - text: Tools + - tabpanel "Servers" [ref=e310]: + - generic [ref=e311]: + - img [ref=e312] + - paragraph [ref=e315]: No MCP servers configured. + - paragraph [ref=e316]: Click "Add Server" to connect an external MCP server. \ No newline at end of file diff --git a/.playwright-mcp/page-2026-04-12T05-28-42-962Z.yml b/.playwright-mcp/page-2026-04-12T05-28-42-962Z.yml new file mode 100644 index 00000000..54b9b6e1 --- /dev/null +++ b/.playwright-mcp/page-2026-04-12T05-28-42-962Z.yml @@ -0,0 +1,134 @@ +- generic [ref=e3]: + - link "Skip to content" [ref=e4] [cursor=pointer]: + - /url: "#main-content" + - complementary "Sidebar" [ref=e5]: + - generic [ref=e6]: + - img [ref=e7] + - generic [ref=e10]: TrustGraph + - generic [ref=e13]: + - generic [ref=e14]: + - generic [ref=e15]: + - img [ref=e16] + - text: Flow + - generic [ref=e20]: + - combobox "Flow" [ref=e21]: + - option "default" [selected] + - img + - generic [ref=e22]: + - img [ref=e23] + - generic [ref=e27]: default + - navigation "Main navigation" [ref=e29]: + - link "Chat" [ref=e30] [cursor=pointer]: + - /url: /chat + - generic [ref=e31]: + - img [ref=e32] + - generic [ref=e34]: Chat + - link "Library" [ref=e35] [cursor=pointer]: + - /url: /library + - generic [ref=e36]: + - img [ref=e37] + - generic [ref=e40]: Library + - link "Graph" [ref=e41] [cursor=pointer]: + - /url: /graph + - generic [ref=e42]: + - img [ref=e43] + - generic [ref=e47]: Graph + - link "Prompts" [ref=e48] [cursor=pointer]: + - /url: /prompts + - generic [ref=e49]: + - img [ref=e50] + - generic [ref=e54]: Prompts + - link "Token Cost" [ref=e55] [cursor=pointer]: + - /url: /token-cost + - generic [ref=e56]: + - img [ref=e57] + - generic [ref=e62]: Token Cost + - link "Knowledge Cores" [ref=e63] [cursor=pointer]: + - /url: /knowledge-cores + - generic [ref=e64]: + - img [ref=e65] + - generic [ref=e77]: Knowledge Cores + - link "Flows" [ref=e78] [cursor=pointer]: + - /url: /flows + - generic [ref=e79]: + - img [ref=e80] + - generic [ref=e84]: Flows + - link "MCP Tools" [ref=e266] [cursor=pointer]: + - /url: /mcp-tools + - generic [ref=e267]: + - img [ref=e268] + - generic [ref=e270]: MCP Tools + - link "Settings" [ref=e85] [cursor=pointer]: + - /url: /settings + - generic [ref=e86]: + - img [ref=e87] + - generic [ref=e90]: Settings + - generic [ref=e92]: + - img [ref=e94] + - generic [ref=e101]: reconnecting + - generic [ref=e102]: + - banner [ref=e103]: + - generic [ref=e104]: + - generic [ref=e105]: + - img [ref=e106] + - text: default + - generic [ref=e110]: + - img [ref=e111] + - text: default + - alert [ref=e257]: + - img [ref=e258] + - generic [ref=e265]: Connection lost. Attempting to reconnect... + - main [ref=e115]: + - generic [ref=e271]: + - generic [ref=e272]: + - generic [ref=e273]: + - img [ref=e274] + - heading "MCP Tools" [level=1] [ref=e276] + - generic [ref=e277]: 0 servers, 0 tools + - generic [ref=e278]: + - button "Refresh" [ref=e279]: + - img [ref=e280] + - text: Refresh + - button "Add Server" [ref=e309]: + - img [ref=e286] + - text: Add Server + - tablist "MCP sections" [ref=e287]: + - tab "Servers" [selected] [ref=e288]: + - img [ref=e289] + - text: Servers + - tab "Tools" [ref=e292]: + - img [ref=e293] + - text: Tools + - tabpanel "Servers" [ref=e310]: + - generic [ref=e311]: + - img [ref=e312] + - paragraph [ref=e315]: No MCP servers configured. + - paragraph [ref=e316]: Click "Add Server" to connect an external MCP server. + - dialog "Add MCP Server" [ref=e318]: + - generic [ref=e319]: + - heading "Add MCP Server" [level=2] [ref=e320] + - button "Close dialog" [ref=e321]: + - img [ref=e322] + - generic [ref=e326]: + - generic [ref=e327]: + - generic [ref=e328]: Key + - textbox "Key" [active] [ref=e329]: + - /placeholder: brave-search + - generic [ref=e330]: + - generic [ref=e331]: URL + - textbox "URL" [ref=e332]: + - /placeholder: http://localhost:8383/mcp + - generic [ref=e333]: + - generic [ref=e334]: Remote Name (optional) + - textbox "Remote Name (optional)" [ref=e335]: + - /placeholder: Tool name at the remote server + - generic [ref=e336]: + - generic [ref=e337]: Auth Token (optional) + - generic [ref=e338]: + - textbox "Auth Token (optional)" [ref=e339]: + - /placeholder: Bearer token for authentication + - button "Show auth token" [ref=e340]: + - img [ref=e341] + - generic [ref=e344]: + - button "Cancel" [ref=e345] + - button "Save" [disabled] [ref=e346] \ No newline at end of file diff --git a/.playwright-mcp/page-2026-04-12T05-29-03-429Z.yml b/.playwright-mcp/page-2026-04-12T05-29-03-429Z.yml new file mode 100644 index 00000000..cb8af391 --- /dev/null +++ b/.playwright-mcp/page-2026-04-12T05-29-03-429Z.yml @@ -0,0 +1,106 @@ +- generic [ref=e3]: + - link "Skip to content" [ref=e4] [cursor=pointer]: + - /url: "#main-content" + - complementary "Sidebar" [ref=e5]: + - generic [ref=e6]: + - img [ref=e7] + - generic [ref=e10]: TrustGraph + - generic [ref=e13]: + - generic [ref=e14]: + - generic [ref=e15]: + - img [ref=e16] + - text: Flow + - generic [ref=e20]: + - combobox "Flow" [ref=e21]: + - option "default" [selected] + - img + - generic [ref=e22]: + - img [ref=e23] + - generic [ref=e27]: default + - navigation "Main navigation" [ref=e29]: + - link "Chat" [ref=e30] [cursor=pointer]: + - /url: /chat + - generic [ref=e31]: + - img [ref=e32] + - generic [ref=e34]: Chat + - link "Library" [ref=e35] [cursor=pointer]: + - /url: /library + - generic [ref=e36]: + - img [ref=e37] + - generic [ref=e40]: Library + - link "Graph" [ref=e41] [cursor=pointer]: + - /url: /graph + - generic [ref=e42]: + - img [ref=e43] + - generic [ref=e47]: Graph + - link "Prompts" [ref=e48] [cursor=pointer]: + - /url: /prompts + - generic [ref=e49]: + - img [ref=e50] + - generic [ref=e54]: Prompts + - link "Token Cost" [ref=e55] [cursor=pointer]: + - /url: /token-cost + - generic [ref=e56]: + - img [ref=e57] + - generic [ref=e62]: Token Cost + - link "Knowledge Cores" [ref=e63] [cursor=pointer]: + - /url: /knowledge-cores + - generic [ref=e64]: + - img [ref=e65] + - generic [ref=e77]: Knowledge Cores + - link "Flows" [ref=e78] [cursor=pointer]: + - /url: /flows + - generic [ref=e79]: + - img [ref=e80] + - generic [ref=e84]: Flows + - link "MCP Tools" [ref=e266] [cursor=pointer]: + - /url: /mcp-tools + - generic [ref=e267]: + - img [ref=e268] + - generic [ref=e270]: MCP Tools + - link "Settings" [ref=e85] [cursor=pointer]: + - /url: /settings + - generic [ref=e86]: + - img [ref=e87] + - generic [ref=e90]: Settings + - generic [ref=e92]: + - img [ref=e94] + - generic [ref=e101]: reconnecting + - generic [ref=e102]: + - banner [ref=e103]: + - generic [ref=e104]: + - generic [ref=e105]: + - img [ref=e106] + - text: default + - generic [ref=e110]: + - img [ref=e111] + - text: default + - alert [ref=e257]: + - img [ref=e258] + - generic [ref=e265]: Connection lost. Attempting to reconnect... + - main [ref=e115]: + - generic [ref=e271]: + - generic [ref=e272]: + - generic [ref=e273]: + - img [ref=e274] + - heading "MCP Tools" [level=1] [ref=e276] + - generic [ref=e277]: 0 servers, 0 tools + - generic [ref=e278]: + - button "Refresh" [ref=e279]: + - img [ref=e280] + - text: Refresh + - button "Add Tool" [ref=e347]: + - img [ref=e286] + - text: Add Tool + - tablist "MCP sections" [ref=e287]: + - tab "Servers" [ref=e288]: + - img [ref=e289] + - text: Servers + - tab "Tools" [active] [selected] [ref=e292]: + - img [ref=e293] + - text: Tools + - tabpanel "Tools" [ref=e348]: + - generic [ref=e349]: + - img [ref=e350] + - paragraph [ref=e352]: No tools configured. + - paragraph [ref=e353]: Click "Add Tool" to create an MCP tool. \ No newline at end of file diff --git a/.playwright-mcp/page-2026-04-12T05-29-09-123Z.yml b/.playwright-mcp/page-2026-04-12T05-29-09-123Z.yml new file mode 100644 index 00000000..87008900 --- /dev/null +++ b/.playwright-mcp/page-2026-04-12T05-29-09-123Z.yml @@ -0,0 +1,145 @@ +- generic [ref=e3]: + - link "Skip to content" [ref=e4] [cursor=pointer]: + - /url: "#main-content" + - complementary "Sidebar" [ref=e5]: + - generic [ref=e6]: + - img [ref=e7] + - generic [ref=e10]: TrustGraph + - generic [ref=e13]: + - generic [ref=e14]: + - generic [ref=e15]: + - img [ref=e16] + - text: Flow + - generic [ref=e20]: + - combobox "Flow" [ref=e21]: + - option "default" [selected] + - img + - generic [ref=e22]: + - img [ref=e23] + - generic [ref=e27]: default + - navigation "Main navigation" [ref=e29]: + - link "Chat" [ref=e30] [cursor=pointer]: + - /url: /chat + - generic [ref=e31]: + - img [ref=e32] + - generic [ref=e34]: Chat + - link "Library" [ref=e35] [cursor=pointer]: + - /url: /library + - generic [ref=e36]: + - img [ref=e37] + - generic [ref=e40]: Library + - link "Graph" [ref=e41] [cursor=pointer]: + - /url: /graph + - generic [ref=e42]: + - img [ref=e43] + - generic [ref=e47]: Graph + - link "Prompts" [ref=e48] [cursor=pointer]: + - /url: /prompts + - generic [ref=e49]: + - img [ref=e50] + - generic [ref=e54]: Prompts + - link "Token Cost" [ref=e55] [cursor=pointer]: + - /url: /token-cost + - generic [ref=e56]: + - img [ref=e57] + - generic [ref=e62]: Token Cost + - link "Knowledge Cores" [ref=e63] [cursor=pointer]: + - /url: /knowledge-cores + - generic [ref=e64]: + - img [ref=e65] + - generic [ref=e77]: Knowledge Cores + - link "Flows" [ref=e78] [cursor=pointer]: + - /url: /flows + - generic [ref=e79]: + - img [ref=e80] + - generic [ref=e84]: Flows + - link "MCP Tools" [ref=e266] [cursor=pointer]: + - /url: /mcp-tools + - generic [ref=e267]: + - img [ref=e268] + - generic [ref=e270]: MCP Tools + - link "Settings" [ref=e85] [cursor=pointer]: + - /url: /settings + - generic [ref=e86]: + - img [ref=e87] + - generic [ref=e90]: Settings + - generic [ref=e92]: + - img [ref=e94] + - generic [ref=e101]: reconnecting + - generic [ref=e102]: + - banner [ref=e103]: + - generic [ref=e104]: + - generic [ref=e105]: + - img [ref=e106] + - text: default + - generic [ref=e110]: + - img [ref=e111] + - text: default + - alert [ref=e257]: + - img [ref=e258] + - generic [ref=e265]: Connection lost. Attempting to reconnect... + - main [ref=e115]: + - generic [ref=e271]: + - generic [ref=e272]: + - generic [ref=e273]: + - img [ref=e274] + - heading "MCP Tools" [level=1] [ref=e276] + - generic [ref=e277]: 0 servers, 0 tools + - generic [ref=e278]: + - button "Refresh" [ref=e279]: + - img [ref=e280] + - text: Refresh + - button "Add Tool" [ref=e347]: + - img [ref=e286] + - text: Add Tool + - tablist "MCP sections" [ref=e287]: + - tab "Servers" [ref=e288]: + - img [ref=e289] + - text: Servers + - tab "Tools" [selected] [ref=e292]: + - img [ref=e293] + - text: Tools + - tabpanel "Tools" [ref=e348]: + - generic [ref=e349]: + - img [ref=e350] + - paragraph [ref=e352]: No tools configured. + - paragraph [ref=e353]: Click "Add Tool" to create an MCP tool. + - dialog "Add MCP Tool" [ref=e355]: + - generic [ref=e356]: + - heading "Add MCP Tool" [level=2] [ref=e357] + - button "Close dialog" [ref=e358]: + - img [ref=e359] + - generic [ref=e363]: + - generic [ref=e364]: + - generic [ref=e365]: + - generic [ref=e366]: Key + - textbox "Key" [active] [ref=e367]: + - /placeholder: brave-search + - generic [ref=e368]: + - generic [ref=e369]: Name + - textbox "Name" [ref=e370]: + - /placeholder: brave-search + - generic [ref=e371]: + - generic [ref=e372]: Description + - textbox "Description" [ref=e373]: + - /placeholder: What this tool does... + - generic [ref=e374]: + - generic [ref=e375]: + - generic [ref=e376]: MCP Server + - textbox "MCP Server" [ref=e377]: + - /placeholder: MCP server key + - generic [ref=e378]: + - generic [ref=e379]: Groups (comma-separated) + - textbox "Groups (comma-separated)" [ref=e380]: + - /placeholder: default + - text: default + - generic [ref=e381]: + - generic [ref=e382]: + - generic [ref=e383]: Arguments + - button "Add" [ref=e384]: + - img [ref=e385] + - text: Add + - paragraph [ref=e386]: No arguments defined. Click "Add" to add one. + - generic [ref=e387]: + - button "Cancel" [ref=e388] + - button "Save" [disabled] [ref=e389] \ No newline at end of file diff --git a/.playwright-mcp/page-2026-04-12T05-29-17-218Z.yml b/.playwright-mcp/page-2026-04-12T05-29-17-218Z.yml new file mode 100644 index 00000000..b3369a6e --- /dev/null +++ b/.playwright-mcp/page-2026-04-12T05-29-17-218Z.yml @@ -0,0 +1,156 @@ +- generic [ref=e3]: + - link "Skip to content" [ref=e4] [cursor=pointer]: + - /url: "#main-content" + - complementary "Sidebar" [ref=e5]: + - generic [ref=e6]: + - img [ref=e7] + - generic [ref=e10]: TrustGraph + - generic [ref=e13]: + - generic [ref=e14]: + - generic [ref=e15]: + - img [ref=e16] + - text: Flow + - generic [ref=e20]: + - combobox "Flow" [ref=e21]: + - option "default" [selected] + - img + - generic [ref=e22]: + - img [ref=e23] + - generic [ref=e27]: default + - navigation "Main navigation" [ref=e29]: + - link "Chat" [ref=e30] [cursor=pointer]: + - /url: /chat + - generic [ref=e31]: + - img [ref=e32] + - generic [ref=e34]: Chat + - link "Library" [ref=e35] [cursor=pointer]: + - /url: /library + - generic [ref=e36]: + - img [ref=e37] + - generic [ref=e40]: Library + - link "Graph" [ref=e41] [cursor=pointer]: + - /url: /graph + - generic [ref=e42]: + - img [ref=e43] + - generic [ref=e47]: Graph + - link "Prompts" [ref=e48] [cursor=pointer]: + - /url: /prompts + - generic [ref=e49]: + - img [ref=e50] + - generic [ref=e54]: Prompts + - link "Token Cost" [ref=e55] [cursor=pointer]: + - /url: /token-cost + - generic [ref=e56]: + - img [ref=e57] + - generic [ref=e62]: Token Cost + - link "Knowledge Cores" [ref=e63] [cursor=pointer]: + - /url: /knowledge-cores + - generic [ref=e64]: + - img [ref=e65] + - generic [ref=e77]: Knowledge Cores + - link "Flows" [ref=e78] [cursor=pointer]: + - /url: /flows + - generic [ref=e79]: + - img [ref=e80] + - generic [ref=e84]: Flows + - link "MCP Tools" [ref=e266] [cursor=pointer]: + - /url: /mcp-tools + - generic [ref=e267]: + - img [ref=e268] + - generic [ref=e270]: MCP Tools + - link "Settings" [ref=e85] [cursor=pointer]: + - /url: /settings + - generic [ref=e86]: + - img [ref=e87] + - generic [ref=e90]: Settings + - generic [ref=e92]: + - img [ref=e94] + - generic [ref=e101]: reconnecting + - generic [ref=e102]: + - banner [ref=e103]: + - generic [ref=e104]: + - generic [ref=e105]: + - img [ref=e106] + - text: default + - generic [ref=e110]: + - img [ref=e111] + - text: default + - alert [ref=e257]: + - img [ref=e258] + - generic [ref=e265]: Connection lost. Attempting to reconnect... + - main [ref=e115]: + - generic [ref=e271]: + - generic [ref=e272]: + - generic [ref=e273]: + - img [ref=e274] + - heading "MCP Tools" [level=1] [ref=e276] + - generic [ref=e277]: 0 servers, 0 tools + - generic [ref=e278]: + - button "Refresh" [ref=e279]: + - img [ref=e280] + - text: Refresh + - button "Add Tool" [ref=e347]: + - img [ref=e286] + - text: Add Tool + - tablist "MCP sections" [ref=e287]: + - tab "Servers" [ref=e288]: + - img [ref=e289] + - text: Servers + - tab "Tools" [selected] [ref=e292]: + - img [ref=e293] + - text: Tools + - tabpanel "Tools" [ref=e348]: + - generic [ref=e349]: + - img [ref=e350] + - paragraph [ref=e352]: No tools configured. + - paragraph [ref=e353]: Click "Add Tool" to create an MCP tool. + - dialog "Add MCP Tool" [ref=e355]: + - generic [ref=e356]: + - heading "Add MCP Tool" [level=2] [ref=e357] + - button "Close dialog" [ref=e358]: + - img [ref=e359] + - generic [ref=e363]: + - generic [ref=e364]: + - generic [ref=e365]: + - generic [ref=e366]: Key + - textbox "Key" [ref=e367]: + - /placeholder: brave-search + - generic [ref=e368]: + - generic [ref=e369]: Name + - textbox "Name" [ref=e370]: + - /placeholder: brave-search + - generic [ref=e371]: + - generic [ref=e372]: Description + - textbox "Description" [ref=e373]: + - /placeholder: What this tool does... + - generic [ref=e374]: + - generic [ref=e375]: + - generic [ref=e376]: MCP Server + - textbox "MCP Server" [ref=e377]: + - /placeholder: MCP server key + - generic [ref=e378]: + - generic [ref=e379]: Groups (comma-separated) + - textbox "Groups (comma-separated)" [ref=e380]: + - /placeholder: default + - text: default + - generic [ref=e381]: + - generic [ref=e382]: + - generic [ref=e383]: Arguments + - button "Add" [active] [ref=e384]: + - img [ref=e385] + - text: Add + - generic [ref=e391]: + - textbox "Argument 1 name" [ref=e392]: + - /placeholder: name + - combobox "Argument 1 type" [ref=e393]: + - option "string" [selected] + - option "number" + - option "boolean" + - option "object" + - textbox "Argument 1 description" [ref=e394]: + - /placeholder: description + - button "Remove argument 1" [ref=e395]: + - img [ref=e396] + - generic [ref=e387]: + - button "Cancel" [ref=e388] + - button "Save" [disabled] [ref=e389] \ No newline at end of file diff --git a/.playwright-mcp/page-2026-04-12T05-29-50-589Z.yml b/.playwright-mcp/page-2026-04-12T05-29-50-589Z.yml new file mode 100644 index 00000000..b692d031 --- /dev/null +++ b/.playwright-mcp/page-2026-04-12T05-29-50-589Z.yml @@ -0,0 +1,106 @@ +- generic [ref=e3]: + - link "Skip to content" [ref=e4] [cursor=pointer]: + - /url: "#main-content" + - complementary "Sidebar" [ref=e5]: + - generic [ref=e6]: + - img [ref=e7] + - generic [ref=e10]: TrustGraph + - generic [ref=e13]: + - generic [ref=e14]: + - generic [ref=e15]: + - img [ref=e16] + - text: Flow + - generic [ref=e20]: + - combobox "Flow" [ref=e21]: + - option "default" [selected] + - img + - generic [ref=e22]: + - img [ref=e23] + - generic [ref=e27]: default + - navigation "Main navigation" [ref=e29]: + - link "Chat" [ref=e30] [cursor=pointer]: + - /url: /chat + - generic [ref=e31]: + - img [ref=e32] + - generic [ref=e34]: Chat + - link "Library" [ref=e35] [cursor=pointer]: + - /url: /library + - generic [ref=e36]: + - img [ref=e37] + - generic [ref=e40]: Library + - link "Graph" [ref=e41] [cursor=pointer]: + - /url: /graph + - generic [ref=e42]: + - img [ref=e43] + - generic [ref=e47]: Graph + - link "Prompts" [ref=e48] [cursor=pointer]: + - /url: /prompts + - generic [ref=e49]: + - img [ref=e50] + - generic [ref=e54]: Prompts + - link "Token Cost" [ref=e55] [cursor=pointer]: + - /url: /token-cost + - generic [ref=e56]: + - img [ref=e57] + - generic [ref=e62]: Token Cost + - link "Knowledge Cores" [ref=e63] [cursor=pointer]: + - /url: /knowledge-cores + - generic [ref=e64]: + - img [ref=e65] + - generic [ref=e77]: Knowledge Cores + - link "Flows" [ref=e78] [cursor=pointer]: + - /url: /flows + - generic [ref=e79]: + - img [ref=e80] + - generic [ref=e84]: Flows + - link "MCP Tools" [ref=e266] [cursor=pointer]: + - /url: /mcp-tools + - generic [ref=e267]: + - img [ref=e268] + - generic [ref=e270]: MCP Tools + - link "Settings" [ref=e85] [cursor=pointer]: + - /url: /settings + - generic [ref=e86]: + - img [ref=e87] + - generic [ref=e90]: Settings + - generic [ref=e92]: + - img [ref=e94] + - generic [ref=e101]: reconnecting + - generic [ref=e102]: + - banner [ref=e103]: + - generic [ref=e104]: + - generic [ref=e105]: + - img [ref=e106] + - text: default + - generic [ref=e110]: + - img [ref=e111] + - text: default + - alert [ref=e257]: + - img [ref=e258] + - generic [ref=e265]: Connection lost. Attempting to reconnect... + - main [ref=e115]: + - generic [ref=e271]: + - generic [ref=e272]: + - generic [ref=e273]: + - img [ref=e274] + - heading "MCP Tools" [level=1] [ref=e276] + - generic [ref=e277]: 0 servers, 0 tools + - generic [ref=e278]: + - button "Refresh" [ref=e279]: + - img [ref=e280] + - text: Refresh + - button "Add Server" [ref=e399]: + - img [ref=e286] + - text: Add Server + - tablist "MCP sections" [ref=e287]: + - tab "Servers" [active] [selected] [ref=e288]: + - img [ref=e289] + - text: Servers + - tab "Tools" [ref=e292]: + - img [ref=e293] + - text: Tools + - tabpanel "Servers" [ref=e400]: + - generic [ref=e401]: + - img [ref=e402] + - paragraph [ref=e405]: No MCP servers configured. + - paragraph [ref=e406]: Click "Add Server" to connect an external MCP server. \ No newline at end of file diff --git a/.playwright-mcp/page-2026-04-12T05-29-55-753Z.yml b/.playwright-mcp/page-2026-04-12T05-29-55-753Z.yml new file mode 100644 index 00000000..277293b8 --- /dev/null +++ b/.playwright-mcp/page-2026-04-12T05-29-55-753Z.yml @@ -0,0 +1,134 @@ +- generic [ref=e3]: + - link "Skip to content" [ref=e4] [cursor=pointer]: + - /url: "#main-content" + - complementary "Sidebar" [ref=e5]: + - generic [ref=e6]: + - img [ref=e7] + - generic [ref=e10]: TrustGraph + - generic [ref=e13]: + - generic [ref=e14]: + - generic [ref=e15]: + - img [ref=e16] + - text: Flow + - generic [ref=e20]: + - combobox "Flow" [ref=e21]: + - option "default" [selected] + - img + - generic [ref=e22]: + - img [ref=e23] + - generic [ref=e27]: default + - navigation "Main navigation" [ref=e29]: + - link "Chat" [ref=e30] [cursor=pointer]: + - /url: /chat + - generic [ref=e31]: + - img [ref=e32] + - generic [ref=e34]: Chat + - link "Library" [ref=e35] [cursor=pointer]: + - /url: /library + - generic [ref=e36]: + - img [ref=e37] + - generic [ref=e40]: Library + - link "Graph" [ref=e41] [cursor=pointer]: + - /url: /graph + - generic [ref=e42]: + - img [ref=e43] + - generic [ref=e47]: Graph + - link "Prompts" [ref=e48] [cursor=pointer]: + - /url: /prompts + - generic [ref=e49]: + - img [ref=e50] + - generic [ref=e54]: Prompts + - link "Token Cost" [ref=e55] [cursor=pointer]: + - /url: /token-cost + - generic [ref=e56]: + - img [ref=e57] + - generic [ref=e62]: Token Cost + - link "Knowledge Cores" [ref=e63] [cursor=pointer]: + - /url: /knowledge-cores + - generic [ref=e64]: + - img [ref=e65] + - generic [ref=e77]: Knowledge Cores + - link "Flows" [ref=e78] [cursor=pointer]: + - /url: /flows + - generic [ref=e79]: + - img [ref=e80] + - generic [ref=e84]: Flows + - link "MCP Tools" [ref=e266] [cursor=pointer]: + - /url: /mcp-tools + - generic [ref=e267]: + - img [ref=e268] + - generic [ref=e270]: MCP Tools + - link "Settings" [ref=e85] [cursor=pointer]: + - /url: /settings + - generic [ref=e86]: + - img [ref=e87] + - generic [ref=e90]: Settings + - generic [ref=e92]: + - img [ref=e94] + - generic [ref=e101]: reconnecting + - generic [ref=e102]: + - banner [ref=e103]: + - generic [ref=e104]: + - generic [ref=e105]: + - img [ref=e106] + - text: default + - generic [ref=e110]: + - img [ref=e111] + - text: default + - alert [ref=e257]: + - img [ref=e258] + - generic [ref=e265]: Connection lost. Attempting to reconnect... + - main [ref=e115]: + - generic [ref=e271]: + - generic [ref=e272]: + - generic [ref=e273]: + - img [ref=e274] + - heading "MCP Tools" [level=1] [ref=e276] + - generic [ref=e277]: 0 servers, 0 tools + - generic [ref=e278]: + - button "Refresh" [ref=e279]: + - img [ref=e280] + - text: Refresh + - button "Add Server" [ref=e399]: + - img [ref=e286] + - text: Add Server + - tablist "MCP sections" [ref=e287]: + - tab "Servers" [selected] [ref=e288]: + - img [ref=e289] + - text: Servers + - tab "Tools" [ref=e292]: + - img [ref=e293] + - text: Tools + - tabpanel "Servers" [ref=e400]: + - generic [ref=e401]: + - img [ref=e402] + - paragraph [ref=e405]: No MCP servers configured. + - paragraph [ref=e406]: Click "Add Server" to connect an external MCP server. + - dialog "Add MCP Server" [ref=e408]: + - generic [ref=e409]: + - heading "Add MCP Server" [level=2] [ref=e410] + - button "Close dialog" [ref=e411]: + - img [ref=e412] + - generic [ref=e416]: + - generic [ref=e417]: + - generic [ref=e418]: Key + - textbox "Key" [active] [ref=e419]: + - /placeholder: brave-search + - generic [ref=e420]: + - generic [ref=e421]: URL + - textbox "URL" [ref=e422]: + - /placeholder: http://localhost:8383/mcp + - generic [ref=e423]: + - generic [ref=e424]: Remote Name (optional) + - textbox "Remote Name (optional)" [ref=e425]: + - /placeholder: Tool name at the remote server + - generic [ref=e426]: + - generic [ref=e427]: Auth Token (optional) + - generic [ref=e428]: + - textbox "Auth Token (optional)" [ref=e429]: + - /placeholder: Bearer token for authentication + - button "Show auth token" [ref=e430]: + - img [ref=e431] + - generic [ref=e434]: + - button "Cancel" [ref=e435] + - button "Save" [disabled] [ref=e436] \ No newline at end of file diff --git a/.playwright-mcp/page-2026-04-12T05-31-12-362Z.yml b/.playwright-mcp/page-2026-04-12T05-31-12-362Z.yml new file mode 100644 index 00000000..569d5d1f --- /dev/null +++ b/.playwright-mcp/page-2026-04-12T05-31-12-362Z.yml @@ -0,0 +1,134 @@ +- generic [ref=e3]: + - link "Skip to content" [ref=e4] [cursor=pointer]: + - /url: "#main-content" + - complementary "Sidebar" [ref=e5]: + - generic [ref=e6]: + - img [ref=e7] + - generic [ref=e10]: TrustGraph + - generic [ref=e13]: + - generic [ref=e14]: + - generic [ref=e15]: + - img [ref=e16] + - text: Flow + - generic [ref=e20]: + - combobox "Flow" [ref=e21]: + - option "default" [selected] + - img + - generic [ref=e22]: + - img [ref=e23] + - generic [ref=e27]: default + - navigation "Main navigation" [ref=e29]: + - link "Chat" [ref=e30] [cursor=pointer]: + - /url: /chat + - generic [ref=e31]: + - img [ref=e32] + - generic [ref=e34]: Chat + - link "Library" [ref=e35] [cursor=pointer]: + - /url: /library + - generic [ref=e36]: + - img [ref=e37] + - generic [ref=e40]: Library + - link "Graph" [ref=e41] [cursor=pointer]: + - /url: /graph + - generic [ref=e42]: + - img [ref=e43] + - generic [ref=e47]: Graph + - link "Prompts" [ref=e48] [cursor=pointer]: + - /url: /prompts + - generic [ref=e49]: + - img [ref=e50] + - generic [ref=e54]: Prompts + - link "Token Cost" [ref=e55] [cursor=pointer]: + - /url: /token-cost + - generic [ref=e56]: + - img [ref=e57] + - generic [ref=e62]: Token Cost + - link "Knowledge Cores" [ref=e63] [cursor=pointer]: + - /url: /knowledge-cores + - generic [ref=e64]: + - img [ref=e65] + - generic [ref=e77]: Knowledge Cores + - link "Flows" [ref=e78] [cursor=pointer]: + - /url: /flows + - generic [ref=e79]: + - img [ref=e80] + - generic [ref=e84]: Flows + - link "MCP Tools" [ref=e266] [cursor=pointer]: + - /url: /mcp-tools + - generic [ref=e267]: + - img [ref=e268] + - generic [ref=e270]: MCP Tools + - link "Settings" [ref=e85] [cursor=pointer]: + - /url: /settings + - generic [ref=e86]: + - img [ref=e87] + - generic [ref=e90]: Settings + - generic [ref=e92]: + - img [ref=e94] + - generic [ref=e101]: reconnecting + - generic [ref=e102]: + - banner [ref=e103]: + - generic [ref=e104]: + - generic [ref=e105]: + - img [ref=e106] + - text: default + - generic [ref=e110]: + - img [ref=e111] + - text: default + - alert [ref=e257]: + - img [ref=e258] + - generic [ref=e265]: Connection lost. Attempting to reconnect... + - main [ref=e115]: + - generic [ref=e271]: + - generic [ref=e272]: + - generic [ref=e273]: + - img [ref=e274] + - heading "MCP Tools" [level=1] [ref=e276] + - generic [ref=e277]: 0 servers, 0 tools + - generic [ref=e278]: + - button "Refresh" [ref=e279]: + - img [ref=e280] + - text: Refresh + - button "Add Server" [ref=e399]: + - img [ref=e286] + - text: Add Server + - tablist "MCP sections" [ref=e287]: + - tab "Servers" [selected] [ref=e288]: + - img [ref=e289] + - text: Servers + - tab "Tools" [ref=e292]: + - img [ref=e293] + - text: Tools + - tabpanel "Servers" [ref=e400]: + - generic [ref=e401]: + - img [ref=e402] + - paragraph [ref=e405]: No MCP servers configured. + - paragraph [ref=e406]: Click "Add Server" to connect an external MCP server. + - dialog "Add MCP Server" [ref=e438]: + - generic [ref=e439]: + - heading "Add MCP Server" [level=2] [ref=e440] + - button "Close dialog" [ref=e441]: + - img [ref=e442] + - generic [ref=e446]: + - generic [ref=e447]: + - generic [ref=e448]: Key + - textbox "Key" [active] [ref=e449]: + - /placeholder: brave-search + - generic [ref=e450]: + - generic [ref=e451]: URL + - textbox "URL" [ref=e452]: + - /placeholder: http://localhost:8383/mcp + - generic [ref=e453]: + - generic [ref=e454]: Remote Name (optional) + - textbox "Remote Name (optional)" [ref=e455]: + - /placeholder: Tool name at the remote server + - generic [ref=e456]: + - generic [ref=e457]: Auth Token (optional) + - generic [ref=e458]: + - textbox "Auth Token (optional)" [ref=e459]: + - /placeholder: Bearer token for authentication + - button "Show auth token" [ref=e460]: + - img [ref=e461] + - generic [ref=e464]: + - button "Cancel" [ref=e465] + - button "Save" [disabled] [ref=e466] \ No newline at end of file diff --git a/.playwright-mcp/page-2026-04-12T05-31-25-532Z.yml b/.playwright-mcp/page-2026-04-12T05-31-25-532Z.yml new file mode 100644 index 00000000..af739735 --- /dev/null +++ b/.playwright-mcp/page-2026-04-12T05-31-25-532Z.yml @@ -0,0 +1,106 @@ +- generic [ref=e3]: + - link "Skip to content" [ref=e4] [cursor=pointer]: + - /url: "#main-content" + - complementary "Sidebar" [ref=e5]: + - generic [ref=e6]: + - img [ref=e7] + - generic [ref=e10]: TrustGraph + - generic [ref=e13]: + - generic [ref=e14]: + - generic [ref=e15]: + - img [ref=e16] + - text: Flow + - generic [ref=e20]: + - combobox "Flow" [ref=e21]: + - option "default" [selected] + - img + - generic [ref=e22]: + - img [ref=e23] + - generic [ref=e27]: default + - navigation "Main navigation" [ref=e29]: + - link "Chat" [ref=e30] [cursor=pointer]: + - /url: /chat + - generic [ref=e31]: + - img [ref=e32] + - generic [ref=e34]: Chat + - link "Library" [ref=e35] [cursor=pointer]: + - /url: /library + - generic [ref=e36]: + - img [ref=e37] + - generic [ref=e40]: Library + - link "Graph" [ref=e41] [cursor=pointer]: + - /url: /graph + - generic [ref=e42]: + - img [ref=e43] + - generic [ref=e47]: Graph + - link "Prompts" [ref=e48] [cursor=pointer]: + - /url: /prompts + - generic [ref=e49]: + - img [ref=e50] + - generic [ref=e54]: Prompts + - link "Token Cost" [ref=e55] [cursor=pointer]: + - /url: /token-cost + - generic [ref=e56]: + - img [ref=e57] + - generic [ref=e62]: Token Cost + - link "Knowledge Cores" [ref=e63] [cursor=pointer]: + - /url: /knowledge-cores + - generic [ref=e64]: + - img [ref=e65] + - generic [ref=e77]: Knowledge Cores + - link "Flows" [ref=e78] [cursor=pointer]: + - /url: /flows + - generic [ref=e79]: + - img [ref=e80] + - generic [ref=e84]: Flows + - link "MCP Tools" [ref=e266] [cursor=pointer]: + - /url: /mcp-tools + - generic [ref=e267]: + - img [ref=e268] + - generic [ref=e270]: MCP Tools + - link "Settings" [ref=e85] [cursor=pointer]: + - /url: /settings + - generic [ref=e86]: + - img [ref=e87] + - generic [ref=e90]: Settings + - generic [ref=e92]: + - img [ref=e94] + - generic [ref=e101]: reconnecting + - generic [ref=e102]: + - banner [ref=e103]: + - generic [ref=e104]: + - generic [ref=e105]: + - img [ref=e106] + - text: default + - generic [ref=e110]: + - img [ref=e111] + - text: default + - alert [ref=e257]: + - img [ref=e258] + - generic [ref=e265]: Connection lost. Attempting to reconnect... + - main [ref=e115]: + - generic [ref=e271]: + - generic [ref=e272]: + - generic [ref=e273]: + - img [ref=e274] + - heading "MCP Tools" [level=1] [ref=e276] + - generic [ref=e277]: 0 servers, 0 tools + - generic [ref=e278]: + - button "Refresh" [ref=e279]: + - img [ref=e280] + - text: Refresh + - button "Add Server" [active] [ref=e399]: + - img [ref=e286] + - text: Add Server + - tablist "MCP sections" [ref=e287]: + - tab "Servers" [selected] [ref=e288]: + - img [ref=e289] + - text: Servers + - tab "Tools" [ref=e292]: + - img [ref=e293] + - text: Tools + - tabpanel "Servers" [ref=e400]: + - generic [ref=e401]: + - img [ref=e402] + - paragraph [ref=e405]: No MCP servers configured. + - paragraph [ref=e406]: Click "Add Server" to connect an external MCP server. \ No newline at end of file diff --git a/.playwright-mcp/page-2026-04-12T05-31-31-420Z.yml b/.playwright-mcp/page-2026-04-12T05-31-31-420Z.yml new file mode 100644 index 00000000..96ab4056 --- /dev/null +++ b/.playwright-mcp/page-2026-04-12T05-31-31-420Z.yml @@ -0,0 +1,136 @@ +- generic [ref=e3]: + - link "Skip to content" [ref=e4] [cursor=pointer]: + - /url: "#main-content" + - complementary "Sidebar" [ref=e5]: + - generic [ref=e6]: + - img [ref=e7] + - generic [ref=e10]: TrustGraph + - generic [ref=e13]: + - generic [ref=e14]: + - generic [ref=e15]: + - img [ref=e16] + - text: Flow + - generic [ref=e20]: + - combobox "Flow" [ref=e21]: + - option "default" [selected] + - img + - generic [ref=e22]: + - img [ref=e23] + - generic [ref=e27]: default + - navigation "Main navigation" [ref=e29]: + - link "Chat" [ref=e30] [cursor=pointer]: + - /url: /chat + - generic [ref=e31]: + - img [ref=e32] + - generic [ref=e34]: Chat + - link "Library" [ref=e35] [cursor=pointer]: + - /url: /library + - generic [ref=e36]: + - img [ref=e37] + - generic [ref=e40]: Library + - link "Graph" [ref=e41] [cursor=pointer]: + - /url: /graph + - generic [ref=e42]: + - img [ref=e43] + - generic [ref=e47]: Graph + - link "Prompts" [ref=e48] [cursor=pointer]: + - /url: /prompts + - generic [ref=e49]: + - img [ref=e50] + - generic [ref=e54]: Prompts + - link "Token Cost" [ref=e55] [cursor=pointer]: + - /url: /token-cost + - generic [ref=e56]: + - img [ref=e57] + - generic [ref=e62]: Token Cost + - link "Knowledge Cores" [ref=e63] [cursor=pointer]: + - /url: /knowledge-cores + - generic [ref=e64]: + - img [ref=e65] + - generic [ref=e77]: Knowledge Cores + - link "Flows" [ref=e78] [cursor=pointer]: + - /url: /flows + - generic [ref=e79]: + - img [ref=e80] + - generic [ref=e84]: Flows + - link "MCP Tools" [ref=e266] [cursor=pointer]: + - /url: /mcp-tools + - generic [ref=e267]: + - img [ref=e268] + - generic [ref=e270]: MCP Tools + - link "Settings" [ref=e85] [cursor=pointer]: + - /url: /settings + - generic [ref=e86]: + - img [ref=e87] + - generic [ref=e90]: Settings + - generic [ref=e92]: + - img [ref=e94] + - generic [ref=e101]: reconnecting + - generic [ref=e102]: + - banner [ref=e103]: + - generic [ref=e104]: + - generic [ref=e105]: + - img [ref=e106] + - text: default + - generic [ref=e110]: + - img [ref=e111] + - text: default + - alert [ref=e257]: + - img [ref=e258] + - generic [ref=e265]: Connection lost. Attempting to reconnect... + - main [ref=e115]: + - generic [ref=e271]: + - generic [ref=e272]: + - generic [ref=e273]: + - img [ref=e274] + - heading "MCP Tools" [level=1] [ref=e276] + - generic [ref=e277]: 0 servers, 0 tools + - generic [ref=e278]: + - button "Refresh" [ref=e279]: + - img [ref=e280] + - text: Refresh + - button "Add Server" [ref=e399]: + - img [ref=e286] + - text: Add Server + - tablist "MCP sections" [ref=e287]: + - tab "Servers" [selected] [ref=e288]: + - img [ref=e289] + - text: Servers + - tab "Tools" [ref=e292]: + - img [ref=e293] + - text: Tools + - tabpanel "Servers" [ref=e400]: + - generic [ref=e401]: + - img [ref=e402] + - paragraph [ref=e405]: No MCP servers configured. + - paragraph [ref=e406]: Click "Add Server" to connect an external MCP server. + - dialog "Add MCP Server" [ref=e468]: + - generic [ref=e469]: + - heading "Add MCP Server" [level=2] [ref=e470] + - button "Close dialog" [ref=e471]: + - img [ref=e472] + - generic [ref=e476]: + - generic [ref=e477]: + - generic [ref=e478]: Key + - textbox "Key" [active] [ref=e479]: + - /placeholder: brave-search + - text: test-stale-state + - generic [ref=e480]: + - generic [ref=e481]: URL + - textbox "URL" [ref=e482]: + - /placeholder: http://localhost:8383/mcp + - text: http://example.com + - generic [ref=e483]: + - generic [ref=e484]: Remote Name (optional) + - textbox "Remote Name (optional)" [ref=e485]: + - /placeholder: Tool name at the remote server + - generic [ref=e486]: + - generic [ref=e487]: Auth Token (optional) + - generic [ref=e488]: + - textbox "Auth Token (optional)" [ref=e489]: + - /placeholder: Bearer token for authentication + - button "Show auth token" [ref=e490]: + - img [ref=e491] + - generic [ref=e494]: + - button "Cancel" [ref=e495] + - button "Save" [ref=e496] \ No newline at end of file diff --git a/.playwright-mcp/page-2026-04-12T05-31-48-160Z.yml b/.playwright-mcp/page-2026-04-12T05-31-48-160Z.yml new file mode 100644 index 00000000..fcbf942b --- /dev/null +++ b/.playwright-mcp/page-2026-04-12T05-31-48-160Z.yml @@ -0,0 +1,136 @@ +- generic [ref=e3]: + - link "Skip to content" [ref=e4] [cursor=pointer]: + - /url: "#main-content" + - complementary "Sidebar" [ref=e5]: + - generic [ref=e6]: + - img [ref=e7] + - generic [ref=e10]: TrustGraph + - generic [ref=e13]: + - generic [ref=e14]: + - generic [ref=e15]: + - img [ref=e16] + - text: Flow + - generic [ref=e20]: + - combobox "Flow" [ref=e21]: + - option "default" [selected] + - img + - generic [ref=e22]: + - img [ref=e23] + - generic [ref=e27]: default + - navigation "Main navigation" [ref=e29]: + - link "Chat" [ref=e30] [cursor=pointer]: + - /url: /chat + - generic [ref=e31]: + - img [ref=e32] + - generic [ref=e34]: Chat + - link "Library" [ref=e35] [cursor=pointer]: + - /url: /library + - generic [ref=e36]: + - img [ref=e37] + - generic [ref=e40]: Library + - link "Graph" [ref=e41] [cursor=pointer]: + - /url: /graph + - generic [ref=e42]: + - img [ref=e43] + - generic [ref=e47]: Graph + - link "Prompts" [ref=e48] [cursor=pointer]: + - /url: /prompts + - generic [ref=e49]: + - img [ref=e50] + - generic [ref=e54]: Prompts + - link "Token Cost" [ref=e55] [cursor=pointer]: + - /url: /token-cost + - generic [ref=e56]: + - img [ref=e57] + - generic [ref=e62]: Token Cost + - link "Knowledge Cores" [ref=e63] [cursor=pointer]: + - /url: /knowledge-cores + - generic [ref=e64]: + - img [ref=e65] + - generic [ref=e77]: Knowledge Cores + - link "Flows" [ref=e78] [cursor=pointer]: + - /url: /flows + - generic [ref=e79]: + - img [ref=e80] + - generic [ref=e84]: Flows + - link "MCP Tools" [ref=e266] [cursor=pointer]: + - /url: /mcp-tools + - generic [ref=e267]: + - img [ref=e268] + - generic [ref=e270]: MCP Tools + - link "Settings" [ref=e85] [cursor=pointer]: + - /url: /settings + - generic [ref=e86]: + - img [ref=e87] + - generic [ref=e90]: Settings + - generic [ref=e92]: + - img [ref=e94] + - generic [ref=e101]: reconnecting + - generic [ref=e102]: + - banner [ref=e103]: + - generic [ref=e104]: + - generic [ref=e105]: + - img [ref=e106] + - text: default + - generic [ref=e110]: + - img [ref=e111] + - text: default + - alert [ref=e257]: + - img [ref=e258] + - generic [ref=e265]: Connection lost. Attempting to reconnect... + - main [ref=e115]: + - generic [ref=e271]: + - generic [ref=e272]: + - generic [ref=e273]: + - img [ref=e274] + - heading "MCP Tools" [level=1] [ref=e276] + - generic [ref=e277]: 0 servers, 0 tools + - generic [ref=e278]: + - button "Refresh" [ref=e279]: + - img [ref=e280] + - text: Refresh + - button "Add Server" [ref=e399]: + - img [ref=e286] + - text: Add Server + - tablist "MCP sections" [ref=e287]: + - tab "Servers" [selected] [ref=e288]: + - img [ref=e289] + - text: Servers + - tab "Tools" [ref=e292]: + - img [ref=e293] + - text: Tools + - tabpanel "Servers" [ref=e400]: + - generic [ref=e401]: + - img [ref=e402] + - paragraph [ref=e405]: No MCP servers configured. + - paragraph [ref=e406]: Click "Add Server" to connect an external MCP server. + - dialog "Add MCP Server" [ref=e498]: + - generic [ref=e499]: + - heading "Add MCP Server" [level=2] [ref=e500] + - button "Close dialog" [ref=e501]: + - img [ref=e502] + - generic [ref=e506]: + - generic [ref=e507]: + - generic [ref=e508]: Key + - textbox "Key" [active] [ref=e509]: + - /placeholder: brave-search + - text: test-stale-state + - generic [ref=e510]: + - generic [ref=e511]: URL + - textbox "URL" [ref=e512]: + - /placeholder: http://localhost:8383/mcp + - text: http://example.com + - generic [ref=e513]: + - generic [ref=e514]: Remote Name (optional) + - textbox "Remote Name (optional)" [ref=e515]: + - /placeholder: Tool name at the remote server + - generic [ref=e516]: + - generic [ref=e517]: Auth Token (optional) + - generic [ref=e518]: + - textbox "Auth Token (optional)" [ref=e519]: + - /placeholder: Bearer token for authentication + - button "Show auth token" [ref=e520]: + - img [ref=e521] + - generic [ref=e524]: + - button "Cancel" [ref=e525] + - button "Save" [ref=e526] \ No newline at end of file diff --git a/ai-context/context-graph-demo/.gitignore b/ai-context/context-graph-demo/.gitignore new file mode 100644 index 00000000..99e677ff --- /dev/null +++ b/ai-context/context-graph-demo/.gitignore @@ -0,0 +1,25 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +*~ diff --git a/ai-context/context-graph-demo/LICENSE b/ai-context/context-graph-demo/LICENSE new file mode 100644 index 00000000..7e31bd3e --- /dev/null +++ b/ai-context/context-graph-demo/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. Please also get an approval + from the project maintainers before applying the Apache License + to your project. + + Copyright 2026 Knownext Inc. + Copyright 2026 Knownext Limited + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/ai-context/context-graph-demo/README.md b/ai-context/context-graph-demo/README.md new file mode 100644 index 00000000..7e94af46 --- /dev/null +++ b/ai-context/context-graph-demo/README.md @@ -0,0 +1,67 @@ + +# Context Graph Demo + +A React application that demonstrates +[TrustGraph](https://trustgraph.ai/) context graph capabilities +The demo provides an interactive graph visualisation, natural-language +querying, explainability views, and ontology browsing — all powered by +a TrustGraph backend. Load your own data to explore. + +See it in action: [Context Graph demo video](https://www.youtube.com/watch?v=sWc7mkhITIo) + +## Features + +- **Graph view** — interactive force-directed graph of entities and + relationships, with domain-based filtering +- **Query view** — natural-language questions answered by the TrustGraph + knowledge graph +- **Explain view** — step-by-step explainability traces showing how + answers were derived +- **Data view** — browse the raw documents loaded into TrustGraph +- **Ontology view** — explore the ontology (types and predicates) + extracted from the dataset + +## Prerequisites + +- Node.js (v18+) +- A running [TrustGraph](https://trustgraph.ai/) instance (tested with + TrustGraph 2.1) + +## Preparing TrustGraph + +This demo requires TrustGraph to be running in ontology mode: + +1. Launch a flow using the `ontology` flow blueprint. +2. Load an OWL ontology into the workbench. +3. Process your data using the new flow. + +## Getting started + +Install dependencies: + +```bash +npm install +``` + +Start the development server: + +```bash +npm run dev +``` + +The Vite dev server proxies `/api/socket` (WebSocket) and other API +routes to the TrustGraph API gateway at `localhost:8088`. If your +TrustGraph instance is running on a different host or port, edit the +proxy targets in `vite.config.js`. + +Build for production: + +```bash +npm run build +``` + +## License + +Copyright 2026 Knownext Inc. and Knownext Limited. +Licensed under the Apache License 2.0 — see [LICENSE](LICENSE) for +details. diff --git a/ai-context/context-graph-demo/eslint.config.js b/ai-context/context-graph-demo/eslint.config.js new file mode 100644 index 00000000..4fa125da --- /dev/null +++ b/ai-context/context-graph-demo/eslint.config.js @@ -0,0 +1,29 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{js,jsx}'], + extends: [ + js.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + parserOptions: { + ecmaVersion: 'latest', + ecmaFeatures: { jsx: true }, + sourceType: 'module', + }, + }, + rules: { + 'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }], + }, + }, +]) diff --git a/ai-context/context-graph-demo/index.html b/ai-context/context-graph-demo/index.html new file mode 100644 index 00000000..3e6d7b10 --- /dev/null +++ b/ai-context/context-graph-demo/index.html @@ -0,0 +1,16 @@ + + + + + + + TrustGraph Context Graph Demo + + + + + +
+ + + diff --git a/ai-context/context-graph-demo/package-lock.json b/ai-context/context-graph-demo/package-lock.json new file mode 100644 index 00000000..b2309e56 --- /dev/null +++ b/ai-context/context-graph-demo/package-lock.json @@ -0,0 +1,3080 @@ +{ + "name": "retail-intelligence-demo", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "retail-intelligence-demo", + "version": "0.0.0", + "dependencies": { + "@tanstack/react-query": "^5.90.21", + "@trustgraph/react-provider": "^1.4.0", + "@trustgraph/react-state": "^1.4.6", + "react": "^19.2.0", + "react-dom": "^19.2.0" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.1", + "eslint": "^9.39.1", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.4.24", + "globals": "^16.5.0", + "typescript": "~5.9.3", + "vite": "^7.3.1" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", + "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz", + "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz", + "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz", + "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", + "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz", + "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz", + "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz", + "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz", + "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", + "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz", + "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz", + "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz", + "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", + "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz", + "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz", + "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", + "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz", + "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", + "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz", + "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz", + "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", + "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", + "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz", + "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz", + "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", + "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", + "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tanstack/query-core": { + "version": "5.91.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.91.0.tgz", + "integrity": "sha512-FYXN8Kk9Q5VKuV6AIVaNwMThSi0nvAtR4X7HQoigf6ePOtFcavJYVIzgFhOVdtbBQtCJE3KimDIMMJM2DR1hjw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.91.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.91.0.tgz", + "integrity": "sha512-S8FODsDTNv0Ym+o/JVBvA6EWiWVhg6K2Q4qFehZyFKk6uW4H9OPbXl4kyiN9hAly0uHJ/1GEbR6kAI4MZWfjEA==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.91.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@trustgraph/client": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@trustgraph/client/-/client-1.6.0.tgz", + "integrity": "sha512-z09X1TNmaRrmZXt4b5aXtJr/D7XjF7Lm7/lpYIPUO0NTJ4QYm2ZDPE/z1bJxpJf29y/Q2A65bWY6+TzPoO9kZQ==", + "license": "Apache-2.0", + "peer": true + }, + "node_modules/@trustgraph/react-provider": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@trustgraph/react-provider/-/react-provider-1.4.0.tgz", + "integrity": "sha512-CRtwrbzEOiKoAekXpjIhz8jbvNbaw6Fm0E2EgLnVp5Jf/6GVfPJ6v3eeqG4Z0AlO23cq8k0j88i+nxGI6llQIQ==", + "license": "Apache-2.0", + "peerDependencies": { + "@trustgraph/client": "^1.4.0", + "react": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/@trustgraph/react-state": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@trustgraph/react-state/-/react-state-1.6.0.tgz", + "integrity": "sha512-NsTfmbNE0zTz/H0/aMTOYjGvJ89bpuoZP56/n9Jtc/35Bx5sqtRi+ECCCkWvPV1nHMQC7MWtlMtmAfhuZm4Bgw==", + "license": "MIT", + "dependencies": { + "@trustgraph/react-provider": "^1.4.0", + "compute-cosine-similarity": "^1.1.0", + "uuid": "^11.0.3" + }, + "peerDependencies": { + "@tanstack/react-query": "^5.0.0", + "react": "^18.0.0 || ^19.0.0", + "zustand": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz", + "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-rc.3", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.8", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.8.tgz", + "integrity": "sha512-PCLz/LXGBsNTErbtB6i5u4eLpHeMfi93aUv5duMmj6caNu6IphS4q6UevDnL36sZQv9lrP11dbPKGMaXPwMKfQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001780", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001780.tgz", + "integrity": "sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/compute-cosine-similarity": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/compute-cosine-similarity/-/compute-cosine-similarity-1.1.0.tgz", + "integrity": "sha512-FXhNx0ILLjGi9Z9+lglLzM12+0uoTnYkHm7GiadXDAr0HGVLm25OivUS1B/LPkbzzvlcXz/1EvWg9ZYyJSdhTw==", + "dependencies": { + "compute-dot": "^1.1.0", + "compute-l2norm": "^1.1.0", + "validate.io-array": "^1.0.5", + "validate.io-function": "^1.0.2" + } + }, + "node_modules/compute-dot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/compute-dot/-/compute-dot-1.1.0.tgz", + "integrity": "sha512-L5Ocet4DdMrXboss13K59OK23GXjiSia7+7Ukc7q4Bl+RVpIXK2W9IHMbWDZkh+JUEvJAwOKRaJDiFUa1LTnJg==", + "dependencies": { + "validate.io-array": "^1.0.3", + "validate.io-function": "^1.0.2" + } + }, + "node_modules/compute-l2norm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/compute-l2norm/-/compute-l2norm-1.1.0.tgz", + "integrity": "sha512-6EHh1Elj90eU28SXi+h2PLnTQvZmkkHWySpoFz+WOlVNLz3DQoC4ISUHSV9n5jMxPHtKGJ01F4uu2PsXBB8sSg==", + "dependencies": { + "validate.io-array": "^1.0.3", + "validate.io-function": "^1.0.2" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.321", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.321.tgz", + "integrity": "sha512-L2C7Q279W2D/J4PLZLk7sebOILDSWos7bMsMNN06rK482umHUrh/3lM8G7IlHFOYip2oAg5nha1rCMxr/rs6ZQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz", + "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.4", + "@esbuild/android-arm": "0.27.4", + "@esbuild/android-arm64": "0.27.4", + "@esbuild/android-x64": "0.27.4", + "@esbuild/darwin-arm64": "0.27.4", + "@esbuild/darwin-x64": "0.27.4", + "@esbuild/freebsd-arm64": "0.27.4", + "@esbuild/freebsd-x64": "0.27.4", + "@esbuild/linux-arm": "0.27.4", + "@esbuild/linux-arm64": "0.27.4", + "@esbuild/linux-ia32": "0.27.4", + "@esbuild/linux-loong64": "0.27.4", + "@esbuild/linux-mips64el": "0.27.4", + "@esbuild/linux-ppc64": "0.27.4", + "@esbuild/linux-riscv64": "0.27.4", + "@esbuild/linux-s390x": "0.27.4", + "@esbuild/linux-x64": "0.27.4", + "@esbuild/netbsd-arm64": "0.27.4", + "@esbuild/netbsd-x64": "0.27.4", + "@esbuild/openbsd-arm64": "0.27.4", + "@esbuild/openbsd-x64": "0.27.4", + "@esbuild/openharmony-arm64": "0.27.4", + "@esbuild/sunos-x64": "0.27.4", + "@esbuild/win32-arm64": "0.27.4", + "@esbuild/win32-ia32": "0.27.4", + "@esbuild/win32-x64": "0.27.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", + "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.26", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz", + "integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.36", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", + "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rollup": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/validate.io-array": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/validate.io-array/-/validate.io-array-1.0.6.tgz", + "integrity": "sha512-DeOy7CnPEziggrOO5CZhVKJw6S3Yi7e9e65R1Nl/RTN1vTQKnzjfvks0/8kQ40FP/dsjRAOd4hxmJ7uLa6vxkg==", + "license": "MIT" + }, + "node_modules/validate.io-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/validate.io-function/-/validate.io-function-1.0.2.tgz", + "integrity": "sha512-LlFybRJEriSuBnUhQyG5bwglhh50EpTL2ul23MPIuR1odjO7XaMLFV8vHGwp7AZciFxtYOeiSCT5st+XSPONiQ==" + }, + "node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + }, + "node_modules/zustand": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.12.tgz", + "integrity": "sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + } + } +} diff --git a/ai-context/context-graph-demo/package.json b/ai-context/context-graph-demo/package.json new file mode 100644 index 00000000..cc484076 --- /dev/null +++ b/ai-context/context-graph-demo/package.json @@ -0,0 +1,31 @@ +{ + "name": "retail-intelligence-demo", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@tanstack/react-query": "^5.90.21", + "@trustgraph/react-provider": "^1.4.0", + "@trustgraph/react-state": "^1.4.6", + "react": "^19.2.0", + "react-dom": "^19.2.0" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.1", + "eslint": "^9.39.1", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.4.24", + "globals": "^16.5.0", + "typescript": "~5.9.3", + "vite": "^7.3.1" + } +} diff --git a/ai-context/context-graph-demo/public/tg.svg b/ai-context/context-graph-demo/public/tg.svg new file mode 100644 index 00000000..7123ae45 --- /dev/null +++ b/ai-context/context-graph-demo/public/tg.svg @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/ai-context/context-graph-demo/retail.json b/ai-context/context-graph-demo/retail.json new file mode 100644 index 00000000..565ab45c --- /dev/null +++ b/ai-context/context-graph-demo/retail.json @@ -0,0 +1 @@ +{"metadata":{"name":"TrustGraph Retail Intelligence Ontology","description":"Ontology for retail ecosystem modeling: consumers, brands, retail channels, and AI agents","version":"1.0","created":"2026-02-25T13:38:28.636Z","modified":"2026-02-25T13:38:28.636Z","creator":"","namespace":"http://trustgraph.ai/retail#","namespaces":{"owl":"http://www.w3.org/2002/07/owl#","rdf":"http://www.w3.org/1999/02/22-rdf-syntax-ns#","rdfs":"http://www.w3.org/2000/01/rdf-schema#","xsd":"http://www.w3.org/2001/XMLSchema#","":"http://trustgraph.ai/retail#"}},"classes":{"retail#Consumer":{"uri":"http://trustgraph.ai/retail#Consumer","type":"owl:Class","rdfs:label":[{"value":"Consumer","lang":"en"}],"rdfs:comment":"Individuals and segments interacting with brands through retail channels"},"retail#Brand":{"uri":"http://trustgraph.ai/retail#Brand","type":"owl:Class","rdfs:label":[{"value":"Brand","lang":"en"}],"rdfs:comment":"Product brands seeking to connect with consumers through retail experiences"},"retail#Retail":{"uri":"http://trustgraph.ai/retail#Retail","type":"owl:Class","rdfs:label":[{"value":"Retail","lang":"en"}],"rdfs:comment":"Channels, touchpoints, and experiences where brands meet consumers"},"retail#Agent":{"uri":"http://trustgraph.ai/retail#Agent","type":"owl:Class","rdfs:label":[{"value":"Agent","lang":"en"}],"rdfs:comment":"AI agents that orchestrate personalized brand-consumer connections"}},"objectProperties":{"retail#hasAffinityFor":{"uri":"http://trustgraph.ai/retail#hasAffinityFor","type":"owl:ObjectProperty","rdfs:label":[{"value":"has affinity for","lang":"en"}],"rdfs:domain":"retail#Consumer","rdfs:range":"retail#Brand"},"retail#frequents":{"uri":"http://trustgraph.ai/retail#frequents","type":"owl:ObjectProperty","rdfs:label":[{"value":"frequents","lang":"en"}],"rdfs:domain":"retail#Consumer","rdfs:range":"retail#Brand"},"retail#purchasesFrom":{"uri":"http://trustgraph.ai/retail#purchasesFrom","type":"owl:ObjectProperty","rdfs:label":[{"value":"purchases from","lang":"en"}],"rdfs:domain":"retail#Consumer","rdfs:range":"retail#Brand"},"retail#advocatesFor":{"uri":"http://trustgraph.ai/retail#advocatesFor","type":"owl:ObjectProperty","rdfs:label":[{"value":"advocates for","lang":"en"}],"rdfs:domain":"retail#Consumer","rdfs:range":"retail#Brand"},"retail#loyalTo":{"uri":"http://trustgraph.ai/retail#loyalTo","type":"owl:ObjectProperty","rdfs:label":[{"value":"loyal to","lang":"en"}],"rdfs:domain":"retail#Consumer","rdfs:range":"retail#Brand"},"retail#shopsVia":{"uri":"http://trustgraph.ai/retail#shopsVia","type":"owl:ObjectProperty","rdfs:label":[{"value":"shops via","lang":"en"}],"rdfs:domain":"retail#Consumer","rdfs:range":"retail#Retail"},"retail#discoversThrough":{"uri":"http://trustgraph.ai/retail#discoversThrough","type":"owl:ObjectProperty","rdfs:label":[{"value":"discovers through","lang":"en"}],"rdfs:domain":"retail#Consumer","rdfs:range":"retail#Retail"},"retail#experiences":{"uri":"http://trustgraph.ai/retail#experiences","type":"owl:ObjectProperty","rdfs:label":[{"value":"experiences","lang":"en"}],"rdfs:domain":"retail#Consumer","rdfs:range":"retail#Retail"},"retail#memberOf":{"uri":"http://trustgraph.ai/retail#memberOf","type":"owl:ObjectProperty","rdfs:label":[{"value":"member of","lang":"en"}],"rdfs:domain":"retail#Consumer","rdfs:range":"retail#Retail"},"retail#merchandisesIn":{"uri":"http://trustgraph.ai/retail#merchandisesIn","type":"owl:ObjectProperty","rdfs:label":[{"value":"merchandises in","lang":"en"}],"rdfs:domain":"retail#Brand","rdfs:range":"retail#Retail"},"retail#activatesVia":{"uri":"http://trustgraph.ai/retail#activatesVia","type":"owl:ObjectProperty","rdfs:label":[{"value":"activates via","lang":"en"}],"rdfs:domain":"retail#Brand","rdfs:range":"retail#Retail"},"retail#promotesOn":{"uri":"http://trustgraph.ai/retail#promotesOn","type":"owl:ObjectProperty","rdfs:label":[{"value":"promotes on","lang":"en"}],"rdfs:domain":"retail#Brand","rdfs:range":"retail#Retail"},"retail#sellsThrough":{"uri":"http://trustgraph.ai/retail#sellsThrough","type":"owl:ObjectProperty","rdfs:label":[{"value":"sells through","lang":"en"}],"rdfs:domain":"retail#Brand","rdfs:range":"retail#Retail"},"retail#rewardsVia":{"uri":"http://trustgraph.ai/retail#rewardsVia","type":"owl:ObjectProperty","rdfs:label":[{"value":"rewards via","lang":"en"}],"rdfs:domain":"retail#Brand","rdfs:range":"retail#Retail"},"retail#recommendsTo":{"uri":"http://trustgraph.ai/retail#recommendsTo","type":"owl:ObjectProperty","rdfs:label":[{"value":"recommends to","lang":"en"}],"rdfs:domain":"retail#Agent","rdfs:range":"retail#Consumer"},"retail#personalizesFor":{"uri":"http://trustgraph.ai/retail#personalizesFor","type":"owl:ObjectProperty","rdfs:label":[{"value":"personalizes for","lang":"en"}],"rdfs:domain":"retail#Agent","rdfs:range":"retail#Consumer"},"retail#monitorsSentimentOf":{"uri":"http://trustgraph.ai/retail#monitorsSentimentOf","type":"owl:ObjectProperty","rdfs:label":[{"value":"monitors sentiment of","lang":"en"}],"rdfs:domain":"retail#Agent","rdfs:range":"retail#Consumer"},"retail#optimizesJourneyFor":{"uri":"http://trustgraph.ai/retail#optimizesJourneyFor","type":"owl:ObjectProperty","rdfs:label":[{"value":"optimizes journey for","lang":"en"}],"rdfs:domain":"retail#Agent","rdfs:range":"retail#Consumer"},"retail#orchestratesCampaignFor":{"uri":"http://trustgraph.ai/retail#orchestratesCampaignFor","type":"owl:ObjectProperty","rdfs:label":[{"value":"orchestrates campaign for","lang":"en"}],"rdfs:domain":"retail#Agent","rdfs:range":"retail#Brand"},"retail#analyzesPerceptionOf":{"uri":"http://trustgraph.ai/retail#analyzesPerceptionOf","type":"owl:ObjectProperty","rdfs:label":[{"value":"analyzes perception of","lang":"en"}],"rdfs:domain":"retail#Agent","rdfs:range":"retail#Brand"},"retail#curatesProductsFor":{"uri":"http://trustgraph.ai/retail#curatesProductsFor","type":"owl:ObjectProperty","rdfs:label":[{"value":"curates products for","lang":"en"}],"rdfs:domain":"retail#Agent","rdfs:range":"retail#Brand"},"retail#tailorsExperienceAt":{"uri":"http://trustgraph.ai/retail#tailorsExperienceAt","type":"owl:ObjectProperty","rdfs:label":[{"value":"tailors experience at","lang":"en"}],"rdfs:domain":"retail#Agent","rdfs:range":"retail#Retail"},"retail#deploysCampaignAt":{"uri":"http://trustgraph.ai/retail#deploysCampaignAt","type":"owl:ObjectProperty","rdfs:label":[{"value":"deploys campaign at","lang":"en"}],"rdfs:domain":"retail#Agent","rdfs:range":"retail#Retail"},"retail#optimizesFlowAt":{"uri":"http://trustgraph.ai/retail#optimizesFlowAt","type":"owl:ObjectProperty","rdfs:label":[{"value":"optimizes flow at","lang":"en"}],"rdfs:domain":"retail#Agent","rdfs:range":"retail#Retail"}},"datatypeProperties":{"retail#segment":{"uri":"http://trustgraph.ai/retail#segment","type":"owl:DatatypeProperty","rdfs:range":"xsd:string","rdfs:label":[{"value":"segment","lang":"en"}],"rdfs:domain":"retail#Consumer"},"retail#preferences":{"uri":"http://trustgraph.ai/retail#preferences","type":"owl:DatatypeProperty","rdfs:range":"xsd:string","rdfs:label":[{"value":"preferences","lang":"en"}],"rdfs:domain":"retail#Consumer"},"retail#journeyStage":{"uri":"http://trustgraph.ai/retail#journeyStage","type":"owl:DatatypeProperty","rdfs:range":"xsd:string","rdfs:label":[{"value":"journey stage","lang":"en"}],"rdfs:domain":"retail#Consumer"},"retail#lifetimeValue":{"uri":"http://trustgraph.ai/retail#lifetimeValue","type":"owl:DatatypeProperty","rdfs:range":"xsd:decimal","rdfs:label":[{"value":"lifetime value","lang":"en"}],"rdfs:domain":"retail#Consumer"},"retail#sentiment":{"uri":"http://trustgraph.ai/retail#sentiment","type":"owl:DatatypeProperty","rdfs:range":"xsd:decimal","rdfs:label":[{"value":"sentiment","lang":"en"}],"rdfs:domain":"retail#Consumer"},"retail#size":{"uri":"http://trustgraph.ai/retail#size","type":"owl:DatatypeProperty","rdfs:range":"xsd:string","rdfs:label":[{"value":"size","lang":"en"}],"rdfs:domain":"retail#Consumer"},"retail#avgSpend":{"uri":"http://trustgraph.ai/retail#avgSpend","type":"owl:DatatypeProperty","rdfs:range":"xsd:string","rdfs:label":[{"value":"average spend","lang":"en"}],"rdfs:domain":"retail#Consumer"},"retail#loyalty":{"uri":"http://trustgraph.ai/retail#loyalty","type":"owl:DatatypeProperty","rdfs:range":"xsd:decimal","rdfs:label":[{"value":"loyalty","lang":"en"}],"rdfs:domain":"retail#Consumer"},"retail#identity":{"uri":"http://trustgraph.ai/retail#identity","type":"owl:DatatypeProperty","rdfs:range":"xsd:string","rdfs:label":[{"value":"identity","lang":"en"}],"rdfs:domain":"retail#Brand"},"retail#positioning":{"uri":"http://trustgraph.ai/retail#positioning","type":"owl:DatatypeProperty","rdfs:range":"xsd:string","rdfs:label":[{"value":"positioning","lang":"en"}],"rdfs:domain":"retail#Brand"},"retail#campaigns":{"uri":"http://trustgraph.ai/retail#campaigns","type":"owl:DatatypeProperty","rdfs:range":"xsd:integer","rdfs:label":[{"value":"campaigns","lang":"en"}],"rdfs:domain":"retail#Brand"},"retail#products":{"uri":"http://trustgraph.ai/retail#products","type":"owl:DatatypeProperty","rdfs:range":"xsd:string","rdfs:label":[{"value":"products","lang":"en"}],"rdfs:domain":"retail#Brand"},"retail#partnerships":{"uri":"http://trustgraph.ai/retail#partnerships","type":"owl:DatatypeProperty","rdfs:range":"xsd:string","rdfs:label":[{"value":"partnerships","lang":"en"}],"rdfs:domain":"retail#Brand"},"retail#category":{"uri":"http://trustgraph.ai/retail#category","type":"owl:DatatypeProperty","rdfs:range":"xsd:string","rdfs:label":[{"value":"category","lang":"en"}],"rdfs:domain":"retail#Brand"},"retail#channel":{"uri":"http://trustgraph.ai/retail#channel","type":"owl:DatatypeProperty","rdfs:range":"xsd:string","rdfs:label":[{"value":"channel","lang":"en"}],"rdfs:domain":"retail#Retail"},"retail#location":{"uri":"http://trustgraph.ai/retail#location","type":"owl:DatatypeProperty","rdfs:range":"xsd:string","rdfs:label":[{"value":"location","lang":"en"}],"rdfs:domain":"retail#Retail"},"retail#traffic":{"uri":"http://trustgraph.ai/retail#traffic","type":"owl:DatatypeProperty","rdfs:range":"xsd:string","rdfs:label":[{"value":"traffic","lang":"en"}],"rdfs:domain":"retail#Retail"},"retail#conversionRate":{"uri":"http://trustgraph.ai/retail#conversionRate","type":"owl:DatatypeProperty","rdfs:range":"xsd:string","rdfs:label":[{"value":"conversion rate","lang":"en"}],"rdfs:domain":"retail#Retail"},"retail#experienceScore":{"uri":"http://trustgraph.ai/retail#experienceScore","type":"owl:DatatypeProperty","rdfs:range":"xsd:decimal","rdfs:label":[{"value":"experience score","lang":"en"}],"rdfs:domain":"retail#Retail"},"retail#capability":{"uri":"http://trustgraph.ai/retail#capability","type":"owl:DatatypeProperty","rdfs:range":"xsd:string","rdfs:label":[{"value":"capability","lang":"en"}],"rdfs:domain":"retail#Agent"},"retail#contextSources":{"uri":"http://trustgraph.ai/retail#contextSources","type":"owl:DatatypeProperty","rdfs:range":"xsd:string","rdfs:label":[{"value":"context sources","lang":"en"}],"rdfs:domain":"retail#Agent"},"retail#accuracy":{"uri":"http://trustgraph.ai/retail#accuracy","type":"owl:DatatypeProperty","rdfs:range":"xsd:string","rdfs:label":[{"value":"accuracy","lang":"en"}],"rdfs:domain":"retail#Agent"},"retail#latency":{"uri":"http://trustgraph.ai/retail#latency","type":"owl:DatatypeProperty","rdfs:range":"xsd:string","rdfs:label":[{"value":"latency","lang":"en"}],"rdfs:domain":"retail#Agent"},"retail#decisionsPerDay":{"uri":"http://trustgraph.ai/retail#decisionsPerDay","type":"owl:DatatypeProperty","rdfs:range":"xsd:string","rdfs:label":[{"value":"decisions per day","lang":"en"}],"rdfs:domain":"retail#Agent"}}} diff --git a/ai-context/context-graph-demo/retail.ttl b/ai-context/context-graph-demo/retail.ttl new file mode 100644 index 00000000..165f3568 --- /dev/null +++ b/ai-context/context-graph-demo/retail.ttl @@ -0,0 +1,310 @@ +@prefix owl: . +@prefix rdf: . +@prefix rdfs: . +@prefix xsd: . +@prefix : . + +# Ontology declaration + a owl:Ontology ; + rdfs:label "TrustGraph Retail Intelligence Ontology" ; + rdfs:comment "Ontology for retail ecosystem modeling: consumers, brands, retail channels, and AI agents" . + +# ============================================================================= +# Classes +# ============================================================================= + +:Consumer a owl:Class ; + rdfs:label "Consumer" ; + rdfs:comment "Individuals and segments interacting with brands through retail channels" . + +:Brand a owl:Class ; + rdfs:label "Brand" ; + rdfs:comment "Product brands seeking to connect with consumers through retail experiences" . + +:Retail a owl:Class ; + rdfs:label "Retail" ; + rdfs:comment "Channels, touchpoints, and experiences where brands meet consumers" . + +:Agent a owl:Class ; + rdfs:label "Agent" ; + rdfs:comment "AI agents that orchestrate personalized brand-consumer connections" . + +# ============================================================================= +# Datatype Properties - Consumer +# ============================================================================= + +:segment a owl:DatatypeProperty ; + rdfs:label "segment" ; + rdfs:domain :Consumer ; + rdfs:range xsd:string . + +:preferences a owl:DatatypeProperty ; + rdfs:label "preferences" ; + rdfs:domain :Consumer ; + rdfs:range xsd:string . + +:journeyStage a owl:DatatypeProperty ; + rdfs:label "journey stage" ; + rdfs:domain :Consumer ; + rdfs:range xsd:string . + +:lifetimeValue a owl:DatatypeProperty ; + rdfs:label "lifetime value" ; + rdfs:domain :Consumer ; + rdfs:range xsd:decimal . + +:sentiment a owl:DatatypeProperty ; + rdfs:label "sentiment" ; + rdfs:domain :Consumer ; + rdfs:range xsd:decimal . + +:size a owl:DatatypeProperty ; + rdfs:label "size" ; + rdfs:domain :Consumer ; + rdfs:range xsd:string . + +:avgSpend a owl:DatatypeProperty ; + rdfs:label "average spend" ; + rdfs:domain :Consumer ; + rdfs:range xsd:string . + +:loyalty a owl:DatatypeProperty ; + rdfs:label "loyalty" ; + rdfs:domain :Consumer ; + rdfs:range xsd:decimal . + +# ============================================================================= +# Datatype Properties - Brand +# ============================================================================= + +:identity a owl:DatatypeProperty ; + rdfs:label "identity" ; + rdfs:domain :Brand ; + rdfs:range xsd:string . + +:positioning a owl:DatatypeProperty ; + rdfs:label "positioning" ; + rdfs:domain :Brand ; + rdfs:range xsd:string . + +:campaigns a owl:DatatypeProperty ; + rdfs:label "campaigns" ; + rdfs:domain :Brand ; + rdfs:range xsd:integer . + +:products a owl:DatatypeProperty ; + rdfs:label "products" ; + rdfs:domain :Brand ; + rdfs:range xsd:string . + +:partnerships a owl:DatatypeProperty ; + rdfs:label "partnerships" ; + rdfs:domain :Brand ; + rdfs:range xsd:string . + +:category a owl:DatatypeProperty ; + rdfs:label "category" ; + rdfs:domain :Brand ; + rdfs:range xsd:string . + +# ============================================================================= +# Datatype Properties - Retail +# ============================================================================= + +:channel a owl:DatatypeProperty ; + rdfs:label "channel" ; + rdfs:domain :Retail ; + rdfs:range xsd:string . + +:location a owl:DatatypeProperty ; + rdfs:label "location" ; + rdfs:domain :Retail ; + rdfs:range xsd:string . + +:traffic a owl:DatatypeProperty ; + rdfs:label "traffic" ; + rdfs:domain :Retail ; + rdfs:range xsd:string . + +:conversionRate a owl:DatatypeProperty ; + rdfs:label "conversion rate" ; + rdfs:domain :Retail ; + rdfs:range xsd:string . + +:experienceScore a owl:DatatypeProperty ; + rdfs:label "experience score" ; + rdfs:domain :Retail ; + rdfs:range xsd:decimal . + +# ============================================================================= +# Datatype Properties - Agent +# ============================================================================= + +:capability a owl:DatatypeProperty ; + rdfs:label "capability" ; + rdfs:domain :Agent ; + rdfs:range xsd:string . + +:contextSources a owl:DatatypeProperty ; + rdfs:label "context sources" ; + rdfs:domain :Agent ; + rdfs:range xsd:string . + +:accuracy a owl:DatatypeProperty ; + rdfs:label "accuracy" ; + rdfs:domain :Agent ; + rdfs:range xsd:string . + +:latency a owl:DatatypeProperty ; + rdfs:label "latency" ; + rdfs:domain :Agent ; + rdfs:range xsd:string . + +:decisionsPerDay a owl:DatatypeProperty ; + rdfs:label "decisions per day" ; + rdfs:domain :Agent ; + rdfs:range xsd:string . + +# ============================================================================= +# Object Properties - Consumer <-> Brand +# ============================================================================= + +:hasAffinityFor a owl:ObjectProperty ; + rdfs:label "has affinity for" ; + rdfs:domain :Consumer ; + rdfs:range :Brand . + +:frequents a owl:ObjectProperty ; + rdfs:label "frequents" ; + rdfs:domain :Consumer ; + rdfs:range :Brand . + +:purchasesFrom a owl:ObjectProperty ; + rdfs:label "purchases from" ; + rdfs:domain :Consumer ; + rdfs:range :Brand . + +:advocatesFor a owl:ObjectProperty ; + rdfs:label "advocates for" ; + rdfs:domain :Consumer ; + rdfs:range :Brand . + +:loyalTo a owl:ObjectProperty ; + rdfs:label "loyal to" ; + rdfs:domain :Consumer ; + rdfs:range :Brand . + +# ============================================================================= +# Object Properties - Consumer <-> Retail +# ============================================================================= + +:shopsVia a owl:ObjectProperty ; + rdfs:label "shops via" ; + rdfs:domain :Consumer ; + rdfs:range :Retail . + +:discoversThrough a owl:ObjectProperty ; + rdfs:label "discovers through" ; + rdfs:domain :Consumer ; + rdfs:range :Retail . + +:experiences a owl:ObjectProperty ; + rdfs:label "experiences" ; + rdfs:domain :Consumer ; + rdfs:range :Retail . + +:memberOf a owl:ObjectProperty ; + rdfs:label "member of" ; + rdfs:domain :Consumer ; + rdfs:range :Retail . + +# ============================================================================= +# Object Properties - Brand <-> Retail +# ============================================================================= + +:merchandisesIn a owl:ObjectProperty ; + rdfs:label "merchandises in" ; + rdfs:domain :Brand ; + rdfs:range :Retail . + +:activatesVia a owl:ObjectProperty ; + rdfs:label "activates via" ; + rdfs:domain :Brand ; + rdfs:range :Retail . + +:promotesOn a owl:ObjectProperty ; + rdfs:label "promotes on" ; + rdfs:domain :Brand ; + rdfs:range :Retail . + +:sellsThrough a owl:ObjectProperty ; + rdfs:label "sells through" ; + rdfs:domain :Brand ; + rdfs:range :Retail . + +:rewardsVia a owl:ObjectProperty ; + rdfs:label "rewards via" ; + rdfs:domain :Brand ; + rdfs:range :Retail . + +# ============================================================================= +# Object Properties - Agent <-> Consumer +# ============================================================================= + +:recommendsTo a owl:ObjectProperty ; + rdfs:label "recommends to" ; + rdfs:domain :Agent ; + rdfs:range :Consumer . + +:personalizesFor a owl:ObjectProperty ; + rdfs:label "personalizes for" ; + rdfs:domain :Agent ; + rdfs:range :Consumer . + +:monitorsSentimentOf a owl:ObjectProperty ; + rdfs:label "monitors sentiment of" ; + rdfs:domain :Agent ; + rdfs:range :Consumer . + +:optimizesJourneyFor a owl:ObjectProperty ; + rdfs:label "optimizes journey for" ; + rdfs:domain :Agent ; + rdfs:range :Consumer . + +# ============================================================================= +# Object Properties - Agent <-> Brand +# ============================================================================= + +:orchestratesCampaignFor a owl:ObjectProperty ; + rdfs:label "orchestrates campaign for" ; + rdfs:domain :Agent ; + rdfs:range :Brand . + +:analyzesPerceptionOf a owl:ObjectProperty ; + rdfs:label "analyzes perception of" ; + rdfs:domain :Agent ; + rdfs:range :Brand . + +:curatesProductsFor a owl:ObjectProperty ; + rdfs:label "curates products for" ; + rdfs:domain :Agent ; + rdfs:range :Brand . + +# ============================================================================= +# Object Properties - Agent <-> Retail +# ============================================================================= + +:tailorsExperienceAt a owl:ObjectProperty ; + rdfs:label "tailors experience at" ; + rdfs:domain :Agent ; + rdfs:range :Retail . + +:deploysCampaignAt a owl:ObjectProperty ; + rdfs:label "deploys campaign at" ; + rdfs:domain :Agent ; + rdfs:range :Retail . + +:optimizesFlowAt a owl:ObjectProperty ; + rdfs:label "optimizes flow at" ; + rdfs:domain :Agent ; + rdfs:range :Retail . diff --git a/ai-context/context-graph-demo/src/App.tsx b/ai-context/context-graph-demo/src/App.tsx new file mode 100644 index 00000000..891f8fb1 --- /dev/null +++ b/ai-context/context-graph-demo/src/App.tsx @@ -0,0 +1,56 @@ +import { useState, useEffect } from "react"; +import type { TabKey, DomainKey, Entity } from "./types"; +import { Header, StatusBar, Toaster } from "./components"; +import { GraphView, QueryView, ExplainView, DataView, OntologyView } from "./pages"; +import { useGraphData, toast } from "./state"; + +export default function App() { + const [activeTab, setActiveTab] = useState("graph"); + const [activeFilter, setActiveFilter] = useState(null); + const [selectedNode, setSelectedNode] = useState(null); + const { entities, isLoading } = useGraphData(); + + // Notification when graph loads + useEffect(() => { + if (!isLoading && entities.length > 0) { + toast.success(`Graph loaded: ${entities.length} entities`); + } + }, [isLoading, entities.length]); + + const handleTabChange = (tab: TabKey) => { + setActiveTab(tab); + if (tab !== "graph") { + setSelectedNode(null); + } + }; + + return ( +
+
+ + {activeTab === "graph" && ( + + )} + + {activeTab === "query" && } + + {activeTab === "explain" && } + + {activeTab === "data" && } + + {activeTab === "ontology" && } + + + +
+ ); +} diff --git a/ai-context/context-graph-demo/src/assets/react.svg b/ai-context/context-graph-demo/src/assets/react.svg new file mode 100644 index 00000000..6c87de9b --- /dev/null +++ b/ai-context/context-graph-demo/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ai-context/context-graph-demo/src/components/common/Badge.tsx b/ai-context/context-graph-demo/src/components/common/Badge.tsx new file mode 100644 index 00000000..d5a0836e --- /dev/null +++ b/ai-context/context-graph-demo/src/components/common/Badge.tsx @@ -0,0 +1,39 @@ +interface BadgeProps { + children: React.ReactNode; + color: string; + size?: "small" | "medium"; + selected?: boolean; + onClick?: () => void; +} + +export function Badge({ + children, + color, + size = "medium", + selected = false, + onClick, +}: BadgeProps) { + const isSmall = size === "small"; + + return ( + + ); +} diff --git a/ai-context/context-graph-demo/src/components/common/Card.tsx b/ai-context/context-graph-demo/src/components/common/Card.tsx new file mode 100644 index 00000000..89b17156 --- /dev/null +++ b/ai-context/context-graph-demo/src/components/common/Card.tsx @@ -0,0 +1,35 @@ +import { surface, border } from "../../theme"; + +interface CardProps { + children: React.ReactNode; + padding?: number | string; + borderRadius?: number; + borderColor?: string; + onClick?: () => void; + style?: React.CSSProperties; +} + +export function Card({ + children, + padding = 24, + borderRadius = 12, + borderColor = border.subtle, + onClick, + style, +}: CardProps) { + return ( +
+ {children} +
+ ); +} diff --git a/ai-context/context-graph-demo/src/components/common/FilterBar.tsx b/ai-context/context-graph-demo/src/components/common/FilterBar.tsx new file mode 100644 index 00000000..01a7f63c --- /dev/null +++ b/ai-context/context-graph-demo/src/components/common/FilterBar.tsx @@ -0,0 +1,78 @@ +import { FilterButton } from "./FilterButton"; +import { text, border } from "../../theme"; + +export interface FilterItem { + key: string; + label: string; + icon?: string; + color?: string; +} + +interface FilterBarProps { + items: FilterItem[]; + selectedKey: string | null; + onSelect: (key: string | null) => void; + stats?: string; + showAll?: boolean; + allLabel?: string; + emptyMessage?: string; + maxItems?: number; +} + +export function FilterBar({ + items, + selectedKey, + onSelect, + stats, + showAll = true, + allLabel = "All", + emptyMessage, + maxItems = 10, +}: FilterBarProps) { + const displayItems = items.slice(0, maxItems); + + return ( +
+ + FILTER: + + + {emptyMessage && items.length === 0 ? ( + {emptyMessage} + ) : ( + <> + {showAll && ( + onSelect(null)} + /> + )} + {displayItems.map((item) => ( + onSelect(selectedKey === item.key ? null : item.key)} + /> + ))} + + )} + + {stats && ( +
+ {stats} +
+ )} +
+ ); +} diff --git a/ai-context/context-graph-demo/src/components/common/FilterButton.tsx b/ai-context/context-graph-demo/src/components/common/FilterButton.tsx new file mode 100644 index 00000000..7b0ea0a9 --- /dev/null +++ b/ai-context/context-graph-demo/src/components/common/FilterButton.tsx @@ -0,0 +1,31 @@ +import { text, border } from "../../theme"; + +interface FilterButtonProps { + label: string; + icon?: string; + color?: string; + isActive: boolean; + onClick: () => void; +} + +export function FilterButton({ label, icon, color, isActive, onClick }: FilterButtonProps) { + const activeColor = color || "#fff"; + + return ( + + ); +} diff --git a/ai-context/context-graph-demo/src/components/common/Header.tsx b/ai-context/context-graph-demo/src/components/common/Header.tsx new file mode 100644 index 00000000..bcb968a9 --- /dev/null +++ b/ai-context/context-graph-demo/src/components/common/Header.tsx @@ -0,0 +1,58 @@ +import type { TabKey } from "../../types"; + +interface HeaderProps { + activeTab: TabKey; + onTabChange: (tab: TabKey) => void; +} + +export function Header({ activeTab, onTabChange }: HeaderProps) { + return ( +
+
+ TrustGraph +
+
+ TrustGraph +
+
+ CONTEXT GRAPH DEMO +
+
+
+
+ {(["graph", "query", "explain", "data", "ontology"] as const).map((tab) => { + const labels: Record = { + graph: "◈ Context Graph", + query: "⚡ Agent Query", + explain: "◉ Explain", + data: "▤ Table Explorer", + ontology: "◇ Ontology", + }; + return ( + + ); + })} +
+
+ ); +} diff --git a/ai-context/context-graph-demo/src/components/common/LoadingState.tsx b/ai-context/context-graph-demo/src/components/common/LoadingState.tsx new file mode 100644 index 00000000..2f129d3d --- /dev/null +++ b/ai-context/context-graph-demo/src/components/common/LoadingState.tsx @@ -0,0 +1,23 @@ +import { semantic, text } from "../../theme"; + +interface LoadingStateProps { + message?: string; + variant?: "loading" | "error"; +} + +export function LoadingState({ message, variant = "loading" }: LoadingStateProps) { + const isError = variant === "error"; + const defaultMessage = isError ? "Error loading data" : "Loading..."; + + return ( +
+ {message || defaultMessage} +
+ ); +} diff --git a/ai-context/context-graph-demo/src/components/common/MessageBubble.tsx b/ai-context/context-graph-demo/src/components/common/MessageBubble.tsx new file mode 100644 index 00000000..bccb3c13 --- /dev/null +++ b/ai-context/context-graph-demo/src/components/common/MessageBubble.tsx @@ -0,0 +1,97 @@ +import { semantic, text, surface, border, withGlow } from "../../theme"; + +export interface Message { + role: string; + text: string; + type?: string; +} + +interface MessageBubbleProps { + message: Message; +} + +export function MessageBubble({ message }: MessageBubbleProps) { + const isUser = message.role === "human"; + const messageType = message.type; + + const getTypeStyles = () => { + switch (messageType) { + case "thinking": + return { + bg: withGlow(semantic.thinking, 0.08), + border: withGlow(semantic.thinking, 0.2), + icon: "◈", + label: "THINKING", + color: semantic.thinking, + }; + case "observation": + return { + bg: withGlow(semantic.observation, 0.08), + border: withGlow(semantic.observation, 0.2), + icon: "◉", + label: "OBSERVATION", + color: semantic.observation, + }; + case "answer": + return { + bg: withGlow(semantic.answer, 0.08), + border: withGlow(semantic.answer, 0.2), + icon: "✓", + label: "ANSWER", + color: semantic.answer, + }; + default: + return null; + } + }; + + const typeStyles = getTypeStyles(); + + if (isUser) { + return ( +
+
+ YOU +
+
+ {message.text} +
+
+ ); + } + + return ( +
+ {typeStyles && ( +
+ {typeStyles.icon} + {typeStyles.label} +
+ )} +
+ {message.text} +
+
+ ); +} diff --git a/ai-context/context-graph-demo/src/components/common/SearchInput.tsx b/ai-context/context-graph-demo/src/components/common/SearchInput.tsx new file mode 100644 index 00000000..d822910e --- /dev/null +++ b/ai-context/context-graph-demo/src/components/common/SearchInput.tsx @@ -0,0 +1,73 @@ +import { text, surface, border, palette } from "../../theme"; + +interface SearchInputProps { + value: string; + onChange: (value: string) => void; + onSubmit: () => void; + placeholder?: string; + buttonText?: string; + isLoading?: boolean; + buttonColor?: string; + disabled?: boolean; +} + +export function SearchInput({ + value, + onChange, + onSubmit, + placeholder = "Search...", + buttonText = "Search", + isLoading = false, + buttonColor = palette.blue, + disabled = false, +}: SearchInputProps) { + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === "Enter" && !e.shiftKey) { + e.preventDefault(); + onSubmit(); + } + }; + + const isDisabled = disabled || isLoading || !value.trim(); + + return ( +
+ onChange(e.target.value)} + onKeyDown={handleKeyDown} + placeholder={placeholder} + disabled={isLoading} + style={{ + flex: 1, + padding: "12px 16px", + borderRadius: 8, + border: `1px solid ${border.medium}`, + background: surface.card, + color: text.primary, + fontSize: 14, + fontFamily: "'IBM Plex Sans', sans-serif", + outline: "none", + }} + /> + +
+ ); +} diff --git a/ai-context/context-graph-demo/src/components/common/SectionLabel.tsx b/ai-context/context-graph-demo/src/components/common/SectionLabel.tsx new file mode 100644 index 00000000..56deb1b9 --- /dev/null +++ b/ai-context/context-graph-demo/src/components/common/SectionLabel.tsx @@ -0,0 +1,22 @@ +import { text } from "../../theme"; + +interface SectionLabelProps { + children: React.ReactNode; + marginBottom?: number; + marginTop?: number; +} + +export function SectionLabel({ children, marginBottom = 10, marginTop }: SectionLabelProps) { + return ( +
+ {children} +
+ ); +} diff --git a/ai-context/context-graph-demo/src/components/common/StatusBar.tsx b/ai-context/context-graph-demo/src/components/common/StatusBar.tsx new file mode 100644 index 00000000..c3f23e09 --- /dev/null +++ b/ai-context/context-graph-demo/src/components/common/StatusBar.tsx @@ -0,0 +1,60 @@ +import { useConnectionState } from "@trustgraph/react-provider"; +import { useProgressStateStore } from "@trustgraph/react-state"; +import { semantic, palette, text, border } from "../../theme"; + +export function StatusBar() { + const connectionState = useConnectionState(); + const activity = useProgressStateStore((state) => state.activity); + + const getStatusDisplay = () => { + if (!connectionState) return { color: text.subtle, text: "Initializing..." }; + switch (connectionState.status) { + case "authenticated": + return { color: semantic.success, text: "Authenticated" }; + case "connected": + return { color: semantic.success, text: "Connected" }; + case "unauthenticated": + return { color: semantic.info, text: "Connected" }; + case "connecting": + return { color: palette.amber, text: "Connecting..." }; + case "reconnecting": + return { color: semantic.warning, text: `Reconnecting (${connectionState.reconnectAttempt}/${connectionState.maxAttempts})...` }; + case "failed": + return { color: semantic.error, text: "Connection failed" }; + default: + return { color: text.subtle, text: connectionState.status }; + } + }; + + const status = getStatusDisplay(); + const activeActivity = activity.size > 0 ? Array.from(activity)[0] : null; + + return ( +
+
+ {activeActivity ? ( + <> + + {activeActivity}... + + ) : ( + <> + + Ready + + )} +
+
+ {status.text} + | + trustgraph.ai +
+
+ ); +} diff --git a/ai-context/context-graph-demo/src/components/common/Toaster.tsx b/ai-context/context-graph-demo/src/components/common/Toaster.tsx new file mode 100644 index 00000000..45c87d2f --- /dev/null +++ b/ai-context/context-graph-demo/src/components/common/Toaster.tsx @@ -0,0 +1,120 @@ +import { useToastStore, Toast, ToastType } from "../../state/toastStore"; +import { semantic, surface, text } from "../../theme"; + +const typeStyles: Record = { + success: { color: semantic.success, icon: "✓" }, + error: { color: semantic.error, icon: "✕" }, + warning: { color: semantic.warning, icon: "!" }, + info: { color: semantic.info, icon: "i" }, +}; + +function ToastItem({ toast, onDismiss }: { toast: Toast; onDismiss: () => void }) { + const style = typeStyles[toast.type]; + + return ( +
+ + {style.icon} + + + {toast.message} + + +
+ ); +} + +export function Toaster() { + const toasts = useToastStore((state) => state.toasts); + const removeToast = useToastStore((state) => state.removeToast); + + if (toasts.length === 0) return null; + + return ( + <> + +
+ {toasts.map((toast) => ( + removeToast(toast.id)} + /> + ))} +
+ + ); +} diff --git a/ai-context/context-graph-demo/src/components/common/Typewriter.tsx b/ai-context/context-graph-demo/src/components/common/Typewriter.tsx new file mode 100644 index 00000000..8503deec --- /dev/null +++ b/ai-context/context-graph-demo/src/components/common/Typewriter.tsx @@ -0,0 +1,37 @@ +import { useState, useEffect, useRef } from "react"; + +interface TypewriterProps { + text: string; + speed?: number; + onDone?: () => void; +} + +export function Typewriter({ text, speed = 12, onDone }: TypewriterProps) { + const [displayed, setDisplayed] = useState(""); + const idx = useRef(0); + + useEffect(() => { + idx.current = 0; + setDisplayed(""); + const interval = setInterval(() => { + idx.current++; + if (idx.current >= text.length) { + setDisplayed(text); + clearInterval(interval); + onDone?.(); + } else { + setDisplayed(text.slice(0, idx.current)); + } + }, speed); + return () => clearInterval(interval); + }, [text, speed, onDone]); + + return ( + + {displayed} + + ▌ + + + ); +} diff --git a/ai-context/context-graph-demo/src/components/common/index.ts b/ai-context/context-graph-demo/src/components/common/index.ts new file mode 100644 index 00000000..540ae1f9 --- /dev/null +++ b/ai-context/context-graph-demo/src/components/common/index.ts @@ -0,0 +1,14 @@ +export { SectionLabel } from "./SectionLabel"; +export { FilterButton } from "./FilterButton"; +export { Header } from "./Header"; +export { StatusBar } from "./StatusBar"; +export { Typewriter } from "./Typewriter"; +export { Card } from "./Card"; +export { Badge } from "./Badge"; +export { LoadingState } from "./LoadingState"; +export { Toaster } from "./Toaster"; +export { SearchInput } from "./SearchInput"; +export { FilterBar } from "./FilterBar"; +export type { FilterItem } from "./FilterBar"; +export { MessageBubble } from "./MessageBubble"; +export type { Message } from "./MessageBubble"; diff --git a/ai-context/context-graph-demo/src/components/graph/ExplainGraph.tsx b/ai-context/context-graph-demo/src/components/graph/ExplainGraph.tsx new file mode 100644 index 00000000..8d24510c --- /dev/null +++ b/ai-context/context-graph-demo/src/components/graph/ExplainGraph.tsx @@ -0,0 +1,451 @@ +import { useEffect, useRef, useState, useCallback, useMemo } from "react"; +import { ZoomControls } from "./ZoomControls"; +import { border, palette, text, withGlow } from "../../theme"; + +// ── Types ─────────────────────────────────────────────────────────── + +export interface ExplainGraphNode { + id: string; + label: string; + color?: string; +} + +export interface ExplainGraphEdge { + id: string; + from: string; + to: string; + label: string; + reasoning?: string; +} + +interface LayoutNode extends ExplainGraphNode { + x: number; + y: number; + vx: number; + vy: number; +} + +interface ExplainGraphProps { + nodes: ExplainGraphNode[]; + edges: ExplainGraphEdge[]; + highlightedNodeIds: string[]; + highlightedEdgeIds: string[]; + onNodeClick?: (nodeId: string) => void; + onEdgeClick?: (edgeId: string) => void; +} + +// ── Simple force layout ───────────────────────────────────────────── + +function computeLayout( + nodes: ExplainGraphNode[], + edges: ExplainGraphEdge[], + width: number, + height: number, +): LayoutNode[] { + if (nodes.length === 0 || width === 0) return []; + + const cx = width / 2; + const cy = height / 2; + + // Initial positions: circle layout + const layoutNodes: LayoutNode[] = nodes.map((n, i) => { + const angle = (Math.PI * 2 * i) / nodes.length - Math.PI / 2; + const radius = Math.min(cx, cy) * 0.55; + return { + ...n, + x: cx + Math.cos(angle) * radius, + y: cy + Math.sin(angle) * radius, + vx: 0, + vy: 0, + }; + }); + + // Run simple force simulation + const iterations = 120; + const repulsion = 2000; + const attraction = 0.005; + const damping = 0.85; + const centerPull = 0.01; + + const nodeMap = new Map(layoutNodes.map((n, i) => [n.id, i])); + + for (let iter = 0; iter < iterations; iter++) { + // Repulsion between all pairs + for (let i = 0; i < layoutNodes.length; i++) { + for (let j = i + 1; j < layoutNodes.length; j++) { + const a = layoutNodes[i]; + const b = layoutNodes[j]; + let dx = a.x - b.x; + let dy = a.y - b.y; + const dist = Math.sqrt(dx * dx + dy * dy) || 1; + const force = repulsion / (dist * dist); + dx = (dx / dist) * force; + dy = (dy / dist) * force; + a.vx += dx; + a.vy += dy; + b.vx -= dx; + b.vy -= dy; + } + } + + // Attraction along edges + for (const edge of edges) { + const ai = nodeMap.get(edge.from); + const bi = nodeMap.get(edge.to); + if (ai === undefined || bi === undefined) continue; + const a = layoutNodes[ai]; + const b = layoutNodes[bi]; + const dx = b.x - a.x; + const dy = b.y - a.y; + const fx = dx * attraction; + const fy = dy * attraction; + a.vx += fx; + a.vy += fy; + b.vx -= fx; + b.vy -= fy; + } + + // Center pull + for (const n of layoutNodes) { + n.vx += (cx - n.x) * centerPull; + n.vy += (cy - n.y) * centerPull; + } + + // Apply velocity + for (const n of layoutNodes) { + n.vx *= damping; + n.vy *= damping; + n.x += n.vx; + n.y += n.vy; + + // Keep in bounds with padding + const pad = 40; + n.x = Math.max(pad, Math.min(width - pad, n.x)); + n.y = Math.max(pad, Math.min(height - pad, n.y)); + } + } + + return layoutNodes; +} + +// ── Component ─────────────────────────────────────────────────────── + +export function ExplainGraph({ + nodes, + edges, + highlightedNodeIds, + highlightedEdgeIds, + onNodeClick, + onEdgeClick, +}: ExplainGraphProps) { + const containerRef = useRef(null); + const svgRef = useRef(null); + const [containerSize, setContainerSize] = useState({ width: 0, height: 0 }); + const [hovered, setHovered] = useState(null); + + // Zoom and pan + const [zoom, setZoom] = useState(1); + const [pan, setPan] = useState({ x: 0, y: 0 }); + const isPanningRef = useRef(false); + const lastPanPosRef = useRef({ x: 0, y: 0 }); + + // Track container size + useEffect(() => { + const container = containerRef.current; + if (!container) return; + const ro = new ResizeObserver((entries) => { + const entry = entries[0]; + if (entry) { + setContainerSize({ width: entry.contentRect.width, height: entry.contentRect.height }); + } + }); + ro.observe(container); + return () => ro.disconnect(); + }, []); + + // Layout + const layoutNodes = useMemo( + () => computeLayout(nodes, edges, containerSize.width, containerSize.height), + [nodes, edges, containerSize], + ); + + const nodeMap = useMemo( + () => new Map(layoutNodes.map(n => [n.id, n])), + [layoutNodes], + ); + + // Grid lines + const gridLines = useMemo(() => { + const lines: React.ReactElement[] = []; + const { width, height } = containerSize; + if (width === 0) return lines; + for (let x = 0; x < width; x += 30) { + lines.push(); + } + for (let y = 0; y < height; y += 30) { + lines.push(); + } + return lines; + }, [containerSize]); + + // Zoom + const handleWheel = useCallback((e: React.WheelEvent) => { + e.preventDefault(); + const delta = e.deltaY > 0 ? 0.9 : 1.1; + const newZoom = Math.min(4, Math.max(0.25, zoom * delta)); + const svg = svgRef.current; + if (!svg) return; + const rect = svg.getBoundingClientRect(); + const cursorX = e.clientX - rect.left; + const cursorY = e.clientY - rect.top; + const zoomRatio = newZoom / zoom; + setPan(p => ({ x: cursorX - (cursorX - p.x) * zoomRatio, y: cursorY - (cursorY - p.y) * zoomRatio })); + setZoom(newZoom); + }, [zoom]); + + // Pan + const handleMouseDown = useCallback((e: React.MouseEvent) => { + if (e.button === 1 || (e.button === 0 && e.shiftKey)) { + e.preventDefault(); + isPanningRef.current = true; + lastPanPosRef.current = { x: e.clientX, y: e.clientY }; + } + }, []); + + const handleMouseMove = useCallback((e: React.MouseEvent) => { + if (!isPanningRef.current) return; + const dx = e.clientX - lastPanPosRef.current.x; + const dy = e.clientY - lastPanPosRef.current.y; + lastPanPosRef.current = { x: e.clientX, y: e.clientY }; + setPan(p => ({ x: p.x + dx, y: p.y + dy })); + }, []); + + const handleMouseUp = useCallback(() => { isPanningRef.current = false; }, []); + + const handleResetView = useCallback(() => { setZoom(1); setPan({ x: 0, y: 0 }); }, []); + + const hasHighlights = highlightedNodeIds.length > 0 || highlightedEdgeIds.length > 0; + const NODE_R = 10; + + if (containerSize.width === 0) { + return
; + } + + return ( +
+ + {gridLines} + + + {/* Edges */} + {edges.map((edge) => { + const from = nodeMap.get(edge.from); + const to = nodeMap.get(edge.to); + if (!from || !to) return null; + + const isHighlighted = highlightedEdgeIds.includes(edge.id); + const isDimmed = hasHighlights && !isHighlighted; + const isEdgeHovered = hovered === `edge-${edge.id}`; + + const mx = (from.x + to.x) / 2 + (from.y - to.y) * 0.15; + const my = (from.y + to.y) / 2 + (to.x - from.x) * 0.15; + const path = `M ${from.x} ${from.y} Q ${mx} ${my} ${to.x} ${to.y}`; + + const alpha = isDimmed ? 0.15 : isHighlighted || isEdgeHovered ? 0.8 : 0.35; + const edgeColor = palette.cyan; + + return ( + onEdgeClick?.(edge.id)} + onMouseEnter={() => setHovered(`edge-${edge.id}`)} + onMouseLeave={() => setHovered(null)} + > + {/* Wider invisible hit area */} + + + {/* Edge label */} + + {edge.label} + + {/* Arrowhead */} + {(() => { + // Point on curve near the end (t=0.85) + const t = 0.85; + const ax = (1 - t) * (1 - t) * from.x + 2 * (1 - t) * t * mx + t * t * to.x; + const ay = (1 - t) * (1 - t) * from.y + 2 * (1 - t) * t * my + t * t * to.y; + const dx = to.x - ax; + const dy = to.y - ay; + const len = Math.sqrt(dx * dx + dy * dy) || 1; + const ux = dx / len; + const uy = dy / len; + // Arrow tip at edge of node circle + const tipX = to.x - ux * NODE_R; + const tipY = to.y - uy * NODE_R; + const arrowSize = 5; + const p1x = tipX - ux * arrowSize + uy * arrowSize * 0.4; + const p1y = tipY - uy * arrowSize - ux * arrowSize * 0.4; + const p2x = tipX - ux * arrowSize - uy * arrowSize * 0.4; + const p2y = tipY - uy * arrowSize + ux * arrowSize * 0.4; + return ( + + ); + })()} + + ); + })} + + {/* Nodes */} + {layoutNodes.map((node) => { + const isHighlighted = highlightedNodeIds.includes(node.id); + const isHovered = hovered === node.id; + const isDimmed = hasHighlights && !isHighlighted; + const nodeColor = node.color || palette.blue; + const alpha = isDimmed ? 0.25 : 1; + const r = isHighlighted || isHovered ? NODE_R * 1.3 : NODE_R; + + return ( + onNodeClick?.(node.id)} + onMouseEnter={() => setHovered(node.id)} + onMouseLeave={() => setHovered(null)} + > + {/* Glow */} + {(isHighlighted || isHovered) && ( + + )} + {/* Circle */} + + {/* Label */} + + {node.label} + + + ); + })} + + + + setZoom(z => Math.min(4, z * 1.2))} + onZoomOut={() => setZoom(z => Math.max(0.25, z / 1.2))} + onReset={handleResetView} + /> + + {/* Empty state */} + {nodes.length === 0 && ( +
+ Graph will populate as explain events arrive +
+ )} + + {/* Tooltip */} + {hovered && !hovered.startsWith("edge-") && (() => { + const node = nodeMap.get(hovered); + if (!node) return null; + return ( +
+
+ {node.label} +
+
+ ); + })()} + + {/* Edge tooltip */} + {hovered?.startsWith("edge-") && (() => { + const edgeId = hovered.slice(5); + const edge = edges.find(e => e.id === edgeId); + if (!edge) return null; + const from = nodeMap.get(edge.from); + const to = nodeMap.get(edge.to); + if (!from || !to) return null; + const mx = ((from.x + to.x) / 2) * zoom + pan.x; + const my = ((from.y + to.y) / 2) * zoom + pan.y; + return ( +
+
+ {edge.label} +
+ {edge.reasoning && ( +
+ {edge.reasoning.length > 150 ? edge.reasoning.slice(0, 150) + "..." : edge.reasoning} +
+ )} +
+ ); + })()} +
+ ); +} diff --git a/ai-context/context-graph-demo/src/components/graph/GraphCanvas.tsx b/ai-context/context-graph-demo/src/components/graph/GraphCanvas.tsx new file mode 100644 index 00000000..b75f36fc --- /dev/null +++ b/ai-context/context-graph-demo/src/components/graph/GraphCanvas.tsx @@ -0,0 +1,596 @@ +import { useEffect, useRef, useCallback, useState, MouseEvent } from "react"; +import type { DomainKey, Entity, GraphNode, OntologyType, Relationship } from "../../types"; +import { ZoomControls } from "./ZoomControls"; +import { border } from "../../theme"; + +interface GraphCanvasProps { + entities: Entity[]; + relationships: Relationship[]; + ontology: OntologyType; + highlightedEntities: string[]; + onNodeClick: (node: GraphNode) => void; + activeFilter: DomainKey | null; +} + +const SETTLE_TIME = 10000; // 10 seconds until nodes settle +const FRAME_INTERVAL = 1000 / 30; // 30fps + +export function GraphCanvas({ entities, relationships, ontology, highlightedEntities, onNodeClick, activeFilter }: GraphCanvasProps) { + const containerRef = useRef(null); + const staticCanvasRef = useRef(null); + const nodesCanvasRef = useRef(null); + const edgesCanvasRef = useRef(null); + const nodesRef = useRef([]); + const animRef = useRef(0); + const hoveredRef = useRef(null); + const settledRef = useRef(false); + const startTimeRef = useRef(0); + const timeRef = useRef(0); + const lastFrameTimeRef = useRef(0); + + // Store view state in refs to avoid triggering resets + const highlightedRef = useRef(highlightedEntities); + const activeFilterRef = useRef(activeFilter); + const relationshipsRef = useRef(relationships); + const ontologyRef = useRef(ontology); + + const [hovered, setHovered] = useState(null); + const [containerSize, setContainerSize] = useState({ width: 0, height: 0 }); + + // Zoom and pan state + const [zoom, setZoom] = useState(1); + const [pan, setPan] = useState({ x: 0, y: 0 }); + const zoomRef = useRef(1); + const panRef = useRef({ x: 0, y: 0 }); + const isPanningRef = useRef(false); + const lastPanPosRef = useRef({ x: 0, y: 0 }); + + // Keep zoom/pan refs in sync + zoomRef.current = zoom; + panRef.current = pan; + + // Keep refs in sync with props + useEffect(() => { + highlightedRef.current = highlightedEntities; + }, [highlightedEntities]); + + useEffect(() => { + activeFilterRef.current = activeFilter; + }, [activeFilter]); + + useEffect(() => { + relationshipsRef.current = relationships; + }, [relationships]); + + useEffect(() => { + ontologyRef.current = ontology; + }, [ontology]); + + // Track container size changes + useEffect(() => { + const container = containerRef.current; + if (!container) return; + + const resizeObserver = new ResizeObserver((entries) => { + const entry = entries[0]; + if (entry) { + setContainerSize({ + width: entry.contentRect.width, + height: entry.contentRect.height, + }); + } + }); + + resizeObserver.observe(container); + return () => resizeObserver.disconnect(); + }, []); + + // Draw static layer (grid + domain labels) + const drawStaticLayer = useCallback((ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement, domainPositions: Record) => { + ctx.clearRect(0, 0, canvas.width, canvas.height); + + // Grid stays fixed (no transform) + ctx.strokeStyle = border.grid; + ctx.lineWidth = 1; + for (let x = 0; x < canvas.width; x += 60) { + ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, canvas.height); ctx.stroke(); + } + for (let y = 0; y < canvas.height; y += 60) { + ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(canvas.width, y); ctx.stroke(); + } + + // Domain labels with zoom/pan transform + ctx.save(); + ctx.translate(panRef.current.x, panRef.current.y); + ctx.scale(zoomRef.current, zoomRef.current); + + const currentOntology = ontologyRef.current; + (Object.entries(domainPositions) as [DomainKey, { x: number; y: number }][]).forEach(([domain, pos]) => { + const data = currentOntology[domain]; + ctx.font = "bold 22px 'IBM Plex Mono', monospace"; + ctx.fillStyle = data.color + "44"; + ctx.textAlign = "center"; + ctx.fillText(data.label.toUpperCase(), pos.x, pos.y - Math.min(canvas.width, canvas.height) * 0.14); + }); + + ctx.restore(); + }, []); + + // Draw nodes layer - reads from refs + const drawNodesLayer = useCallback((ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement, time: number) => { + ctx.clearRect(0, 0, canvas.width, canvas.height); + + // Apply zoom/pan transform + ctx.save(); + ctx.translate(panRef.current.x, panRef.current.y); + ctx.scale(zoomRef.current, zoomRef.current); + + const nodes = nodesRef.current; + const settled = settledRef.current; + const highlighted = highlightedRef.current; + const filter = activeFilterRef.current; + const rels = relationshipsRef.current; + + nodes.forEach((node) => { + const isHighlighted = highlighted && highlighted.includes(node.id); + const isHovered = hoveredRef.current === node.id; + const isDimmed = highlighted && highlighted.length > 0 && !isHighlighted; + const isFiltered = filter && node.domain !== filter && !rels.some( + r => r.domain.includes(filter) && (r.from === node.id || r.to === node.id) + ); + + const alpha = isFiltered ? 0.15 : isDimmed ? 0.3 : 1; + const r = isHighlighted || isHovered ? node.r * 1.4 : node.r; + const pulseR = isHighlighted && !settled ? Math.sin(time * 3) * 3 : 0; + + // Glow + if ((isHighlighted || isHovered) && !isFiltered) { + ctx.beginPath(); + ctx.arc(node.x, node.y, r + 12 + pulseR, 0, Math.PI * 2); + const grd = ctx.createRadialGradient(node.x, node.y, r, node.x, node.y, r + 12 + pulseR); + grd.addColorStop(0, node.glow); + grd.addColorStop(1, "rgba(0,0,0,0)"); + ctx.fillStyle = grd; + ctx.fill(); + } + + // Node circle + ctx.beginPath(); + ctx.arc(node.x, node.y, r, 0, Math.PI * 2); + ctx.fillStyle = node.color + Math.round(alpha * 255 * 0.2).toString(16).padStart(2, "0"); + ctx.fill(); + ctx.strokeStyle = node.color + Math.round(alpha * 255).toString(16).padStart(2, "0"); + ctx.lineWidth = isHighlighted ? 2.5 : 1.5; + ctx.stroke(); + + // Label + ctx.font = `${isHighlighted ? "bold " : ""}${isHovered ? 17 : 14}px 'IBM Plex Sans', sans-serif`; + ctx.fillStyle = `rgba(255,255,255,${alpha * (isHighlighted ? 1 : 0.75)})`; + ctx.textAlign = "center"; + ctx.fillText(node.label, node.x, node.y + r + 18); + + // Update node positions (spring physics + drift) - only if not settled + if (!settled) { + node.x += (node.targetX - node.x) * 0.02; + node.y += (node.targetY - node.y) * 0.02; + node.x += Math.sin(time + node.targetX * 0.01) * 0.3; + node.y += Math.cos(time + node.targetY * 0.01) * 0.3; + } + }); + + ctx.restore(); + }, []); + + // Draw edges layer - reads from refs + const drawEdgesLayer = useCallback((ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement, time: number) => { + ctx.clearRect(0, 0, canvas.width, canvas.height); + + // Apply zoom/pan transform + ctx.save(); + ctx.translate(panRef.current.x, panRef.current.y); + ctx.scale(zoomRef.current, zoomRef.current); + + const nodes = nodesRef.current; + const highlighted = highlightedRef.current; + const filter = activeFilterRef.current; + const rels = relationshipsRef.current; + + const filteredRels = filter + ? rels.filter((r) => r.domain.includes(filter)) + : rels; + + filteredRels.forEach((rel) => { + const fromNode = nodes.find((n) => n.id === rel.from); + const toNode = nodes.find((n) => n.id === rel.to); + if (!fromNode || !toNode) return; + + const isHighlighted = + highlighted && + highlighted.includes(rel.from) && + highlighted.includes(rel.to); + + const baseAlpha = isHighlighted ? 0.7 : 0.12; + const pulse = isHighlighted ? Math.sin(time * 4) * 0.15 + 0.15 : 0; + + ctx.beginPath(); + ctx.moveTo(fromNode.x, fromNode.y); + // Curved edges + const mx = (fromNode.x + toNode.x) / 2 + (fromNode.y - toNode.y) * 0.1; + const my = (fromNode.y + toNode.y) / 2 + (toNode.x - fromNode.x) * 0.1; + ctx.quadraticCurveTo(mx, my, toNode.x, toNode.y); + + const gradient = ctx.createLinearGradient(fromNode.x, fromNode.y, toNode.x, toNode.y); + gradient.addColorStop(0, fromNode.color + Math.round((baseAlpha + pulse) * 255).toString(16).padStart(2, "0")); + gradient.addColorStop(1, toNode.color + Math.round((baseAlpha + pulse) * 255).toString(16).padStart(2, "0")); + ctx.strokeStyle = gradient; + ctx.lineWidth = isHighlighted ? 3 : 1.5; + ctx.stroke(); + + // Animated particles on highlighted edges + if (isHighlighted) { + const t = (time * 2) % 1; + const px = (1 - t) * (1 - t) * fromNode.x + 2 * (1 - t) * t * mx + t * t * toNode.x; + const py = (1 - t) * (1 - t) * fromNode.y + 2 * (1 - t) * t * my + t * t * toNode.y; + ctx.beginPath(); + ctx.arc(px, py, 3, 0, Math.PI * 2); + ctx.fillStyle = "#fff"; + ctx.fill(); + } + }); + + ctx.restore(); + }, []); + + // Animation loop function - separate from setup + const runAnimation = useCallback(() => { + const nodesCanvas = nodesCanvasRef.current; + const edgesCanvas = edgesCanvasRef.current; + const nodesCtx = nodesCanvas?.getContext("2d"); + const edgesCtx = edgesCanvas?.getContext("2d"); + + if (!nodesCtx || !nodesCanvas || !edgesCtx || !edgesCanvas) return; + + // Capture validated references for the closure + const validNodesCtx = nodesCtx; + const validNodesCanvas = nodesCanvas; + const validEdgesCtx = edgesCtx; + const validEdgesCanvas = edgesCanvas; + + function animate(currentTime: number) { + // Throttle to target fps + if (currentTime - lastFrameTimeRef.current < FRAME_INTERVAL) { + animRef.current = requestAnimationFrame(animate); + return; + } + lastFrameTimeRef.current = currentTime; + timeRef.current += 0.01; + + // Check if we should settle + if (!settledRef.current && currentTime - startTimeRef.current > SETTLE_TIME) { + settledRef.current = true; + } + + const hasHighlights = highlightedRef.current && highlightedRef.current.length > 0; + const isSettled = settledRef.current; + + // Draw edges layer + drawEdgesLayer(validEdgesCtx, validEdgesCanvas, timeRef.current); + + // Draw nodes layer + if (!isSettled || hasHighlights || hoveredRef.current) { + drawNodesLayer(validNodesCtx, validNodesCanvas, timeRef.current); + } + + // Continue animation if not settled, or if there are highlights + if (!isSettled || hasHighlights) { + animRef.current = requestAnimationFrame(animate); + } else { + // Settled with no highlights - do one final draw and stop + drawNodesLayer(validNodesCtx, validNodesCanvas, timeRef.current); + drawEdgesLayer(validEdgesCtx, validEdgesCanvas, timeRef.current); + animRef.current = 0; + } + } + + animRef.current = requestAnimationFrame(animate); + }, [drawNodesLayer, drawEdgesLayer]); + + // Main setup - only runs when data or size changes + useEffect(() => { + const staticCanvas = staticCanvasRef.current; + const nodesCanvas = nodesCanvasRef.current; + const edgesCanvas = edgesCanvasRef.current; + if (!staticCanvas || !nodesCanvas || !edgesCanvas || containerSize.width === 0) return; + + // Cancel any existing animation + if (animRef.current) { + cancelAnimationFrame(animRef.current); + animRef.current = 0; + } + + // Setup all canvases + [staticCanvas, nodesCanvas, edgesCanvas].forEach(canvas => { + canvas.width = containerSize.width * 2; + canvas.height = containerSize.height * 2; + canvas.style.width = containerSize.width + "px"; + canvas.style.height = containerSize.height + "px"; + }); + + const cx = staticCanvas.width / 2; + const cy = staticCanvas.height / 2; + + // Position nodes in domain clusters + const domainKeys = Object.keys(ontology); + const domainPositions: Record = {}; + domainKeys.forEach((domain, i) => { + const angle = (Math.PI * 2 * i) / domainKeys.length - Math.PI / 2; + const radius = Math.min(cx, cy) * 0.45; + domainPositions[domain] = { + x: cx + Math.cos(angle) * radius, + y: cy + Math.sin(angle) * radius, + }; + }); + + nodesRef.current = entities.map((e) => { + const dp = domainPositions[e.domain]; + const subIdx = ontology[e.domain].subclasses.findIndex((s) => s.id === e.id); + const total = ontology[e.domain].subclasses.length; + const angle = ((Math.PI * 2) / total) * subIdx - Math.PI / 2; + const radius = Math.min(staticCanvas.width, staticCanvas.height) * 0.1; + return { + ...e, + x: dp.x + Math.cos(angle) * radius, + y: dp.y + Math.sin(angle) * radius, + vx: 0, + vy: 0, + targetX: dp.x + Math.cos(angle) * radius, + targetY: dp.y + Math.sin(angle) * radius, + r: 18, + }; + }); + + const staticCtx = staticCanvas.getContext("2d"); + if (!staticCtx) return; + + // Draw static layer once + drawStaticLayer(staticCtx, staticCanvas, domainPositions); + + // Reset animation state + settledRef.current = false; + startTimeRef.current = performance.now(); + timeRef.current = 0; + lastFrameTimeRef.current = 0; + + // Start animation + runAnimation(); + + return () => { + if (animRef.current) { + cancelAnimationFrame(animRef.current); + animRef.current = 0; + } + }; + }, [entities, ontology, containerSize, drawStaticLayer, runAnimation]); + + // Restart animation when highlights change (without resetting positions) + useEffect(() => { + const hasHighlights = highlightedEntities && highlightedEntities.length > 0; + + // If we have highlights and animation isn't running, restart it + if (hasHighlights && animRef.current === 0) { + runAnimation(); + } + }, [highlightedEntities, runAnimation]); + + // Redraw on filter change (without resetting) + useEffect(() => { + const nodesCanvas = nodesCanvasRef.current; + const edgesCanvas = edgesCanvasRef.current; + const nodesCtx = nodesCanvas?.getContext("2d"); + const edgesCtx = edgesCanvas?.getContext("2d"); + + if (nodesCtx && nodesCanvas && edgesCtx && edgesCanvas && settledRef.current && animRef.current === 0) { + drawNodesLayer(nodesCtx, nodesCanvas, timeRef.current); + drawEdgesLayer(edgesCtx, edgesCanvas, timeRef.current); + } + }, [activeFilter, drawNodesLayer, drawEdgesLayer]); + + const handleMouseMove = useCallback((e: MouseEvent) => { + // Handle panning first + if (isPanningRef.current) { + const dx = (e.clientX - lastPanPosRef.current.x) * 2; + const dy = (e.clientY - lastPanPosRef.current.y) * 2; + lastPanPosRef.current = { x: e.clientX, y: e.clientY }; + setPan(p => ({ x: p.x + dx, y: p.y + dy })); + return; + } + + const canvas = nodesCanvasRef.current; + if (!canvas) return; + const rect = canvas.getBoundingClientRect(); + + // Transform screen coordinates to world coordinates (accounting for zoom/pan) + const screenX = (e.clientX - rect.left) * 2; + const screenY = (e.clientY - rect.top) * 2; + const x = (screenX - panRef.current.x) / zoomRef.current; + const y = (screenY - panRef.current.y) / zoomRef.current; + + const nodes = nodesRef.current; + let found: string | null = null; + for (const node of nodes) { + const dx = node.x - x; + const dy = node.y - y; + if (Math.sqrt(dx * dx + dy * dy) < node.r * 1.5) { + found = node.id; + break; + } + } + const wasHovered = hoveredRef.current; + hoveredRef.current = found; + setHovered(found); + canvas.style.cursor = isPanningRef.current ? "grabbing" : (found ? "pointer" : "default"); + + // Redraw if hover state changed and we're settled + if (wasHovered !== found && settledRef.current) { + const nodesCanvas = nodesCanvasRef.current; + const edgesCanvas = edgesCanvasRef.current; + const nodesCtx = nodesCanvas?.getContext("2d"); + const edgesCtx = edgesCanvas?.getContext("2d"); + + if (nodesCtx && nodesCanvas && edgesCtx && edgesCanvas) { + drawNodesLayer(nodesCtx, nodesCanvas, timeRef.current); + drawEdgesLayer(edgesCtx, edgesCanvas, timeRef.current); + } + } + }, [drawNodesLayer, drawEdgesLayer]); + + const handleClick = useCallback((e: MouseEvent) => { + // Don't trigger click if we were panning + if (e.shiftKey) return; + if (hoveredRef.current && onNodeClick) { + const node = nodesRef.current.find((n) => n.id === hoveredRef.current); + if (node) onNodeClick(node); + } + }, [onNodeClick]); + + // Redraw all layers (used when zoom/pan changes) + const redrawAllLayers = useCallback(() => { + const staticCanvas = staticCanvasRef.current; + const nodesCanvas = nodesCanvasRef.current; + const edgesCanvas = edgesCanvasRef.current; + const staticCtx = staticCanvas?.getContext("2d"); + const nodesCtx = nodesCanvas?.getContext("2d"); + const edgesCtx = edgesCanvas?.getContext("2d"); + + if (!staticCtx || !staticCanvas || !nodesCtx || !nodesCanvas || !edgesCtx || !edgesCanvas) return; + + // Recalculate domain positions for static layer redraw + const cx = staticCanvas.width / 2; + const cy = staticCanvas.height / 2; + const domainKeys = Object.keys(ontologyRef.current); + const domainPositions: Record = {}; + domainKeys.forEach((domain, i) => { + const angle = (Math.PI * 2 * i) / domainKeys.length - Math.PI / 2; + const radius = Math.min(cx, cy) * 0.45; + domainPositions[domain] = { + x: cx + Math.cos(angle) * radius, + y: cy + Math.sin(angle) * radius, + }; + }); + + drawStaticLayer(staticCtx, staticCanvas, domainPositions); + drawEdgesLayer(edgesCtx, edgesCanvas, timeRef.current); + drawNodesLayer(nodesCtx, nodesCanvas, timeRef.current); + }, [drawStaticLayer, drawEdgesLayer, drawNodesLayer]); + + // Zoom handler - zoom towards cursor position + const handleWheel = useCallback((e: React.WheelEvent) => { + e.preventDefault(); + const delta = e.deltaY > 0 ? 0.9 : 1.1; + const newZoom = Math.min(4, Math.max(0.25, zoomRef.current * delta)); + + const canvas = nodesCanvasRef.current; + if (!canvas) return; + const rect = canvas.getBoundingClientRect(); + const cursorX = (e.clientX - rect.left) * 2; // Account for 2x canvas scaling + const cursorY = (e.clientY - rect.top) * 2; + + // Adjust pan to zoom towards cursor + const zoomRatio = newZoom / zoomRef.current; + const newPanX = cursorX - (cursorX - panRef.current.x) * zoomRatio; + const newPanY = cursorY - (cursorY - panRef.current.y) * zoomRatio; + + setZoom(newZoom); + setPan({ x: newPanX, y: newPanY }); + }, []); + + // Pan handlers + const handleMouseDown = useCallback((e: MouseEvent) => { + if (e.button === 1 || (e.button === 0 && e.shiftKey)) { + e.preventDefault(); + isPanningRef.current = true; + lastPanPosRef.current = { x: e.clientX, y: e.clientY }; + } + }, []); + + const handleMouseUp = useCallback(() => { + isPanningRef.current = false; + }, []); + + // Reset zoom/pan + const handleResetView = useCallback(() => { + setZoom(1); + setPan({ x: 0, y: 0 }); + }, []); + + // Redraw when zoom/pan changes + useEffect(() => { + if (containerSize.width > 0) { + redrawAllLayers(); + } + }, [zoom, pan, containerSize, redrawAllLayers]); + + const canvasStyle: React.CSSProperties = { + position: "absolute", + top: 0, + left: 0, + width: "100%", + height: "100%", + }; + + return ( +
+ {/* Layer 1: Static (grid + domain labels) */} + + {/* Layer 2: Edges */} + + {/* Layer 3: Nodes (on top for interaction) */} + + + setZoom(z => Math.min(4, z * 1.2))} + onZoomOut={() => setZoom(z => Math.max(0.25, z / 1.2))} + onReset={handleResetView} + /> + + {/* Tooltip */} + {hovered && (() => { + const node = nodesRef.current.find((n) => n.id === hovered); + if (!node) return null; + // Transform node position to screen coordinates + const sx = (node.x * zoomRef.current + panRef.current.x) / 2; + const sy = (node.y * zoomRef.current + panRef.current.y) / 2; + return ( +
+
+ {node.icon} {node.label} +
+
+ {Object.entries(node.props || {}).map(([k, v]) => ( +
{k}: {String(v)}
+ ))} +
+
+ ); + })()} +
+ ); +} diff --git a/ai-context/context-graph-demo/src/components/graph/GraphCanvasSVG.tsx b/ai-context/context-graph-demo/src/components/graph/GraphCanvasSVG.tsx new file mode 100644 index 00000000..39964f2e --- /dev/null +++ b/ai-context/context-graph-demo/src/components/graph/GraphCanvasSVG.tsx @@ -0,0 +1,456 @@ +import { useEffect, useRef, useState, useCallback, useMemo } from "react"; +import type { DomainKey, Entity, GraphNode, OntologyType, Relationship } from "../../types"; +import { ZoomControls } from "./ZoomControls"; +import { border } from "../../theme"; + +interface GraphCanvasSVGProps { + entities: Entity[]; + relationships: Relationship[]; + ontology: OntologyType; + highlightedEntities: string[]; + onNodeClick: (node: GraphNode) => void; + activeFilter: DomainKey | null; +} + +const SETTLE_TIME = 10000; // 10 seconds until nodes settle + +export function GraphCanvasSVG({ entities, relationships, ontology, highlightedEntities, onNodeClick, activeFilter }: GraphCanvasSVGProps) { + const containerRef = useRef(null); + const svgRef = useRef(null); + const [containerSize, setContainerSize] = useState({ width: 0, height: 0 }); + const [hovered, setHovered] = useState(null); + const [settled, setSettled] = useState(false); + const [time, setTime] = useState(0); + const animRef = useRef(0); + const startTimeRef = useRef(0); + const lastFrameTimeRef = useRef(0); + + // Zoom and pan state + const [zoom, setZoom] = useState(1); + const [pan, setPan] = useState({ x: 0, y: 0 }); + const isPanningRef = useRef(false); + const lastPanPosRef = useRef({ x: 0, y: 0 }); + + // Track container size + useEffect(() => { + const container = containerRef.current; + if (!container) return; + + const resizeObserver = new ResizeObserver((entries) => { + const entry = entries[0]; + if (entry) { + setContainerSize({ + width: entry.contentRect.width, + height: entry.contentRect.height, + }); + } + }); + + resizeObserver.observe(container); + return () => resizeObserver.disconnect(); + }, []); + + // Calculate node positions + const { nodes, domainPositions } = useMemo(() => { + if (containerSize.width === 0) return { nodes: [], domainPositions: {} }; + + const width = containerSize.width; + const height = containerSize.height; + const cx = width / 2; + const cy = height / 2; + + const domainKeys = Object.keys(ontology); + const domainPositions: Record = {}; + domainKeys.forEach((domain, i) => { + const angle = (Math.PI * 2 * i) / domainKeys.length - Math.PI / 2; + const radius = Math.min(cx, cy) * 0.45; + domainPositions[domain] = { + x: cx + Math.cos(angle) * radius, + y: cy + Math.sin(angle) * radius, + }; + }); + + const nodes: GraphNode[] = entities.map((e) => { + const dp = domainPositions[e.domain]; + const subIdx = ontology[e.domain].subclasses.findIndex((s) => s.id === e.id); + const total = ontology[e.domain].subclasses.length; + const angle = ((Math.PI * 2) / total) * subIdx - Math.PI / 2; + const radius = Math.min(width, height) * 0.1; + const x = dp.x + Math.cos(angle) * radius; + const y = dp.y + Math.sin(angle) * radius; + return { + ...e, + x, + y, + vx: 0, + vy: 0, + targetX: x, + targetY: y, + r: 9, // Half size since we're not doing 2x canvas scaling + }; + }); + + return { nodes, domainPositions }; + }, [entities, ontology, containerSize]); + + // Animation loop for breathing effect + useEffect(() => { + if (containerSize.width === 0) return; + + startTimeRef.current = performance.now(); + setSettled(false); + setTime(0); + + const frameInterval = 1000 / 30; // 30fps + + function animate(currentTime: number) { + if (currentTime - lastFrameTimeRef.current < frameInterval) { + animRef.current = requestAnimationFrame(animate); + return; + } + lastFrameTimeRef.current = currentTime; + + // Check if should settle + if (currentTime - startTimeRef.current > SETTLE_TIME) { + setSettled(true); + // Continue animation only if there are highlights + if (highlightedEntities && highlightedEntities.length > 0) { + setTime(t => t + 0.01); + animRef.current = requestAnimationFrame(animate); + } + return; + } + + setTime(t => t + 0.01); + animRef.current = requestAnimationFrame(animate); + } + + animRef.current = requestAnimationFrame(animate); + return () => cancelAnimationFrame(animRef.current); + }, [containerSize, entities, ontology]); + + // Restart animation when highlights change + useEffect(() => { + if (highlightedEntities && highlightedEntities.length > 0 && settled && animRef.current === 0) { + const frameInterval = 1000 / 30; + + function animate(currentTime: number) { + if (currentTime - lastFrameTimeRef.current < frameInterval) { + animRef.current = requestAnimationFrame(animate); + return; + } + lastFrameTimeRef.current = currentTime; + setTime(t => t + 0.01); + + if (highlightedEntities && highlightedEntities.length > 0) { + animRef.current = requestAnimationFrame(animate); + } else { + animRef.current = 0; + } + } + + animRef.current = requestAnimationFrame(animate); + } + }, [highlightedEntities, settled]); + + // Generate grid lines + const gridLines = useMemo(() => { + const lines: React.ReactElement[] = []; + const { width, height } = containerSize; + if (width === 0) return lines; + + for (let x = 0; x < width; x += 30) { + lines.push( + + ); + } + for (let y = 0; y < height; y += 30) { + lines.push( + + ); + } + return lines; + }, [containerSize]); + + // Calculate edge path with curve + const getEdgePath = useCallback((fromNode: GraphNode, toNode: GraphNode, time: number, isSettled: boolean) => { + const driftX1 = isSettled ? 0 : Math.sin(time + fromNode.targetX * 0.01) * 0.3; + const driftY1 = isSettled ? 0 : Math.cos(time + fromNode.targetY * 0.01) * 0.3; + const driftX2 = isSettled ? 0 : Math.sin(time + toNode.targetX * 0.01) * 0.3; + const driftY2 = isSettled ? 0 : Math.cos(time + toNode.targetY * 0.01) * 0.3; + + const x1 = fromNode.x + driftX1; + const y1 = fromNode.y + driftY1; + const x2 = toNode.x + driftX2; + const y2 = toNode.y + driftY2; + + const mx = (x1 + x2) / 2 + (y1 - y2) * 0.1; + const my = (y1 + y2) / 2 + (x2 - x1) * 0.1; + + return { path: `M ${x1} ${y1} Q ${mx} ${my} ${x2} ${y2}`, mx, my, x1, y1, x2, y2 }; + }, []); + + // Get node position with drift + const getNodePosition = useCallback((node: GraphNode, time: number, isSettled: boolean) => { + if (isSettled) { + return { x: node.x, y: node.y }; + } + const driftX = Math.sin(time + node.targetX * 0.01) * 0.3; + const driftY = Math.cos(time + node.targetY * 0.01) * 0.3; + return { x: node.x + driftX, y: node.y + driftY }; + }, []); + + const handleNodeClick = useCallback((node: GraphNode) => { + onNodeClick(node); + }, [onNodeClick]); + + // Zoom handler - zoom towards cursor position + const handleWheel = useCallback((e: React.WheelEvent) => { + e.preventDefault(); + const delta = e.deltaY > 0 ? 0.9 : 1.1; + const newZoom = Math.min(4, Math.max(0.25, zoom * delta)); + + // Get cursor position relative to SVG + const svg = svgRef.current; + if (!svg) return; + const rect = svg.getBoundingClientRect(); + const cursorX = e.clientX - rect.left; + const cursorY = e.clientY - rect.top; + + // Adjust pan to zoom towards cursor + const zoomRatio = newZoom / zoom; + const newPanX = cursorX - (cursorX - pan.x) * zoomRatio; + const newPanY = cursorY - (cursorY - pan.y) * zoomRatio; + + setZoom(newZoom); + setPan({ x: newPanX, y: newPanY }); + }, [zoom, pan]); + + // Pan handlers + const handleMouseDown = useCallback((e: React.MouseEvent) => { + // Only pan with middle mouse or when holding space (we'll just use middle mouse for now) + if (e.button === 1 || e.button === 0 && e.shiftKey) { + e.preventDefault(); + isPanningRef.current = true; + lastPanPosRef.current = { x: e.clientX, y: e.clientY }; + } + }, []); + + const handleMouseMove = useCallback((e: React.MouseEvent) => { + if (!isPanningRef.current) return; + const dx = e.clientX - lastPanPosRef.current.x; + const dy = e.clientY - lastPanPosRef.current.y; + lastPanPosRef.current = { x: e.clientX, y: e.clientY }; + setPan(p => ({ x: p.x + dx, y: p.y + dy })); + }, []); + + const handleMouseUp = useCallback(() => { + isPanningRef.current = false; + }, []); + + // Reset zoom/pan + const handleResetView = useCallback(() => { + setZoom(1); + setPan({ x: 0, y: 0 }); + }, []); + + if (containerSize.width === 0) { + return
; + } + + const filteredRels = activeFilter + ? relationships.filter((r) => r.domain.includes(activeFilter)) + : relationships; + + return ( +
+ + {/* Grid - outside transform so it stays fixed */} + {gridLines} + + {/* Transformed content */} + + + {/* Domain labels */} + + {(Object.entries(domainPositions) as [DomainKey, { x: number; y: number }][]).map(([domain, pos]) => { + const data = ontology[domain]; + return ( + + {data.label.toUpperCase()} + + ); + })} + + + {/* Edges */} + + {filteredRels.map((rel, i) => { + const fromNode = nodes.find((n) => n.id === rel.from); + const toNode = nodes.find((n) => n.id === rel.to); + if (!fromNode || !toNode) return null; + + const isHighlighted = + highlightedEntities && + highlightedEntities.includes(rel.from) && + highlightedEntities.includes(rel.to); + + const { path, mx, my, x1, y1, x2, y2 } = getEdgePath(fromNode, toNode, time, settled); + const baseAlpha = isHighlighted ? 0.7 : 0.12; + const pulse = isHighlighted ? Math.sin(time * 4) * 0.15 + 0.15 : 0; + const alpha = Math.min(1, baseAlpha + pulse); + + // Particle position on curve (quadratic bezier) + const t = (time * 2) % 1; + const px = (1 - t) * (1 - t) * x1 + 2 * (1 - t) * t * mx + t * t * x2; + const py = (1 - t) * (1 - t) * y1 + 2 * (1 - t) * t * my + t * t * y2; + + return ( + + + + + + + + + {isHighlighted && ( + + )} + + ); + })} + + + {/* Nodes */} + + {nodes.map((node) => { + const isHighlighted = highlightedEntities && highlightedEntities.includes(node.id); + const isHovered = hovered === node.id; + const isDimmed = highlightedEntities && highlightedEntities.length > 0 && !isHighlighted; + const isFiltered = activeFilter && node.domain !== activeFilter && !relationships.some( + r => r.domain.includes(activeFilter) && (r.from === node.id || r.to === node.id) + ); + + const alpha = isFiltered ? 0.15 : isDimmed ? 0.3 : 1; + const r = isHighlighted || isHovered ? node.r * 1.4 : node.r; + const pulseR = isHighlighted && !settled ? Math.sin(time * 3) * 1.5 : 0; + const { x, y } = getNodePosition(node, time, settled); + + return ( + handleNodeClick(node)} + onMouseEnter={() => setHovered(node.id)} + onMouseLeave={() => setHovered(null)} + > + {/* Glow */} + {(isHighlighted || isHovered) && !isFiltered && ( + + + + + + + + + )} + + {/* Node circle */} + + + {/* Label */} + + {node.label} + + + ); + })} + + {/* Close transform group */} + + + setZoom(z => Math.min(4, z * 1.2))} + onZoomOut={() => setZoom(z => Math.max(0.25, z / 1.2))} + onReset={handleResetView} + /> + + {/* Tooltip */} + {hovered && (() => { + const node = nodes.find((n) => n.id === hovered); + if (!node) return null; + const { x, y } = getNodePosition(node, time, settled); + return ( +
+
+ {node.icon} {node.label} +
+
+ {Object.entries(node.props || {}).map(([k, v]) => ( +
{k}: {String(v)}
+ ))} +
+
+ ); + })()} +
+ ); +} diff --git a/ai-context/context-graph-demo/src/components/graph/NodeDetailPanel.tsx b/ai-context/context-graph-demo/src/components/graph/NodeDetailPanel.tsx new file mode 100644 index 00000000..ffb93a9d --- /dev/null +++ b/ai-context/context-graph-demo/src/components/graph/NodeDetailPanel.tsx @@ -0,0 +1,70 @@ +import type { Entity, Relationship, OntologyType } from "../../types"; +import { SectionLabel, Card } from "../common"; +import { text, border } from "../../theme"; + +interface NodeDetailPanelProps { + node: Entity; + relationships: Relationship[]; + entities: Entity[]; + ontology: OntologyType; + propertyLabels: Record; + onClose: () => void; + onNodeSelect: (node: Entity) => void; +} + +export function NodeDetailPanel({ node, relationships, entities, ontology, propertyLabels, onClose, onNodeSelect }: NodeDetailPanelProps) { + // Filter relationships for this node + const nodeRelationships = relationships.filter( + r => r.from === node.id || r.to === node.id + ); + + return ( +
+
+
+ {ontology[node.domain].label.toUpperCase()} ENTITY +
+ +
+
+ {node.icon} {node.label} +
+
+ PROPERTIES + {Object.entries(node.props || {}).map(([k, v]) => ( +
+ {propertyLabels[k] || k} + {String(v)} +
+ ))} +
+
+ RELATIONSHIPS + {nodeRelationships.map((r, i) => { + const otherId = r.from === node.id ? r.to : r.from; + const other = entities.find(e => e.id === otherId); + const direction = r.from === node.id ? "→" : "←"; + return ( + { if (other) onNodeSelect(other); }} + style={{ marginBottom: 4 }} + > +
+ {direction} {other?.label} +
+
+ {r.predicate.replace(/_/g, " ")} +
+
+ ); + })} +
+
+ ); +} diff --git a/ai-context/context-graph-demo/src/components/graph/ZoomControls.tsx b/ai-context/context-graph-demo/src/components/graph/ZoomControls.tsx new file mode 100644 index 00000000..7e3e3ab3 --- /dev/null +++ b/ai-context/context-graph-demo/src/components/graph/ZoomControls.tsx @@ -0,0 +1,73 @@ +import { surface, border, text } from "../../theme"; + +interface ZoomControlsProps { + zoom: number; + onZoomIn: () => void; + onZoomOut: () => void; + onReset: () => void; +} + +export function ZoomControls({ zoom, onZoomIn, onZoomOut, onReset }: ZoomControlsProps) { + const buttonStyle: React.CSSProperties = { + width: 28, + height: 28, + border: "none", + borderRadius: 4, + background: border.medium, + color: text.subtle, + cursor: "pointer", + fontSize: 16, + fontWeight: "bold", + }; + + return ( + <> + {/* Zoom controls */} +
+ + + +
+ + {/* Zoom indicator */} + {zoom !== 1 && ( +
+ {Math.round(zoom * 100)}% +
+ )} + + ); +} diff --git a/ai-context/context-graph-demo/src/components/graph/index.ts b/ai-context/context-graph-demo/src/components/graph/index.ts new file mode 100644 index 00000000..6f8ebcaf --- /dev/null +++ b/ai-context/context-graph-demo/src/components/graph/index.ts @@ -0,0 +1,6 @@ +export { GraphCanvas } from "./GraphCanvas"; +export { GraphCanvasSVG } from "./GraphCanvasSVG"; +export { ExplainGraph } from "./ExplainGraph"; +export type { ExplainGraphNode, ExplainGraphEdge } from "./ExplainGraph"; +export { NodeDetailPanel } from "./NodeDetailPanel"; +export { ZoomControls } from "./ZoomControls"; diff --git a/ai-context/context-graph-demo/src/components/index.ts b/ai-context/context-graph-demo/src/components/index.ts new file mode 100644 index 00000000..aca26425 --- /dev/null +++ b/ai-context/context-graph-demo/src/components/index.ts @@ -0,0 +1,7 @@ +// Common shared components +export { SectionLabel, FilterButton, Header, StatusBar, Typewriter, Card, Badge, LoadingState, Toaster, SearchInput, FilterBar, MessageBubble } from "./common"; +export type { FilterItem, Message } from "./common"; + +// Graph visualization components +export { GraphCanvas, GraphCanvasSVG, ExplainGraph, NodeDetailPanel, ZoomControls } from "./graph"; +export type { ExplainGraphNode, ExplainGraphEdge } from "./graph"; diff --git a/ai-context/context-graph-demo/src/config.ts b/ai-context/context-graph-demo/src/config.ts new file mode 100644 index 00000000..1a831d15 --- /dev/null +++ b/ai-context/context-graph-demo/src/config.ts @@ -0,0 +1,2 @@ +// TrustGraph collection identifier +export const COLLECTION = "default"; diff --git a/ai-context/context-graph-demo/src/index.css b/ai-context/context-graph-demo/src/index.css new file mode 100644 index 00000000..57ced3fc --- /dev/null +++ b/ai-context/context-graph-demo/src/index.css @@ -0,0 +1,18 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html, body, #root { + width: 100%; + height: 100%; + background: #0A0A0F; +} + +body { + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} diff --git a/ai-context/context-graph-demo/src/main.tsx b/ai-context/context-graph-demo/src/main.tsx new file mode 100644 index 00000000..9f851904 --- /dev/null +++ b/ai-context/context-graph-demo/src/main.tsx @@ -0,0 +1,29 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { SocketProvider } from '@trustgraph/react-provider' +import { NotificationProvider, NotificationHandler } from '@trustgraph/react-state' +import { toast } from './state' +import './index.css' +import App from './App' + +const queryClient = new QueryClient() + +const notificationHandler: NotificationHandler = { + success: (message: string) => toast.success(message), + error: (message: string) => toast.error(message), + warning: (message: string) => toast.warning(message), + info: (message: string) => toast.info(message), +} + +createRoot(document.getElementById('root')!).render( + + + + + + + + + , +) diff --git a/ai-context/context-graph-demo/src/pages/DataView.tsx b/ai-context/context-graph-demo/src/pages/DataView.tsx new file mode 100644 index 00000000..58e355e7 --- /dev/null +++ b/ai-context/context-graph-demo/src/pages/DataView.tsx @@ -0,0 +1,393 @@ +import { useState, useCallback, useMemo } from "react"; +import { SectionLabel, Card, LoadingState, SearchInput, FilterBar } from "../components"; +import type { FilterItem } from "../components"; +import { useSchemas, useEmbeddings, useRowEmbeddingsQuery, useRowsQuery } from "@trustgraph/react-state"; +import { COLLECTION } from "../config"; +import { semantic, palette, text, border, surface } from "../theme"; + +// Schema field type +interface SchemaField { + name: string; + type: string; + description?: string; +} + +// Schema type based on what useSchemas returns +interface SchemaData { + name: string; + description?: string; + fields?: SchemaField[]; + indexes?: { name: string; fields: string[] }[]; +} + +interface SchemaInfo { + key: string; + name: string; + description?: string; + fields: SchemaField[]; + indexes: { name: string; fields: string[] }[]; +} + +// Type for accumulated results with schema info and row data +interface AccumulatedMatch { + schemaKey: string; + index_name: string; + index_value: string[]; + text: string; + score: number; + rowData?: Record; +} + +export function DataView() { + // Input state + const [searchTerm, setSearchTerm] = useState(""); + + // Filter state (display only - doesn't trigger re-fetch) + const [selectedSchema, setSelectedSchema] = useState(null); + + // Results state + const [allMatches, setAllMatches] = useState([]); + const [isSearching, setIsSearching] = useState(false); + const [hasSearched, setHasSearched] = useState(false); + + // Fetch schemas + const { schemas: rawSchemas, schemasLoading, schemasError } = useSchemas(); + + // Embeddings hook - we'll use refetch for manual triggering + const [embeddingsTerm, setEmbeddingsTerm] = useState(""); + const { embeddings, isLoading: embeddingsLoading, refetch: _refetchEmbeddings } = useEmbeddings({ + flow: "default", + term: embeddingsTerm, + }); + + // Row embeddings query + const { executeQueryAsync } = useRowEmbeddingsQuery({ flow: "default" }); + + // Rows query for fetching full row data + const { executeQueryAsync: executeRowsQueryAsync } = useRowsQuery({ flow: "default" }); + + // Parse schemas into usable format + const schemas: SchemaInfo[] = useMemo(() => { + return (rawSchemas || []).map((s: unknown, idx: number) => { + if (Array.isArray(s)) { + const schemaData = s[1] as SchemaData | undefined; + return { + key: String(s[0]), + name: schemaData?.name || String(s[0]), + description: schemaData?.description, + fields: schemaData?.fields || [], + indexes: schemaData?.indexes || [], + }; + } + const schemaObj = s as SchemaData & { key?: string }; + return { + key: schemaObj.key || schemaObj.name || `schema-${idx}`, + name: schemaObj.name || `Schema ${idx}`, + description: schemaObj.description, + fields: schemaObj.fields || [], + indexes: schemaObj.indexes || [], + }; + }); + }, [rawSchemas]); + + // Build GraphQL query for a schema + const buildGraphQLQuery = useCallback((schema: SchemaInfo) => { + const gqlName = schema.key.replace(/-/g, '_'); + const fieldNames = schema.fields.map(f => f.name).join('\n '); + return `query { ${gqlName} { ${fieldNames} } }`; + }, []); + + // Core search function - searches ALL schemas, stores ALL results + const performSearch = useCallback(async (vectors: number[][]) => { + try { + // Always search ALL schemas + const embeddingsResults = await Promise.all( + schemas.map(async (schema) => { + try { + const matches = await executeQueryAsync({ + vectors, + schemaName: schema.key, + collection: COLLECTION, + limit: 10, + }); + return matches.map(m => ({ ...m, schemaKey: schema.key })); + } catch { + return []; + } + }) + ); + + const flatMatches = embeddingsResults.flat(); + + // Deduplicate + const seen = new Set(); + const uniqueMatches = flatMatches.filter(match => { + const key = `${match.schemaKey}:${match.index_value.join(',')}`; + if (seen.has(key)) return false; + seen.add(key); + return true; + }); + + // Fetch full row data for schemas with matches + const schemaKeysWithMatches = [...new Set(uniqueMatches.map(m => m.schemaKey))]; + const rowDataBySchema: Record[]> = {}; + + await Promise.all( + schemaKeysWithMatches.map(async (schemaKey) => { + const schema = schemas.find(s => s.key === schemaKey); + if (!schema || schema.fields.length === 0) return; + + try { + const query = buildGraphQLQuery(schema); + const result = await executeRowsQueryAsync({ query, collection: COLLECTION }); + const gqlName = schemaKey.replace(/-/g, '_'); + const rows = (result?.data as Record)?.[gqlName] || []; + rowDataBySchema[schemaKey] = rows as Record[]; + } catch (err) { + console.error(`Failed to fetch rows for ${schemaKey}:`, err); + } + }) + ); + + // Match row data to embeddings results + const matchesWithRowData = uniqueMatches.map(match => { + const rows = rowDataBySchema[match.schemaKey] || []; + const indexFields = match.index_name.split('.'); + const indexFieldName = indexFields[indexFields.length - 1]; + + const matchedRow = rows.find(row => { + const rowValue = row[indexFieldName]; + return match.index_value.some(iv => + String(rowValue).toLowerCase() === iv.toLowerCase() + ); + }); + + return { ...match, rowData: matchedRow }; + }); + + setAllMatches(matchesWithRowData); + setHasSearched(true); + } finally { + setIsSearching(false); + } + }, [schemas, executeQueryAsync, executeRowsQueryAsync, buildGraphQLQuery]); + + // Handle search button click + const handleSearch = useCallback(async () => { + const term = searchTerm.trim(); + if (!term) return; + + setIsSearching(true); + setAllMatches([]); + + // If same term, use refetch; otherwise set new term + if (term === embeddingsTerm && embeddings && embeddings.length > 0) { + // Same term - we already have embeddings, just re-run the search + await performSearch(embeddings); + } else { + // New term - update embeddings term and wait for it + setEmbeddingsTerm(term); + } + }, [searchTerm, embeddingsTerm, embeddings, performSearch]); + + // When embeddings become available for a new term, run the search + // This only triggers when embeddingsTerm changes and embeddings load + const prevEmbeddingsTermRef = useMemo(() => ({ current: "" }), []); + + if ( + isSearching && + embeddingsTerm && + embeddings && + embeddings.length > 0 && + !embeddingsLoading && + prevEmbeddingsTermRef.current !== embeddingsTerm + ) { + prevEmbeddingsTermRef.current = embeddingsTerm; + performSearch(embeddings); + } + + // Filter results for display (doesn't affect stored data) + const displayMatches = useMemo(() => { + if (!selectedSchema) return allMatches; + return allMatches.filter(m => m.schemaKey === selectedSchema); + }, [allMatches, selectedSchema]); + + // Group filtered matches by schema for display + const matchesBySchema = useMemo(() => { + return displayMatches.reduce((acc, match) => { + if (!acc[match.schemaKey]) { + acc[match.schemaKey] = []; + } + acc[match.schemaKey].push(match); + return acc; + }, {} as Record); + }, [displayMatches]); + + if (schemasLoading) { + return ; + } + + if (schemasError) { + return ; + } + + // Build filter items from schemas + const filterItems: FilterItem[] = schemas.slice(0, 10).map((schema) => ({ + key: schema.key, + label: schema.name, + })); + + const filterStats = selectedSchema + ? `${displayMatches.length} of ${allMatches.length} results` + : `${allMatches.length} results`; + + return ( +
+ {/* Schema Filter Bar */} + + + {/* Search Input */} +
+ SEARCH DATA + +
+ + {/* Results Area */} +
+ {!hasSearched && !isSearching ? ( +
+ Enter a search term to find data across tables. +
+ ) : isSearching ? ( +
+ Searching... +
+ ) : displayMatches.length === 0 ? ( +
+ {selectedSchema ? "No matches in this schema. Try selecting 'All'." : "No matches found."} +
+ ) : ( +
+ {Object.entries(matchesBySchema).map(([schemaKey, schemaMatches]) => { + if (!schemaMatches || schemaMatches.length === 0) return null; + const schema = schemas.find(s => s.key === schemaKey); + + return ( + + {/* Table Header */} +
+ + ▤ {schema?.name || schemaKey} + + + {schemaMatches.length} matches + +
+ + {/* Results List */} +
+ {schemaMatches.map((match, idx) => ( +
{ + e.currentTarget.style.background = surface.card; + }} + onMouseLeave={(e) => { + e.currentTarget.style.background = "transparent"; + }} + > + {match.rowData ? ( +
+ {Object.entries(match.rowData).map(([key, value]) => ( +
+ + {key} + +
+ {String(value ?? "")} +
+
+ ))} +
+ ) : ( +
+ {match.text} +
+ )} + +
+ 0.8 ? semantic.success : match.score > 0.5 ? palette.amber : text.subtle, + }}> + {(match.score * 100).toFixed(1)}% match + +
+
+ ))} +
+
+ ); + })} +
+ )} +
+
+ ); +} diff --git a/ai-context/context-graph-demo/src/pages/ExplainView.tsx b/ai-context/context-graph-demo/src/pages/ExplainView.tsx new file mode 100644 index 00000000..5f69fbe5 --- /dev/null +++ b/ai-context/context-graph-demo/src/pages/ExplainView.tsx @@ -0,0 +1,1411 @@ +import { useState, useEffect, useRef, useCallback, useMemo } from "react"; +import { SectionLabel, SearchInput, ExplainGraph } from "../components"; +import type { ExplainGraphNode, ExplainGraphEdge } from "../components"; +import { COLLECTION } from "../config"; +import { useInference } from "@trustgraph/react-state"; +import type { ExplainEvent, Triple, Term } from "@trustgraph/react-state"; +import { useSocket } from "@trustgraph/react-provider"; +import type { BaseApi } from "@trustgraph/react-provider"; +import { palette, text, border, withGlow, semantic } from "../theme"; + +// ── Namespaces ────────────────────────────────────────────────────── +const TG = "https://trustgraph.ai/ns/"; +const TG_QUERY = TG + "query"; +const TG_CONCEPT = TG + "concept"; +const TG_ENTITY = TG + "entity"; +const TG_EDGE_COUNT = TG + "edgeCount"; +const TG_SELECTED_EDGE = TG + "selectedEdge"; +const TG_EDGE = TG + "edge"; +const TG_REASONING = TG + "reasoning"; +const TG_CONTENT = TG + "content"; +const TG_CONTAINS = TG + "contains"; +const TG_CHUNK_COUNT = TG + "chunkCount"; +const TG_ACTION = TG + "action"; +const TG_ARGUMENTS = TG + "arguments"; +const TG_THOUGHT = TG + "thought"; +const TG_OBSERVATION = TG + "observation"; +const TG_DOCUMENT = TG + "document"; +const RDF_TYPE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"; +const PROV = "http://www.w3.org/ns/prov#"; +const PROV_STARTED_AT_TIME = PROV + "startedAtTime"; +const PROV_WAS_DERIVED_FROM = PROV + "wasDerivedFrom"; +const RDFS_LABEL = "http://www.w3.org/2000/01/rdf-schema#label"; + +// ── Types ─────────────────────────────────────────────────────────── + +interface EdgeSelection { + edgeUri: string; + edge?: { s: string; p: string; o: string }; + edgeLabels?: { s: string; p: string; o: string }; + reasoning?: string; + sources?: ProvenanceChain[]; +} + +interface ProvenanceChain { + chain: { uri: string; label: string }[]; +} + +interface QuestionData { + query?: string; + timestamp?: string; +} + +interface GroundingData { + concepts: string[]; +} + +interface ExplorationData { + edgeCount?: string; + chunkCount?: string; + entities: string[]; + entityLabels?: string[]; +} + +interface FocusData { + edgeSelections: EdgeSelection[]; +} + +interface SynthesisData { + contentLength?: number; +} + +interface AnalysisData { + action?: string; + arguments?: string; + thoughtUri?: string; + observationUri?: string; +} + +interface ConclusionData { + documentUri?: string; +} + +interface ReflectionData { + documentUri?: string; + reflectionType?: string; +} + +type EventData = QuestionData | GroundingData | ExplorationData | FocusData | SynthesisData | AnalysisData | ConclusionData | ReflectionData; + +interface SourcePanelState { + chunkUri: string; + documentUri: string; + documentTitle?: string; + documentTags?: string[]; + chunkText?: string; + loading: boolean; + error?: string; +} + +interface ExplainNode { + explainId: string; + explainGraph: string; + eventType: string; + data?: EventData; + fetched: boolean; + fetching: boolean; + error?: string; +} + +// ── Helpers ───────────────────────────────────────────────────────── + +function shortUri(uri: string): string { + if (uri.startsWith("urn:trustgraph:prov:")) return "tg:prov:" + uri.slice(20); + if (uri.startsWith("urn:trustgraph:")) return "tg:" + uri.slice(15); + if (uri.startsWith(TG)) return "tg:" + uri.slice(TG.length); + if (uri.startsWith(PROV)) return "prov:" + uri.slice(PROV.length); + if (uri.startsWith("http://www.w3.org/2000/01/rdf-schema#")) return "rdfs:" + uri.slice(37); + if (uri.startsWith("http://www.w3.org/1999/02/22-rdf-syntax-ns#")) return "rdf:" + uri.slice(43); + if (uri.startsWith("urn:")) return uri; + const pos = Math.max(uri.lastIndexOf("#"), uri.lastIndexOf("/")); + return pos >= 0 ? uri.slice(pos + 1) : uri; +} + +// Ordered type checks — mirrors the Python ExplainEntity.from_triples logic. +// Each entry: [type URI to look for, display name]. +// First match wins. +const TYPE_CHECKS: [string, string][] = [ + [TG + "GraphRagQuestion", "question"], + [TG + "DocRagQuestion", "question"], + [TG + "AgentQuestion", "question"], + [TG + "Question", "question"], + [TG + "Grounding", "grounding"], + [TG + "Exploration", "exploration"], + [TG + "Focus", "focus"], + [TG + "Synthesis", "synthesis"], + [TG + "Reflection", "reflection"], + [TG + "Thought", "reflection"], + [TG + "Observation", "reflection"], + [TG + "Analysis", "analysis"], + [TG + "Conclusion", "conclusion"], +]; + +function getEventTypeFromTriples(triples: Triple[]): string { + const types = new Set(); + for (const t of triples) { + if (predIri(t) === RDF_TYPE) types.add(objValue(t)); + } + for (const [typeUri, displayName] of TYPE_CHECKS) { + if (types.has(typeUri)) return displayName; + } + return "unknown"; +} + +function eventTypeColor(eventType: string): string { + switch (eventType) { + case "question": return palette.amber; + case "grounding": return palette.orange; + case "exploration": return palette.blue; + case "focus": return palette.purple; + case "analysis": return palette.purple; + case "reflection": return palette.cyan; + case "synthesis": return palette.emerald; + case "conclusion": return palette.emerald; + default: return text.muted; + } +} + +// Get predicate IRI from a triple +function predIri(triple: Triple): string { + return triple.p.t === "i" ? triple.p.i : ""; +} + +// Get object value (string) from a triple +function objValue(triple: Triple): string { + const o = triple.o; + if (o.t === "i") return o.i; + if (o.t === "l") return o.v; + if (o.t === "b") return o.d; + return ""; +} + +// Get object as quoted triple {s, p, o} if it's a triple term +function objQuotedTriple(triple: Triple): { s: string; p: string; o: string } | null { + const o = triple.o; + if (o.t === "t" && o.tr) { + return { + s: o.tr.s.t === "i" ? o.tr.s.i : (o.tr.s as any).v || "", + p: o.tr.p.t === "i" ? o.tr.p.i : (o.tr.p as any).v || "", + o: o.tr.o.t === "i" ? o.tr.o.i : (o.tr.o as any).v || "", + }; + } + return null; +} + +// ── KG query helpers (using the socket API) ───────────────────────── + +async function queryTriples( + api: ReturnType, + subject: string, + predicate?: string, + limit = 100, + collection = COLLECTION, + graph?: string, +): Promise { + const s: Term = { t: "i", i: subject }; + const p: Term | undefined = predicate ? { t: "i", i: predicate } : undefined; + return api.triplesQuery(s, p, undefined, limit, collection, graph); +} + +// Backoff retry for eventually-consistent event triples. +// Calls onUpdate each time new triples arrive, settles when two consecutive +// fetches return the same count, or after maxTries (6 = 1 initial + 5 retries). +// Backoff: 50ms × 3 each retry, capped at 1500ms. +async function queryTriplesUntilSettled( + api: ReturnType, + subject: string, + onUpdate: (triples: Triple[]) => void, + limit = 100, + collection = COLLECTION, + graph?: string, + maxTries = 6, +): Promise { + let prevCount = -1; + let settled: Triple[] = []; + let delay = 50; + + for (let attempt = 0; attempt < maxTries; attempt++) { + const triples = await queryTriples(api, subject, undefined, limit, collection, graph); + + if (triples.length !== prevCount) { + settled = triples; + onUpdate(triples); + } else { + // Two consecutive identical counts — settled + return settled; + } + + prevCount = triples.length; + + if (attempt < maxTries - 1) { + await new Promise(r => setTimeout(r, delay)); + delay = Math.min(delay * 3, 1500); + } + } + + return settled; +} + +// Resolve rdfs:label for a URI, with cache +async function resolveLabel( + api: ReturnType, + uri: string, + cache: Map, +): Promise { + if (cache.has(uri)) return cache.get(uri)!; + try { + const triples = await api.triplesQuery( + { t: "i", i: uri }, + { t: "i", i: RDFS_LABEL }, + undefined, + 1, + COLLECTION, + ); + const label = triples.length > 0 ? objValue(triples[0]) : shortUri(uri); + cache.set(uri, label); + return label; + } catch { + const fallback = shortUri(uri); + cache.set(uri, fallback); + return fallback; + } +} + +// Trace prov:wasDerivedFrom chain up to root +async function traceProvenanceChain( + api: ReturnType, + startUri: string, + labelCache: Map, + maxDepth = 10, +): Promise { + const chain: { uri: string; label: string }[] = []; + let current: string | null = startUri; + + for (let i = 0; i < maxDepth && current; i++) { + const label = await resolveLabel(api, current, labelCache); + chain.push({ uri: current, label }); + + // Find parent + const parentTriples = await api.triplesQuery( + { t: "i", i: current }, + { t: "i", i: PROV_WAS_DERIVED_FROM }, + undefined, + 1, + COLLECTION, + ); + + const parentUri = parentTriples.length > 0 ? objValue(parentTriples[0]) : null; + if (!parentUri || parentUri === current) break; + current = parentUri; + } + + return { chain }; +} + +// Query edge provenance: find subgraphs containing the edge via tg:contains +async function queryEdgeProvenance( + api: ReturnType, + edge: { s: string; p: string; o: string }, + labelCache: Map, +): Promise { + // Find subgraphs that contain this edge: ?subgraph tg:contains <> + const oTerm: Term = (edge.o.startsWith("http") || edge.o.startsWith("urn:")) + ? { t: "i", i: edge.o } + : { t: "l", v: edge.o }; + + const containsTriples = await api.triplesQuery( + undefined, + { t: "i", i: TG_CONTAINS }, + { + t: "t", + tr: { + s: { t: "i", i: edge.s }, + p: { t: "i", i: edge.p }, + o: oTerm, + }, + }, + 10, + COLLECTION, + ); + + // For each subgraph, follow wasDerivedFrom to sources + const chains: ProvenanceChain[] = []; + for (const t of containsTriples) { + const subgraphUri = t.s.t === "i" ? t.s.i : ""; + if (!subgraphUri) continue; + + const derivedTriples = await api.triplesQuery( + { t: "i", i: subgraphUri }, + { t: "i", i: PROV_WAS_DERIVED_FROM }, + undefined, + 10, + COLLECTION, + ); + + for (const dt of derivedTriples) { + const sourceUri = objValue(dt); + if (sourceUri) { + const chain = await traceProvenanceChain(api, sourceUri, labelCache); + chains.push(chain); + } + } + } + + return chains; +} + +// ── Parse basic event data (synchronous, from already-fetched triples) ── + +function parseBasicEventData(eventType: string, triples: Triple[]): EventData { + switch (eventType) { + case "question": { + const data: QuestionData = {}; + for (const t of triples) { + const p = predIri(t); + if (p === TG_QUERY) data.query = objValue(t); + if (p === PROV_STARTED_AT_TIME) data.timestamp = objValue(t); + } + return data; + } + + case "grounding": { + const concepts: string[] = []; + for (const t of triples) { + if (predIri(t) === TG_CONCEPT) { + const v = objValue(t); + if (v) concepts.push(v); + } + } + return { concepts } as GroundingData; + } + + case "exploration": { + const data: ExplorationData = { entities: [] }; + for (const t of triples) { + const p = predIri(t); + if (p === TG_EDGE_COUNT) data.edgeCount = objValue(t); + if (p === TG_CHUNK_COUNT) data.chunkCount = objValue(t); + if (p === TG_ENTITY) { + const uri = objValue(t); + if (uri) data.entities.push(uri); + } + } + return data; + } + + case "focus": { + const edgeSelUris: string[] = []; + for (const t of triples) { + if (predIri(t) === TG_SELECTED_EDGE) { + const uri = objValue(t); + if (uri) edgeSelUris.push(uri); + } + } + return { + edgeSelections: edgeSelUris.map(uri => ({ edgeUri: uri })), + } as FocusData; + } + + case "synthesis": { + const data: SynthesisData = {}; + for (const t of triples) { + if (predIri(t) === TG_CONTENT) { + data.contentLength = objValue(t).length; + } + } + return data; + } + + case "analysis": { + const data: AnalysisData = {}; + for (const t of triples) { + const p = predIri(t); + if (p === TG_ACTION) data.action = objValue(t); + if (p === TG_ARGUMENTS) data.arguments = objValue(t); + if (p === TG_THOUGHT) data.thoughtUri = objValue(t); + if (p === TG_OBSERVATION) data.observationUri = objValue(t); + } + return data; + } + + case "conclusion": { + const data: ConclusionData = {}; + for (const t of triples) { + if (predIri(t) === TG_DOCUMENT) data.documentUri = objValue(t); + } + return data; + } + + case "reflection": { + const data: ReflectionData = {}; + for (const t of triples) { + if (predIri(t) === TG_DOCUMENT) data.documentUri = objValue(t); + } + return data; + } + + default: + return {}; + } +} + +// ── Enrich event data (async — labels, edge details, provenance) ──── + +async function enrichEventData( + api: ReturnType, + eventType: string, + _triples: Triple[], + basicData: EventData, + labelCache: Map, + explainGraph: string, +): Promise { + switch (eventType) { + case "exploration": { + const data = { ...(basicData as ExplorationData) }; + if (data.entities.length > 0) { + data.entityLabels = await Promise.all( + data.entities.map(uri => resolveLabel(api, uri, labelCache)) + ); + } + return data; + } + + case "focus": { + const basic = basicData as FocusData; + const edgeSelections = await Promise.all(basic.edgeSelections.map(async (basicSel) => { + const edgeTriples = await queryTriples( + api, basicSel.edgeUri, undefined, 100, COLLECTION, explainGraph, + ); + + const sel: EdgeSelection = { edgeUri: basicSel.edgeUri }; + for (const et of edgeTriples) { + const p = predIri(et); + if (p === TG_EDGE) sel.edge = objQuotedTriple(et) || undefined; + if (p === TG_REASONING) sel.reasoning = objValue(et); + } + + if (sel.edge) { + const [labels, sources] = await Promise.all([ + Promise.all([ + resolveLabel(api, sel.edge.s, labelCache), + resolveLabel(api, sel.edge.p, labelCache), + resolveLabel(api, sel.edge.o, labelCache), + ]), + queryEdgeProvenance(api, sel.edge, labelCache), + ]); + sel.edgeLabels = { s: labels[0], p: labels[1], o: labels[2] }; + sel.sources = sources; + } + + return sel; + })); + + return { edgeSelections } as FocusData; + } + + default: + return basicData; + } +} + +// ── Component ─────────────────────────────────────────────────────── + +type QueryMode = "graph-rag" | "doc-rag" | "agent"; + +const queryModeLabels: Record = { + "graph-rag": "Graph RAG", + "doc-rag": "Doc RAG", + "agent": "Agent", +}; + +export function ExplainView() { + const [input, setInput] = useState(""); + const [queryMode, setQueryMode] = useState("graph-rag"); + const [response, setResponse] = useState(""); + const [agentMessages, setAgentMessages] = useState<{ type: string; text: string; done?: boolean }[]>([]); + const [isQuerying, setIsQuerying] = useState(false); + const [explainNodes, setExplainNodes] = useState([]); + const [error, setError] = useState(null); + const [highlightedNodeIds, setHighlightedNodeIds] = useState([]); + const [highlightedEdgeIds, setHighlightedEdgeIds] = useState([]); + const [sourcePanel, setSourcePanel] = useState(null); + const scrollRef = useRef(null); + const explainScrollRef = useRef(null); + const labelCacheRef = useRef(new Map()); + + const { graphRag, documentRag, agent } = useInference({}); + const socket = useSocket(); + + useEffect(() => { + scrollRef.current?.scrollIntoView({ behavior: "smooth" }); + }, [response]); + + useEffect(() => { + explainScrollRef.current?.scrollIntoView({ behavior: "smooth" }); + }, [explainNodes]); + + // Fetch event data when new nodes arrive + // Use a ref to access current nodes without re-rendering + const nodesRef = useRef(explainNodes); + nodesRef.current = explainNodes; + + const fetchNode = useCallback(async (explainId: string) => { + setExplainNodes(prev => prev.map(n => + n.explainId === explainId ? { ...n, fetching: true } : n + )); + + try { + const api = socket.flow("default"); + const node = nodesRef.current.find(n => n.explainId === explainId); + if (!node) return; + + const updateNode = (updates: Partial) => { + setExplainNodes(prev => prev.map(n => + n.explainId === explainId ? { ...n, ...updates } : n + )); + }; + + // Phase 1: Fetch event triples with backoff until settled. + // These are eventually consistent — render progressively as they arrive. + let latestEventType = "unknown"; + let latestBasicData: EventData = {}; + + const settledTriples = await queryTriplesUntilSettled( + api, node.explainId, + (triples) => { + latestEventType = getEventTypeFromTriples(triples); + latestBasicData = parseBasicEventData(latestEventType, triples); + updateNode({ eventType: latestEventType, data: latestBasicData, fetched: true, fetching: false }); + }, + 100, COLLECTION, node.explainGraph, + ); + + if (settledTriples.length === 0) { + updateNode({ fetched: true, fetching: false }); + return; + } + + // Phase 2: Enrich with KG lookups (labels, edge details, provenance). + // These reference known-to-exist data — no retry needed, just fetch once. + const enriched = await enrichEventData(api, latestEventType, settledTriples, latestBasicData, labelCacheRef.current, node.explainGraph); + if (enriched !== latestBasicData) { + updateNode({ data: enriched }); + } + } catch (err) { + setExplainNodes(prev => prev.map(n => + n.explainId === explainId + ? { ...n, error: String(err), fetching: false } + : n + )); + } + }, [socket]); + + useEffect(() => { + for (const node of explainNodes) { + if (!node.fetched && !node.fetching && !node.error) { + fetchNode(node.explainId); + } + } + }, [explainNodes, fetchNode]); + + const addExplainEvent = useCallback((event: ExplainEvent) => { + setExplainNodes(prev => { + if (prev.some(n => n.explainId === event.explainId)) return prev; + return [...prev, { + explainId: event.explainId, + explainGraph: event.explainGraph, + eventType: "unknown", + fetched: false, + fetching: false, + }]; + }); + }, []); + + const handleSubmit = useCallback(async (query: string) => { + if (!query.trim() || isQuerying) return; + + setIsQuerying(true); + setResponse(""); + setAgentMessages([]); + setExplainNodes([]); + setHighlightedNodeIds([]); + setHighlightedEdgeIds([]); + setSourcePanel(null); + setError(null); + setInput(""); + labelCacheRef.current.clear(); + + const trimmed = query.trim(); + + try { + switch (queryMode) { + case "graph-rag": { + await graphRag({ + input: trimmed, + collection: COLLECTION, + options: { maxSubgraphSize: 150 }, + callbacks: { + onChunk: (chunk: string) => setResponse(prev => prev + chunk), + onExplain: addExplainEvent, + onError: (err: string) => setError(err), + }, + }); + break; + } + + case "doc-rag": { + await documentRag({ + input: trimmed, + collection: COLLECTION, + callbacks: { + onChunk: (chunk: string) => setResponse(prev => prev + chunk), + onExplain: addExplainEvent, + onError: (err: string) => setError(err), + }, + }); + break; + } + + case "agent": { + // Track current streaming message per type + const accum: Record = {}; + + const appendChunk = (type: string, chunk: string, complete?: boolean) => { + accum[type] = (accum[type] || "") + chunk; + const currentText = accum[type]; + setAgentMessages(prev => { + // Find existing in-progress message of this type at end + const lastIdx = prev.length - 1; + if (lastIdx >= 0 && prev[lastIdx].type === type && !prev[lastIdx].done) { + const updated = [...prev]; + updated[lastIdx] = { type, text: currentText, done: !!complete }; + return updated; + } + // New message + return [...prev, { type, text: currentText, done: !!complete }]; + }); + if (complete) { + accum[type] = ""; + } + }; + + await agent({ + input: trimmed, + callbacks: { + onThink: (chunk: string, complete?: boolean) => appendChunk("thinking", chunk, complete), + onObserve: (chunk: string, complete?: boolean) => appendChunk("observation", chunk, complete), + onAnswer: (chunk: string, complete?: boolean) => appendChunk("answer", chunk, complete), + onExplain: addExplainEvent, + onError: (err: string) => setError(err), + }, + }); + break; + } + } + } catch (err) { + setError(String(err)); + } finally { + setIsQuerying(false); + } + }, [graphRag, documentRag, agent, queryMode, isQuerying, addExplainEvent]); + + // ── Derive graph nodes and edges from explain events ────────────── + const { graphNodes, graphEdges } = useMemo(() => { + const nodeMap = new Map(); + const edgeList: ExplainGraphEdge[] = []; + + for (const node of explainNodes) { + if (!node.fetched || !node.data) continue; + + if (node.eventType === "exploration") { + const d = node.data as ExplorationData; + const labels = d.entityLabels || []; + d.entities.forEach((uri, i) => { + if (!nodeMap.has(uri)) { + nodeMap.set(uri, { id: uri, label: labels[i] || shortUri(uri), color: palette.blue }); + } + }); + } + + if (node.eventType === "focus") { + const d = node.data as FocusData; + for (const sel of d.edgeSelections) { + if (!sel.edge) continue; + const { s, p, o } = sel.edge; + const sLabel = sel.edgeLabels?.s || shortUri(s); + const pLabel = sel.edgeLabels?.p || shortUri(p); + const oLabel = sel.edgeLabels?.o || shortUri(o); + + // Ensure nodes exist + if (!nodeMap.has(s)) nodeMap.set(s, { id: s, label: sLabel, color: palette.pink }); + if (!nodeMap.has(o)) nodeMap.set(o, { id: o, label: oLabel, color: palette.pink }); + + edgeList.push({ + id: sel.edgeUri, + from: s, + to: o, + label: pLabel, + reasoning: sel.reasoning, + }); + } + } + } + + return { graphNodes: Array.from(nodeMap.values()), graphEdges: edgeList }; + }, [explainNodes]); + + // ── Entity/edge click → neighbourhood highlight on graph ───────── + const handleEntityClick = useCallback((entityUri: string) => { + // Highlight this node + connected edges + neighbour nodes + const connectedEdges = graphEdges.filter(e => e.from === entityUri || e.to === entityUri); + const neighbourIds = new Set([entityUri]); + const edgeIds: string[] = []; + for (const e of connectedEdges) { + edgeIds.push(e.id); + neighbourIds.add(e.from); + neighbourIds.add(e.to); + } + setHighlightedNodeIds(Array.from(neighbourIds)); + setHighlightedEdgeIds(edgeIds); + }, [graphEdges]); + + const handleEdgeClick = useCallback((sel: EdgeSelection) => { + // Highlight this edge + its two endpoint nodes + const nodeIds: string[] = []; + if (sel.edge) { + nodeIds.push(sel.edge.s, sel.edge.o); + } + setHighlightedNodeIds(nodeIds); + setHighlightedEdgeIds([sel.edgeUri]); + }, []); + + const handleSourceClick = useCallback((source: ProvenanceChain) => { + // chain[0] = chunk (closest to edge), chain[last] = root document + const chunkNode = source.chain[0]; + const docNode = source.chain[source.chain.length - 1]; + if (!chunkNode || !docNode) return; + + // Same chunk — ignore (use the × button to close) + if (sourcePanel?.chunkUri === chunkNode.uri) return; + + setSourcePanel({ + chunkUri: chunkNode.uri, + documentUri: docNode.uri, + loading: true, + }); + + const librarian = socket.librarian(); + + // Fetch parent document metadata (title, tags) from librarian + librarian.getDocumentMetadata(docNode.uri).then(meta => { + setSourcePanel(prev => prev?.chunkUri === chunkNode.uri + ? { ...prev, documentTitle: meta?.title, documentTags: meta?.tags } + : prev + ); + }).catch(() => { + // Metadata not available — that's OK + }); + + // The chunk URI is itself a document ID in the librarian — stream it directly + let chunkText = ""; + librarian.streamDocument( + chunkNode.uri, + (content, _chunkIndex, _totalChunks, complete) => { + try { + chunkText += atob(content); + } catch { + chunkText += content; + } + if (complete) { + setSourcePanel(prev => prev?.chunkUri === chunkNode.uri + ? { ...prev, chunkText, loading: false } + : prev + ); + } + }, + (err) => { + setSourcePanel(prev => prev?.chunkUri === chunkNode.uri + ? { ...prev, loading: false, error: err } + : prev + ); + }, + ); + }, [socket, sourcePanel?.chunkUri]); + + return ( +
+ {/* LHS: Query + Response */} +
+
+ {queryModeLabels[queryMode].toUpperCase()} QUERY +
+ {(["graph-rag", "doc-rag", "agent"] as QueryMode[]).map(mode => ( + + ))} +
+ handleSubmit(input)} + placeholder="Ask a question..." + buttonText="Query" + isLoading={isQuerying} + buttonColor={palette.cyan} + /> +
+ +
+ {error && ( +
+
ERROR
+
{error}
+
+ )} + + {!response && !isQuerying && !error && agentMessages.length === 0 && ( +
+ Ask a question to see {queryModeLabels[queryMode]} in action with live explainability. +
+ )} + + {/* Streaming response for graph-rag and doc-rag */} + {(response || (isQuerying && queryMode !== "agent")) && ( +
+ {response && ( +
+
+ RESPONSE +
+
{response}
+
+ )} + {isQuerying && ( +
+ {response ? "Streaming..." : "Processing query..."} +
+ )} +
+ )} + + {/* Agent messages */} + {queryMode === "agent" && agentMessages.length > 0 && ( +
+ {agentMessages.map((msg, i) => { + const colors: Record = { + thinking: palette.purple, + observation: palette.blue, + answer: palette.emerald, + }; + const color = colors[msg.type] || text.muted; + return ( +
+
+ {msg.type} +
+
{msg.text}
+
+ ); + })} +
+ )} + + {queryMode === "agent" && isQuerying && agentMessages.length === 0 && ( +
+ Agent is working... +
+ )} + +
+
+ + {/* Source text panel — shown when a provenance link is clicked */} + {sourcePanel && ( +
+ {/* Header with document metadata */} +
+
+ SOURCE + {sourcePanel.documentTitle ? ( + + {sourcePanel.documentTitle} + + ) : ( + + {shortUri(sourcePanel.documentUri)} + + )} + {sourcePanel.documentTags && sourcePanel.documentTags.length > 0 && ( + + {sourcePanel.documentTags.map((tag, i) => ( + + {tag} + + ))} + + )} +
+ +
+ + {/* Chunk text content */} +
+ {sourcePanel.loading && ( +
+ Loading source text... +
+ )} + {sourcePanel.error && ( +
+ {sourcePanel.error} +
+ )} + {sourcePanel.chunkText && ( +
+ {sourcePanel.chunkText} +
+ )} +
+
+ )} +
+ + {/* RHS: Graph + Explainability panel */} +
+ {/* Graph view — top half */} +
+ { + setHighlightedNodeIds(prev => + prev.includes(nodeId) ? prev.filter(id => id !== nodeId) : [...prev, nodeId] + ); + }} + onEdgeClick={(edgeId) => { + setHighlightedEdgeIds(prev => + prev.includes(edgeId) ? prev.filter(id => id !== edgeId) : [...prev, edgeId] + ); + }} + /> +
+ + {/* Event cards — bottom half */} +
+
+ + EVENTS + {explainNodes.length > 0 && ( + + {explainNodes.length} event{explainNodes.length !== 1 ? "s" : ""} + + )} + +
+ +
+ {explainNodes.length === 0 && !isQuerying && ( +
+ Explain events will appear here as the query progresses. +
+ )} + + {isQuerying && explainNodes.length === 0 && ( +
+ Waiting for explain events... +
+ )} + +
+ {explainNodes.map((node, idx) => ( + + ))} +
+
+
+
+
+
+ ); +} + +// ── ExplainCard ───────────────────────────────────────────────────── + +function ExplainCard({ node, index, onEntityClick, onEdgeClick, onSourceClick }: { + node: ExplainNode; + index: number; + onEntityClick?: (uri: string) => void; + onEdgeClick?: (sel: EdgeSelection) => void; + onSourceClick?: (source: ProvenanceChain) => void; +}) { + const typeColor = eventTypeColor(node.eventType); + + return ( +
+ {/* Header */} +
+ + {index + 1} + + + {node.eventType} + + {node.fetching && ( + loading... + )} +
+ + {/* Event data */} + {node.fetched && node.data && ( + + )} + + {node.error && ( +
{node.error}
+ )} +
+ ); +} + +// ── EventDataView ─────────────────────────────────────────────────── + +function EventDataView({ eventType, data, onEntityClick, onEdgeClick, onSourceClick }: { + eventType: string; + data: EventData; + onEntityClick?: (uri: string) => void; + onEdgeClick?: (sel: EdgeSelection) => void; + onSourceClick?: (source: ProvenanceChain) => void; +}) { + const mono = { fontFamily: "'IBM Plex Mono', monospace" } as const; + + switch (eventType) { + case "question": { + const d = data as QuestionData; + return ( +
+ {d.query && ( +
+ Query: {d.query} +
+ )} + {d.timestamp && ( +
+ {d.timestamp} +
+ )} +
+ ); + } + + case "grounding": { + const d = data as GroundingData; + return ( +
+ {d.concepts.length > 0 && ( + <> +
+ {d.concepts.length} concept{d.concepts.length !== 1 ? "s" : ""} extracted +
+
+ {d.concepts.map((concept, i) => ( + + {concept} + + ))} +
+ + )} +
+ ); + } + + case "exploration": { + const d = data as ExplorationData; + return ( +
+ {d.edgeCount && ( +
+ Subgraph extracted: {d.edgeCount} edges +
+ )} + {d.chunkCount && ( +
+ Chunks retrieved: {d.chunkCount} +
+ )} + {d.entityLabels && d.entityLabels.length > 0 && ( +
+
+ {d.entityLabels.length} seed entit{d.entityLabels.length !== 1 ? "ies" : "y"} +
+
+ {d.entityLabels.map((label, i) => ( + onEntityClick?.(d.entities[i])} + style={{ + fontSize: 11, padding: "3px 8px", borderRadius: 4, + background: withGlow(palette.blue, 0.1), + border: `1px solid ${withGlow(palette.blue, 0.2)}`, + color: text.secondary, ...mono, + cursor: onEntityClick ? "pointer" : "default", + transition: "all 0.15s ease", + }} + onMouseEnter={e => { if (onEntityClick) (e.currentTarget.style.background = withGlow(palette.blue, 0.25)); }} + onMouseLeave={e => { (e.currentTarget.style.background = withGlow(palette.blue, 0.1)); }} + > + {label} + + ))} +
+
+ )} +
+ ); + } + + case "focus": { + const d = data as FocusData; + return ( +
+ {d.edgeSelections && d.edgeSelections.length > 0 && ( + <> +
+ Focused on {d.edgeSelections.length} edge{d.edgeSelections.length !== 1 ? "s" : ""} +
+ {d.edgeSelections.map((sel, i) => ( + onEdgeClick?.(sel)} onSourceClick={onSourceClick} /> + ))} + + )} +
+ ); + } + + case "synthesis": { + const d = data as SynthesisData; + return ( +
+ {d.contentLength != null && ( +
+ Synthesis: {d.contentLength} chars +
+ )} +
+ ); + } + + case "analysis": { + const d = data as AnalysisData; + let parsedArgs: Record | null = null; + if (d.arguments) { + try { parsedArgs = JSON.parse(d.arguments); } catch { /* ignore */ } + } + return ( +
+ {d.action && ( +
+ Tool: {d.action} +
+ )} + {parsedArgs && Object.entries(parsedArgs).map(([key, val]) => ( +
+ {key}: {String(val)} +
+ ))} + {!parsedArgs && d.arguments && ( +
+ {d.arguments} +
+ )} +
+ ); + } + + case "conclusion": { + const d = data as ConclusionData; + return ( +
+ {d.documentUri && ( +
+ {shortUri(d.documentUri)} +
+ )} +
+ ); + } + + case "reflection": { + const d = data as ReflectionData; + return ( +
+ {d.documentUri && ( +
+ {shortUri(d.documentUri)} +
+ )} +
+ ); + } + + default: + return null; + } +} + +// ── EdgeSelectionView ─────────────────────────────────────────────── + +function EdgeSelectionView({ sel, onClick, onSourceClick }: { + sel: EdgeSelection; + onClick?: () => void; + onSourceClick?: (source: ProvenanceChain) => void; +}) { + const mono = { fontFamily: "'IBM Plex Mono', monospace" } as const; + + return ( +
{ if (onClick) e.currentTarget.style.background = withGlow(palette.purple, 0.08); }} + onMouseLeave={e => { e.currentTarget.style.background = "transparent"; }} + > + {/* Edge triple */} + {sel.edgeLabels && ( +
+ {sel.edgeLabels.s} + + {sel.edgeLabels.p} + + {sel.edgeLabels.o} +
+ )} + + {/* Provenance sources — clickable to view source text */} + {sel.sources && sel.sources.length > 0 && ( +
+ {sel.sources.map((source, si) => { + const chainLabel = source.chain.map(c => c.label).join(" → "); + return ( + { + e.stopPropagation(); + onSourceClick?.(source); + }} + title={`View source: ${chainLabel}`} + style={{ + fontSize: 10, padding: "2px 7px", borderRadius: 4, + background: withGlow(palette.amber, 0.08), + border: `1px solid ${withGlow(palette.amber, 0.2)}`, + color: text.hint, ...mono, + cursor: onSourceClick ? "pointer" : "default", + transition: "all 0.15s ease", + }} + onMouseEnter={e => { if (onSourceClick) { e.currentTarget.style.background = withGlow(palette.amber, 0.2); e.currentTarget.style.color = palette.amber; } }} + onMouseLeave={e => { e.currentTarget.style.background = withGlow(palette.amber, 0.08); e.currentTarget.style.color = text.hint; }} + > + {chainLabel} + + ); + })} +
+ )} + + {/* Reasoning - compact */} + {sel.reasoning && ( +
+ {sel.reasoning.length > 120 ? sel.reasoning.slice(0, 120) + "..." : sel.reasoning} +
+ )} +
+ ); +} diff --git a/ai-context/context-graph-demo/src/pages/GraphView.tsx b/ai-context/context-graph-demo/src/pages/GraphView.tsx new file mode 100644 index 00000000..4ddaadb7 --- /dev/null +++ b/ai-context/context-graph-demo/src/pages/GraphView.tsx @@ -0,0 +1,93 @@ +import type { DomainKey, Entity, OntologyDomain } from "../types"; +import { GraphCanvasSVG as GraphCanvas, NodeDetailPanel, LoadingState, FilterBar } from "../components"; +import type { FilterItem } from "../components"; +import { useGraphData } from "../state"; + +interface GraphViewProps { + activeFilter: DomainKey | null; + onFilterChange: (filter: DomainKey | null) => void; + selectedNode: Entity | null; + onNodeSelect: (node: Entity | null) => void; +} + +export function GraphView({ activeFilter, onFilterChange, selectedNode, onNodeSelect }: GraphViewProps) { + const { entities, relationships, ontology, propertyLabels, isLoading, isError } = useGraphData(); + + const highlightedEntities = selectedNode + ? [selectedNode.id, ...relationships.filter(r => r.from === selectedNode.id || r.to === selectedNode.id).map(r => r.from === selectedNode.id ? r.to : r.from)] + : []; + + // Compute relevant filter domains based on selected node's connections + const relevantDomains = selectedNode + ? (() => { + const domains = new Set([selectedNode.domain]); + const connectedIds = relationships + .filter(r => r.from === selectedNode.id || r.to === selectedNode.id) + .map(r => r.from === selectedNode.id ? r.to : r.from); + for (const id of connectedIds) { + const entity = entities.find(e => e.id === id); + if (entity) domains.add(entity.domain); + } + return domains; + })() + : null; + + if (isLoading) { + return ; + } + + if (isError || !ontology) { + return ; + } + + // Build filter items from relevant domains + const filterItems: FilterItem[] = selectedNode + ? (Object.entries(ontology) as [DomainKey, OntologyDomain][]) + .filter(([key]) => relevantDomains?.has(key)) + .slice(0, 10) + .map(([key, data]) => ({ + key, + label: data.label, + icon: data.icon, + color: data.color, + })) + : []; + + return ( + <> + {/* Domain Filter Bar */} + onFilterChange(key as DomainKey | null)} + stats={`${entities.length} entities · ${relationships.length} relationships`} + emptyMessage={selectedNode ? undefined : "Select a node to filter"} + /> + + {/* Main Content */} +
+
+ onNodeSelect(selectedNode?.id === node.id ? null : node)} + activeFilter={activeFilter} + /> +
+ {selectedNode && ( + onNodeSelect(null)} + onNodeSelect={onNodeSelect} + /> + )} +
+ + ); +} diff --git a/ai-context/context-graph-demo/src/pages/OntologyView.tsx b/ai-context/context-graph-demo/src/pages/OntologyView.tsx new file mode 100644 index 00000000..e5191ef7 --- /dev/null +++ b/ai-context/context-graph-demo/src/pages/OntologyView.tsx @@ -0,0 +1,116 @@ +import type { DomainKey, OntologyDomain } from "../types"; +import { SectionLabel, Card, Badge, LoadingState } from "../components"; +import { useGraphData, useOntologySchema } from "../state"; +import { getLocalName } from "../utils"; +import { text, surface, border } from "../theme"; + +export function OntologyView() { + const { ontology, isLoading: graphLoading } = useGraphData(); + const { schema, isLoading: schemaLoading } = useOntologySchema(); + + const isLoading = graphLoading || schemaLoading; + + if (isLoading || !ontology || !schema) { + return ; + } + + // Count total instances + const totalInstances = Object.values(ontology).reduce((sum, d) => sum + d.subclasses.length, 0); + + return ( +
+
+ ONTOLOGY SCHEMA + + {/* Ontology class cards */} +
+ {(Object.entries(ontology) as [DomainKey, OntologyDomain][]).map(([key, data]) => { + // Find datatype properties for this domain from schema + const domainProps = schema.datatypeProperties + .filter(p => p.domain && getLocalName(p.domain) === data.label) + .map(p => p.label); + + return ( + +
+ {data.icon} +
+
{data.label}
+
owl:Class
+
+
+
{data.description}
+ PROPERTIES ({domainProps.length}) +
+ {domainProps.map((p) => ( + {p} + ))} +
+ INSTANCES ({data.subclasses.length}) + {data.subclasses.map((sc) => ( +
+ {sc.label} + {sc.id} +
+ ))} +
+ ); + })} +
+ + {/* Relationship predicates (Object Properties) */} + + RELATIONSHIP PREDICATES ({schema.objectProperties.length}) +
+ {schema.objectProperties.map((prop) => { + const fromDomain = prop.domain ? getLocalName(prop.domain).toLowerCase() as DomainKey : null; + const toDomain = prop.range ? getLocalName(prop.range).toLowerCase() as DomainKey : null; + + return ( + +
+ {prop.label} +
+
+ {fromDomain && ontology[fromDomain] && ( + {ontology[fromDomain].label} + )} + {fromDomain && toDomain && " → "} + {toDomain && ontology[toDomain] && ( + {ontology[toDomain].label} + )} +
+
+ ); + })} +
+
+ + {/* Triple count summary */} +
+ {[ + { label: "Classes", value: schema.classes.length }, + { label: "Instances", value: totalInstances }, + { label: "Object Props", value: schema.objectProperties.length }, + { label: "Data Props", value: schema.datatypeProperties.length }, + ].map((s) => ( +
+
{s.value}
+
{s.label.toUpperCase()}
+
+ ))} +
+
+
+ ); +} diff --git a/ai-context/context-graph-demo/src/pages/QueryView.tsx b/ai-context/context-graph-demo/src/pages/QueryView.tsx new file mode 100644 index 00000000..73b72972 --- /dev/null +++ b/ai-context/context-graph-demo/src/pages/QueryView.tsx @@ -0,0 +1,231 @@ +import { useState, useEffect, useRef } from "react"; +import { GraphCanvasSVG as GraphCanvas, NodeDetailPanel, SectionLabel, Badge, LoadingState, SearchInput, MessageBubble } from "../components"; +import { useGraphData } from "../state"; +import { COLLECTION } from "../config"; +import type { Entity } from "../types"; +import { useChat, useConversation, useEmbeddings, useGraphEmbeddings } from "@trustgraph/react-state"; +import { getLocalName } from "../utils"; +import { palette, text, border, withGlow } from "../theme"; + +// Type for embedding result items +interface EmbeddingResultItem { + id: string; + uri: string; + label: string; + color: string; + icon: string; + isEntity: boolean; +} + +export function QueryView() { + const [customInput, setCustomInput] = useState(""); + const [queryForEmbeddings, setQueryForEmbeddings] = useState(undefined); + const [selectedEntityId, setSelectedEntityId] = useState(null); + const [selectedNode, setSelectedNode] = useState(null); + const scrollRef = useRef(null); + + const { entities, relationships, ontology, propertyLabels, isLoading: graphLoading } = useGraphData(); + const { submitMessage, isSubmitting } = useChat(); + const messages = useConversation((state) => state.messages); + const setChatMode = useConversation((state) => state.setChatMode); + + // Get embeddings for the query text - only fetch when we have a committed query + const { embeddings, isLoading: embeddingsLoading } = useEmbeddings({ + flow: "default", + term: queryForEmbeddings || undefined, + }); + + // Get graph entities from embeddings - only fetch when we have embeddings + const hasEmbeddings = embeddings && embeddings.length > 0; + const { graphEmbeddings, isLoading: graphEmbeddingsLoading } = useGraphEmbeddings({ + vecs: hasEmbeddings ? embeddings : [[]], + limit: hasEmbeddings ? 10 : 0, + collection: COLLECTION, + }); + + // Set chat mode to agent on mount + useEffect(() => { + setChatMode("agent"); + }, [setChatMode]); + + // Auto-scroll to bottom when messages change + useEffect(() => { + scrollRef.current?.scrollIntoView({ behavior: "smooth" }); + }, [messages]); + + const handleSubmit = (query: string) => { + if (query.trim() && !isSubmitting) { + const trimmedQuery = query.trim(); + submitMessage({ input: trimmedQuery }); + setQueryForEmbeddings(trimmedQuery); + setSelectedEntityId(null); + setSelectedNode(null); + setCustomInput(""); + } + }; + + + // Match graph embedding entities to our loaded entities for labels and highlighting + // graphEmbeddings returns RDF terms: { t: "i", i: "http://..." } + // Only show matched entities, deduplicated by URI + const embeddingResults: EmbeddingResultItem[] = []; + const seenUris = new Set(); + + for (const ge of (hasEmbeddings && graphEmbeddings || []) as { t: string; i?: string }[]) { + const uri = ge.i; + if (!uri || seenUris.has(uri)) continue; + + const entityId = getLocalName(uri); + const found = entities.find(e => e.id === entityId || e.uri === uri); + + // Only include actual entities, not properties/concepts + if (found) { + seenUris.add(uri); + embeddingResults.push({ + id: entityId, + uri, + label: found.label, + color: found.color, + icon: found.icon, + isEntity: true, + }); + } + } + + // Auto-select first embedding result when results arrive + useEffect(() => { + if (embeddingResults.length > 0 && !selectedEntityId && !selectedNode) { + setSelectedEntityId(embeddingResults[0].id); + } + }, [embeddingResults.length, selectedEntityId, selectedNode]); + + // Extract entity IDs for highlighting on graph + // Priority: selectedNode (graph click) > selectedEntityId (button click) > all embedding results + const highlightedEntities = (() => { + const focusId = selectedNode?.id || selectedEntityId; + if (!focusId) { + return embeddingResults.map(e => e.id); + } + // Find all entities connected to the focused entity + const connected = new Set([focusId]); + for (const rel of relationships) { + if (rel.from === focusId) { + connected.add(rel.to); + } else if (rel.to === focusId) { + connected.add(rel.from); + } + } + return Array.from(connected); + })(); + + if (graphLoading || !ontology) { + return ; + } + + return ( +
+
+ {/* Query input area */} +
+ AGENT QUERIES + + handleSubmit(customInput)} + placeholder="Type your own question..." + buttonText="Ask" + isLoading={isSubmitting} + buttonColor={palette.amber} + /> +
+ + {/* Related entities from graph embeddings */} + {queryForEmbeddings && ( +
+ + RELATED ENTITIES {(embeddingsLoading || graphEmbeddingsLoading) && loading...} + +
+ {embeddingResults.length === 0 && !embeddingsLoading && !graphEmbeddingsLoading && ( + No related concepts found + )} + {embeddingResults.map((item) => { + const isSelected = selectedEntityId === item.id; + return ( + { + setSelectedEntityId(isSelected ? null : item.id); + setSelectedNode(null); + }} + > + {item.icon} + {item.label} + + ); + })} +
+
+ )} + + {/* Response area */} +
+ {messages.length === 0 ? ( +
+ Type your question to get started. +
+ ) : ( +
+ {messages.map((msg, idx) => ( + + ))} + {isSubmitting && ( +
+ Processing... +
+ )} +
+
+ )} +
+
+ + {/* Graph visualization */} +
+ { + setSelectedNode(selectedNode?.id === node.id ? null : node); + setSelectedEntityId(null); + }} + activeFilter={null} + /> +
+ {selectedNode && ( + setSelectedNode(null)} + onNodeSelect={(node) => { + setSelectedNode(node); + setSelectedEntityId(null); + }} + /> + )} +
+ ); +} diff --git a/ai-context/context-graph-demo/src/pages/index.ts b/ai-context/context-graph-demo/src/pages/index.ts new file mode 100644 index 00000000..08ad3690 --- /dev/null +++ b/ai-context/context-graph-demo/src/pages/index.ts @@ -0,0 +1,5 @@ +export { GraphView } from "./GraphView"; +export { QueryView } from "./QueryView"; +export { ExplainView } from "./ExplainView"; +export { DataView } from "./DataView"; +export { OntologyView } from "./OntologyView"; diff --git a/ai-context/context-graph-demo/src/state/index.ts b/ai-context/context-graph-demo/src/state/index.ts new file mode 100644 index 00000000..32554341 --- /dev/null +++ b/ai-context/context-graph-demo/src/state/index.ts @@ -0,0 +1,9 @@ +// Main data hook - provides entities, relationships, and ontology +export { useGraphData } from "./useGraphData"; + +// Schema hook - for OWL ontology schema view +export { useOntologySchema } from "./useOntologySchema"; + +// Toast notifications +export { useToastStore, toast } from "./toastStore"; +export type { Toast, ToastType } from "./toastStore"; diff --git a/ai-context/context-graph-demo/src/state/toastStore.ts b/ai-context/context-graph-demo/src/state/toastStore.ts new file mode 100644 index 00000000..2f5f1f12 --- /dev/null +++ b/ai-context/context-graph-demo/src/state/toastStore.ts @@ -0,0 +1,52 @@ +import { create } from "zustand"; + +export type ToastType = "success" | "error" | "warning" | "info"; + +export interface Toast { + id: string; + type: ToastType; + message: string; + persistent?: boolean; +} + +interface ToastStore { + toasts: Toast[]; + addToast: (type: ToastType, message: string, persistent?: boolean) => void; + removeToast: (id: string) => void; +} + +let toastId = 0; + +export const useToastStore = create((set) => ({ + toasts: [], + + addToast: (type, message, persistent = false) => { + const id = `toast-${++toastId}`; + set((state) => ({ + toasts: [...state.toasts.slice(-3), { id, type, message, persistent }], + })); + + // Auto-dismiss after 6 seconds unless explicitly persistent + if (!persistent) { + setTimeout(() => { + set((state) => ({ + toasts: state.toasts.filter((t) => t.id !== id), + })); + }, 6000); + } + }, + + removeToast: (id) => { + set((state) => ({ + toasts: state.toasts.filter((t) => t.id !== id), + })); + }, +})); + +// Helper functions for easy access outside React +export const toast = { + success: (message: string) => useToastStore.getState().addToast("success", message), + error: (message: string) => useToastStore.getState().addToast("error", message), + warning: (message: string) => useToastStore.getState().addToast("warning", message), + info: (message: string) => useToastStore.getState().addToast("info", message), +}; diff --git a/ai-context/context-graph-demo/src/state/useGraphData.ts b/ai-context/context-graph-demo/src/state/useGraphData.ts new file mode 100644 index 00000000..001be00d --- /dev/null +++ b/ai-context/context-graph-demo/src/state/useGraphData.ts @@ -0,0 +1,243 @@ +import { useState, useEffect, useMemo } from "react"; +import { useSocket } from "@trustgraph/react-provider"; +import type { Triple } from "@trustgraph/react-state"; +import type { Entity, Relationship, DomainKey, OntologyType } from "../types"; +import { COLLECTION } from "../config"; +import { domainColors } from "../theme"; + +const RDF_TYPE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"; +const RDFS_LABEL = "http://www.w3.org/2000/01/rdf-schema#label"; +const RDFS_COMMENT = "http://www.w3.org/2000/01/rdf-schema#comment"; +const OWL_CLASS = "http://www.w3.org/2002/07/owl#Class"; +const OWL_DATATYPE_PROPERTY = "http://www.w3.org/2002/07/owl#DatatypeProperty"; +const OWL_OBJECT_PROPERTY = "http://www.w3.org/2002/07/owl#ObjectProperty"; + +// Helper to extract value from a Term +function getTermValue(term: { t: string; i?: string; v?: string }): string { + if (term.t === "i") return term.i || ""; + if (term.t === "l") return term.v || ""; + return ""; +} + +// Helper to create a short ID from a URI +function uriToId(uri: string): string { + const hashIndex = uri.lastIndexOf("#"); + const slashIndex = uri.lastIndexOf("/"); + const index = Math.max(hashIndex, slashIndex); + return index >= 0 ? uri.substring(index + 1) : uri; +} + +// Helper to get icon for a class (placeholder for now) +function getClassIcon(_classUri: string): string { + return "●"; +} + +// Helper to extract predicate name from URI +function predicateToName(uri: string): string { + const hashIndex = uri.lastIndexOf("#"); + const slashIndex = uri.lastIndexOf("/"); + const index = Math.max(hashIndex, slashIndex); + const name = index >= 0 ? uri.substring(index + 1) : uri; + return name.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase(); +} + +export function useGraphData(domain?: DomainKey) { + const socket = useSocket(); + const [triples, setTriples] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [isError, setIsError] = useState(false); + const [error, setError] = useState(null); + + useEffect(() => { + let cancelled = false; + + (async () => { + try { + setIsLoading(true); + setIsError(false); + setError(null); + + const api = socket.flow("default"); + const result = await api.triplesQuery( + undefined, undefined, undefined, + 10000, COLLECTION, "", + ); + + if (!cancelled) { + setTriples(result); + setIsLoading(false); + } + } catch (err) { + if (!cancelled) { + setIsError(true); + setError(err instanceof Error ? err : new Error(String(err))); + setIsLoading(false); + } + } + })(); + + return () => { cancelled = true; }; + }, [socket]); + + // Process all data from the query + const { entities, relationships, ontology, propertyLabels } = useMemo(() => { + if (isLoading || !triples) { + return { entities: [], relationships: [], ontology: undefined, propertyLabels: {} }; + } + + // First pass: collect all labels, comments, and find OWL classes and properties + const allLabels = new Map(); + const allComments = new Map(); + const owlClasses = new Set(); + const propertyUris = new Set(); + + for (const triple of triples) { + const subjectUri = getTermValue(triple.s); + const predicate = getTermValue(triple.p); + const objectUri = getTermValue(triple.o); + + if (predicate === RDFS_LABEL) { + allLabels.set(subjectUri, getTermValue(triple.o)); + } else if (predicate === RDFS_COMMENT) { + allComments.set(subjectUri, getTermValue(triple.o)); + } else if (predicate === RDF_TYPE) { + if (objectUri === OWL_CLASS) { + owlClasses.add(subjectUri); + } else if (objectUri === OWL_DATATYPE_PROPERTY || objectUri === OWL_OBJECT_PROPERTY) { + propertyUris.add(subjectUri); + } + } + } + + // Build property labels map: local name -> label + const propertyLabels: Record = {}; + for (const propUri of propertyUris) { + const localName = uriToId(propUri); + const label = allLabels.get(propUri); + if (label) { + propertyLabels[localName] = label; + } + } + + // Build class config dynamically from discovered OWL classes + const classConfig = new Map(); + let colorIndex = 0; + for (const classUri of owlClasses) { + const localName = uriToId(classUri).toLowerCase(); + const palette = domainColors[colorIndex % domainColors.length]; + classConfig.set(classUri, { + domain: localName, + color: palette.color, + glow: palette.glow, + icon: getClassIcon(classUri), + label: allLabels.get(classUri) || uriToId(classUri), + description: allComments.get(classUri) || "", + }); + colorIndex++; + } + + // Second pass: find entities (instances of OWL classes) and their properties + const entityMap = new Map(); + const entityProps = new Map>(); + + // Collect entity properties first + for (const triple of triples) { + const subjectUri = getTermValue(triple.s); + const predicate = getTermValue(triple.p); + const value = getTermValue(triple.o); + + // Skip schema-level predicates and URIs as values + if (predicate !== RDF_TYPE && predicate !== RDFS_LABEL && predicate !== RDFS_COMMENT && + value && !value.startsWith("http")) { + if (!entityProps.has(subjectUri)) { + entityProps.set(subjectUri, {}); + } + const propName = uriToId(predicate); + entityProps.get(subjectUri)![propName] = value; + } + } + + // Find entities by type (instances of OWL classes) + for (const triple of triples) { + const subjectUri = getTermValue(triple.s); + const predicate = getTermValue(triple.p); + const objectUri = getTermValue(triple.o); + + if (predicate === RDF_TYPE && classConfig.has(objectUri)) { + const config = classConfig.get(objectUri)!; + if (domain && config.domain !== domain) continue; + + const entityId = uriToId(subjectUri); + entityMap.set(subjectUri, { + id: entityId, + uri: subjectUri, + label: allLabels.get(subjectUri) || entityId, + props: entityProps.get(subjectUri) || {}, + domain: config.domain, + color: config.color, + glow: config.glow, + icon: config.icon, + }); + } + } + + // Find relationships: triples where both subject and object are known entities + const relationships: Relationship[] = []; + const entityUris = new Set(entityMap.keys()); + + for (const triple of triples) { + const subjectUri = getTermValue(triple.s); + const predicate = getTermValue(triple.p); + const objectUri = getTermValue(triple.o); + + // Skip rdf:type and rdfs:label + if (predicate === RDF_TYPE || predicate === RDFS_LABEL) continue; + + // If both subject and object are entities, it's a relationship + if (entityUris.has(subjectUri) && entityUris.has(objectUri)) { + const fromEntity = entityMap.get(subjectUri)!; + const toEntity = entityMap.get(objectUri)!; + + relationships.push({ + from: fromEntity.id, + to: toEntity.id, + predicate: predicateToName(predicate), + domain: [fromEntity.domain, toEntity.domain], + }); + } + } + + const entities = Array.from(entityMap.values()); + + // Build ontology metadata dynamically from discovered classes + const ontology: OntologyType = {}; + for (const [, config] of classConfig) { + ontology[config.domain] = { + label: config.label, + color: config.color, + glow: config.glow, + icon: config.icon, + description: config.description, + properties: [], + subclasses: entities.filter(e => e.domain === config.domain).map(e => ({ + id: e.id, + uri: e.uri, + label: e.label, + props: e.props, + })), + }; + } + + return { entities, relationships, ontology, propertyLabels }; + }, [isLoading, triples, domain]); + + return { + entities, + relationships, + ontology, + propertyLabels, + isLoading, + isError, + error, + }; +} diff --git a/ai-context/context-graph-demo/src/state/useOntologySchema.ts b/ai-context/context-graph-demo/src/state/useOntologySchema.ts new file mode 100644 index 00000000..b5dcd14c --- /dev/null +++ b/ai-context/context-graph-demo/src/state/useOntologySchema.ts @@ -0,0 +1,178 @@ +import { useMemo } from "react"; +import { useTriples } from "@trustgraph/react-state"; +import { COLLECTION } from "../config"; +const RDF_TYPE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"; +const RDFS_LABEL = "http://www.w3.org/2000/01/rdf-schema#label"; +const RDFS_DOMAIN = "http://www.w3.org/2000/01/rdf-schema#domain"; +const RDFS_RANGE = "http://www.w3.org/2000/01/rdf-schema#range"; +const RDFS_COMMENT = "http://www.w3.org/2000/01/rdf-schema#comment"; +const OWL_CLASS = "http://www.w3.org/2002/07/owl#Class"; +const OWL_OBJECT_PROPERTY = "http://www.w3.org/2002/07/owl#ObjectProperty"; +const OWL_DATATYPE_PROPERTY = "http://www.w3.org/2002/07/owl#DatatypeProperty"; + +// Helper to extract value from a Term +function getTermValue(term: { t: string; i?: string; v?: string }): string { + if (term.t === "i") return term.i || ""; + if (term.t === "l") return term.v || ""; + return ""; +} + +// Helper to get local name from URI +function getLocalName(uri: string): string { + const hashIndex = uri.lastIndexOf("#"); + const slashIndex = uri.lastIndexOf("/"); + const index = Math.max(hashIndex, slashIndex); + return index >= 0 ? uri.substring(index + 1) : uri; +} + +export interface OntologyClass { + uri: string; + label: string; + comment?: string; +} + +export interface OntologyProperty { + uri: string; + label: string; + domain?: string; + range?: string; +} + +export interface OntologySchema { + classes: OntologyClass[]; + objectProperties: OntologyProperty[]; + datatypeProperties: OntologyProperty[]; + // Sets for quick lookup + objectPropertyUris: Set; + datatypePropertyUris: Set; +} + +export function useOntologySchema() { + // Query for classes + const classTriples = useTriples({ + p: { t: "i", i: RDF_TYPE }, + o: { t: "i", i: OWL_CLASS }, + limit: 100, + collection: COLLECTION, + }); + + // Query for object properties + const objectPropertyTriples = useTriples({ + p: { t: "i", i: RDF_TYPE }, + o: { t: "i", i: OWL_OBJECT_PROPERTY }, + limit: 100, + collection: COLLECTION, + }); + + // Query for datatype properties + const datatypePropertyTriples = useTriples({ + p: { t: "i", i: RDF_TYPE }, + o: { t: "i", i: OWL_DATATYPE_PROPERTY }, + limit: 100, + collection: COLLECTION, + }); + + // Query for all triples to get labels, domains, ranges + const allTriples = useTriples({ + limit: 1000, + collection: COLLECTION, + }); + + const isLoading = classTriples.isLoading || objectPropertyTriples.isLoading || + datatypePropertyTriples.isLoading || allTriples.isLoading; + const isError = classTriples.isError || objectPropertyTriples.isError || + datatypePropertyTriples.isError || allTriples.isError; + const error = classTriples.error || objectPropertyTriples.error || + datatypePropertyTriples.error || allTriples.error; + + const schema = useMemo((): OntologySchema | undefined => { + if (isLoading) return undefined; + + // Build a map of URI -> metadata from all triples + const metadata = new Map(); + + for (const triple of allTriples.triples || []) { + const subject = getTermValue(triple.s); + const predicate = getTermValue(triple.p); + const value = getTermValue(triple.o); + + if (!metadata.has(subject)) { + metadata.set(subject, {}); + } + const meta = metadata.get(subject)!; + + if (predicate === RDFS_LABEL) { + meta.label = value; + } else if (predicate === RDFS_DOMAIN) { + meta.domain = value; + } else if (predicate === RDFS_RANGE) { + meta.range = value; + } else if (predicate === RDFS_COMMENT) { + meta.comment = value; + } + } + + // Build classes list + const classes: OntologyClass[] = []; + for (const triple of classTriples.triples || []) { + const uri = getTermValue(triple.s); + const meta = metadata.get(uri) || {}; + classes.push({ + uri, + label: meta.label || getLocalName(uri), + comment: meta.comment, + }); + } + + // Build object properties list + const objectProperties: OntologyProperty[] = []; + const objectPropertyUris = new Set(); + for (const triple of objectPropertyTriples.triples || []) { + const uri = getTermValue(triple.s); + const meta = metadata.get(uri) || {}; + objectPropertyUris.add(uri); + objectProperties.push({ + uri, + label: meta.label || getLocalName(uri), + domain: meta.domain, + range: meta.range, + }); + } + + // Build datatype properties list + const datatypeProperties: OntologyProperty[] = []; + const datatypePropertyUris = new Set(); + for (const triple of datatypePropertyTriples.triples || []) { + const uri = getTermValue(triple.s); + const meta = metadata.get(uri) || {}; + datatypePropertyUris.add(uri); + datatypeProperties.push({ + uri, + label: meta.label || getLocalName(uri), + domain: meta.domain, + range: meta.range, + }); + } + + return { + classes, + objectProperties, + datatypeProperties, + objectPropertyUris, + datatypePropertyUris, + }; + }, [ + isLoading, + classTriples.triples, + objectPropertyTriples.triples, + datatypePropertyTriples.triples, + allTriples.triples, + ]); + + return { + schema, + isLoading, + isError, + error, + }; +} diff --git a/ai-context/context-graph-demo/src/theme/colors.ts b/ai-context/context-graph-demo/src/theme/colors.ts new file mode 100644 index 00000000..c6ca9cb0 --- /dev/null +++ b/ai-context/context-graph-demo/src/theme/colors.ts @@ -0,0 +1,72 @@ +// Primary palette (migrated from useGraphData.ts) +export const palette = { + emerald: "#6EE7B7", + pink: "#F9A8D4", + blue: "#93C5FD", + amber: "#FCD34D", + purple: "#C4B5FD", + rose: "#FDA4AF", + cyan: "#67E8F9", + red: "#FCA5A5", + orange: "#F97316", +}; + +// Semantic colors +export const semantic = { + success: palette.emerald, + error: "#f66", + warning: palette.orange, + info: palette.blue, + thinking: palette.blue, + observation: palette.purple, + answer: palette.emerald, + user: palette.amber, +}; + +// Text colors (dark theme) +export const text = { + primary: "#ddd", + secondary: "#bbb", + muted: "#aaa", + subtle: "#888", + faint: "#666", + disabled: "#555", + hint: "#444", +}; + +// Surface/background colors +export const surface = { + base: "#0A0A0F", + overlay: "rgba(15,15,20,0.95)", + overlayLight: "rgba(15,15,20,0.8)", + card: "rgba(255,255,255,0.02)", + cardHover: "rgba(255,255,255,0.04)", +}; + +// Border colors +export const border = { + subtle: "rgba(255,255,255,0.04)", + default: "rgba(255,255,255,0.06)", + medium: "rgba(255,255,255,0.1)", + grid: "rgba(255,255,255,0.015)", +}; + +// Helper: Generate glow color from hex +export function withGlow(hex: string, opacity = 0.4): string { + const r = parseInt(hex.slice(1, 3), 16); + const g = parseInt(hex.slice(3, 5), 16); + const b = parseInt(hex.slice(5, 7), 16); + return `rgba(${r},${g},${b},${opacity})`; +} + +// Domain color palette (array for cycling) +export const domainColors = [ + { color: palette.emerald, glow: withGlow(palette.emerald) }, + { color: palette.pink, glow: withGlow(palette.pink) }, + { color: palette.blue, glow: withGlow(palette.blue) }, + { color: palette.amber, glow: withGlow(palette.amber) }, + { color: palette.purple, glow: withGlow(palette.purple) }, + { color: palette.rose, glow: withGlow(palette.rose) }, + { color: palette.cyan, glow: withGlow(palette.cyan) }, + { color: palette.red, glow: withGlow(palette.red) }, +]; diff --git a/ai-context/context-graph-demo/src/theme/index.ts b/ai-context/context-graph-demo/src/theme/index.ts new file mode 100644 index 00000000..bbdc1712 --- /dev/null +++ b/ai-context/context-graph-demo/src/theme/index.ts @@ -0,0 +1 @@ +export * from "./colors"; diff --git a/ai-context/context-graph-demo/src/types/index.ts b/ai-context/context-graph-demo/src/types/index.ts new file mode 100644 index 00000000..89dd67dd --- /dev/null +++ b/ai-context/context-graph-demo/src/types/index.ts @@ -0,0 +1,65 @@ +// ── Domain Types ───────────────────────────────────────────────── +export type DomainKey = string; + +export interface EntityProps { + [key: string]: string | number; +} + +export interface Subclass { + id: string; + uri?: string; + label: string; + props: EntityProps; +} + +export interface OntologyDomain { + label: string; + color: string; + glow: string; + icon: string; + description: string; + properties: string[]; + subclasses: Subclass[]; +} + +export type OntologyType = Record; + +// ── Relationship Types ─────────────────────────────────────────── +export interface Relationship { + from: string; + to: string; + predicate: string; + strength?: number; + domain: [DomainKey, DomainKey]; +} + +// ── Query Types ────────────────────────────────────────────────── +export interface DemoQuery { + q: string; + thinking: string[]; + answer: string; + entities: string[]; + triples: number; +} + +// ── Entity Types ───────────────────────────────────────────────── +export interface Entity extends Subclass { + domain: DomainKey; + color: string; + glow: string; + icon: string; +} + +export interface GraphNode extends Entity { + x: number; + y: number; + vx: number; + vy: number; + targetX: number; + targetY: number; + r: number; +} + +// ── UI State Types ─────────────────────────────────────────────── +export type TabKey = "graph" | "query" | "explain" | "ontology" | "data"; +export type QueryPhase = "idle" | "thinking" | "answering" | "done"; diff --git a/ai-context/context-graph-demo/src/utils/index.ts b/ai-context/context-graph-demo/src/utils/index.ts new file mode 100644 index 00000000..1883c845 --- /dev/null +++ b/ai-context/context-graph-demo/src/utils/index.ts @@ -0,0 +1 @@ +export { getLocalName } from "./uri"; diff --git a/ai-context/context-graph-demo/src/utils/uri.ts b/ai-context/context-graph-demo/src/utils/uri.ts new file mode 100644 index 00000000..7f7e5fab --- /dev/null +++ b/ai-context/context-graph-demo/src/utils/uri.ts @@ -0,0 +1,9 @@ +/** + * Extract the local name from a URI by taking the fragment after # or the last path segment + */ +export function getLocalName(uri: string): string { + const hashIndex = uri.lastIndexOf("#"); + const slashIndex = uri.lastIndexOf("/"); + const index = Math.max(hashIndex, slashIndex); + return index >= 0 ? uri.substring(index + 1) : uri; +} diff --git a/ai-context/context-graph-demo/src/vite-env.d.ts b/ai-context/context-graph-demo/src/vite-env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/ai-context/context-graph-demo/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/ai-context/context-graph-demo/trustgraph-retail-demo.jsx b/ai-context/context-graph-demo/trustgraph-retail-demo.jsx new file mode 100644 index 00000000..32f4c01e --- /dev/null +++ b/ai-context/context-graph-demo/trustgraph-retail-demo.jsx @@ -0,0 +1,843 @@ +import { useState, useEffect, useRef, useCallback, useMemo } from "react"; + +// ═══════════════════════════════════════════════════════════════════ +// TRUSTGRAPH RETAIL INTELLIGENCE DEMO +// Ontology-Driven Context Graph: Consumer × Agent × Retail × Brand +// ═══════════════════════════════════════════════════════════════════ + +// ── Ontology Schema ────────────────────────────────────────────── +const ONTOLOGY = { + consumer: { + label: "Consumer", + color: "#6EE7B7", + glow: "rgba(110,231,183,0.4)", + icon: "👤", + description: "Individuals and segments interacting with brands through retail channels", + properties: ["segment", "preferences", "journeyStage", "lifetime_value", "sentiment"], + subclasses: [ + { id: "cs1", label: "Urban Millennials", props: { size: "2.4M", avgSpend: "$142/mo", loyalty: 0.78, journeyStage: "Engaged" } }, + { id: "cs2", label: "Active Families", props: { size: "1.8M", avgSpend: "$218/mo", loyalty: 0.85, journeyStage: "Loyal" } }, + { id: "cs3", label: "Eco-Conscious Gen Z", props: { size: "3.1M", avgSpend: "$96/mo", loyalty: 0.62, journeyStage: "Exploring" } }, + { id: "cs4", label: "Luxury Seekers", props: { size: "890K", avgSpend: "$384/mo", loyalty: 0.91, journeyStage: "Advocate" } }, + { id: "cs5", label: "Weekend Warriors", props: { size: "1.5M", avgSpend: "$167/mo", loyalty: 0.73, journeyStage: "Engaged" } }, + ], + }, + brand: { + label: "Brand", + color: "#F9A8D4", + glow: "rgba(249,168,212,0.4)", + icon: "✦", + description: "Product brands seeking to connect with consumers through retail experiences", + properties: ["identity", "positioning", "campaigns", "products", "partnerships"], + subclasses: [ + { id: "br1", label: "Lumière Beauty", props: { category: "Cosmetics", positioning: "Premium", campaigns: 12, sentiment: 0.87 } }, + { id: "br2", label: "Nordic Trail", props: { category: "Outdoor Apparel", positioning: "Sustainable", campaigns: 8, sentiment: 0.82 } }, + { id: "br3", label: "Velo Sport", props: { category: "Athletics", positioning: "Performance", campaigns: 15, sentiment: 0.79 } }, + { id: "br4", label: "Casa Verde", props: { category: "Home & Living", positioning: "Artisanal", campaigns: 6, sentiment: 0.90 } }, + { id: "br5", label: "Artisan Coffee Co.", props: { category: "F&B", positioning: "Community", campaigns: 10, sentiment: 0.85 } }, + ], + }, + retail: { + label: "Retail", + color: "#93C5FD", + glow: "rgba(147,197,253,0.4)", + icon: "🏬", + description: "Channels, touchpoints, and experiences where brands meet consumers", + properties: ["channel", "location", "traffic", "conversionRate", "experience_score"], + subclasses: [ + { id: "rt1", label: "Flagship Store NYC", props: { channel: "Physical", traffic: "48K/mo", conversion: "12.3%", experience: 0.91 } }, + { id: "rt2", label: "Mobile Commerce App", props: { channel: "Digital", traffic: "1.2M/mo", conversion: "4.7%", experience: 0.78 } }, + { id: "rt3", label: "Pop-Up Experience", props: { channel: "Experiential", traffic: "8K/event", conversion: "18.6%", experience: 0.95 } }, + { id: "rt4", label: "Social Commerce", props: { channel: "Social", traffic: "890K/mo", conversion: "3.2%", experience: 0.72 } }, + { id: "rt5", label: "Loyalty Hub", props: { channel: "Omnichannel", traffic: "340K/mo", conversion: "22.1%", experience: 0.88 } }, + ], + }, + agent: { + label: "Agent", + color: "#FCD34D", + glow: "rgba(252,211,77,0.4)", + icon: "⚡", + description: "AI agents that orchestrate personalized brand-consumer connections", + properties: ["capability", "contextSources", "accuracy", "latency", "decisions_per_day"], + subclasses: [ + { id: "ag1", label: "Recommendation Agent", props: { capability: "Product Discovery", accuracy: "94.2%", latency: "120ms", decisions: "2.1M/day" } }, + { id: "ag2", label: "Personalization Agent", props: { capability: "Experience Tailoring", accuracy: "91.8%", latency: "85ms", decisions: "890K/day" } }, + { id: "ag3", label: "Campaign Orchestrator", props: { capability: "Brand Activation", accuracy: "88.5%", latency: "200ms", decisions: "340K/day" } }, + { id: "ag4", label: "Sentiment Analyst", props: { capability: "Brand Perception", accuracy: "96.1%", latency: "150ms", decisions: "1.5M/day" } }, + { id: "ag5", label: "Journey Optimizer", props: { capability: "Path Optimization", accuracy: "89.7%", latency: "180ms", decisions: "560K/day" } }, + ], + }, +}; + +// ── Ontology Relationships (Triples) ────────────────────────────── +const RELATIONSHIPS = [ + // Consumer ↔ Brand + { from: "cs1", to: "br1", predicate: "has_affinity_for", strength: 0.85, domain: ["consumer", "brand"] }, + { from: "cs1", to: "br5", predicate: "frequents", strength: 0.69, domain: ["consumer", "brand"] }, + { from: "cs2", to: "br2", predicate: "has_affinity_for", strength: 0.78, domain: ["consumer", "brand"] }, + { from: "cs2", to: "br3", predicate: "purchases_from", strength: 0.88, domain: ["consumer", "brand"] }, + { from: "cs3", to: "br2", predicate: "advocates_for", strength: 0.71, domain: ["consumer", "brand"] }, + { from: "cs3", to: "br4", predicate: "has_affinity_for", strength: 0.65, domain: ["consumer", "brand"] }, + { from: "cs3", to: "br5", predicate: "frequents", strength: 0.58, domain: ["consumer", "brand"] }, + { from: "cs4", to: "br1", predicate: "loyal_to", strength: 0.92, domain: ["consumer", "brand"] }, + { from: "cs4", to: "br4", predicate: "purchases_from", strength: 0.82, domain: ["consumer", "brand"] }, + { from: "cs5", to: "br3", predicate: "has_affinity_for", strength: 0.76, domain: ["consumer", "brand"] }, + { from: "cs5", to: "br5", predicate: "frequents", strength: 0.74, domain: ["consumer", "brand"] }, + // Consumer ↔ Retail + { from: "cs1", to: "rt2", predicate: "shops_via", strength: 0.82, domain: ["consumer", "retail"] }, + { from: "cs1", to: "rt4", predicate: "discovers_through", strength: 0.71, domain: ["consumer", "retail"] }, + { from: "cs2", to: "rt1", predicate: "shops_via", strength: 0.85, domain: ["consumer", "retail"] }, + { from: "cs2", to: "rt5", predicate: "member_of", strength: 0.90, domain: ["consumer", "retail"] }, + { from: "cs3", to: "rt3", predicate: "experiences", strength: 0.88, domain: ["consumer", "retail"] }, + { from: "cs3", to: "rt4", predicate: "discovers_through", strength: 0.79, domain: ["consumer", "retail"] }, + { from: "cs4", to: "rt1", predicate: "shops_via", strength: 0.94, domain: ["consumer", "retail"] }, + { from: "cs4", to: "rt3", predicate: "experiences", strength: 0.86, domain: ["consumer", "retail"] }, + { from: "cs5", to: "rt2", predicate: "shops_via", strength: 0.72, domain: ["consumer", "retail"] }, + { from: "cs5", to: "rt5", predicate: "member_of", strength: 0.68, domain: ["consumer", "retail"] }, + // Brand ↔ Retail + { from: "br1", to: "rt1", predicate: "merchandises_in", strength: 0.90, domain: ["brand", "retail"] }, + { from: "br1", to: "rt3", predicate: "activates_via", strength: 0.85, domain: ["brand", "retail"] }, + { from: "br2", to: "rt3", predicate: "activates_via", strength: 0.82, domain: ["brand", "retail"] }, + { from: "br2", to: "rt4", predicate: "promotes_on", strength: 0.75, domain: ["brand", "retail"] }, + { from: "br3", to: "rt2", predicate: "sells_through", strength: 0.80, domain: ["brand", "retail"] }, + { from: "br3", to: "rt5", predicate: "rewards_via", strength: 0.77, domain: ["brand", "retail"] }, + { from: "br4", to: "rt1", predicate: "merchandises_in", strength: 0.88, domain: ["brand", "retail"] }, + { from: "br4", to: "rt4", predicate: "promotes_on", strength: 0.70, domain: ["brand", "retail"] }, + { from: "br5", to: "rt3", predicate: "activates_via", strength: 0.79, domain: ["brand", "retail"] }, + { from: "br5", to: "rt5", predicate: "rewards_via", strength: 0.83, domain: ["brand", "retail"] }, + // Agent ↔ Consumer + { from: "ag1", to: "cs1", predicate: "recommends_to", strength: 0.87, domain: ["agent", "consumer"] }, + { from: "ag1", to: "cs3", predicate: "recommends_to", strength: 0.81, domain: ["agent", "consumer"] }, + { from: "ag2", to: "cs4", predicate: "personalizes_for", strength: 0.93, domain: ["agent", "consumer"] }, + { from: "ag2", to: "cs2", predicate: "personalizes_for", strength: 0.84, domain: ["agent", "consumer"] }, + { from: "ag4", to: "cs1", predicate: "monitors_sentiment_of", strength: 0.78, domain: ["agent", "consumer"] }, + { from: "ag4", to: "cs3", predicate: "monitors_sentiment_of", strength: 0.82, domain: ["agent", "consumer"] }, + { from: "ag5", to: "cs2", predicate: "optimizes_journey_for", strength: 0.86, domain: ["agent", "consumer"] }, + { from: "ag5", to: "cs5", predicate: "optimizes_journey_for", strength: 0.75, domain: ["agent", "consumer"] }, + // Agent ↔ Brand + { from: "ag3", to: "br1", predicate: "orchestrates_campaign_for", strength: 0.88, domain: ["agent", "brand"] }, + { from: "ag3", to: "br2", predicate: "orchestrates_campaign_for", strength: 0.82, domain: ["agent", "brand"] }, + { from: "ag3", to: "br5", predicate: "orchestrates_campaign_for", strength: 0.79, domain: ["agent", "brand"] }, + { from: "ag4", to: "br1", predicate: "analyzes_perception_of", strength: 0.91, domain: ["agent", "brand"] }, + { from: "ag4", to: "br3", predicate: "analyzes_perception_of", strength: 0.85, domain: ["agent", "brand"] }, + { from: "ag1", to: "br3", predicate: "curates_products_for", strength: 0.83, domain: ["agent", "brand"] }, + { from: "ag1", to: "br4", predicate: "curates_products_for", strength: 0.77, domain: ["agent", "brand"] }, + // Agent ↔ Retail + { from: "ag2", to: "rt1", predicate: "tailors_experience_at", strength: 0.89, domain: ["agent", "retail"] }, + { from: "ag2", to: "rt2", predicate: "tailors_experience_at", strength: 0.85, domain: ["agent", "retail"] }, + { from: "ag3", to: "rt3", predicate: "deploys_campaign_at", strength: 0.81, domain: ["agent", "retail"] }, + { from: "ag3", to: "rt4", predicate: "deploys_campaign_at", strength: 0.86, domain: ["agent", "retail"] }, + { from: "ag5", to: "rt1", predicate: "optimizes_flow_at", strength: 0.84, domain: ["agent", "retail"] }, + { from: "ag5", to: "rt5", predicate: "optimizes_flow_at", strength: 0.80, domain: ["agent", "retail"] }, +]; + +// ── Pre-built Agent Queries & Responses ──────────────────────────── +const DEMO_QUERIES = [ + { + q: "Which brands should activate at the Pop-Up Experience to reach Eco-Conscious Gen Z?", + thinking: [ + "Traversing ontology: Consumer[cs3] → has_affinity_for → Brand[br2, br4, br5]", + "Traversing ontology: Consumer[cs3] → experiences → Retail[rt3]", + "Cross-referencing: Brand activations at Retail[rt3]", + "Ranking by: affinity strength × activation fit × conversion potential", + ], + answer: "Nordic Trail and Artisan Coffee Co. are the strongest activation candidates for the Pop-Up Experience targeting Eco-Conscious Gen Z. Nordic Trail's sustainability positioning aligns with this segment's values (affinity: 0.71) and already activates via experiential retail (strength: 0.82). Artisan Coffee Co. has existing frequency with this segment (0.58) and pop-up activation experience (0.79). Casa Verde is a secondary candidate — lower affinity (0.65) but high experiential fit.", + entities: ["cs3", "br2", "br5", "br4", "rt3"], + triples: 8, + }, + { + q: "How should Lumière Beauty optimize its engagement with Luxury Seekers across channels?", + thinking: [ + "Resolving entities: Brand[br1] = Lumière Beauty, Consumer[cs4] = Luxury Seekers", + "Traversing: Brand[br1] → merchandises_in → Retail[rt1], activates_via → Retail[rt3]", + "Traversing: Consumer[cs4] → shops_via → Retail[rt1], experiences → Retail[rt3]", + "Agent context: Personalization Agent[ag2] → personalizes_for → Consumer[cs4] (0.93)", + "Aggregating channel overlap and engagement signals", + ], + answer: "Lumière Beauty has exceptional alignment with Luxury Seekers — the strongest brand-consumer affinity in the graph (0.92). Both converge on Flagship Store NYC and Pop-Up Experiences. The Personalization Agent already achieves 93% accuracy for this segment. Recommendation: Deepen the flagship in-store experience with agent-powered personalized consultations, and create exclusive pop-up previews. The loyalty-to-advocacy pipeline is strong (journey stage: Advocate) — leverage this for referral programs through the Loyalty Hub.", + entities: ["br1", "cs4", "rt1", "rt3", "ag2", "rt5"], + triples: 12, + }, + { + q: "What's the optimal agent configuration for the Mobile Commerce channel?", + thinking: [ + "Resolving: Retail[rt2] = Mobile Commerce App", + "Traversing: Agent → tailors_experience_at → Retail[rt2]", + "Traversing: Consumer → shops_via → Retail[rt2]: [cs1, cs5]", + "Traversing: Brand → sells_through → Retail[rt2]: [br3]", + "Evaluating agent capabilities against mobile channel requirements", + ], + answer: "Mobile Commerce currently has the Personalization Agent deployed (experience tailoring, 85% strength). Primary consumer segments are Urban Millennials (0.82) and Weekend Warriors (0.72). Add the Recommendation Agent — it already serves Urban Millennials (0.87) and can curate Velo Sport products (the channel's primary brand). The Journey Optimizer should be connected to reduce the gap between the channel's high traffic (1.2M/mo) and moderate conversion (4.7%). Projected improvement: 2.1% conversion lift through graph-informed product sequencing.", + entities: ["rt2", "ag2", "ag1", "ag5", "cs1", "cs5", "br3"], + triples: 11, + }, +]; + +// ── Helper: find all entities ──────────────────────────────────────── +function getAllEntities() { + const all = []; + Object.entries(ONTOLOGY).forEach(([domain, data]) => { + data.subclasses.forEach((sc) => { + all.push({ ...sc, domain, color: data.color, glow: data.glow, icon: data.icon }); + }); + }); + return all; +} + +// ── Graph Visualization (Canvas-based force layout) ───────────────── +function GraphCanvas({ highlightedEntities, onNodeClick, activeFilter }) { + const canvasRef = useRef(null); + const nodesRef = useRef([]); + const animRef = useRef(null); + const hoveredRef = useRef(null); + const [hovered, setHovered] = useState(null); + + const entities = useMemo(() => getAllEntities(), []); + + useEffect(() => { + const canvas = canvasRef.current; + if (!canvas) return; + const rect = canvas.parentElement.getBoundingClientRect(); + canvas.width = rect.width * 2; + canvas.height = rect.height * 2; + canvas.style.width = rect.width + "px"; + canvas.style.height = rect.height + "px"; + + const cx = canvas.width / 2; + const cy = canvas.height / 2; + + // Position nodes in domain clusters + const domainPositions = { + consumer: { x: cx - cx * 0.35, y: cy - cy * 0.32 }, + brand: { x: cx + cx * 0.35, y: cy - cy * 0.32 }, + retail: { x: cx + cx * 0.35, y: cy + cy * 0.32 }, + agent: { x: cx - cx * 0.35, y: cy + cy * 0.32 }, + }; + + nodesRef.current = entities.map((e, i) => { + const dp = domainPositions[e.domain]; + const subIdx = ONTOLOGY[e.domain].subclasses.findIndex((s) => s.id === e.id); + const total = ONTOLOGY[e.domain].subclasses.length; + const angle = ((Math.PI * 2) / total) * subIdx - Math.PI / 2; + const radius = Math.min(canvas.width, canvas.height) * 0.1; + return { + ...e, + x: dp.x + Math.cos(angle) * radius, + y: dp.y + Math.sin(angle) * radius, + vx: 0, + vy: 0, + targetX: dp.x + Math.cos(angle) * radius, + targetY: dp.y + Math.sin(angle) * radius, + r: 18, + }; + }); + + const ctx = canvas.getContext("2d"); + let time = 0; + + function draw() { + time += 0.005; + ctx.clearRect(0, 0, canvas.width, canvas.height); + + // Subtle grid + ctx.strokeStyle = "rgba(255,255,255,0.015)"; + ctx.lineWidth = 1; + for (let x = 0; x < canvas.width; x += 60) { + ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, canvas.height); ctx.stroke(); + } + for (let y = 0; y < canvas.height; y += 60) { + ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(canvas.width, y); ctx.stroke(); + } + + // Domain labels + Object.entries(domainPositions).forEach(([domain, pos]) => { + const data = ONTOLOGY[domain]; + ctx.font = "bold 22px 'IBM Plex Mono', monospace"; + ctx.fillStyle = data.color + "44"; + ctx.textAlign = "center"; + ctx.fillText(data.label.toUpperCase(), pos.x, pos.y - Math.min(canvas.width, canvas.height) * 0.14); + }); + + // Draw edges + const nodes = nodesRef.current; + const filteredRels = activeFilter + ? RELATIONSHIPS.filter((r) => r.domain.includes(activeFilter)) + : RELATIONSHIPS; + + filteredRels.forEach((rel) => { + const fromNode = nodes.find((n) => n.id === rel.from); + const toNode = nodes.find((n) => n.id === rel.to); + if (!fromNode || !toNode) return; + + const isHighlighted = + highlightedEntities && + highlightedEntities.includes(rel.from) && + highlightedEntities.includes(rel.to); + + const baseAlpha = isHighlighted ? 0.7 : 0.12; + const pulse = isHighlighted ? Math.sin(time * 4) * 0.15 + 0.15 : 0; + + ctx.beginPath(); + ctx.moveTo(fromNode.x, fromNode.y); + // Curved edges + const mx = (fromNode.x + toNode.x) / 2 + (fromNode.y - toNode.y) * 0.1; + const my = (fromNode.y + toNode.y) / 2 + (toNode.x - fromNode.x) * 0.1; + ctx.quadraticCurveTo(mx, my, toNode.x, toNode.y); + + const gradient = ctx.createLinearGradient(fromNode.x, fromNode.y, toNode.x, toNode.y); + gradient.addColorStop(0, fromNode.color + Math.round((baseAlpha + pulse) * 255).toString(16).padStart(2, "0")); + gradient.addColorStop(1, toNode.color + Math.round((baseAlpha + pulse) * 255).toString(16).padStart(2, "0")); + ctx.strokeStyle = gradient; + ctx.lineWidth = isHighlighted ? 3 : 1.5; + ctx.stroke(); + + // Animated particles on highlighted edges + if (isHighlighted) { + const t = (time * 2 + rel.strength) % 1; + const px = (1 - t) * (1 - t) * fromNode.x + 2 * (1 - t) * t * mx + t * t * toNode.x; + const py = (1 - t) * (1 - t) * fromNode.y + 2 * (1 - t) * t * my + t * t * toNode.y; + ctx.beginPath(); + ctx.arc(px, py, 3, 0, Math.PI * 2); + ctx.fillStyle = "#fff"; + ctx.fill(); + } + }); + + // Draw nodes + nodes.forEach((node) => { + const isHighlighted = highlightedEntities && highlightedEntities.includes(node.id); + const isHovered = hoveredRef.current === node.id; + const isDimmed = highlightedEntities && highlightedEntities.length > 0 && !isHighlighted; + const isFiltered = activeFilter && node.domain !== activeFilter && !RELATIONSHIPS.some( + r => r.domain.includes(activeFilter) && (r.from === node.id || r.to === node.id) + ); + + const alpha = isFiltered ? 0.15 : isDimmed ? 0.3 : 1; + const r = isHighlighted || isHovered ? node.r * 1.4 : node.r; + const pulseR = isHighlighted ? Math.sin(time * 3) * 3 : 0; + + // Glow + if ((isHighlighted || isHovered) && !isFiltered) { + ctx.beginPath(); + ctx.arc(node.x, node.y, r + 12 + pulseR, 0, Math.PI * 2); + const grd = ctx.createRadialGradient(node.x, node.y, r, node.x, node.y, r + 12 + pulseR); + grd.addColorStop(0, node.glow); + grd.addColorStop(1, "rgba(0,0,0,0)"); + ctx.fillStyle = grd; + ctx.fill(); + } + + // Node circle + ctx.beginPath(); + ctx.arc(node.x, node.y, r, 0, Math.PI * 2); + ctx.fillStyle = node.color + Math.round(alpha * 255 * 0.2).toString(16).padStart(2, "0"); + ctx.fill(); + ctx.strokeStyle = node.color + Math.round(alpha * 255).toString(16).padStart(2, "0"); + ctx.lineWidth = isHighlighted ? 2.5 : 1.5; + ctx.stroke(); + + // Label + ctx.font = `${isHighlighted ? "bold " : ""}${isHovered ? 17 : 14}px 'IBM Plex Sans', sans-serif`; + ctx.fillStyle = `rgba(255,255,255,${alpha * (isHighlighted ? 1 : 0.75)})`; + ctx.textAlign = "center"; + ctx.fillText(node.label, node.x, node.y + r + 18); + + // Spring physics + node.x += (node.targetX - node.x) * 0.02; + node.y += (node.targetY - node.y) * 0.02; + node.x += Math.sin(time + node.targetX * 0.01) * 0.3; + node.y += Math.cos(time + node.targetY * 0.01) * 0.3; + }); + + animRef.current = requestAnimationFrame(draw); + } + + draw(); + return () => cancelAnimationFrame(animRef.current); + }, [entities, highlightedEntities, activeFilter]); + + const handleMouseMove = useCallback((e) => { + const canvas = canvasRef.current; + const rect = canvas.getBoundingClientRect(); + const x = (e.clientX - rect.left) * 2; + const y = (e.clientY - rect.top) * 2; + const nodes = nodesRef.current; + let found = null; + for (const node of nodes) { + const dx = node.x - x; + const dy = node.y - y; + if (Math.sqrt(dx * dx + dy * dy) < node.r * 1.5) { + found = node.id; + break; + } + } + hoveredRef.current = found; + setHovered(found); + canvas.style.cursor = found ? "pointer" : "default"; + }, []); + + const handleClick = useCallback((e) => { + if (hoveredRef.current && onNodeClick) { + const node = nodesRef.current.find((n) => n.id === hoveredRef.current); + if (node) onNodeClick(node); + } + }, [onNodeClick]); + + return ( +
+ + {hovered && (() => { + const node = nodesRef.current.find((n) => n.id === hovered); + if (!node) return null; + const canvas = canvasRef.current; + const rect = canvas.getBoundingClientRect(); + const sx = node.x / 2; + const sy = node.y / 2; + return ( +
+
+ {node.icon} {node.label} +
+
+ {Object.entries(node.props || {}).map(([k, v]) => ( +
{k}: {String(v)}
+ ))} +
+
+ ); + })()} +
+ ); +} + +// ── Typewriter Effect ──────────────────────────────────────────────── +function Typewriter({ text, speed = 12, onDone }) { + const [displayed, setDisplayed] = useState(""); + const idx = useRef(0); + + useEffect(() => { + idx.current = 0; + setDisplayed(""); + const interval = setInterval(() => { + idx.current++; + if (idx.current >= text.length) { + setDisplayed(text); + clearInterval(interval); + onDone && onDone(); + } else { + setDisplayed(text.slice(0, idx.current)); + } + }, speed); + return () => clearInterval(interval); + }, [text, speed]); + + return {displayed}; +} + +// ── Main App ───────────────────────────────────────────────────────── +export default function TrustGraphRetailDemo() { + const [activeTab, setActiveTab] = useState("graph"); + const [activeFilter, setActiveFilter] = useState(null); + const [selectedQuery, setSelectedQuery] = useState(null); + const [queryPhase, setQueryPhase] = useState("idle"); // idle, thinking, answering, done + const [thinkingStep, setThinkingStep] = useState(0); + const [selectedNode, setSelectedNode] = useState(null); + const [showOntology, setShowOntology] = useState(false); + + const runQuery = (idx) => { + setSelectedQuery(idx); + setQueryPhase("thinking"); + setThinkingStep(0); + const q = DEMO_QUERIES[idx]; + let step = 0; + const interval = setInterval(() => { + step++; + if (step >= q.thinking.length) { + clearInterval(interval); + setTimeout(() => setQueryPhase("answering"), 400); + } + setThinkingStep(step); + }, 800); + }; + + const highlightedEntities = selectedQuery !== null && queryPhase !== "idle" + ? DEMO_QUERIES[selectedQuery].entities + : selectedNode + ? [selectedNode.id, ...RELATIONSHIPS.filter(r => r.from === selectedNode.id || r.to === selectedNode.id).map(r => r.from === selectedNode.id ? r.to : r.from)] + : []; + + return ( +
+ {/* ── Header ────────────────────────────────────────── */} +
+
+
TG
+
+
+ TrustGraph +
+
+ RETAIL INTELLIGENCE PLATFORM +
+
+
+
+ {["graph", "query", "ontology"].map((tab) => ( + + ))} +
+
+ + {/* ── Domain Filter Bar ──────────────────────────────── */} + {activeTab === "graph" && ( +
+ FILTER: + + {Object.entries(ONTOLOGY).map(([key, data]) => ( + + ))} +
+ {getAllEntities().length} entities · {RELATIONSHIPS.length} relationships +
+
+ )} + + {/* ── Main Content ──────────────────────────────────── */} +
+ + {/* ── Graph View ──────────────────────────────────── */} + {activeTab === "graph" && ( + <> +
+ setSelectedNode(selectedNode?.id === node.id ? null : node)} + activeFilter={activeFilter} + /> +
+ {/* Side panel for selected node */} + {selectedNode && ( +
+
+
+ {ONTOLOGY[selectedNode.domain].label.toUpperCase()} ENTITY +
+ +
+
+ {selectedNode.icon} {selectedNode.label} +
+
+
PROPERTIES
+ {Object.entries(selectedNode.props || {}).map(([k, v]) => ( +
+ {k} + {String(v)} +
+ ))} +
+
+
RELATIONSHIPS
+ {RELATIONSHIPS.filter(r => r.from === selectedNode.id || r.to === selectedNode.id).map((r, i) => { + const otherId = r.from === selectedNode.id ? r.to : r.from; + const other = getAllEntities().find(e => e.id === otherId); + const direction = r.from === selectedNode.id ? "→" : "←"; + return ( +
{ const n = getAllEntities().find(e => e.id === otherId); if (n) setSelectedNode(n); }}> +
+ {direction} {other?.label} +
+
+ {r.predicate.replace(/_/g, " ")} · strength: {r.strength} +
+
+ ); + })} +
+
+ )} + + )} + + {/* ── Agent Query View ────────────────────────────── */} + {activeTab === "query" && ( +
+
+ {/* Query selector */} +
+
+ SELECT A QUERY TO SEE GRAPH-POWERED AGENT INTELLIGENCE +
+ {DEMO_QUERIES.map((dq, idx) => ( + + ))} +
+ + {/* Response area */} + {selectedQuery !== null && ( +
+ {/* Graph traversal steps */} + {(queryPhase === "thinking" || queryPhase === "answering" || queryPhase === "done") && ( +
+
+ ◈ GRAPH TRAVERSAL +
+ {DEMO_QUERIES[selectedQuery].thinking.map((step, i) => ( +
+ {i < thinkingStep && } + {step} +
+ ))} + {queryPhase === "thinking" && ( +
+ Traversing graph... +
+ )} +
+ )} + + {/* Answer */} + {(queryPhase === "answering" || queryPhase === "done") && ( +
+
+
+ AGENT RESPONSE +
+
+ {DEMO_QUERIES[selectedQuery].triples} triples traversed · {DEMO_QUERIES[selectedQuery].entities.length} entities resolved +
+
+
+ setQueryPhase("done")} + /> +
+
+ )} +
+ )} +
+ + {/* Graph visualization alongside query */} +
+ {}} activeFilter={null} /> +
+
+ )} + + {/* ── Ontology View ──────────────────────────────── */} + {activeTab === "ontology" && ( +
+
+
+ ONTOLOGY SCHEMA · RETAIL INTELLIGENCE DOMAIN +
+ + {/* Ontology class cards */} +
+ {Object.entries(ONTOLOGY).map(([key, data]) => ( +
+
+ {data.icon} +
+
{data.label}
+
owl:Class
+
+
+
{data.description}
+
PROPERTIES
+
+ {data.properties.map((p) => ( + {p} + ))} +
+
+ INSTANCES ({data.subclasses.length}) +
+ {data.subclasses.map((sc) => ( +
+ {sc.label} + {sc.id} +
+ ))} +
+ ))} +
+ + {/* Relationship predicates */} +
+
+ RELATIONSHIP PREDICATES +
+
+ {[...new Set(RELATIONSHIPS.map(r => r.predicate))].map((pred) => { + const sample = RELATIONSHIPS.find(r => r.predicate === pred); + const fromDomain = sample.domain[0]; + const toDomain = sample.domain[1]; + return ( +
+
+ {pred.replace(/_/g, " ")} +
+
+ {ONTOLOGY[fromDomain].label} + {" → "} + {ONTOLOGY[toDomain].label} +
+
+ ); + })} +
+
+ + {/* Triple count summary */} +
+ {[ + { label: "Classes", value: 4 }, + { label: "Instances", value: getAllEntities().length }, + { label: "Predicates", value: [...new Set(RELATIONSHIPS.map(r => r.predicate))].length }, + { label: "Triples", value: RELATIONSHIPS.length }, + ].map((s) => ( +
+
{s.value}
+
{s.label.toUpperCase()}
+
+ ))} +
+
+
+ )} +
+ + {/* ── Bottom Status Bar ──────────────────────────────── */} +
+
+ ◈ Ontology: Consumer × Agent × Retail × Brand + ⬡ GraphRAG: Active + ⚡ Agent Orchestration: Online +
+
+ Context Graph Connected + | + trustgraph.ai +
+
+
+ ); +} diff --git a/ai-context/context-graph-demo/tsconfig.json b/ai-context/context-graph-demo/tsconfig.json new file mode 100644 index 00000000..a4c834a6 --- /dev/null +++ b/ai-context/context-graph-demo/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/ai-context/context-graph-demo/vite.config.js b/ai-context/context-graph-demo/vite.config.js new file mode 100644 index 00000000..fba45ddf --- /dev/null +++ b/ai-context/context-graph-demo/vite.config.js @@ -0,0 +1,39 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import path from 'path' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], + resolve: { + dedupe: ['react', 'react-dom', '@tanstack/react-query'], + alias: { + react: path.resolve('./node_modules/react'), + 'react-dom': path.resolve('./node_modules/react-dom'), + }, + }, + server: { + proxy: { + "/api/socket": { + // target: "wss://broker.app.trustgraph.ai/", + target: "ws://localhost:8088/", + changeOrigin: true, + ws: true, + secure: false, + rewrite: (path) => path.replace("/api/socket", "/api/v1/socket"), + }, + "/api/export-core": { + target: "http://localhost:8088/", + changeOrigin: true, + secure: false, + rewrite: (x) => x.replace("/api/export-core", "/api/v1/export-core"), + }, + "/api/import-core": { + target: "http://localhost:8088/", + changeOrigin: true, + secure: false, + rewrite: (x) => x.replace("/api/import-core", "/api/v1/import-core"), + }, + }, + }, +}) diff --git a/ai-context/trustgraph-client/.github/workflows/ci.yml b/ai-context/trustgraph-client/.github/workflows/ci.yml new file mode 100644 index 00000000..67cee0e9 --- /dev/null +++ b/ai-context/trustgraph-client/.github/workflows/ci.yml @@ -0,0 +1,34 @@ +name: CI + +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + + - name: Install dependencies + run: npm install + + - name: Type check + run: npm run typecheck + + - name: Lint + run: npm run lint + + - name: Run tests + run: npm test + + - name: Build + run: npm run build diff --git a/ai-context/trustgraph-client/.github/workflows/publish.yml b/ai-context/trustgraph-client/.github/workflows/publish.yml new file mode 100644 index 00000000..fd681ba0 --- /dev/null +++ b/ai-context/trustgraph-client/.github/workflows/publish.yml @@ -0,0 +1,51 @@ +name: Publish to npm + +on: + push: + tags: + - 'v*' + +jobs: + publish: + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + + - name: Upgrade npm for OIDC support + run: npm install -g npm@latest + + - name: Install dependencies + run: npm install + + - name: Type check + run: npm run typecheck + + - name: Lint + run: npm run lint + + - name: Run tests + run: npm test + + - name: Build + run: npm run build + + - name: Verify version matches tag + run: | + TAG_VERSION=${GITHUB_REF#refs/tags/v} + PKG_VERSION=$(node -p "require('./package.json').version") + if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then + echo "Tag version ($TAG_VERSION) doesn't match package.json ($PKG_VERSION)" + exit 1 + fi + + - name: Publish to npm + run: npm publish --access public --provenance diff --git a/ai-context/trustgraph-client/.gitignore b/ai-context/trustgraph-client/.gitignore new file mode 100644 index 00000000..58aa58fb --- /dev/null +++ b/ai-context/trustgraph-client/.gitignore @@ -0,0 +1,7 @@ +node_modules/ +dist/ +*.log +.DS_Store +*.tsbuildinfo +package-lock.json +*~ diff --git a/ai-context/trustgraph-client/.prettierrc b/ai-context/trustgraph-client/.prettierrc new file mode 100644 index 00000000..8b6b8803 --- /dev/null +++ b/ai-context/trustgraph-client/.prettierrc @@ -0,0 +1,3 @@ +{ + "printWidth": 79 +} diff --git a/ai-context/trustgraph-client/LICENSE b/ai-context/trustgraph-client/LICENSE new file mode 100644 index 00000000..d9a10c0d --- /dev/null +++ b/ai-context/trustgraph-client/LICENSE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/ai-context/trustgraph-client/README.md b/ai-context/trustgraph-client/README.md new file mode 100644 index 00000000..cb25bc94 --- /dev/null +++ b/ai-context/trustgraph-client/README.md @@ -0,0 +1,319 @@ +# @trustgraph/client + +TypeScript/JavaScript client library for TrustGraph WebSocket API. This package provides a framework-agnostic client for communicating with TrustGraph services. + +## Features + +- 🌐 **WebSocket-based** - Real-time communication with TrustGraph services +- 📦 **Zero Dependencies** - No external runtime dependencies +- 🔐 **Authentication Support** - Optional API key authentication +- 🔄 **Auto-reconnection** - Handles connection failures gracefully +- 📝 **Full TypeScript Support** - Complete type definitions +- 🎯 **Framework Agnostic** - Works with any JavaScript framework or vanilla JS + +## Installation + +```bash +npm install @trustgraph/client +``` + +## Quick Start + +```typescript +import { createTrustGraphSocket } from "@trustgraph/client"; + +// Create a socket connection +const socket = createTrustGraphSocket("your-username"); + +// Query triples from the knowledge graph +const triples = await socket.triplesQuery( + { v: "http://example.org/subject", e: true }, + { v: "http://example.org/predicate", e: true }, + undefined, + 10, // limit +); + +console.log(triples); +``` + +## With Authentication + +```typescript +const socket = createTrustGraphSocket("your-username", "your-api-key"); +``` + +## Core APIs + +### Knowledge Graph Operations + +**Query Triples** + +```typescript +const triples = await socket.triplesQuery( + subject?: Value, // Optional subject filter + predicate?: Value, // Optional predicate filter + object?: Value, // Optional object filter + limit: number, // Maximum results + collection?: string // Optional collection name +); +``` + +**Graph Embeddings Query** + +```typescript +const entities = await socket.graphEmbeddingsQuery( + vectors: number[][], // Embedding vectors + limit: number, // Maximum results + collection?: string // Optional collection name +); +``` + +### Text & LLM Operations + +**Text Completion** + +```typescript +const response = await socket.textCompletion( + system: string, // System prompt + prompt: string, // User prompt + temperature?: number +); +``` + +**Graph RAG** + +```typescript +const answer = await socket.graphRag( + query: string, + options?: { + 'entity-limit'?: number, + 'triple-limit'?: number, + 'max-subgraph-size'?: number, + 'max-path-length'?: number + }, + collection?: string +); +``` + +**Agent** + +```typescript +socket.agent( + question: string, + think: (thought: string) => void, // Called when agent is thinking + observe: (observation: string) => void, // Called on observations + answer: (answer: string) => void, // Called with final answer + error: (error: string) => void, // Called on errors + collection?: string +); +``` + +**Embeddings** + +```typescript +const vectors = await socket.embeddings(text: string); +``` + +### Document Operations + +**Load Document** + +```typescript +await socket.loadDocument( + id: string, // Document ID + data: string, // Base64-encoded document + metadata: Triple[], // Document metadata as triples + collection?: string +); +``` + +**Load Text** + +```typescript +await socket.loadText( + id: string, // Document ID + text: string, // Plain text content + charset: string, // Character encoding (e.g., 'utf-8') + metadata: Triple[], // Document metadata as triples + collection?: string +); +``` + +### Library Operations + +**List Documents** + +```typescript +const docs = await socket.library.listDocuments( + user?: string, + collection?: string +); +``` + +**Get Document** + +```typescript +const doc = await socket.library.getDocument( + id: string, + user?: string, + collection?: string +); +``` + +**Delete Document** + +```typescript +await socket.library.deleteDocument( + id: string, + user?: string, + collection?: string +); +``` + +### Flow Operations + +Flows represent processing pipelines for documents and queries. + +**Create Flow API** + +```typescript +const flowApi = socket.flow("flow-id"); +// flowApi has same methods as socket but scoped to this flow +``` + +**Start Flow** + +```typescript +await socket.flows.startFlow( + flowId: string, + className: string, + description: string +); +``` + +**Stop Flow** + +```typescript +await socket.flows.stopFlow(flowId: string); +``` + +**List Flows** + +```typescript +const flowIds = await socket.flows.getFlows(); +``` + +**Get Flow Definition** + +```typescript +const flowDef = await socket.flows.getFlow(flowId: string); +``` + +**List Flow Classes** + +```typescript +const classes = await socket.flows.getFlowClasses(); +``` + +**Get Flow Class** + +```typescript +const classDef = await socket.flows.getFlowClass(className: string); +``` + +## Connection State Monitoring + +```typescript +// Subscribe to connection state changes +const unsubscribe = socket.onConnectionStateChange((state) => { + console.log("Status:", state.status); // 'connecting' | 'connected' | 'authenticated' | 'disconnected' | 'error' + console.log("Authenticated:", state.authenticated); + console.log("Error:", state.error); +}); + +// Unsubscribe when done +unsubscribe(); +``` + +## Data Types + +### Value + +Represents a subject, predicate, or object in a triple: + +```typescript +interface Value { + v: string; // Value (URI or literal) + e: boolean; // Is entity (true) or literal (false) + label?: string; // Optional human-readable label +} +``` + +### Triple + +Represents a subject-predicate-object relationship: + +```typescript +interface Triple { + s: Value; // Subject + p: Value; // Predicate + o: Value; // Object +} +``` + +## Advanced Usage + +### Custom Timeout and Retries + +Most methods accept optional timeout and retry parameters: + +```typescript +await socket.triplesQuery( + subject, + predicate, + object, + limit, + collection, + 30000, // timeout in ms + 5, // retry attempts +); +``` + +### Closing the Connection + +```typescript +socket.close(); +``` + +## Error Handling + +All async methods return Promises that reject on error: + +```typescript +try { + const result = await socket.triplesQuery(...); +} catch (error) { + console.error('Query failed:', error); +} +``` + +## React Integration + +For React applications, use the companion package: + +```bash +npm install @trustgraph/react-provider +``` + +See [@trustgraph/react-provider](https://github.com/trustgraph-ai/trustgraph-client) for React-specific hooks and providers. + +## API Reference + +Full API documentation is available in the TypeScript definitions. Your IDE will provide autocomplete and inline documentation for all methods. + +## License + +Apache 2.0 + +(c) KnowNext Inc., KnowNext Limited 2025 + diff --git a/ai-context/trustgraph-client/docs/tech-specs/client-module.md b/ai-context/trustgraph-client/docs/tech-specs/client-module.md new file mode 100644 index 00000000..93e24d9e --- /dev/null +++ b/ai-context/trustgraph-client/docs/tech-specs/client-module.md @@ -0,0 +1,44 @@ +# TrustGraph Client Module - Technical Specification + +## Overview + +This module extracts reusable code from the existing TrustGraph Workbench +application and packages it as a standalone client library. The goal is to +enable developers to build TrustGraph user experiences without having to +reimplement API communication and state management from scratch. + +## Goals + +- Extract and package reusable WebSocket API code from TrustGraph Workbench +- Provide a clean, well-documented interface for TrustGraph WebSocket + communication +- Enable developers to quickly build TrustGraph UX applications +- Eliminate code duplication across TrustGraph UI projects +- Maintain compatibility with existing TrustGraph backend services + +## Non-Goals + +- REST API implementations (WebSocket only) +- UI components or presentation layer code +- Backend service implementations +- Authentication/authorization logic beyond what's needed for WebSocket + connections +- Application-specific business logic + +## Architecture + +## API Design + +## Implementation Plan + +## Testing Strategy + +## Dependencies + +## Security Considerations + +## Performance Considerations + +## Open Questions + +## References diff --git a/ai-context/trustgraph-client/docs/tech-specs/streaming-support.md b/ai-context/trustgraph-client/docs/tech-specs/streaming-support.md new file mode 100644 index 00000000..3f4207f1 --- /dev/null +++ b/ai-context/trustgraph-client/docs/tech-specs/streaming-support.md @@ -0,0 +1,808 @@ +# Streaming Support for TrustGraph Client + +**Status**: Draft for Review +**Author**: Claude +**Date**: 2025-11-27 +**Version**: 1.0 + +## Executive Summary + +Extend the TrustGraph TypeScript client to support streaming responses for Graph RAG, Document RAG, Text Completion, and Prompt services. The client already has streaming infrastructure (`ServiceCallMulti`) used by Agent, but the other services only support single-response mode. This spec proposes minimal changes to enable streaming across all services while maintaining backward compatibility. + +## Background + +### Current State + +The client has **two request patterns**: + +1. **Single-response** (`makeRequest` → `ServiceCall`) + - Used by: text-completion, graph-rag, document-rag, prompt, and most other services + - Returns Promise that resolves with single response + - Example: `graphRag(text: string): Promise` + +2. **Multi-response** (`makeRequestMulti` → `ServiceCallMulti`) + - Used by: agent (thoughts/observations/answer), knowledge.getKgCore (large graph streaming) + - Accepts `receiver: (resp: unknown) => boolean` callback + - Receiver returns `true` to signal end-of-stream + - Example: `agent(question, think, observe, answer, error): void` + +### Backend Streaming Protocol + +Per `STREAMING-IMPLEMENTATION-NOTES.txt`, the backend supports streaming when `streaming: true` is added to requests: + +**Graph RAG / Document RAG**: +- Chunks arrive with `chunk` field +- Final chunk has `end_of_stream: true` + +**Text Completion**: +- Chunks arrive with `response` field +- Final chunk has `end_of_stream: true` + +**Prompt**: +- Chunks arrive with `text` field +- Final chunk has `end_of_stream: true` + +**Agent** (already implemented): +- Multiple messages with `chunk_type` (thought/observation/final-answer) +- Final chunk has `end_of_dialog: true` + +## Problem Statement + +**Primary Issue**: Users who want streaming responses for Graph RAG, Document RAG, Text Completion, or Prompt services must: +1. Drop down to `makeRequestMulti` and handle raw responses +2. Manually parse `chunk`/`response`/`text` fields +3. Check `end_of_stream` flag +4. Handle errors mid-stream + +**Secondary Issue**: The Agent API doesn't correctly implement the backend streaming protocol. The backend sends: +``` +{chunk_type: "thought", content: "I need to", end_of_message: false, end_of_dialog: false} +{chunk_type: "thought", content: " search", end_of_message: false, end_of_dialog: false} +``` + +But the client expects: +``` +{thought?: string, observation?: string, answer?: string} +``` + +The Agent implementation needs to be updated to handle incremental chunks with completion flags. + +## Goals + +1. **Fix Agent API** to correctly implement backend streaming protocol with chunk-level callbacks +2. **Add streaming variants** for text-completion, graph-rag, document-rag, and prompt services +3. **Maintain backward compatibility** - existing non-streaming APIs unchanged (except Agent which needs fixing) +4. **Policy-free implementation** - no state management (accumulation, buffering, etc.) in client layer +5. **Minimal callback interface** - single receiver callback with chunk and completion flag +6. **Minimal type changes** - reuse existing request/response types where possible + +## Non-Goals + +- Changing the existing non-streaming APIs +- Supporting streaming for services that don't stream (embeddings, triples, etc.) +- Implementing state management (accumulation, buffering) - that belongs in higher layers +- Changing the underlying `ServiceCallMulti` implementation + +## Design + +### 1. Type Additions + +Add streaming-specific response types to `src/models/messages.ts`: + +```typescript +// Agent streaming response (NEW - replaces old AgentResponse for streaming) +export interface AgentStreamingResponse { + chunk_type?: "thought" | "action" | "observation" | "final-answer" | "error"; + content?: string; + end_of_message?: boolean; // Current chunk type is complete + end_of_dialog?: boolean; // Entire agent dialog is complete + + // Legacy fields for backward compatibility with non-streaming + thought?: string; + observation?: string; + answer?: string; + error?: string; +} + +// Generic streaming response wrapper for RAG/completion services +export interface StreamingChunk { + chunk?: string; // Graph RAG, Document RAG + response?: string; // Text Completion + text?: string; // Prompt + end_of_stream?: boolean; + error?: { + message: string; + type?: string; + }; +} + +// Request types get optional streaming flag +export interface AgentRequest { + question: string; + user?: string; + streaming?: boolean; // NEW - enable streaming mode +} + +export interface GraphRagRequest { + query: string; + user?: string; + collection?: string; + "entity-limit"?: number; + "triple-limit"?: number; + "max-subgraph-size"?: number; + "max-path-length"?: number; + streaming?: boolean; // NEW +} + +export interface DocumentRagRequest { + query: string; + user?: string; + collection?: string; + "doc-limit"?: number; + streaming?: boolean; // NEW +} + +export interface TextCompletionRequest { + system: string; + prompt: string; + streaming?: boolean; // NEW +} + +export interface PromptRequest { + id: string; + terms: Record; + streaming?: boolean; // NEW +} + +export interface PromptResponse { + text: string; +} +``` + +### 2. BaseApi Additions + +No changes needed to `BaseApi` - `makeRequestMulti` already exists. + +### 3. FlowApi Changes + +#### 3.1 Fix Agent Method + +Update the existing `agent()` method to correctly handle the backend streaming protocol: + +```typescript +export class FlowApi { + /** + * Interacts with an AI agent that provides streaming responses + * BREAKING CHANGE: Callbacks now receive (chunk, complete) instead of full messages + */ + agent( + question: string, + think: (chunk: string, complete: boolean) => void, + observe: (chunk: string, complete: boolean) => void, + answer: (chunk: string, complete: boolean) => void, + error: (s: string) => void, + ) { + const receiver = (response: unknown) => { + const resp = response as AgentStreamingResponse; + + // Check for errors + if (resp.chunk_type === "error" || resp.error) { + const errorMessage = resp.content || resp.error || "Unknown agent error"; + error(typeof errorMessage === "string" ? errorMessage : String(errorMessage)); + return true; // End streaming on error + } + + // Handle streaming chunks by chunk_type + const content = resp.content || ""; + const messageComplete = !!resp.end_of_message; + const dialogComplete = !!resp.end_of_dialog; + + switch (resp.chunk_type) { + case "thought": + think(content, messageComplete); + break; + case "observation": + observe(content, messageComplete); + break; + case "final-answer": + answer(content, messageComplete); + break; + case "action": + // Actions are typically not streamed incrementally, just logged + console.log("Agent action:", content); + break; + } + + return dialogComplete; // End when backend signals end_of_dialog + }; + + return this.api + .makeRequestMulti( + "agent", + { + question: question, + user: this.api.user, + streaming: true, // Always use streaming mode + }, + receiver, + 120000, + 2, + this.flowId, + ) + .catch((err) => { + const errorMessage = + err instanceof Error ? err.message : err?.toString() || "Unknown error"; + error(`Agent request failed: ${errorMessage}`); + }); + } + +#### 3.2 Add New Streaming Methods + +Add streaming variants for other services alongside existing methods in `src/socket/trustgraph-socket.ts`: + +```typescript + // ... existing non-streaming methods unchanged ... + + /** + * Performs Graph RAG query with streaming response + * @param text - Query text + * @param receiver - Called for each chunk with (chunk, complete) where complete=true on final chunk + * @param onError - Called on error + * @param options - Graph RAG options + * @param collection - Collection name + */ + graphRagStreaming( + text: string, + receiver: (chunk: string, complete: boolean) => void, + onError: (error: string) => void, + options?: GraphRagOptions, + collection?: string, + ): void { + const recv = (response: unknown): boolean => { + const resp = response as StreamingChunk; + + if (resp.error) { + onError(resp.error.message); + return true; // End streaming + } + + const chunk = resp.chunk || ""; + const complete = !!resp.end_of_stream; + + receiver(chunk, complete); + + return complete; // End when backend signals end_of_stream + }; + + this.api.makeRequestMulti( + "graph-rag", + { + query: text, + user: this.api.user, + collection: collection || "default", + "entity-limit": options?.entityLimit, + "triple-limit": options?.tripleLimit, + "max-subgraph-size": options?.maxSubgraphSize, + "max-path-length": options?.pathLength, + streaming: true, + }, + recv, + 60000, + undefined, + this.flowId, + ); + } + + /** + * Performs Document RAG query with streaming response + * @param text - Query text + * @param receiver - Called for each chunk with (chunk, complete) where complete=true on final chunk + * @param onError - Called on error + * @param docLimit - Maximum documents to retrieve + * @param collection - Collection name + */ + documentRagStreaming( + text: string, + receiver: (chunk: string, complete: boolean) => void, + onError: (error: string) => void, + docLimit?: number, + collection?: string, + ): void { + const recv = (response: unknown): boolean => { + const resp = response as StreamingChunk; + + if (resp.error) { + onError(resp.error.message); + return true; + } + + const chunk = resp.chunk || ""; + const complete = !!resp.end_of_stream; + + receiver(chunk, complete); + + return complete; + }; + + this.api.makeRequestMulti( + "document-rag", + { + query: text, + user: this.api.user, + collection: collection || "default", + "doc-limit": docLimit, + streaming: true, + }, + recv, + 60000, + undefined, + this.flowId, + ); + } + + /** + * Performs text completion with streaming response + * @param system - System prompt + * @param text - User prompt + * @param receiver - Called for each chunk with (chunk, complete) where complete=true on final chunk + * @param onError - Called on error + */ + textCompletionStreaming( + system: string, + text: string, + receiver: (chunk: string, complete: boolean) => void, + onError: (error: string) => void, + ): void { + const recv = (response: unknown): boolean => { + const resp = response as StreamingChunk; + + if (resp.error) { + onError(resp.error.message); + return true; + } + + // Text completion uses 'response' field, not 'chunk' + const chunk = resp.response || ""; + const complete = !!resp.end_of_stream; + + receiver(chunk, complete); + + return complete; + }; + + this.api.makeRequestMulti( + "text-completion", + { + system: system, + prompt: text, + streaming: true, + }, + recv, + 30000, + undefined, + this.flowId, + ); + } + + /** + * Executes a prompt template with streaming response + * @param id - Prompt template ID + * @param terms - Template variables + * @param receiver - Called for each chunk with (chunk, complete) where complete=true on final chunk + * @param onError - Called on error + */ + promptStreaming( + id: string, + terms: Record, + receiver: (chunk: string, complete: boolean) => void, + onError: (error: string) => void, + ): void { + const recv = (response: unknown): boolean => { + const resp = response as StreamingChunk; + + if (resp.error) { + onError(resp.error.message); + return true; + } + + // Prompt service uses 'text' field + const chunk = resp.text || ""; + const complete = !!resp.end_of_stream; + + receiver(chunk, complete); + + return complete; + }; + + this.api.makeRequestMulti( + "prompt", + { + id: id, + terms: terms, + streaming: true, + }, + recv, + 30000, + undefined, + this.flowId, + ); + } +} +``` + +### 4. BaseApi Convenience Methods (Optional) + +For users who don't need flow routing, add streaming methods to BaseApi: + +```typescript +export class BaseApi { + // Existing methods... + + /** + * Streaming text completion without flow routing + */ + textCompletionStreaming( + system: string, + prompt: string, + receiver: (chunk: string, complete: boolean) => void, + onError: (error: string) => void, + ): void { + const flowApi = new FlowApi(this, undefined); + flowApi.textCompletionStreaming(system, prompt, receiver, onError); + } + + // Similar for graphRagStreaming, documentRagStreaming, promptStreaming... +} +``` + +**Recommendation**: Add these for consistency with existing non-streaming methods on BaseApi. + +## Implementation Plan + +### Phase 1: Core Types (1 hour) +1. Add `streaming?: boolean` to request types +2. Add `StreamingChunk` interface +3. Add `PromptRequest` and `PromptResponse` types (currently missing) + +### Phase 2: FlowApi Streaming Methods (2 hours) +1. Implement `textCompletionStreaming` +2. Implement `graphRagStreaming` +3. Implement `documentRagStreaming` +4. Implement `promptStreaming` +5. Add JSDoc comments + +### Phase 3: BaseApi Convenience Methods (1 hour) +1. Add streaming methods to BaseApi +2. Update interface definitions +3. Update README with streaming examples + +### Phase 4: Testing (2 hours) +1. Add unit tests for streaming methods +2. Add integration tests against mock WebSocket +3. Test error handling mid-stream +4. Test timeout behavior +5. Test concurrent streaming requests + +### Phase 5: Documentation (1 hour) +1. Update README with streaming examples +2. Add streaming guide to docs/ +3. Update API reference + +**Total Estimated Time**: 7 hours + +## Testing Strategy + +### Unit Tests + +```typescript +describe("FlowApi streaming", () => { + it("should stream graph-rag chunks", async () => { + const chunks: Array<{ chunk: string; complete: boolean }> = []; + + flowApi.graphRagStreaming( + "test query", + (chunk, complete) => { + chunks.push({ chunk, complete }); + }, + (error) => fail(error), + ); + + // Simulate streaming chunks + mockWebSocket.simulateMessage({ chunk: "Hello", end_of_stream: false }); + mockWebSocket.simulateMessage({ chunk: " world", end_of_stream: false }); + mockWebSocket.simulateMessage({ chunk: "", end_of_stream: true }); + + expect(chunks).toEqual([ + { chunk: "Hello", complete: false }, + { chunk: " world", complete: false }, + { chunk: "", complete: true }, + ]); + }); + + it("should handle errors mid-stream", async () => { + let errorMsg = ""; + const chunks: string[] = []; + + flowApi.graphRagStreaming( + "test query", + (chunk, complete) => { + chunks.push(chunk); + }, + (error) => { + errorMsg = error; + }, + ); + + mockWebSocket.simulateMessage({ chunk: "Partial", end_of_stream: false }); + mockWebSocket.simulateMessage({ + error: { message: "LLM timeout" }, + end_of_stream: true, + }); + + expect(errorMsg).toBe("LLM timeout"); + expect(chunks).toEqual(["Partial"]); // Receiver gets chunks before error + }); +}); +``` + +### Integration Tests + +Test against actual TrustGraph backend (manual testing): +1. Start TrustGraph backend with streaming enabled +2. Test each streaming method with real queries +3. Verify chunks arrive in order +4. Verify end_of_stream handling +5. Test error scenarios (invalid query, timeout) + +## Migration Guide + +### For Users + +#### Graph RAG / Document RAG / Text Completion / Prompt + +**Before (non-streaming)**: +```typescript +const response = await flowApi.graphRag("What is machine learning?"); +console.log(response); // Full text after 10-30 seconds +``` + +**After (streaming)**: +```typescript +let accumulated = ""; + +flowApi.graphRagStreaming( + "What is machine learning?", + (chunk, complete) => { + accumulated += chunk; + updateDisplay(accumulated); + + if (complete) { + console.log("Final:", accumulated); + } + }, + (error) => { + console.error("Error:", error); + } +); +``` + +#### Agent (BREAKING CHANGE) + +**Before (old client - incorrect)**: +```typescript +flowApi.agent( + "What is machine learning?", + (thought) => console.log("Thinking:", thought), // Full thought received + (observation) => console.log("Observing:", observation), // Full observation received + (answer) => console.log("Answer:", answer), // Full answer received + (error) => console.error(error), +); +``` + +**After (updated to match backend)**: +```typescript +let currentThought = ""; +let currentObservation = ""; +let currentAnswer = ""; + +flowApi.agent( + "What is machine learning?", + (chunk, complete) => { + currentThought += chunk; + updateThinkingDisplay(currentThought); + if (complete) { + console.log("Thought complete:", currentThought); + currentThought = ""; // Reset for next thought + } + }, + (chunk, complete) => { + currentObservation += chunk; + updateObservationDisplay(currentObservation); + if (complete) { + console.log("Observation complete:", currentObservation); + currentObservation = ""; + } + }, + (chunk, complete) => { + currentAnswer += chunk; + updateAnswerDisplay(currentAnswer); + if (complete) { + console.log("Final answer:", currentAnswer); + } + }, + (error) => console.error(error), +); +``` + +### Gradual Adoption + +**For Graph RAG / Document RAG / Text Completion / Prompt**: +1. Continue using non-streaming APIs (no breaking changes) +2. Add streaming variants for user-facing chat interfaces first +3. Keep non-streaming for background tasks +4. Optionally add feature flag to toggle streaming on/off + +**For Agent (BREAKING CHANGE)**: +1. Existing Agent users MUST update their callbacks to handle (chunk, complete) signature +2. Add accumulation logic in callback handlers +3. Use `complete` flag to detect when to reset accumulator or take final action + +## Risks and Mitigations + +### Risk 1: BREAKING CHANGE for Agent API +**Concern**: Existing Agent users must update their code when they upgrade. + +**Mitigation**: +- Document the breaking change clearly in release notes +- Provide migration examples in this spec +- Consider: Add deprecation warning in previous version before breaking change +- Consider: Bump major version to signal breaking change +- The old API was incorrect anyway - this fixes a bug in the client + +### Risk 2: API Surface Growth +**Concern**: Adding 4 new methods per API class (FlowApi, BaseApi) increases maintenance burden. + +**Mitigation**: +- Methods share identical structure (only field name differs: chunk/response/text) +- Could extract common streaming handler if needed +- Backend already implements streaming, so no protocol risk + +### Risk 3: TypeScript Type Safety +**Concern**: `StreamingChunk` union type may be confusing (chunk vs response vs text). + +**Mitigation**: +- Each service method documents which field it uses +- Runtime code checks correct field +- Implementation is simple enough that field selection is obvious + +### Risk 4: State Management in User Code +**Concern**: Users must manually accumulate chunks if they need full text. + +**Mitigation**: +- This is intentional - client stays policy-free +- Higher-level abstractions (React hooks, etc.) can provide accumulation +- For users who don't need streaming behavior, non-streaming APIs remain unchanged + +## Future Enhancements + +### 1. Async Iterator API +Provide a modern streaming API using async iterators: + +```typescript +async *graphRagStream(text: string): AsyncGenerator { + // Wraps graphRagStreaming in async iterator +} + +// Usage: +for await (const chunk of flowApi.graphRagStream("query")) { + console.log(chunk); +} +``` + +### 2. Retry on Stream Interruption +Currently, retries only apply to initial request. Could add mid-stream retry: +- Detect connection drop mid-stream +- Resume from last chunk (if backend supports resumption) + +### 3. Client-Side Buffering +For very fast chunk arrival, buffer multiple chunks before calling receiver: +- Reduces callback frequency +- Could be opt-in via options parameter +- Note: This would add policy to the client, may be better in higher layers + +### 4. Stream Cancellation +Allow users to cancel in-flight streaming requests: +```typescript +const cancel = flowApi.graphRagStreaming(...); +// Later: +cancel(); +``` + +## Alternatives Considered + +### Alternative 1: Separate Callbacks for Chunk and Complete +Use three callbacks: onChunk, onComplete, onError: + +```typescript +graphRagStreaming( + text: string, + onChunk: (chunk: string, accumulated: string) => void, + onComplete: (fullText: string) => void, + onError: (error: string) => void, +) +``` + +**Rejected because**: +- Adds state management (accumulation) to the client layer +- Harder for implementations that need both signals at once +- More verbose callback signature + +### Alternative 2: Unified Streaming Flag on Existing Methods +Modify existing methods to detect streaming callbacks: + +```typescript +graphRag( + text: string, + options?: GraphRagOptions, + collection?: string, + receiver?: (chunk: string, complete: boolean) => void, +): Promise | void +``` + +**Rejected because**: +- Violates single responsibility principle +- Return type becomes conditional (Promise vs void) +- Hard to type correctly in TypeScript +- Confusing API (streaming vs non-streaming behavior implicit) + +### Alternative 3: Separate StreamingFlowApi Class +Create a parallel API class for streaming: + +```typescript +export class StreamingFlowApi { + graphRag(text: string, receiver: ..., onError: ...): void; + documentRag(text: string, receiver: ..., onError: ...): void; +} +``` + +**Rejected because**: +- Duplicates all configuration and state management +- Users must manage two API instances +- No clear benefit over method suffixes + +## Open Questions + +1. **Should we add streaming to Prompt service?** + - Prompt service is not currently in client (no PromptRequest/Response types) + - Could add it alongside streaming support + - **Decision**: Yes, add it for completeness (mentioned in backend docs) + +2. **Should we add TypeScript overloads?** + - Allow `graphRagStreaming(text, callbacks)` vs `graphRagStreaming(text, options, callbacks)` + - **Decision**: Use optional parameters (simpler implementation) + +## Conclusion + +This proposal adds streaming support to the TrustGraph client and fixes the Agent API to correctly implement the backend protocol: + +**Changes**: +1. **Fix Agent API** (BREAKING): Update callbacks to receive `(chunk, complete)` instead of full messages +2. Add `streaming?: boolean` flag to all request types +3. Add `AgentStreamingResponse` and `StreamingChunk` response types +4. Add `*Streaming` method variants to FlowApi and BaseApi for RAG/completion services +5. Use consistent two-callback pattern: `receiver(chunk, complete)` and `onError(message)` across all services + +The implementation is straightforward (~7-10 hours including Agent fix), stays minimal and focused, and provides a clean foundation for higher-level abstractions to build upon. + +**Key Design Principles**: +- **Policy-free**: No accumulation or buffering in client layer +- **Minimal callbacks**: Single receiver gets both chunk and completion signal +- **Protocol-correct**: Agent now properly implements backend's chunk_type/content/end_of_message protocol +- **Consistent**: Same pattern across all streaming services +- **Backward compatible**: Existing non-streaming APIs unchanged (except Agent which needs fixing) + +**Breaking Changes**: +- Agent API callbacks change from `(fullMessage: string)` to `(chunk: string, complete: boolean)` +- Requires major version bump + +**Recommendation**: Approve and implement in current sprint. diff --git a/ai-context/trustgraph-client/eslint.config.js b/ai-context/trustgraph-client/eslint.config.js new file mode 100644 index 00000000..462b81af --- /dev/null +++ b/ai-context/trustgraph-client/eslint.config.js @@ -0,0 +1,35 @@ +import js from "@eslint/js"; +import tseslint from "typescript-eslint"; +import globals from "globals"; + +export default tseslint.config( + js.configs.recommended, + ...tseslint.configs.recommended, + { + files: ["**/*.{ts,tsx}"], + languageOptions: { + parser: tseslint.parser, + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + }, + globals: { + ...globals.browser, + ...globals.es2021, + }, + }, + rules: { + "@typescript-eslint/no-unused-vars": [ + "warn", + { + argsIgnorePattern: "^_", + varsIgnorePattern: "^_", + }, + ], + "@typescript-eslint/no-explicit-any": "warn", + }, + }, + { + ignores: ["dist/**", "node_modules/**", "*.config.js"], + }, +); diff --git a/ai-context/trustgraph-client/package.json b/ai-context/trustgraph-client/package.json new file mode 100644 index 00000000..c47b1002 --- /dev/null +++ b/ai-context/trustgraph-client/package.json @@ -0,0 +1,66 @@ +{ + "name": "@trustgraph/client", + "version": "1.6.0", + "description": "TypeScript client for TrustGraph", + "type": "module", + "main": "dist/index.esm.js", + "module": "dist/index.esm.js", + "types": "dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.esm.js", + "require": "./dist/index.cjs" + } + }, + "files": [ + "dist" + ], + "scripts": { + "build": "rollup -c", + "dev": "rollup -c -w", + "test": "vitest run", + "test:watch": "vitest", + "test:ui": "vitest --ui", + "lint": "eslint src", + "typecheck": "tsc --noEmit", + "prettify": "prettier --write .", + "prepare": "npm run build" + }, + "keywords": [ + "trustgraph", + "websocket", + "typescript", + "client" + ], + "author": "KnowNext Limited", + "license": "Apache-2.0", + "devDependencies": { + "@eslint/js": "^9.37.0", + "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-node-resolve": "^15.2.3", + "@rollup/plugin-typescript": "^11.1.6", + "@vitest/ui": "^3.2.4", + "eslint": "^9.39.3", + "globals": "^16.4.0", + "happy-dom": "^20.0.10", + "jiti": "^2.6.1", + "prettier": "^3.6.2", + "rollup": "^4.9.0", + "tslib": "^2.6.2", + "typescript": "^5.3.3", + "typescript-eslint": "^8.46.0", + "vitest": "^3.2.4" + }, + "directories": { + "doc": "docs" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/trustgraph-ai/trustgraph-client.git" + }, + "bugs": { + "url": "https://github.com/trustgraph-ai/trustgraph-client/issues" + }, + "homepage": "https://github.com/trustgraph-ai/trustgraph-client#readme" +} diff --git a/ai-context/trustgraph-client/rollup.config.js b/ai-context/trustgraph-client/rollup.config.js new file mode 100644 index 00000000..ffa5e319 --- /dev/null +++ b/ai-context/trustgraph-client/rollup.config.js @@ -0,0 +1,30 @@ +import resolve from "@rollup/plugin-node-resolve"; +import commonjs from "@rollup/plugin-commonjs"; +import typescript from "@rollup/plugin-typescript"; + +export default { + input: "src/index.ts", + output: [ + { + file: "dist/index.cjs", + format: "cjs", + sourcemap: true, + }, + { + file: "dist/index.esm.js", + format: "esm", + sourcemap: true, + }, + ], + external: ["react", "react-dom"], + plugins: [ + resolve(), + commonjs(), + typescript({ + tsconfig: "./tsconfig.json", + declaration: true, + declarationDir: "dist", + rootDir: "src", + }), + ], +}; diff --git a/ai-context/trustgraph-client/src/__tests__/flows-api.test.ts b/ai-context/trustgraph-client/src/__tests__/flows-api.test.ts new file mode 100644 index 00000000..e011af72 --- /dev/null +++ b/ai-context/trustgraph-client/src/__tests__/flows-api.test.ts @@ -0,0 +1,221 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { FlowsApi } from "../socket/trustgraph-socket"; +import { FlowResponse } from "../models/messages"; + +describe("FlowsApi", () => { + let mockApi: { + makeRequest: ReturnType; + }; + let flowsApi: FlowsApi; + + beforeEach(() => { + mockApi = { + makeRequest: vi.fn(), + }; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + flowsApi = new FlowsApi(mockApi as any); + }); + + describe("startFlow", () => { + it("should call makeRequest with correct types and parameters", async () => { + const mockResponse: FlowResponse = { + flow: "started", + description: "Flow started successfully", + }; + mockApi.makeRequest.mockResolvedValue(mockResponse); + + const result = await flowsApi.startFlow( + "test-flow-id", + "test-class", + "Test description", + ); + + expect(mockApi.makeRequest).toHaveBeenCalledWith( + "flow", + { + operation: "start-flow", + "flow-id": "test-flow-id", + "blueprint-name": "test-class", + description: "Test description", + }, + 30000, + ); + expect(result).toEqual(mockResponse); + }); + + it("should use FlowRequest and FlowResponse types", async () => { + const mockResponse: FlowResponse = {}; + mockApi.makeRequest.mockResolvedValue(mockResponse); + + await flowsApi.startFlow("id", "class", "desc"); + + // Verify the call signature matches FlowRequest/FlowResponse types + const callArgs = mockApi.makeRequest.mock.calls[0]; + const request = callArgs[1]; + + // These properties should match FlowRequest interface + expect(request).toHaveProperty("operation"); + expect(request).toHaveProperty("flow-id"); + expect(request).toHaveProperty("blueprint-name"); + expect(request).toHaveProperty("description"); + }); + }); + + describe("stopFlow", () => { + it("should call makeRequest with correct types and parameters", async () => { + const mockResponse: FlowResponse = { + flow: "stopped", + description: "Flow stopped successfully", + }; + mockApi.makeRequest.mockResolvedValue(mockResponse); + + const result = await flowsApi.stopFlow("test-flow-id"); + + expect(mockApi.makeRequest).toHaveBeenCalledWith( + "flow", + { + operation: "stop-flow", + "flow-id": "test-flow-id", + }, + 30000, + ); + expect(result).toEqual(mockResponse); + }); + + it("should use FlowRequest and FlowResponse types", async () => { + const mockResponse: FlowResponse = {}; + mockApi.makeRequest.mockResolvedValue(mockResponse); + + await flowsApi.stopFlow("id"); + + // Verify the call signature matches FlowRequest/FlowResponse types + const callArgs = mockApi.makeRequest.mock.calls[0]; + const request = callArgs[1]; + + // These properties should match FlowRequest interface + expect(request).toHaveProperty("operation"); + expect(request).toHaveProperty("flow-id"); + }); + }); + + describe("getFlows", () => { + it("should return flow-ids array from response", async () => { + const mockResponse: FlowResponse = { + "flow-ids": ["flow1", "flow2", "flow3"], + }; + mockApi.makeRequest.mockResolvedValue(mockResponse); + + const result = await flowsApi.getFlows(); + + expect(mockApi.makeRequest).toHaveBeenCalledWith( + "flow", + { + operation: "list-flows", + }, + 60000, + ); + expect(result).toEqual(["flow1", "flow2", "flow3"]); + }); + + it("should return empty array when flow-ids is undefined", async () => { + const mockResponse: FlowResponse = {}; + mockApi.makeRequest.mockResolvedValue(mockResponse); + + const result = await flowsApi.getFlows(); + + expect(result).toEqual([]); + }); + + it("should handle response with flow-ids property correctly", async () => { + // This test ensures we're accessing the hyphenated property name correctly + const mockResponse = { + "flow-ids": ["test-flow"], + "other-property": "should-be-ignored", + }; + mockApi.makeRequest.mockResolvedValue(mockResponse); + + const result = await flowsApi.getFlows(); + + expect(result).toEqual(["test-flow"]); + }); + }); + + describe("getFlowBlueprints", () => { + it("should return blueprint-names array from response", async () => { + const mockResponse: FlowResponse = { + "blueprint-names": ["class1", "class2"], + }; + mockApi.makeRequest.mockResolvedValue(mockResponse); + + const result = await flowsApi.getFlowBlueprints(); + + expect(mockApi.makeRequest).toHaveBeenCalledWith( + "flow", + { + operation: "list-blueprints", + }, + 60000, + ); + expect(result).toEqual(["class1", "class2"]); + }); + + it("should handle response with blueprint-names property correctly", async () => { + // This test ensures we're accessing the hyphenated property name correctly + const mockResponse = { + "blueprint-names": ["test-class"], + "other-property": "should-be-ignored", + }; + mockApi.makeRequest.mockResolvedValue(mockResponse); + + const result = await flowsApi.getFlowBlueprints(); + + expect(result).toEqual(["test-class"]); + }); + }); + + describe("getFlow", () => { + it("should call makeRequest with correct parameters and parse JSON", async () => { + const flowDefinition = { type: "flow", config: "test" }; + const mockResponse: FlowResponse = { + flow: JSON.stringify(flowDefinition), // Must be valid JSON string + description: "Test flow", + }; + mockApi.makeRequest.mockResolvedValue(mockResponse); + + const result = await flowsApi.getFlow("test-flow-id"); + + expect(mockApi.makeRequest).toHaveBeenCalledWith( + "flow", + { + operation: "get-flow", + "flow-id": "test-flow-id", + }, + 60000, + ); + expect(result).toEqual(flowDefinition); // Result should be parsed JSON + }); + }); + + describe("getFlowBlueprint", () => { + it("should call makeRequest with correct parameters and parse JSON", async () => { + const blueprintDefinition = { type: "blueprint", name: "test-blueprint" }; + const mockResponse: FlowResponse = { + "blueprint-definition": JSON.stringify(blueprintDefinition), // Must be valid JSON string + description: "Test blueprint", + }; + mockApi.makeRequest.mockResolvedValue(mockResponse); + + const result = await flowsApi.getFlowBlueprint("test-class"); + + expect(mockApi.makeRequest).toHaveBeenCalledWith( + "flow", + { + operation: "get-blueprint", + "blueprint-name": "test-class", + }, + 60000, + ); + expect(result).toEqual(blueprintDefinition); // Result should be parsed JSON + }); + }); +}); diff --git a/ai-context/trustgraph-client/src/__tests__/messages.test.ts b/ai-context/trustgraph-client/src/__tests__/messages.test.ts new file mode 100644 index 00000000..65d96c9e --- /dev/null +++ b/ai-context/trustgraph-client/src/__tests__/messages.test.ts @@ -0,0 +1,370 @@ +import { describe, it, expect } from "vitest"; +import type { + RequestMessage, + ApiResponse, + TextCompletionRequest, + TextCompletionResponse, + GraphRagRequest, + GraphRagResponse, + AgentRequest, + AgentResponse, + EmbeddingsRequest, + EmbeddingsResponse, + GraphEmbeddingsQueryRequest, + GraphEmbeddingsQueryResponse, + TriplesQueryRequest, + LoadDocumentRequest, + LoadTextRequest, + LibraryRequest, + LibraryResponse, + FlowRequest, + FlowResponse, + DocumentMetadata, + ProcessingMetadata, +} from "../models/messages"; + +describe("Message Types", () => { + describe("RequestMessage", () => { + it("should have correct structure", () => { + const message: RequestMessage = { + id: "test-id", + service: "test-service", + request: { test: "data" }, + }; + + expect(message.id).toBe("test-id"); + expect(message.service).toBe("test-service"); + expect(message.request).toEqual({ test: "data" }); + }); + }); + + describe("ApiResponse", () => { + it("should have correct structure", () => { + const response: ApiResponse = { + id: "test-id", + response: { result: "success" }, + }; + + expect(response.id).toBe("test-id"); + expect(response.response).toEqual({ result: "success" }); + }); + }); + + describe("TextCompletionRequest", () => { + it("should have correct structure", () => { + const request: TextCompletionRequest = { + system: "You are a helpful assistant", + prompt: "Hello, world!", + }; + + expect(request.system).toBe("You are a helpful assistant"); + expect(request.prompt).toBe("Hello, world!"); + }); + }); + + describe("TextCompletionResponse", () => { + it("should have correct structure", () => { + const response: TextCompletionResponse = { + response: "Hello! How can I help you today?", + }; + + expect(response.response).toBe("Hello! How can I help you today?"); + }); + }); + + describe("GraphRagRequest", () => { + it("should have correct structure with required query", () => { + const request: GraphRagRequest = { + query: "What is the capital of France?", + }; + + expect(request.query).toBe("What is the capital of France?"); + }); + + it("should have correct structure with optional parameters", () => { + const request: GraphRagRequest = { + query: "What is the capital of France?", + "entity-limit": 100, + "triple-limit": 50, + "max-subgraph-size": 2000, + "max-path-length": 3, + }; + + expect(request.query).toBe("What is the capital of France?"); + expect(request["entity-limit"]).toBe(100); + expect(request["triple-limit"]).toBe(50); + expect(request["max-subgraph-size"]).toBe(2000); + expect(request["max-path-length"]).toBe(3); + }); + }); + + describe("GraphRagResponse", () => { + it("should have correct structure", () => { + const response: GraphRagResponse = { + response: "The capital of France is Paris.", + }; + + expect(response.response).toBe("The capital of France is Paris."); + }); + }); + + describe("AgentRequest", () => { + it("should have correct structure", () => { + const request: AgentRequest = { + question: "What is the weather like today?", + }; + + expect(request.question).toBe("What is the weather like today?"); + }); + }); + + describe("AgentResponse", () => { + it("should have correct structure with all fields", () => { + const response: AgentResponse = { + thought: "I need to check the weather", + observation: "Weather API shows sunny conditions", + answer: "It is sunny today", + error: undefined, + }; + + expect(response.thought).toBe("I need to check the weather"); + expect(response.observation).toBe("Weather API shows sunny conditions"); + expect(response.answer).toBe("It is sunny today"); + expect(response.error).toBeUndefined(); + }); + + it("should handle error response", () => { + const response: AgentResponse = { + error: { type: "agent-error", message: "Weather service unavailable" }, + }; + + expect(response.error?.message).toBe("Weather service unavailable"); + expect(response.error?.type).toBe("agent-error"); + }); + }); + + describe("EmbeddingsRequest", () => { + it("should have correct structure", () => { + const request: EmbeddingsRequest = { + texts: ["This is a test sentence for embedding", "Another text"], + }; + + expect(request.texts).toEqual(["This is a test sentence for embedding", "Another text"]); + }); + }); + + describe("EmbeddingsResponse", () => { + it("should have correct structure", () => { + // vectors[text_index][dimension_index] - one vector per input text + const response: EmbeddingsResponse = { + vectors: [ + [0.1, 0.2, 0.3], // First text's vector + [0.4, 0.5, 0.6], // Second text's vector + ], + }; + + expect(response.vectors).toEqual([ + [0.1, 0.2, 0.3], + [0.4, 0.5, 0.6], + ]); + }); + }); + + describe("GraphEmbeddingsQueryRequest", () => { + it("should have correct structure", () => { + const request: GraphEmbeddingsQueryRequest = { + vector: [0.1, 0.2, 0.3], + limit: 10, + }; + + expect(request.vector).toEqual([0.1, 0.2, 0.3]); + expect(request.limit).toBe(10); + }); + }); + + describe("GraphEmbeddingsQueryResponse", () => { + it("should have correct structure", () => { + const response: GraphEmbeddingsQueryResponse = { + entities: [ + { entity: { t: "i", i: "http://example.org/entity1" }, score: 0.95 }, + { entity: { t: "i", i: "http://example.org/entity2" }, score: 0.87 }, + ], + }; + + expect(response.entities).toHaveLength(2); + expect(response.entities[0].score).toBe(0.95); + expect(response.entities[0].entity?.t).toBe("i"); + expect((response.entities[0].entity as { t: "i"; i: string }).i).toBe("http://example.org/entity1"); + expect(response.entities[1].score).toBe(0.87); + }); + }); + + describe("TriplesQueryRequest", () => { + it("should have correct structure with all fields", () => { + const request: TriplesQueryRequest = { + s: { t: "i", i: "http://example.org/subject" }, + p: { t: "i", i: "http://example.org/predicate" }, + o: { t: "l", v: "object value" }, + limit: 100, + }; + + expect((request.s as { t: "i"; i: string }).i).toBe("http://example.org/subject"); + expect((request.p as { t: "i"; i: string }).i).toBe("http://example.org/predicate"); + expect((request.o as { t: "l"; v: string }).v).toBe("object value"); + expect(request.limit).toBe(100); + }); + + it("should handle optional fields", () => { + const request: TriplesQueryRequest = { + limit: 50, + }; + + expect(request.s).toBeUndefined(); + expect(request.p).toBeUndefined(); + expect(request.o).toBeUndefined(); + expect(request.limit).toBe(50); + }); + }); + + describe("LoadDocumentRequest", () => { + it("should have correct structure", () => { + const request: LoadDocumentRequest = { + id: "doc-123", + data: "base64-encoded-document-data", + metadata: [ + { + s: { t: "i", i: "http://example.org/doc-123" }, + p: { t: "i", i: "http://example.org/title" }, + o: { t: "l", v: "Test Document" }, + }, + ], + }; + + expect(request.id).toBe("doc-123"); + expect(request.data).toBe("base64-encoded-document-data"); + expect(request.metadata).toHaveLength(1); + }); + }); + + describe("LoadTextRequest", () => { + it("should have correct structure", () => { + const request: LoadTextRequest = { + id: "text-123", + text: "This is some text to load", + charset: "utf-8", + metadata: [], + }; + + expect(request.id).toBe("text-123"); + expect(request.text).toBe("This is some text to load"); + expect(request.charset).toBe("utf-8"); + expect(request.metadata).toEqual([]); + }); + }); + + describe("DocumentMetadata", () => { + it("should have correct structure", () => { + const metadata: DocumentMetadata = { + id: "doc-123", + time: 1640995200000, + kind: "pdf", + title: "Test Document", + comments: "A test document", + metadata: [], + user: "test-user", + tags: ["test", "document"], + }; + + expect(metadata.id).toBe("doc-123"); + expect(metadata.time).toBe(1640995200000); + expect(metadata.kind).toBe("pdf"); + expect(metadata.title).toBe("Test Document"); + expect(metadata.comments).toBe("A test document"); + expect(metadata.user).toBe("test-user"); + expect(metadata.tags).toEqual(["test", "document"]); + }); + }); + + describe("ProcessingMetadata", () => { + it("should have correct structure", () => { + const metadata: ProcessingMetadata = { + id: "proc-123", + "document-id": "doc-123", + time: 1640995200000, + flow: "default-flow", + user: "test-user", + collection: "test-collection", + tags: ["processing", "test"], + }; + + expect(metadata.id).toBe("proc-123"); + expect(metadata["document-id"]).toBe("doc-123"); + expect(metadata.time).toBe(1640995200000); + expect(metadata.flow).toBe("default-flow"); + expect(metadata.user).toBe("test-user"); + expect(metadata.collection).toBe("test-collection"); + expect(metadata.tags).toEqual(["processing", "test"]); + }); + }); + + describe("LibraryRequest", () => { + it("should have correct structure", () => { + const request: LibraryRequest = { + operation: "list_documents", + user: "test-user", + collection: "test-collection", + }; + + expect(request.operation).toBe("list_documents"); + expect(request.user).toBe("test-user"); + expect(request.collection).toBe("test-collection"); + }); + }); + + describe("LibraryResponse", () => { + it("should have correct structure", () => { + const response: LibraryResponse = { + error: new Error(), + "document-metadatas": [ + { + id: "doc-1", + title: "Document 1", + time: 1640995200000, + }, + ], + }; + + expect(response.error).toBeInstanceOf(Error); + expect(response["document-metadatas"]).toHaveLength(1); + expect(response["document-metadatas"]![0].id).toBe("doc-1"); + }); + }); + + describe("FlowRequest", () => { + it("should have correct structure", () => { + const request: FlowRequest = { + operation: "get_flow", + "flow-id": "default-flow", + }; + + expect(request.operation).toBe("get_flow"); + expect(request["flow-id"]).toBe("default-flow"); + }); + }); + + describe("FlowResponse", () => { + it("should have correct structure", () => { + const response: FlowResponse = { + "flow-ids": ["flow-1", "flow-2"], + flow: "flow-definition", + description: "A test flow", + error: undefined, + }; + + expect(response["flow-ids"]).toEqual(["flow-1", "flow-2"]); + expect(response.flow).toBe("flow-definition"); + expect(response.description).toBe("A test flow"); + expect(response.error).toBeUndefined(); + }); + }); +}); diff --git a/ai-context/trustgraph-client/src/__tests__/service-call-multi.test.ts b/ai-context/trustgraph-client/src/__tests__/service-call-multi.test.ts new file mode 100644 index 00000000..c414c574 --- /dev/null +++ b/ai-context/trustgraph-client/src/__tests__/service-call-multi.test.ts @@ -0,0 +1,285 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { ServiceCallMulti } from "../socket/service-call-multi"; + +// Mock WebSocket constants +vi.stubGlobal("WebSocket", { + OPEN: 1, + CONNECTING: 0, + CLOSING: 2, + CLOSED: 3, +}); + +// Mock Socket interface +const mockSocket = { + inflight: {} as Record, + ws: { + send: vi.fn(), + readyState: 1, // WebSocket.OPEN + }, + reopen: vi.fn(), +}; + +// Mock setTimeout and clearTimeout +const mockSetTimeout = vi.fn(); +const mockClearTimeout = vi.fn(); + +vi.stubGlobal("setTimeout", mockSetTimeout); +vi.stubGlobal("clearTimeout", mockClearTimeout); + +describe("ServiceCallMulti", () => { + let mockSuccess: ReturnType; + let mockError: ReturnType; + let mockReceiver: ReturnType; + let serviceCallMulti: ServiceCallMulti; + + beforeEach(() => { + vi.clearAllMocks(); + mockSuccess = vi.fn(); + mockError = vi.fn(); + mockReceiver = vi.fn(); + mockSocket.inflight = {} as Record; + mockSocket.ws = { + send: vi.fn(), + readyState: 1, // WebSocket.OPEN + }; + mockSocket.reopen.mockClear(); + + serviceCallMulti = new ServiceCallMulti( + "test-mid", + { id: "test-id", service: "test-service", request: { test: "data" } }, + mockSuccess, + mockError, + 5000, // 5 second timeout + 3, // 3 retries + // eslint-disable-next-line @typescript-eslint/no-explicit-any + mockSocket as any, + mockReceiver, + ); + }); + + it("should initialize with correct properties", () => { + expect(serviceCallMulti.mid).toBe("test-mid"); + expect(serviceCallMulti.timeout).toBe(5000); + expect(serviceCallMulti.retries).toBe(3); + expect(serviceCallMulti.complete).toBe(false); + expect(serviceCallMulti.socket).toBe(mockSocket); + expect(serviceCallMulti.receiver).toBe(mockReceiver); + }); + + it("should register itself in socket inflight when started", () => { + serviceCallMulti.start(); + + expect(mockSocket.inflight["test-mid"]).toBe(serviceCallMulti); + }); + + it("should send message on successful attempt", () => { + serviceCallMulti.start(); + + expect(mockSocket.ws.send).toHaveBeenCalledWith( + JSON.stringify({ + id: "test-id", + service: "test-service", + request: { test: "data" }, + }), + ); + expect(mockSetTimeout).toHaveBeenCalled(); + }); + + it("should handle response when receiver returns true (completion)", () => { + mockReceiver.mockReturnValue(true); // Signal completion + const response = { result: "success" }; + + serviceCallMulti.start(); + serviceCallMulti.onReceived(response); + + expect(mockReceiver).toHaveBeenCalledWith(response); + expect(serviceCallMulti.complete).toBe(true); + expect(mockSuccess).toHaveBeenCalledWith(response); + expect(mockClearTimeout).toHaveBeenCalled(); + expect(mockSocket.inflight["test-mid"]).toBeUndefined(); + }); + + it("should handle response when receiver returns false (continue)", () => { + mockReceiver.mockReturnValue(false); // Signal to continue + const response = { partial: "data" }; + + serviceCallMulti.start(); + serviceCallMulti.onReceived(response); + + expect(mockReceiver).toHaveBeenCalledWith(response); + expect(serviceCallMulti.complete).toBe(false); + expect(mockSuccess).not.toHaveBeenCalled(); + expect(mockClearTimeout).not.toHaveBeenCalled(); + expect(mockSocket.inflight["test-mid"]).toBe(serviceCallMulti); + }); + + it("should handle timeout and retry", () => { + serviceCallMulti.start(); + + // Initial retries should be 3, but start() calls attempt() which decrements to 2 + expect(serviceCallMulti.retries).toBe(2); + + // Simulate timeout + serviceCallMulti.onTimeout(); + + expect(mockClearTimeout).toHaveBeenCalled(); + expect(serviceCallMulti.retries).toBe(1); // Should decrement from 2 to 1 + }); + + it("should exhaust retries and call error callback", () => { + // Set retries to 0 to force immediate failure + serviceCallMulti.retries = 0; + + serviceCallMulti.start(); + + expect(mockError).toHaveBeenCalledWith("Ran out of retries"); + expect(mockSocket.inflight["test-mid"]).toBeUndefined(); + }); + + it("should handle WebSocket send failure", () => { + mockSocket.ws.send.mockImplementation(() => { + throw new Error("Connection failed"); + }); + + serviceCallMulti.start(); + + expect(mockSocket.reopen).toHaveBeenCalled(); + + // With exponential backoff, the delay should be calculated as: + // SOCKET_RECONNECTION_TIMEOUT * Math.pow(2, 3 - retries) + random + // Since retries is decremented to 2 after start(), it's 3 - 2 = 1 + // So base delay is 2000 * 2^1 = 4000, plus random up to 1000 + // The delay should be between 4000 and 5000ms (capped at 30000) + const callArgs = mockSetTimeout.mock.calls[0]; + expect(callArgs[0]).toEqual(expect.any(Function)); + expect(callArgs[1]).toBeGreaterThanOrEqual(4000); + expect(callArgs[1]).toBeLessThanOrEqual(5000); + }); + + it("should handle missing WebSocket connection", () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (mockSocket as any).ws = null; + + serviceCallMulti.start(); + + // Should trigger reopen and schedule with exponential backoff + expect(mockSocket.reopen).toHaveBeenCalled(); + + // Same calculation as above - base delay 4000ms + random up to 1000ms + const callArgs = mockSetTimeout.mock.calls[0]; + expect(callArgs[0]).toEqual(expect.any(Function)); + expect(callArgs[1]).toBeGreaterThanOrEqual(4000); + expect(callArgs[1]).toBeLessThanOrEqual(5000); + }); + + it("should not process response if already complete", () => { + const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => {}); + + serviceCallMulti.complete = true; + serviceCallMulti.onReceived({ result: "test" }); + + expect(consoleSpy).toHaveBeenCalledWith( + "test-mid", + "should not happen, request is already complete", + ); + + consoleSpy.mockRestore(); + }); + + it("should not timeout if already complete", () => { + const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => {}); + + serviceCallMulti.complete = true; + serviceCallMulti.onTimeout(); + + expect(consoleSpy).toHaveBeenCalledWith( + "test-mid", + "timeout should not happen, request is already complete", + ); + + consoleSpy.mockRestore(); + }); + + it("should not attempt if already complete", () => { + const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => {}); + + serviceCallMulti.complete = true; + serviceCallMulti.attempt(); + + expect(consoleSpy).toHaveBeenCalledWith( + "test-mid", + "attempt should not be called, request is already complete", + ); + + consoleSpy.mockRestore(); + }); + + it("should handle streaming responses correctly", () => { + mockReceiver + .mockReturnValueOnce(false) // First response - continue + .mockReturnValueOnce(false) // Second response - continue + .mockReturnValueOnce(true); // Third response - complete + + serviceCallMulti.start(); + + // First response + serviceCallMulti.onReceived({ chunk: 1 }); + expect(serviceCallMulti.complete).toBe(false); + expect(mockSuccess).not.toHaveBeenCalled(); + + // Second response + serviceCallMulti.onReceived({ chunk: 2 }); + expect(serviceCallMulti.complete).toBe(false); + expect(mockSuccess).not.toHaveBeenCalled(); + + // Third response (final) + serviceCallMulti.onReceived({ chunk: 3, final: true }); + expect(serviceCallMulti.complete).toBe(true); + expect(mockSuccess).toHaveBeenCalledWith({ chunk: 3, final: true }); + }); + + it("should handle receiver function errors gracefully", () => { + mockReceiver.mockImplementation(() => { + throw new Error("Receiver error"); + }); + + serviceCallMulti.start(); + + expect(() => { + serviceCallMulti.onReceived({ test: "data" }); + }).toThrow("Receiver error"); + }); + + it("should handle multiple timeout scenarios", () => { + const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => {}); + + serviceCallMulti.start(); + + // After start, retries should be 2 (decremented from 3) + expect(serviceCallMulti.retries).toBe(2); + + // First timeout + serviceCallMulti.onTimeout(); + expect(serviceCallMulti.retries).toBe(1); + + // Second timeout + serviceCallMulti.onTimeout(); + expect(serviceCallMulti.retries).toBe(0); + + consoleSpy.mockRestore(); + }); + + it("should clean up properly when receiver signals completion", () => { + mockReceiver.mockReturnValue(true); + + serviceCallMulti.start(); + + const response = { final: true }; + serviceCallMulti.onReceived(response); + + expect(serviceCallMulti.complete).toBe(true); + expect(mockClearTimeout).toHaveBeenCalled(); + expect(mockSocket.inflight["test-mid"]).toBeUndefined(); + expect(mockSuccess).toHaveBeenCalledWith(response); + }); +}); diff --git a/ai-context/trustgraph-client/src/__tests__/service-call.test.ts b/ai-context/trustgraph-client/src/__tests__/service-call.test.ts new file mode 100644 index 00000000..acd72111 --- /dev/null +++ b/ai-context/trustgraph-client/src/__tests__/service-call.test.ts @@ -0,0 +1,239 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { ServiceCall } from "../socket/service-call"; + +// Mock WebSocket constants +vi.stubGlobal("WebSocket", { + OPEN: 1, + CONNECTING: 0, + CLOSING: 2, + CLOSED: 3, +}); + +// Mock Socket interface +const mockSocket = { + inflight: {} as Record, + ws: { + send: vi.fn(), + readyState: 1, // WebSocket.OPEN + }, + reopen: vi.fn(), +}; + +// Mock setTimeout and clearTimeout +const mockSetTimeout = vi.fn(); +const mockClearTimeout = vi.fn(); + +vi.stubGlobal("setTimeout", mockSetTimeout); +vi.stubGlobal("clearTimeout", mockClearTimeout); + +describe("ServiceCall", () => { + let mockSuccess: ReturnType; + let mockError: ReturnType; + let serviceCall: ServiceCall; + + beforeEach(() => { + vi.clearAllMocks(); + mockSuccess = vi.fn(); + mockError = vi.fn(); + mockSocket.inflight = {} as Record; + mockSocket.ws = { + send: vi.fn(), + readyState: 1, // WebSocket.OPEN + }; + mockSocket.reopen.mockClear(); + + serviceCall = new ServiceCall( + "test-mid", + { id: "test-id", service: "test-service", request: { test: "data" } }, + mockSuccess, + mockError, + 5000, // 5 second timeout + 3, // 3 retries + // eslint-disable-next-line @typescript-eslint/no-explicit-any + mockSocket as any, + ); + }); + + it("should initialize with correct properties", () => { + expect(serviceCall.mid).toBe("test-mid"); + expect(serviceCall.timeout).toBe(5000); + expect(serviceCall.retries).toBe(3); + expect(serviceCall.complete).toBe(false); + expect(serviceCall.socket).toBe(mockSocket); + }); + + it("should register itself in socket inflight when started", () => { + serviceCall.start(); + + expect(mockSocket.inflight["test-mid"]).toBe(serviceCall); + }); + + it("should send message on successful attempt", () => { + serviceCall.start(); + + expect(mockSocket.ws.send).toHaveBeenCalledWith( + JSON.stringify({ + id: "test-id", + service: "test-service", + request: { test: "data" }, + }), + ); + expect(mockSetTimeout).toHaveBeenCalled(); + }); + + it("should handle successful response", () => { + const responseData = { result: "success" }; + const message = { response: responseData }; + + serviceCall.start(); + serviceCall.onReceived(message); + + expect(serviceCall.complete).toBe(true); + expect(mockSuccess).toHaveBeenCalledWith(responseData); + expect(mockClearTimeout).toHaveBeenCalled(); + expect(mockSocket.inflight["test-mid"]).toBeUndefined(); + }); + + it("should handle timeout and retry", () => { + serviceCall.start(); + + // Initial retries should be 3, but start() calls attempt() which decrements to 2 + expect(serviceCall.retries).toBe(2); + + // Simulate timeout + serviceCall.onTimeout(); + + expect(mockClearTimeout).toHaveBeenCalled(); + expect(serviceCall.retries).toBe(1); // Should decrement from 2 to 1 + }); + + it("should exhaust retries and call error callback", () => { + // Set retries to 0 to force immediate failure + serviceCall.retries = 0; + + serviceCall.start(); + + expect(mockError).toHaveBeenCalledWith("Ran out of retries"); + expect(mockSocket.inflight["test-mid"]).toBeUndefined(); + }); + + it("should handle WebSocket send failure", () => { + mockSocket.ws.send.mockImplementation(() => { + throw new Error("Connection failed"); + }); + + serviceCall.start(); + + // Should NOT call reopen anymore - BaseApi handles reconnection + expect(mockSocket.reopen).not.toHaveBeenCalled(); + + // With exponential backoff, the delay should be calculated as: + // SOCKET_RECONNECTION_TIMEOUT * Math.pow(2, 3 - retries) + random + // Since retries is decremented to 2 after start(), it's 3 - 2 = 1 + // So base delay is 2000 * 2^1 = 4000, plus random up to 1000 + // The delay should be between 4000 and 5000ms (capped at 30000) + const callArgs = mockSetTimeout.mock.calls[0]; + expect(callArgs[0]).toEqual(expect.any(Function)); + expect(callArgs[1]).toBeGreaterThanOrEqual(4000); + expect(callArgs[1]).toBeLessThanOrEqual(5000); + }); + + it("should handle missing WebSocket connection", () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (mockSocket as any).ws = null; + + serviceCall.start(); + + // Should NOT trigger reopen - just wait for BaseApi to reconnect + expect(mockSocket.reopen).not.toHaveBeenCalled(); + + // Same calculation as above - base delay 4000ms + random up to 1000ms + const callArgs = mockSetTimeout.mock.calls[0]; + expect(callArgs[0]).toEqual(expect.any(Function)); + expect(callArgs[1]).toBeGreaterThanOrEqual(4000); + expect(callArgs[1]).toBeLessThanOrEqual(5000); + }); + + it("should not process response if already complete", () => { + const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => {}); + + serviceCall.complete = true; + serviceCall.onReceived({ result: "test" }); + + expect(consoleSpy).toHaveBeenCalledWith( + "test-mid", + "should not happen, request is already complete", + ); + + consoleSpy.mockRestore(); + }); + + it("should not timeout if already complete", () => { + const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => {}); + + serviceCall.complete = true; + serviceCall.onTimeout(); + + expect(consoleSpy).toHaveBeenCalledWith( + "test-mid", + "timeout should not happen, request is already complete", + ); + + consoleSpy.mockRestore(); + }); + + it("should not attempt if already complete", () => { + const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => {}); + + serviceCall.complete = true; + serviceCall.attempt(); + + expect(consoleSpy).toHaveBeenCalledWith( + "test-mid", + "attempt should not be called, request is already complete", + ); + + consoleSpy.mockRestore(); + }); + + it("should handle multiple retries correctly", () => { + mockSocket.ws.send.mockImplementation(() => { + throw new Error("Connection failed"); + }); + + serviceCall.start(); + + // Should have decremented retries and scheduled a retry + expect(serviceCall.retries).toBe(2); + // Should NOT call reopen - BaseApi handles reconnection + expect(mockSocket.reopen).not.toHaveBeenCalled(); + }); + + it("should clean up properly on successful response", () => { + serviceCall.start(); + + const responseData = { success: true }; + const message = { response: responseData }; + serviceCall.onReceived(message); + + expect(serviceCall.complete).toBe(true); + expect(mockClearTimeout).toHaveBeenCalled(); + expect(mockSocket.inflight["test-mid"]).toBeUndefined(); + expect(mockSuccess).toHaveBeenCalledWith(responseData); + }); + + it("should handle edge case of negative retries", () => { + serviceCall.retries = -1; + + serviceCall.attempt(); + + expect(mockError).toHaveBeenCalledWith("Ran out of retries"); + }); + + it("should bind timeout callbacks correctly", () => { + serviceCall.start(); + + // Verify that setTimeout was called with a bound function + expect(mockSetTimeout).toHaveBeenCalledWith(expect.any(Function), 5000); + }); +}); diff --git a/ai-context/trustgraph-client/src/index.ts b/ai-context/trustgraph-client/src/index.ts new file mode 100644 index 00000000..c7b5f4b2 --- /dev/null +++ b/ai-context/trustgraph-client/src/index.ts @@ -0,0 +1,10 @@ +// @trustgraph/client +// TrustGraph TypeScript Client + +// Export models (data types) +export * from "./models/Triple"; +export * from "./models/messages"; +export * from "./models/namespaces"; + +// Export socket client +export * from "./socket/trustgraph-socket"; diff --git a/ai-context/trustgraph-client/src/models/Triple.ts b/ai-context/trustgraph-client/src/models/Triple.ts new file mode 100644 index 00000000..c9d7ca4c --- /dev/null +++ b/ai-context/trustgraph-client/src/models/Triple.ts @@ -0,0 +1,40 @@ +// Term type discriminators matching the wire format +// i = IRI, b = BLANK node, l = LITERAL, t = TRIPLE (reified) +export type TermType = "i" | "b" | "l" | "t"; + +export interface IriTerm { + t: "i"; + i: string; +} + +export interface BlankTerm { + t: "b"; + d: string; +} + +export interface LiteralTerm { + t: "l"; + v: string; + dt?: string; // datatype + ln?: string; // language +} + +export interface TripleTerm { + t: "t"; + tr?: Triple; +} + +export type Term = IriTerm | BlankTerm | LiteralTerm | TripleTerm; + +export interface PartialTriple { + s?: Term; + p?: Term; + o?: Term; +} + +export interface Triple { + s: Term; + p: Term; + o: Term; + g?: string; // graph (renamed from direc to match backend) +} diff --git a/ai-context/trustgraph-client/src/models/messages.ts b/ai-context/trustgraph-client/src/models/messages.ts new file mode 100644 index 00000000..26198521 --- /dev/null +++ b/ai-context/trustgraph-client/src/models/messages.ts @@ -0,0 +1,496 @@ +import { Triple, Term } from "./Triple"; + +// FIXME: Better types? +export type Request = object; +export type Response = object; +export type Error = object | string; + +export interface ResponseError { + type?: string; + message: string; +} + +export interface RequestMessage { + id: string; + service: string; + request: Request; + flow?: string; +} + +export interface ApiResponse { + id: string; + response: Response; +} + +export interface Metadata { + id?: string; + metadata?: Triple[]; + user?: string; + collection?: string; +} + +export interface EntityEmbeddings { + entity?: Term; + vectors?: number[][]; +} + +export interface GraphEmbeddings { + metadata?: Metadata; + entities?: EntityEmbeddings[]; +} + +export interface TextCompletionRequest { + system: string; + prompt: string; + streaming?: boolean; +} + +export interface TextCompletionResponse { + response: string; + // Streaming fields + end_of_stream?: boolean; + error?: { + message: string; + type?: string; + }; + // Token usage (appears in final message) + in_token?: number; + out_token?: number; + model?: string; +} + +export interface GraphRagRequest { + query: string; + user?: string; + collection?: string; + "entity-limit"?: number; // Default: 50 + "triple-limit"?: number; // Default: 30 + "max-subgraph-size"?: number; // Default: 1000 + "max-path-length"?: number; // Default: 2 + streaming?: boolean; +} + +export interface GraphRagResponse { + response: string; + // Streaming fields + chunk?: string; + end_of_stream?: boolean; + error?: { + message: string; + type?: string; + }; + // Token usage (appears in final message) + in_token?: number; + out_token?: number; + model?: string; + // Explainability fields + message_type?: "chunk" | "explain"; + explain_id?: string; + explain_graph?: string; // Named graph where explain data is stored (e.g., urn:graph:retrieval) + end_of_session?: boolean; +} + +export interface DocumentRagRequest { + query: string; + user?: string; + collection?: string; + "doc-limit"?: number; // Default: 20 + streaming?: boolean; +} + +export interface DocumentRagResponse { + response: string; + // Streaming fields + chunk?: string; + end_of_stream?: boolean; + error?: { + message: string; + type?: string; + }; + // Token usage (appears in final message) + in_token?: number; + out_token?: number; + model?: string; + // Explainability fields + message_type?: "chunk" | "explain"; + explain_id?: string; + explain_graph?: string; + end_of_session?: boolean; +} + +export interface AgentRequest { + question: string; + user?: string; + streaming?: boolean; +} + +export interface AgentResponse { + // Streaming response format (new protocol) + chunk_type?: "thought" | "action" | "observation" | "answer" | "final-answer" | "explain" | "error"; + content?: string; + end_of_message?: boolean; + end_of_dialog?: boolean; + + // Legacy fields for backward compatibility with non-streaming + thought?: string; + observation?: string; + answer?: string; + error?: ResponseError; + + // Token usage (appears in final message) + in_token?: number; + out_token?: number; + model?: string; + + // Explainability fields + message_type?: "chunk" | "explain"; + explain_id?: string; + explain_graph?: string; +} + +export interface EmbeddingsRequest { + texts: string[]; +} + +export interface EmbeddingsResponse { + vectors: number[][]; // One vector per input text +} + +export interface GraphEmbeddingsQueryRequest { + vector: number[]; // Single query vector + limit: number; + user?: string; + collection?: string; +} + +export interface EntityMatch { + entity: Term | null; + score: number; +} + +export interface GraphEmbeddingsQueryResponse { + entities: EntityMatch[]; +} + +export interface TriplesQueryRequest { + s?: Term; + p?: Term; + o?: Term; + g?: string; // Named graph URI filter (plain string, not Term) + limit: number; + user?: string; + collection?: string; +} + +export interface TriplesQueryResponse { + response: Triple[]; +} + +export interface RowsQueryRequest { + query: string; + user?: string; + collection?: string; + variables?: Record; + operation_name?: string; +} + +export interface RowsQueryResponse { + data?: Record; + errors?: Record[]; + extensions?: Record; + values?: unknown[]; +} + +export interface NlpQueryRequest { + question: string; + max_results?: number; +} + +export interface NlpQueryResponse { + graphql_query?: string; + variables?: Record; + detected_schemas?: Record[]; + confidence?: number; +} + +export interface StructuredQueryRequest { + question: string; + user?: string; + collection?: string; +} + +export interface StructuredQueryResponse { + data?: Record; + errors?: Record[]; +} + +export interface RowEmbeddingsQueryRequest { + vector: number[]; // Single query vector + schema_name: string; + user?: string; + collection?: string; + index_name?: string; + limit?: number; +} + +export interface RowEmbeddingsMatch { + index_name: string; + index_value: string[]; + text: string; + score: number; +} + +export interface RowEmbeddingsQueryResponse { + matches?: RowEmbeddingsMatch[]; + error?: { + message: string; + type?: string; + }; +} + +export interface LoadDocumentRequest { + id?: string; + data: string; + metadata?: Triple[]; +} + +export type LoadDocumentResponse = void; + +export interface LoadTextRequest { + id?: string; + text: string; + charset?: string; + metadata?: Triple[]; +} + +export type LoadTextResponse = void; + +export interface DocumentMetadata { + id?: string; + time?: number; + kind?: string; + title?: string; + comments?: string; + metadata?: Triple[]; + user?: string; + tags?: string[]; + "document-type"?: string; +} + +export interface ProcessingMetadata { + id?: string; + "document-id"?: string; + time?: number; + flow?: string; + user?: string; + collection?: string; + tags?: string[]; +} + +export interface LibraryRequest { + operation: string; + "document-id"?: string; + "processing-id"?: string; + "document-metadata"?: DocumentMetadata; + "processing-metadata"?: ProcessingMetadata; + content?: string; + user?: string; + collection?: string; + metadata?: Triple[]; + id?: string; + flow?: string; +} + +export interface LibraryResponse { + error: Error; + "document-metadata"?: DocumentMetadata; + content?: string; + "document-metadatas"?: DocumentMetadata[]; + "processing-metadata"?: ProcessingMetadata; +} + +export interface KnowledgeRequest { + operation: string; + user?: string; + id?: string; + flow?: string; + collection?: string; + triples?: Triple[]; + "graph-embeddings"?: GraphEmbeddings; +} + +export interface KnowledgeResponse { + error?: Error; + ids?: string[]; + eos?: boolean; + triples?: Triple[]; + "graph-embeddings"?: GraphEmbeddings; +} + +export interface FlowRequest { + operation: string; + "blueprint-name"?: string; + "blueprint-definition"?: string; + description?: string; + "flow-id"?: string; + parameters?: Record; + user?: string; +} + +export interface FlowResponse { + "blueprint-names"?: string[]; + "flow-ids"?: string[]; + ids?: string[]; + flow?: string; + "blueprint-definition"?: string; + description?: string; + error?: + | { + message?: string; + } + | Error; +} + +export interface PromptRequest { + id: string; + terms: Record; + streaming?: boolean; +} + +export interface PromptResponse { + text: string; + // Streaming fields + end_of_stream?: boolean; + error?: { + message: string; + type?: string; + }; + // Token usage (appears in final message) + in_token?: number; + out_token?: number; + model?: string; +} + +export type ConfigRequest = object; +export type ConfigResponse = object; + +// Chunked Upload Types + +export interface ChunkedUploadDocumentMetadata { + id: string; + time: number; + kind: string; + title: string; + comments?: string; + metadata?: Triple[]; + user: string; + collection?: string; + tags?: string[]; +} + +export interface BeginUploadRequest { + operation: "begin-upload"; + "document-metadata": ChunkedUploadDocumentMetadata; + "total-size": number; + "chunk-size"?: number; +} + +export interface BeginUploadResponse { + "upload-id": string; + "chunk-size": number; + "total-chunks": number; + error?: ResponseError; +} + +export interface UploadChunkRequest { + operation: "upload-chunk"; + "upload-id": string; + "chunk-index": number; + content: string; // base64-encoded + user: string; +} + +export interface UploadChunkResponse { + "upload-id": string; + "chunk-index": number; + "chunks-received": number; + "total-chunks": number; + "bytes-received": number; + "total-bytes": number; + error?: ResponseError; +} + +export interface CompleteUploadRequest { + operation: "complete-upload"; + "upload-id": string; + user: string; +} + +export interface CompleteUploadResponse { + "document-id": string; + "object-id": string; + error?: ResponseError; +} + +export interface GetUploadStatusRequest { + operation: "get-upload-status"; + "upload-id": string; + user: string; +} + +export interface GetUploadStatusResponse { + "upload-id": string; + "upload-state": "in-progress" | "completed" | "expired"; + "chunks-received": number; + "total-chunks": number; + "received-chunks": number[]; + "missing-chunks": number[]; + "bytes-received": number; + "total-bytes": number; + error?: ResponseError; +} + +export interface AbortUploadRequest { + operation: "abort-upload"; + "upload-id": string; + user: string; +} + +export interface AbortUploadResponse { + error?: ResponseError; +} + +export interface ListUploadsRequest { + operation: "list-uploads"; + user: string; +} + +export interface UploadSession { + "upload-id": string; + "document-id": string; + "document-metadata-json": string; + "total-size": number; + "chunk-size": number; + "total-chunks": number; + "chunks-received": number; + "created-at": string; +} + +export interface ListUploadsResponse { + "upload-sessions": UploadSession[]; + error?: ResponseError; +} + +export interface StreamDocumentRequest { + operation: "stream-document"; + "document-id": string; + "chunk-size"?: number; + user: string; +} + +export interface StreamDocumentResponse { + content: string; // base64-encoded chunk + "chunk-index": number; + "total-chunks": number; + error?: ResponseError; +} diff --git a/ai-context/trustgraph-client/src/models/namespaces.ts b/ai-context/trustgraph-client/src/models/namespaces.ts new file mode 100644 index 00000000..df75fc04 --- /dev/null +++ b/ai-context/trustgraph-client/src/models/namespaces.ts @@ -0,0 +1,42 @@ +/** + * RDF namespace constants for TrustGraph + * Used for querying explainability data, provenance chains, and knowledge graph + */ + +// TrustGraph namespace +export const TG = "https://trustgraph.ai/ns/"; +export const TG_QUERY = TG + "query"; +export const TG_EDGE_COUNT = TG + "edgeCount"; +export const TG_SELECTED_EDGE = TG + "selectedEdge"; +export const TG_EDGE = TG + "edge"; +export const TG_REASONING = TG + "reasoning"; +export const TG_CONTENT = TG + "content"; +export const TG_REIFIES = TG + "reifies"; +export const TG_DOCUMENT = TG + "document"; + +// W3C PROV-O namespace +export const PROV = "http://www.w3.org/ns/prov#"; +export const PROV_STARTED_AT_TIME = PROV + "startedAtTime"; +export const PROV_WAS_DERIVED_FROM = PROV + "wasDerivedFrom"; +export const PROV_WAS_GENERATED_BY = PROV + "wasGeneratedBy"; +export const PROV_ACTIVITY = PROV + "Activity"; +export const PROV_ENTITY = PROV + "Entity"; + +// RDFS namespace +export const RDFS = "http://www.w3.org/2000/01/rdf-schema#"; +export const RDFS_LABEL = RDFS + "label"; + +// RDF namespace +export const RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; +export const RDF_TYPE = RDF + "type"; + +// Schema.org namespace (used in document metadata) +export const SCHEMA = "https://schema.org/"; +export const SCHEMA_NAME = SCHEMA + "name"; +export const SCHEMA_DESCRIPTION = SCHEMA + "description"; +export const SCHEMA_AUTHOR = SCHEMA + "author"; +export const SCHEMA_KEYWORDS = SCHEMA + "keywords"; + +// SKOS namespace +export const SKOS = "http://www.w3.org/2004/02/skos/core#"; +export const SKOS_DEFINITION = SKOS + "definition"; diff --git a/ai-context/trustgraph-client/src/socket/service-call-multi.ts b/ai-context/trustgraph-client/src/socket/service-call-multi.ts new file mode 100644 index 00000000..16e3e6ff --- /dev/null +++ b/ai-context/trustgraph-client/src/socket/service-call-multi.ts @@ -0,0 +1,171 @@ +import { RequestMessage } from "../models/messages"; + +// Constant defining the delay before attempting to reconnect a WebSocket +// (2 seconds) +export const SOCKET_RECONNECTION_TIMEOUT = 2000; + +// Forward declare Socket type to avoid circular dependency +// Using a minimal interface that matches what BaseApi provides +interface Socket { + ws?: WebSocket; + inflight: { [key: string]: ServiceCallMulti }; + reopen: () => void; + getNextId?: () => string; + user?: string; +} + +export class ServiceCallMulti { + constructor( + mid: string, + msg: RequestMessage, + success: (resp: unknown) => void, + error: (err: object | string) => void, + timeout: number, + retries: number, + socket: Socket, + receiver: (resp: unknown) => boolean, + ) { + this.mid = mid; + this.msg = msg; + this.success = success; + this.error = error; + this.timeout = timeout; + this.retries = retries; + this.socket = socket; + this.complete = false; + this.receiver = receiver; + } + + mid: string; + msg: RequestMessage; + success: (resp: unknown) => void; + error: (err: object | string) => void; + receiver: (resp: unknown) => boolean; + timeoutId?: ReturnType; + timeout: number; + retries: number; + socket: Socket; + complete: boolean; + + start() { + this.socket.inflight[this.mid] = this; + this.attempt(); + } + + onReceived(resp: object) { + if (this.complete == true) + console.log(this.mid, "should not happen, request is already complete"); + + const fin = this.receiver(resp); + + if (fin) { + this.complete = true; + + // console.log("Received for", this.mid); + clearTimeout(this.timeoutId); + this.timeoutId = undefined; + delete this.socket.inflight[this.mid]; + this.success(resp); + } + } + + /** + * Called when socket connects - immediately retry if we were waiting + */ + retryNow() { + if (this.complete) return; + + // Clear any pending backoff timer + clearTimeout(this.timeoutId); + this.timeoutId = undefined; + + // Restore retry count since we didn't actually fail + this.retries++; + + // Attempt immediately + this.attempt(); + } + + onTimeout() { + if (this.complete == true) + console.log( + this.mid, + "timeout should not happen, request is already complete", + ); + + console.log("Request", this.mid, "timed out"); + clearTimeout(this.timeoutId); + this.attempt(); + } + + attempt() { + // console.log("attempt:", this.mid); + + if (this.complete == true) + console.log( + this.mid, + "attempt should not be called, request is already complete", + ); + + this.retries--; + + if (this.retries < 0) { + console.log("Request", this.mid, "ran out of retries"); + + clearTimeout(this.timeoutId); + delete this.socket.inflight[this.mid]; + + this.error("Ran out of retries"); + return; // Exit early - no more attempts + } + + // Check if WebSocket connection is available and ready + if (this.socket.ws && this.socket.ws.readyState === WebSocket.OPEN) { + try { + this.socket.ws.send(JSON.stringify(this.msg)); + this.timeoutId = setTimeout(this.onTimeout.bind(this), this.timeout); + + return; + } catch (e) { + console.log("Error:", e); + console.log("Message send failure, retry..."); + + // Calculate backoff delay with jitter + const backoffDelay = Math.min( + SOCKET_RECONNECTION_TIMEOUT * Math.pow(2, 3 - this.retries) + + Math.random() * 1000, + 30000, // Max 30 seconds + ); + + this.timeoutId = setTimeout(this.attempt.bind(this), backoffDelay); + + console.log("Reopen..."); + // Attempt to reopen the WebSocket connection + this.socket.reopen(); + } + } else { + // No WebSocket connection available or not ready + // Check if socket is connecting + if ( + this.socket.ws && + this.socket.ws.readyState === WebSocket.CONNECTING + ) { + // Wait a bit longer for connection to establish + setTimeout(this.attempt.bind(this), 500); + } else { + // Socket is closed or closing, trigger reopen + console.log("Socket not ready, reopening..."); + this.socket.reopen(); + + // Calculate backoff delay + const backoffDelay = Math.min( + SOCKET_RECONNECTION_TIMEOUT * Math.pow(2, 3 - this.retries) + + Math.random() * 1000, + 30000, + ); + + setTimeout(this.attempt.bind(this), backoffDelay); + } + } + } +} diff --git a/ai-context/trustgraph-client/src/socket/service-call.ts b/ai-context/trustgraph-client/src/socket/service-call.ts new file mode 100644 index 00000000..4b5fe80b --- /dev/null +++ b/ai-context/trustgraph-client/src/socket/service-call.ts @@ -0,0 +1,239 @@ +import { RequestMessage } from "../models/messages"; + +// Constant defining the delay before attempting to reconnect a WebSocket +// (2 seconds) +export const SOCKET_RECONNECTION_TIMEOUT = 2000; + +// Forward declare Socket type to avoid circular dependency +// Using a minimal interface that matches what BaseApi provides +interface Socket { + ws?: WebSocket; + inflight: { [key: string]: ServiceCall }; + reopen: () => void; + getNextId?: () => string; + user?: string; +} + +/** + * ServiceCall represents a single request/response cycle over a WebSocket + * connection with built-in retry logic, timeout handling, and completion + * tracking. + * + * This class manages the lifecycle of a service call including: + * - Sending the initial request + * - Handling timeouts and retries + * - Managing completion state + * - Cleaning up resources + */ +export class ServiceCall { + constructor( + mid: string, // Message ID - unique identifier for this request + msg: RequestMessage, // The actual message/request to send + success: (resp: unknown) => void, // Callback function called on + // successful response + error: (err: object | string) => void, // Callback function called on error/failure + timeout: number, // Timeout duration in milliseconds + retries: number, // Number of retry attempts allowed + socket: Socket, // WebSocket instance to send the message through + ) { + this.mid = mid; + this.msg = msg; + this.success = success; + this.error = error; + this.timeout = timeout; + this.retries = retries; + this.socket = socket; + this.complete = false; // Track if this request has completed + } + + // Properties + mid: string; // Message identifier + msg: RequestMessage; // The request message + success: (resp: unknown) => void; // Success callback + error: (err: object | string) => void; // Error callback + timeoutId?: ReturnType; // Reference to the active timeout timer + timeout: number; // Timeout duration in milliseconds + retries: number; // Remaining retry attempts + socket: Socket; // WebSocket connection reference + complete: boolean; // Flag indicating if request is complete + + /** + * Initiates the service call by registering it with the socket's inflight + * requests and making the first attempt to send the message + */ + start() { + // Register this request as "in-flight" so responses can be matched to it + this.socket.inflight[this.mid] = this; + // Make the first attempt to send the message + this.attempt(); + } + + /** + * Called when a response is received for this request + * Handles cleanup and calls the success or error callback based on response + * + * @param resp - The response object received from the server + */ + onReceived(resp: object) { + // Defensive check - this shouldn't happen but log if it does + if (this.complete == true) + console.log(this.mid, "should not happen, request is already complete"); + + // Mark as complete to prevent duplicate processing + this.complete = true; + + // Clean up timeout timer + clearTimeout(this.timeoutId); + this.timeoutId = undefined; + + // Remove from inflight requests tracker + delete this.socket.inflight[this.mid]; + + // Check if the response contains an error (error can be directly in resp or nested under response) + let errorToHandle: unknown = null; + + // Check for direct error in response + if (resp && typeof resp === "object" && "error" in resp) { + errorToHandle = (resp as Record).error; + } + // Check for nested error under response property + else if (resp && typeof resp === "object" && "response" in resp) { + const response = (resp as Record).response; + if (response && typeof response === "object" && "error" in response) { + errorToHandle = (response as Record).error; + } + } + + if (errorToHandle) { + // Response contains an error - call error callback + const errorObj = errorToHandle as Record; + const errorMessage = + (typeof errorObj.message === "string" ? errorObj.message : null) || + (typeof errorObj.type === "string" ? errorObj.type : null) || + "Unknown error"; + console.log( + "ServiceCall: API error detected in response:", + errorMessage, + "Full error:", + errorToHandle, + ); + this.error(new Error(errorMessage)); + return; + } + + // Extract the response field from the message object + // The resp parameter is the full message: {id, response, complete} + // We need to pass just the response field to the success callback + const responseData = (resp as { response?: unknown }).response; + this.success(responseData); + } + + /** + * Called when socket connects - immediately retry if we were waiting + */ + retryNow() { + if (this.complete) return; + + // Clear any pending backoff timer + clearTimeout(this.timeoutId); + this.timeoutId = undefined; + + // Restore retry count since we didn't actually fail + this.retries++; + + // Attempt immediately + this.attempt(); + } + + /** + * Called when the request times out + * Triggers another attempt if retries are available + */ + onTimeout() { + // Defensive check - this shouldn't happen but log if it does + if (this.complete == true) + console.log( + this.mid, + "timeout should not happen, request is already complete", + ); + + console.log("Request", this.mid, "timed out"); + + // Clear the current timeout + clearTimeout(this.timeoutId); + + // Try again (this will check retry count) + this.attempt(); + } + + /** + * Calculates exponential backoff delay with jitter + * @returns backoff delay in milliseconds + */ + calculateBackoff() { + return Math.min( + SOCKET_RECONNECTION_TIMEOUT * Math.pow(2, 3 - this.retries) + + Math.random() * 1000, + 30000, // Max 30 seconds + ); + } + + /** + * Core retry logic - attempts to send the message over the WebSocket + * Handles retries and waits for BaseApi to handle reconnection + */ + attempt() { + // Defensive check - this shouldn't be called on completed requests + if (this.complete == true) + console.log( + this.mid, + "attempt should not be called, request is already complete", + ); + + // Decrement retry counter + this.retries--; + + // Check if we've exhausted all retries + if (this.retries < 0) { + console.log("Request", this.mid, "ran out of retries"); + + // Clean up and call error callback + clearTimeout(this.timeoutId); + delete this.socket.inflight[this.mid]; + this.error("Ran out of retries"); + return; // Exit early - no more attempts + } + + // Check if WebSocket connection is available and ready + if (this.socket.ws && this.socket.ws.readyState === WebSocket.OPEN) { + try { + // Attempt to send the message as JSON + this.socket.ws.send(JSON.stringify(this.msg)); + + // Set up timeout for this attempt + this.timeoutId = setTimeout(this.onTimeout.bind(this), this.timeout); + + return; // Success - message sent, waiting for response or timeout + } catch (e) { + // Handle send failure - wait for BaseApi to handle reconnection + console.log("Error:", e); + console.log( + "Message send failure, waiting for socket reconnection...", + ); + + // Schedule retry with backoff - let BaseApi handle the reconnection + this.timeoutId = setTimeout( + this.attempt.bind(this), + this.calculateBackoff(), + ); + } + } else { + // No WebSocket connection available or not ready + // Let BaseApi handle reconnection, just wait and retry + console.log("Request", this.mid, "waiting for socket reconnection..."); + + // Use consistent backoff for all waiting scenarios + setTimeout(this.attempt.bind(this), this.calculateBackoff()); + } + } +} diff --git a/ai-context/trustgraph-client/src/socket/trustgraph-socket.ts b/ai-context/trustgraph-client/src/socket/trustgraph-socket.ts new file mode 100644 index 00000000..e3adf6b3 --- /dev/null +++ b/ai-context/trustgraph-client/src/socket/trustgraph-socket.ts @@ -0,0 +1,2353 @@ +// Import core types and classes for the TrustGraph API +import { Triple, Term } from "../models/Triple"; +import { ServiceCallMulti } from "./service-call-multi"; +import { ServiceCall } from "./service-call"; + +// Import all message types for different services +import { + AgentRequest, + AgentResponse, + ConfigRequest, + ConfigResponse, + DocumentMetadata, + DocumentRagRequest, + DocumentRagResponse, + EmbeddingsRequest, + EmbeddingsResponse, + EntityMatch, + FlowRequest, + FlowResponse, + GraphEmbeddingsQueryRequest, + GraphEmbeddingsQueryResponse, + GraphRagRequest, + GraphRagResponse, + // KnowledgeRequest, + // KnowledgeResponse, + LibraryRequest, + LibraryResponse, + LoadDocumentRequest, + LoadDocumentResponse, + LoadTextRequest, + LoadTextResponse, + NlpQueryRequest, + NlpQueryResponse, + RowsQueryRequest, + RowsQueryResponse, + RowEmbeddingsQueryRequest, + RowEmbeddingsQueryResponse, + RowEmbeddingsMatch, + PromptRequest, + PromptResponse, + // ProcessingMetadata, + RequestMessage, + StructuredQueryRequest, + StructuredQueryResponse, + TextCompletionRequest, + TextCompletionResponse, + TriplesQueryRequest, + TriplesQueryResponse, + // Chunked upload types + ChunkedUploadDocumentMetadata, + BeginUploadRequest, + BeginUploadResponse, + UploadChunkRequest, + UploadChunkResponse, + CompleteUploadRequest, + CompleteUploadResponse, + GetUploadStatusRequest, + GetUploadStatusResponse, + AbortUploadRequest, + AbortUploadResponse, + ListUploadsRequest, + ListUploadsResponse, + UploadSession, + StreamDocumentRequest, + StreamDocumentResponse, + // EntityEmbeddings, + // Error, + // GraphEmbedding, + // Metadata, + // Request, + // Response, +} from "../models/messages"; + +// GraphRAG options interface for configurable parameters +export interface GraphRagOptions { + entityLimit?: number; + tripleLimit?: number; + maxSubgraphSize?: number; + pathLength?: number; +} + +// Metadata included in final streaming message +export interface StreamingMetadata { + in_token?: number; + out_token?: number; + model?: string; +} + +// Explainability event data +export interface ExplainEvent { + explainId: string; + explainGraph: string; // Named graph where explain data is stored (e.g., urn:graph:retrieval) +} + +// Configuration constants +const SOCKET_RECONNECTION_TIMEOUT = 2000; // 2 seconds between reconnection +// attempts +const SOCKET_URL = "/api/socket"; // WebSocket endpoint path + +/** + * Socket interface defining all available operations for the TrustGraph API + * This provides a unified interface for various AI/ML and knowledge graph + * operations + */ +export interface Socket { + close: () => void; + + // Text completion using AI models + textCompletion: (system: string, text: string) => Promise; + + // Graph-based Retrieval Augmented Generation + graphRag: (text: string, options?: GraphRagOptions) => Promise; + + // Agent interaction with streaming callbacks for different phases + // BREAKING CHANGE: Callbacks now receive (chunk, complete, metadata?) instead of full messages + agent: ( + question: string, + think: (chunk: string, complete: boolean, metadata?: StreamingMetadata) => void, + observe: (chunk: string, complete: boolean, metadata?: StreamingMetadata) => void, + answer: (chunk: string, complete: boolean, metadata?: StreamingMetadata) => void, + error: (e: string) => void, + onExplain?: (event: ExplainEvent) => void, + ) => void; + + // Streaming variants for RAG and completion services + graphRagStreaming: ( + text: string, + receiver: (chunk: string, complete: boolean, metadata?: StreamingMetadata) => void, + onError: (error: string) => void, + options?: GraphRagOptions, + collection?: string, + ) => void; + + documentRagStreaming: ( + text: string, + receiver: (chunk: string, complete: boolean, metadata?: StreamingMetadata) => void, + onError: (error: string) => void, + docLimit?: number, + collection?: string, + onExplain?: (event: ExplainEvent) => void, + ) => void; + + textCompletionStreaming: ( + system: string, + text: string, + receiver: (chunk: string, complete: boolean, metadata?: StreamingMetadata) => void, + onError: (error: string) => void, + ) => void; + + promptStreaming: ( + id: string, + terms: Record, + receiver: (chunk: string, complete: boolean, metadata?: StreamingMetadata) => void, + onError: (error: string) => void, + ) => void; + + // Generate embeddings for texts (batch) + embeddings: (texts: string[]) => Promise; + + // Query graph using embedding vector + graphEmbeddingsQuery: (vec: number[], limit: number) => Promise; + + // Query knowledge graph triples (subject-predicate-object) + triplesQuery: ( + s?: Term, // Subject (optional) + p?: Term, // Predicate (optional) + o?: Term, // Object (optional) + limit?: number, + collection?: string, + graph?: string, // Named graph URI filter + ) => Promise; + + // Load a document into the system + loadDocument: ( + document: string, // Base64-encoded document + id?: string, // Optional document ID + metadata?: Triple[], // Optional metadata as triples + ) => Promise; + + // Load plain text into the system + loadText: (text: string, id?: string, metadata?: Triple[]) => Promise; + + // Load a document into the library with full metadata + loadLibraryDocument: ( + document: string, + mimeType: string, + id?: string, + metadata?: Triple[], + ) => Promise; +} + +/** + * Generates a random message ID using cryptographically secure random values + * @param length - Number of random characters to generate + * @returns Random string of specified length + */ +function makeid(length: number) { + const array = new Uint32Array(length); + crypto.getRandomValues(array); + + const characters = "abcdefghijklmnopqrstuvwxyz1234567890"; + + return array.reduce( + (acc, current) => acc + characters[current % characters.length], + "", + ); +} + +/** + * BaseApi - Core WebSocket client for TrustGraph API + * Manages connection lifecycle, message routing, and provides base request + * functionality + */ +// Connection state interface for UI consumption +export interface ConnectionState { + status: + | "connecting" + | "connected" + | "reconnecting" + | "failed" + | "authenticated" + | "unauthenticated"; + hasApiKey: boolean; + reconnectAttempt?: number; + maxAttempts?: number; + nextRetryIn?: number; + lastError?: string; +} + +export class BaseApi { + ws?: WebSocket; // WebSocket connection instance + tag: string; // Unique client identifier + id: number; // Counter for generating unique message IDs + token?: string; // Optional authentication token + user: string; // User identifier for API requests + socketUrl: string; // WebSocket URL + inflight: { [key: string]: ServiceCall } = {}; // Track active requests by + // message ID + reconnectAttempts: number = 0; // Track reconnection attempts + maxReconnectAttempts: number = 10; // Maximum reconnection attempts + reconnectTimer?: number; // Timer for reconnection attempts + reconnectionState: "idle" | "reconnecting" | "failed" = "idle"; // Connection state + + // Connection state tracking for UI + private connectionStateListeners: ((state: ConnectionState) => void)[] = []; + private lastError?: string; + + constructor(user: string, token?: string, socketUrl?: string) { + this.tag = makeid(16); // Generate unique client tag + this.id = 1; // Start message ID counter + this.token = token; // Store authentication token + this.user = user; // Store user identifier + this.socketUrl = socketUrl || SOCKET_URL; // Use provided URL or default + + console.log( + "SOCKET: opening socket...", + token ? "with auth" : "without auth", + "user:", + user, + ); + this.openSocket(); // Establish WebSocket connection + console.log("SOCKET: socket opened"); + } + + /** + * Subscribe to connection state changes for UI updates + */ + onConnectionStateChange(listener: (state: ConnectionState) => void) { + this.connectionStateListeners.push(listener); + // Immediately send current state + listener(this.getConnectionState()); + + // Return unsubscribe function + return () => { + const index = this.connectionStateListeners.indexOf(listener); + if (index > -1) { + this.connectionStateListeners.splice(index, 1); + } + }; + } + + /** + * Get current connection state + */ + private getConnectionState(): ConnectionState { + const hasApiKey = !!this.token; + + // Determine status based on WebSocket state and reconnection state + let status: ConnectionState["status"]; + + if (!this.ws || this.ws.readyState === WebSocket.CLOSED) { + if (this.reconnectionState === "failed") { + status = "failed"; + } else if (this.reconnectionState === "reconnecting") { + status = "reconnecting"; + } else { + status = "connecting"; + } + } else if (this.ws.readyState === WebSocket.CONNECTING) { + status = "connecting"; + } else if (this.ws.readyState === WebSocket.OPEN) { + status = hasApiKey ? "authenticated" : "unauthenticated"; + } else { + status = "connecting"; + } + + const state: ConnectionState = { + status, + hasApiKey, + lastError: this.lastError, + }; + + // Add reconnection details if applicable + if (status === "reconnecting") { + state.reconnectAttempt = this.reconnectAttempts; + state.maxAttempts = this.maxReconnectAttempts; + } + + return state; + } + + /** + * Notify all listeners of connection state changes + */ + private notifyStateChange() { + const state = this.getConnectionState(); + this.connectionStateListeners.forEach((listener) => { + try { + listener(state); + } catch (error) { + console.error("Error in connection state listener:", error); + } + }); + } + + /** + * Establishes WebSocket connection and sets up event handlers + */ + openSocket() { + // Don't create multiple connections + if ( + this.ws && + (this.ws.readyState === WebSocket.CONNECTING || + this.ws.readyState === WebSocket.OPEN) + ) { + return; + } + + // Clean up old socket if exists + if (this.ws) { + this.ws.removeEventListener("message", this.onMessage); + this.ws.removeEventListener("close", this.onClose); + this.ws.removeEventListener("open", this.onOpen); + this.ws.removeEventListener("error", this.onError); + this.ws = undefined; + } + + try { + // Build WebSocket URL with optional token parameter + const wsUrl = this.token + ? `${this.socketUrl}?token=${this.token}` + : this.socketUrl; + console.log( + "SOCKET: connecting to", + wsUrl.replace(/token=[^&]*/, "token=***"), + ); + this.ws = new WebSocket(wsUrl); + } catch (e) { + console.error("[socket creation error]", e); + this.scheduleReconnect(); + return; + } + + // Bind event handlers to maintain proper 'this' context + this.onMessage = this.onMessage.bind(this); + this.onClose = this.onClose.bind(this); + this.onOpen = this.onOpen.bind(this); + this.onError = this.onError.bind(this); + + // Attach event listeners + this.ws.addEventListener("message", this.onMessage); + this.ws.addEventListener("close", this.onClose); + this.ws.addEventListener("open", this.onOpen); + this.ws.addEventListener("error", this.onError); + } + + // Handle incoming messages from server + onMessage(message: MessageEvent) { + if (!message.data) return; + + try { + const obj = JSON.parse(message.data); + + // Skip messages without ID (can't route them) + if (!obj.id) return; + + // Route response to the corresponding inflight request + if (this.inflight[obj.id]) { + // Pass the whole message object so receiver can access 'complete' flag + this.inflight[obj.id].onReceived(obj); + } + } catch (e) { + console.error("[socket message parse error]", e); + } + } + + // Handle connection closure - automatically attempt reconnection + onClose(event: CloseEvent) { + console.log("[socket close]", event.code, event.reason); + this.lastError = `Connection closed: ${event.reason || "Unknown reason"}`; + this.ws = undefined; + this.notifyStateChange(); + this.scheduleReconnect(); + } + + // Handle successful connection + onOpen() { + console.log("[socket open]"); + this.reconnectAttempts = 0; // Reset reconnection attempts on success + this.reconnectionState = "idle"; // Reset connection state + this.lastError = undefined; // Clear any previous errors + + // Clear any pending reconnect timer + if (this.reconnectTimer) { + clearTimeout(this.reconnectTimer); + this.reconnectTimer = undefined; + } + + // Notify UI of successful connection + this.notifyStateChange(); + + // Immediately retry any pending requests that were waiting for connection + for (const mid in this.inflight) { + this.inflight[mid].retryNow(); + } + } + + // Handle socket errors + onError(event: Event) { + console.error("[socket error]", event); + this.lastError = "Connection error occurred"; + this.notifyStateChange(); + } + + /** + * Schedules a reconnection attempt with exponential backoff + */ + scheduleReconnect() { + // Prevent concurrent reconnection attempts + if (this.reconnectionState === "reconnecting") { + console.log("[socket] Reconnection already in progress, skipping"); + return; + } + + // Don't schedule if already scheduled + if (this.reconnectTimer) return; + + this.reconnectionState = "reconnecting"; + this.reconnectAttempts++; + this.notifyStateChange(); // Notify UI of reconnection attempt + + if (this.reconnectAttempts > this.maxReconnectAttempts) { + console.error("[socket] Max reconnection attempts reached"); + this.reconnectionState = "failed"; + this.lastError = "Max reconnection attempts exceeded"; + this.notifyStateChange(); + // Notify all pending requests of the failure + for (const mid in this.inflight) { + this.inflight[mid].error(new Error("WebSocket connection failed")); + } + return; + } + + // Calculate exponential backoff with jitter + const backoffDelay = Math.min( + SOCKET_RECONNECTION_TIMEOUT * Math.pow(2, this.reconnectAttempts - 1) + + Math.random() * 1000, + 30000, // Max 30 seconds + ); + + console.log( + `[socket] Reconnecting in ${backoffDelay}ms (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`, + ); + + this.reconnectTimer = setTimeout(() => { + this.reconnectTimer = undefined; + this.reopen(); + }, backoffDelay) as unknown as number; + } + + /** + * Reopens the WebSocket connection (used after connection failures) + */ + reopen() { + console.log("[socket reopen]"); + // Check if we're already connected or connecting + if ( + this.ws && + (this.ws.readyState === WebSocket.OPEN || + this.ws.readyState === WebSocket.CONNECTING) + ) { + return; + } + this.openSocket(); + } + + /** + * Closes the WebSocket connection and cleans up + */ + close() { + // Clear reconnection timer + if (this.reconnectTimer) { + clearTimeout(this.reconnectTimer); + this.reconnectTimer = undefined; + } + + // Clean up WebSocket + if (this.ws) { + // Remove event listeners to prevent memory leaks + this.ws.removeEventListener("message", this.onMessage); + this.ws.removeEventListener("close", this.onClose); + this.ws.removeEventListener("open", this.onOpen); + this.ws.removeEventListener("error", this.onError); + + this.ws.close(); + this.ws = undefined; + } + + // Clear any remaining inflight requests + for (const mid in this.inflight) { + this.inflight[mid].error(new Error("Socket closed")); + } + this.inflight = {}; + } + + /** + * Generates the next unique message ID for requests + * Format: {clientTag}-{incrementingNumber} + */ + getNextId() { + const mid = this.tag + "-" + this.id.toString(); + this.id++; + return mid; + } + + /** + * Core method for making service requests over WebSocket + * @param service - Name of the service to call + * @param request - Request payload + * @param timeout - Request timeout in milliseconds (default: 10000) + * @param retries - Number of retry attempts (default: 3) + * @param flow - Optional flow identifier + * @returns Promise resolving to the service response + */ + makeRequest( + service: string, + request: RequestType, + timeout?: number, + retries?: number, + flow?: string, + ) { + const mid = this.getNextId(); + + // Set default values + if (timeout == undefined) timeout = 10000; + if (retries == undefined) retries = 3; + + // Construct the request message + const msg: RequestMessage = { + id: mid, + service: service, + request: request, + }; + + // Add flow identifier if provided + if (flow) msg.flow = flow; + + // Return a Promise that will be resolved/rejected by the ServiceCall + return new Promise((resolve, reject) => { + const call = new ServiceCall( + mid, + msg, + resolve as (resp: unknown) => void, + reject as (err: object | string) => void, + timeout, + retries, + this, + ); + + call.start(); + // Commented out debug logging: console.log("-->", msg); + }).then((obj) => { + // Commented out success logging: console.log("Success for", mid); + return obj as ResponseType; + }); + } + + /** + * Makes a request that can receive multiple responses (streaming) + * Used for operations that return data in chunks + */ + makeRequestMulti( + service: string, + request: RequestType, + receiver: (resp: unknown) => boolean, // Callback to handle each response chunk + timeout?: number, + retries?: number, + flow?: string, + ) { + const mid = this.getNextId(); + + // Set defaults + if (timeout == undefined) timeout = 10000; + if (retries == undefined) retries = 3; + + // Construct request message + const msg: RequestMessage = { + id: mid, + service: service, + request: request, + }; + + if (flow) msg.flow = flow; + + return new Promise((resolve, reject) => { + const call = new ServiceCallMulti( + mid, + msg, + resolve as (resp: unknown) => void, + reject as (err: object | string) => void, + timeout, + retries, + this as any, // eslint-disable-line @typescript-eslint/no-explicit-any + receiver, + ); + + call.start(); + }).then((obj) => { + return obj as ResponseType; + }); + } + + /** + * Convenience method for making flow-specific requests + * Defaults to "default" flow if none specified + */ + makeFlowRequest( + service: string, + request: RequestType, + timeout?: number, + retries?: number, + flow?: string, + ) { + if (!flow) flow = "default"; + + return this.makeRequest( + service, + request, + timeout, + retries, + flow, + ); + } + + // Factory methods for creating specialized API instances + librarian() { + return new LibrarianApi(this); + } + + flows() { + return new FlowsApi(this); + } + + flow(id: string) { + return new FlowApi(this, id); + } + + knowledge() { + return new KnowledgeApi(this); + } + + config() { + return new ConfigApi(this); + } + + collectionManagement() { + return new CollectionManagementApi(this); + } +} + +/** + * LibrarianApi - Manages document storage and retrieval + * Handles document lifecycle including upload, processing, and removal + */ +export class LibrarianApi { + api: BaseApi; + + constructor(api: BaseApi) { + this.api = api; + } + + /** + * Retrieves list of all documents in the system + */ + getDocuments() { + return this.api + .makeRequest( + "librarian", + { + operation: "list-documents", + user: this.api.user, + }, + 60000, // 60 second timeout for potentially large lists + ) + .then((r) => r["document-metadatas"] || []); + } + + /** + * Retrieves list of documents currently being processed + */ + getProcessing() { + return this.api + .makeRequest( + "librarian", + { + operation: "list-processing", + user: this.api.user, + }, + 60000, + ) + .then((r) => r["processing-metadata"] || []); + } + + /** + * Retrieves metadata for a single document by ID + * @param documentId - Document URI/ID to fetch + * @returns Document metadata including title, comments, tags, and RDF metadata + */ + getDocumentMetadata(documentId: string): Promise { + return this.api + .makeRequest( + "librarian", + { + operation: "get-document-metadata", + "document-id": documentId, + user: this.api.user, + }, + 30000, + ) + .then((r) => r["document-metadata"] || null); + } + + /** + * Uploads a document to the library with full metadata + * @param document - Base64-encoded document content + * @param id - Optional document identifier + * @param metadata - Optional metadata as triples + * @param mimeType - Document MIME type + * @param title - Document title + * @param comments - Additional comments + * @param tags - Document tags for categorization + */ + loadDocument( + document: string, // base64-encoded doc + mimeType: string, + title: string, + comments: string, + tags: string[], + id?: string, + metadata?: Triple[], + ) { + return this.api.makeRequest( + "librarian", + { + operation: "add-document", + "document-metadata": { + id: id, + time: Math.floor(Date.now() / 1000), // Unix timestamp + kind: mimeType, + title: title, + comments: comments, + metadata: metadata, + user: this.api.user, + tags: tags, + }, + content: document, + }, + 30000, // 30 second timeout for document upload + ); + } + + /** + * Removes a document from the library + */ + removeDocument(id: string, collection?: string) { + return this.api.makeRequest( + "librarian", + { + operation: "remove-document", + "document-id": id, + user: this.api.user, + collection: collection || "default", + }, + 30000, + ); + } + + /** + * Adds a document to the processing queue + * @param id - Processing job identifier + * @param doc_id - Document to process + * @param flow - Processing flow to use + * @param collection - Collection to add processed data to + * @param tags - Tags for the processing job + */ + addProcessing( + id: string, + doc_id: string, + flow: string, + collection?: string, + tags?: string[], + ) { + return this.api.makeRequest( + "librarian", + { + operation: "add-processing", + "processing-metadata": { + id: id, + "document-id": doc_id, + time: Math.floor(Date.now() / 1000), + flow: flow, + user: this.api.user, + collection: collection ? collection : "default", + tags: tags ? tags : [], + }, + }, + 30000, + ); + } + + // ========== Chunked Upload API ========== + + /** + * Initialize a chunked upload session for large documents (>2MB) + * @param metadata - Document metadata including id, title, kind (MIME type), etc. + * @param totalSize - Total size of the document in bytes + * @param chunkSize - Optional chunk size (default: 5MB) + * @returns Upload session info including upload-id and total-chunks + */ + beginUpload( + metadata: ChunkedUploadDocumentMetadata, + totalSize: number, + chunkSize?: number, + ): Promise { + return this.api + .makeRequest( + "librarian", + { + operation: "begin-upload", + "document-metadata": metadata, + "total-size": totalSize, + "chunk-size": chunkSize, + }, + 30000, + ) + .then((r) => { + if (r.error) { + throw new Error(r.error.message); + } + return r; + }); + } + + /** + * Upload a single chunk of a document + * Chunks can be uploaded in any order and in parallel + * @param uploadId - Upload session ID from beginUpload + * @param chunkIndex - Zero-based chunk index + * @param content - Base64-encoded chunk content + * @returns Progress info including chunks-received and bytes-received + */ + uploadChunk( + uploadId: string, + chunkIndex: number, + content: string, + ): Promise { + return this.api + .makeRequest( + "librarian", + { + operation: "upload-chunk", + "upload-id": uploadId, + "chunk-index": chunkIndex, + content: content, + user: this.api.user, + }, + 60000, // Longer timeout for chunk uploads + ) + .then((r) => { + if (r.error) { + throw new Error(r.error.message); + } + return r; + }); + } + + /** + * Finalize a chunked upload after all chunks are received + * Triggers document processing + * @param uploadId - Upload session ID from beginUpload + * @returns Document ID and object ID + */ + completeUpload(uploadId: string): Promise { + return this.api + .makeRequest( + "librarian", + { + operation: "complete-upload", + "upload-id": uploadId, + user: this.api.user, + }, + 30000, + ) + .then((r) => { + if (r.error) { + throw new Error(r.error.message); + } + return r; + }); + } + + /** + * Check upload progress (useful for resuming interrupted uploads) + * @param uploadId - Upload session ID + * @returns Status including received/missing chunks + */ + getUploadStatus(uploadId: string): Promise { + return this.api + .makeRequest( + "librarian", + { + operation: "get-upload-status", + "upload-id": uploadId, + user: this.api.user, + }, + 30000, + ) + .then((r) => { + if (r.error) { + throw new Error(r.error.message); + } + return r; + }); + } + + /** + * Cancel an in-progress upload and clean up + * @param uploadId - Upload session ID to abort + */ + abortUpload(uploadId: string): Promise { + return this.api + .makeRequest( + "librarian", + { + operation: "abort-upload", + "upload-id": uploadId, + user: this.api.user, + }, + 30000, + ) + .then((r) => { + if (r.error) { + throw new Error(r.error.message); + } + }); + } + + /** + * List pending upload sessions for the current user + * @returns Array of upload sessions with metadata and progress + */ + listUploads(): Promise { + return this.api + .makeRequest( + "librarian", + { + operation: "list-uploads", + user: this.api.user, + }, + 30000, + ) + .then((r) => { + if (r.error) { + throw new Error(r.error.message); + } + return r["upload-sessions"] || []; + }); + } + + /** + * Stream a document in chunks for retrieval (streaming response) + * Sends one request, receives multiple chunk responses via callback + * @param documentId - Document ID to retrieve + * @param onChunk - Callback for each chunk: (content, chunkIndex, totalChunks, complete) => void + * @param onError - Callback for errors + * @param chunkSize - Optional chunk size (default: 1MB) + */ + streamDocument( + documentId: string, + onChunk: (content: string, chunkIndex: number, totalChunks: number, complete: boolean) => void, + onError: (error: string) => void, + chunkSize?: number, + ): void { + const receiver = (message: unknown): boolean => { + const msg = message as { response?: StreamDocumentResponse; complete?: boolean; error?: string }; + + // Check for top-level error + if (msg.error) { + onError(msg.error); + return true; + } + + const resp = msg.response; + if (!resp) { + return !!msg.complete; + } + + // Check for response-level error + if (resp.error) { + onError(resp.error.message); + return true; + } + + const complete = !!msg.complete; + onChunk(resp.content, resp["chunk-index"], resp["total-chunks"], complete); + + return complete; + }; + + this.api.makeRequestMulti( + "librarian", + { + operation: "stream-document", + "document-id": documentId, + "chunk-size": chunkSize, + user: this.api.user, + }, + receiver, + 300000, // 5 minute timeout for full document stream + ); + } +} + +/** + * FlowsApi - Manages processing flows and configuration + * Flows define how documents and data are processed through the system + */ +export class FlowsApi { + api: BaseApi; + + constructor(api: BaseApi) { + this.api = api; + } + + /** + * Retrieves list of available flows + */ + getFlows() { + return this.api + .makeRequest( + "flow", + { + operation: "list-flows", + }, + 60000, + ) + .then((r) => r["flow-ids"] || []); + } + + /** + * Retrieves definition of a specific flow + */ + getFlow(id: string) { + return this.api + .makeRequest( + "flow", + { + operation: "get-flow", + "flow-id": id, + }, + 60000, + ) + .then((r) => JSON.parse(r.flow || "{}")); // Parse JSON flow definition + } + + // Configuration management methods + + /** + * Retrieves all configuration settings + */ + getConfigAll() { + return this.api.makeRequest( + "config", + { + operation: "config", + }, + 60000, + ); + } + + /** + * Retrieves specific configuration values by key + */ + getConfig(keys: { type: string; key: string }[]) { + return this.api.makeRequest( + "config", + { + operation: "get", + keys: keys, + }, + 60000, + ); + } + + /** + * Updates configuration values + */ + putConfig(values: { type: string; key: string; value: string }[]) { + return this.api.makeRequest( + "config", + { + operation: "put", + values: values, + }, + 60000, + ); + } + + /** + * Deletes configuration entries + */ + deleteConfig(keys: { type: string; key: string }) { + return this.api.makeRequest( + "config", + { + operation: "delete", + keys: keys, + }, + 30000, + ); + } + + // Prompt management - specialized config operations for AI prompts + + /** + * Retrieves list of available prompt templates + */ + getPrompts() { + return this.getConfigAll().then((r) => { + const config = r as Record< + string, + Record> + >; + return JSON.parse(config.config.prompt["template-index"]); + }); + } + + /** + * Retrieves a specific prompt template + */ + getPrompt(id: string) { + return this.getConfigAll().then((r) => { + const config = r as Record< + string, + Record> + >; + return JSON.parse(config.config.prompt[`template.${id}`]); + }); + } + + /** + * Retrieves the system prompt configuration + */ + getSystemPrompt() { + return this.getConfigAll().then((r) => { + const config = r as Record< + string, + Record> + >; + return JSON.parse(config.config.prompt.system); + }); + } + + // Flow blueprint management - templates for creating flows + + /** + * Retrieves list of available flow blueprints (templates) + */ + getFlowBlueprints() { + return this.api + .makeRequest( + "flow", + { + operation: "list-blueprints", + }, + 60000, + ) + .then((r) => r["blueprint-names"]); + } + + /** + * Retrieves definition of a specific flow blueprint + */ + getFlowBlueprint(name: string) { + return this.api + .makeRequest( + "flow", + { + operation: "get-blueprint", + "blueprint-name": name, + }, + 60000, + ) + .then((r) => JSON.parse(r["blueprint-definition"] || "{}")); + } + + /** + * Deletes a flow blueprint + */ + deleteFlowBlueprint(name: string) { + return this.api.makeRequest( + "flow", + { + operation: "delete-blueprint", + "blueprint-name": name, + }, + 30000, + ); + } + + // Flow lifecycle management + + /** + * Starts a new flow instance + */ + startFlow( + id: string, + blueprint_name: string, + description: string, + parameters?: Record, + ) { + const request: FlowRequest = { + operation: "start-flow", + "flow-id": id, + "blueprint-name": blueprint_name, + description: description, + }; + + // Only include parameters if provided and not empty + if (parameters && Object.keys(parameters).length > 0) { + request.parameters = parameters; + } + + return this.api + .makeRequest("flow", request, 30000) + .then((response) => { + if (response.error) { + let errorMessage = "Flow start failed"; + if ( + typeof response.error === "object" && + response.error && + "message" in response.error + ) { + errorMessage = + (response.error as { message?: string }).message || errorMessage; + } else if (typeof response.error === "string") { + errorMessage = response.error; + } + throw new Error(errorMessage); + } + return response; + }); + } + + /** + * Stops a running flow instance + */ + stopFlow(id: string) { + return this.api.makeRequest( + "flow", + { + operation: "stop-flow", + "flow-id": id, + }, + 30000, + ); + } +} + +/** + * FlowApi - Interface for interacting with a specific flow instance + * Provides flow-specific versions of core AI/ML operations + */ +export class FlowApi { + api: BaseApi; + flowId: string; + + constructor(api: BaseApi, flowId: string) { + this.api = api; + this.flowId = flowId; // All requests will be routed through this flow + } + + /** + * Performs text completion using AI models within this flow + */ + textCompletion(system: string, text: string): Promise { + return this.api + .makeRequest( + "text-completion", + { + system: system, // System prompt/instructions + prompt: text, // User prompt + }, + 30000, + undefined, // Use default retries + this.flowId, // Route through this flow + ) + .then((r) => r.response); + } + + /** + * Performs Graph RAG (Retrieval Augmented Generation) query + */ + graphRag(text: string, options?: GraphRagOptions, collection?: string) { + return this.api + .makeRequest( + "graph-rag", + { + query: text, + user: this.api.user, + collection: collection || "default", + "entity-limit": options?.entityLimit, + "triple-limit": options?.tripleLimit, + "max-subgraph-size": options?.maxSubgraphSize, + "max-path-length": options?.pathLength, + }, + 60000, // Longer timeout for complex graph operations + undefined, + this.flowId, + ) + .then((r) => r.response); + } + + /** + * Performs Document RAG (Retrieval Augmented Generation) query + */ + documentRag(text: string, docLimit?: number, collection?: string) { + return this.api + .makeRequest( + "document-rag", + { + query: text, + user: this.api.user, + collection: collection || "default", + "doc-limit": docLimit || 20, + }, + 60000, // Longer timeout for document operations + undefined, + this.flowId, + ) + .then((r) => r.response); + } + + /** + * Interacts with an AI agent that provides streaming responses + * BREAKING CHANGE: Callbacks now receive (chunk, complete, metadata?) instead of full messages + */ + agent( + question: string, + think: (chunk: string, complete: boolean, metadata?: StreamingMetadata) => void, + observe: (chunk: string, complete: boolean, metadata?: StreamingMetadata) => void, + answer: (chunk: string, complete: boolean, metadata?: StreamingMetadata) => void, + error: (s: string) => void, + onExplain?: (event: ExplainEvent) => void, + ) { + const receiver = (message: unknown) => { + const msg = message as { response?: AgentResponse; complete?: boolean; error?: string }; + + // Check for top-level error + if (msg.error) { + error(msg.error); + return true; + } + + const resp = msg.response || {}; + + // Check for errors in response + if (resp.chunk_type === "error" || resp.error) { + error(resp.error?.message || "Unknown agent error"); + return true; // End streaming on error + } + + // Handle explainability events (agent uses chunk_type="explain") + if ((resp.chunk_type === "explain" || resp.message_type === "explain") && resp.explain_id && resp.explain_graph) { + onExplain?.({ + explainId: resp.explain_id, + explainGraph: resp.explain_graph, + }); + return false; + } + + // Handle streaming chunks by chunk_type + const content = resp.content || ""; + const messageComplete = !!resp.end_of_message; + const dialogComplete = !!msg.complete; + + // Extract metadata from final message + const metadata: StreamingMetadata | undefined = dialogComplete && (resp.in_token || resp.out_token || resp.model) + ? { in_token: resp.in_token, out_token: resp.out_token, model: resp.model } + : undefined; + + switch (resp.chunk_type) { + case "thought": + think(content, messageComplete, metadata); + break; + case "observation": + observe(content, messageComplete, metadata); + break; + case "answer": + case "final-answer": + answer(content, messageComplete, metadata); + break; + case "action": + // Actions are typically not streamed incrementally, just logged + console.log("Agent action:", content); + break; + } + + return dialogComplete; // End when backend signals complete + }; + + return this.api + .makeRequestMulti( + "agent", + { + question: question, + user: this.api.user, + streaming: true, // Always use streaming mode + }, + receiver, + 120000, + 2, + this.flowId, + ) + .catch((err) => { + const errorMessage = + err instanceof Error ? err.message : err?.toString() || "Unknown error"; + error(`Agent request failed: ${errorMessage}`); + }); + } + + /** + * Performs Graph RAG query with streaming response + * @param text - Query text + * @param receiver - Called for each chunk with (chunk, complete) where complete=true on final chunk + * @param onError - Called on error + * @param options - Graph RAG options (including explainable flag) + * @param collection - Collection name + * @param onExplain - Optional callback for explainability events + */ + graphRagStreaming( + text: string, + receiver: (chunk: string, complete: boolean, metadata?: StreamingMetadata) => void, + onError: (error: string) => void, + options?: GraphRagOptions, + collection?: string, + onExplain?: (event: ExplainEvent) => void, + ): void { + const recv = (message: unknown): boolean => { + const msg = message as { response?: GraphRagResponse; complete?: boolean; error?: string }; + + // Check for top-level error + if (msg.error) { + onError(msg.error); + return true; + } + + const resp = (msg.response || {}) as GraphRagResponse; + + // Check for response-level error + if (resp.error) { + onError(resp.error.message); + return true; + } + + // Handle explainability events + if (resp.message_type === "explain" && resp.explain_id && resp.explain_graph) { + onExplain?.({ + explainId: resp.explain_id, + explainGraph: resp.explain_graph, + }); + // Don't return true - more messages may follow + return false; + } + + // Handle chunk messages (default behavior) + const chunk = resp.response || resp.chunk || ""; + const complete = !!resp.end_of_session || !!msg.complete; + + // Extract metadata from final message + const metadata: StreamingMetadata | undefined = complete && (resp.in_token || resp.out_token || resp.model) + ? { in_token: resp.in_token, out_token: resp.out_token, model: resp.model } + : undefined; + + receiver(chunk, complete, metadata); + + return complete; + }; + + this.api.makeRequestMulti( + "graph-rag", + { + query: text, + user: this.api.user, + collection: collection || "default", + "entity-limit": options?.entityLimit, + "triple-limit": options?.tripleLimit, + "max-subgraph-size": options?.maxSubgraphSize, + "max-path-length": options?.pathLength, + streaming: true, + }, + recv, + 60000, + undefined, + this.flowId, + ); + } + + /** + * Performs Document RAG query with streaming response + * @param text - Query text + * @param receiver - Called for each chunk with (chunk, complete) where complete=true on final chunk + * @param onError - Called on error + * @param docLimit - Maximum documents to retrieve + * @param collection - Collection name + */ + documentRagStreaming( + text: string, + receiver: (chunk: string, complete: boolean, metadata?: StreamingMetadata) => void, + onError: (error: string) => void, + docLimit?: number, + collection?: string, + onExplain?: (event: ExplainEvent) => void, + ): void { + const recv = (message: unknown): boolean => { + const msg = message as { response?: DocumentRagResponse; complete?: boolean; error?: string }; + + // Check for top-level error + if (msg.error) { + onError(msg.error); + return true; + } + + const resp = (msg.response || {}) as DocumentRagResponse; + + // Check for response-level error + if (resp.error) { + onError(resp.error.message); + return true; + } + + // Handle explainability events + if (resp.message_type === "explain" && resp.explain_id && resp.explain_graph) { + onExplain?.({ + explainId: resp.explain_id, + explainGraph: resp.explain_graph, + }); + return false; + } + + const chunk = resp.response || resp.chunk || ""; + const complete = !!resp.end_of_session || !!msg.complete; + + // Extract metadata from final message + const metadata: StreamingMetadata | undefined = complete && (resp.in_token || resp.out_token || resp.model) + ? { in_token: resp.in_token, out_token: resp.out_token, model: resp.model } + : undefined; + + receiver(chunk, complete, metadata); + + return complete; + }; + + this.api.makeRequestMulti( + "document-rag", + { + query: text, + user: this.api.user, + collection: collection || "default", + "doc-limit": docLimit, + streaming: true, + }, + recv, + 60000, + undefined, + this.flowId, + ); + } + + /** + * Performs text completion with streaming response + * @param system - System prompt + * @param text - User prompt + * @param receiver - Called for each chunk with (chunk, complete) where complete=true on final chunk + * @param onError - Called on error + */ + textCompletionStreaming( + system: string, + text: string, + receiver: (chunk: string, complete: boolean, metadata?: StreamingMetadata) => void, + onError: (error: string) => void, + ): void { + const recv = (message: unknown): boolean => { + const msg = message as { response?: TextCompletionResponse; complete?: boolean; error?: string }; + + // Check for top-level error + if (msg.error) { + onError(msg.error); + return true; + } + + const resp = (msg.response || {}) as TextCompletionResponse; + + // Check for response-level error + if (resp.error) { + onError(resp.error.message); + return true; + } + + // Text completion uses 'response' field for chunks + const chunk = resp.response || ""; + const complete = !!msg.complete; + + // Extract metadata from final message + const metadata: StreamingMetadata | undefined = complete && (resp.in_token || resp.out_token || resp.model) + ? { in_token: resp.in_token, out_token: resp.out_token, model: resp.model } + : undefined; + + receiver(chunk, complete, metadata); + + return complete; + }; + + this.api.makeRequestMulti( + "text-completion", + { + system: system, + prompt: text, + streaming: true, + }, + recv, + 30000, + undefined, + this.flowId, + ); + } + + /** + * Executes a prompt template with streaming response + * @param id - Prompt template ID + * @param terms - Template variables + * @param receiver - Called for each chunk with (chunk, complete) where complete=true on final chunk + * @param onError - Called on error + */ + promptStreaming( + id: string, + terms: Record, + receiver: (chunk: string, complete: boolean, metadata?: StreamingMetadata) => void, + onError: (error: string) => void, + ): void { + const recv = (message: unknown): boolean => { + const msg = message as { response?: PromptResponse; complete?: boolean; error?: string }; + + // Check for top-level error + if (msg.error) { + onError(msg.error); + return true; + } + + const resp = (msg.response || {}) as PromptResponse; + + // Check for response-level error + if (resp.error) { + onError(resp.error.message); + return true; + } + + // Prompt service uses 'text' field for chunks + const chunk = resp.text || ""; + const complete = !!msg.complete; + + // Extract metadata from final message + const metadata: StreamingMetadata | undefined = complete && (resp.in_token || resp.out_token || resp.model) + ? { in_token: resp.in_token, out_token: resp.out_token, model: resp.model } + : undefined; + + receiver(chunk, complete, metadata); + + return complete; + }; + + this.api.makeRequestMulti( + "prompt", + { + id: id, + terms: terms, + streaming: true, + }, + recv, + 30000, + undefined, + this.flowId, + ); + } + + /** + * Generates embeddings for multiple texts within this flow. + * Returns vectors[text_index][dimension_index] - one vector per input text. + */ + embeddings(texts: string[]) { + return this.api + .makeRequest( + "embeddings", + { + texts: texts, + }, + 30000, + undefined, + this.flowId, + ) + .then((r) => r.vectors); + } + + /** + * Queries the knowledge graph using a single embedding vector + */ + graphEmbeddingsQuery( + vec: number[], + limit: number | undefined, + collection?: string, + ) { + return this.api + .makeRequest( + "graph-embeddings", + { + vector: vec, + limit: limit ? limit : 20, // Default to 20 results + user: this.api.user, + collection: collection || "default", + }, + 30000, + undefined, + this.flowId, + ) + .then((r) => r.entities); + } + + /** + * Queries knowledge graph triples (subject-predicate-object relationships) + * All parameters are optional - omitted parameters act as wildcards + */ + triplesQuery( + s?: Term, + p?: Term, + o?: Term, + limit?: number, + collection?: string, + graph?: string, + ) { + return this.api + .makeRequest( + "triples", + { + s: s, // Subject + p: p, // Predicate + o: o, // Object + g: graph, // Named graph URI filter + limit: limit ? limit : 20, + user: this.api.user, + collection: collection || "default", + }, + 30000, + undefined, + this.flowId, + ) + .then((r) => r.response); + } + + /** + * Loads a document into this flow for processing + */ + loadDocument( + document: string, // base64-encoded document + id?: string, + metadata?: Triple[], + ) { + return this.api.makeRequest( + "document-load", + { + id: id, + metadata: metadata, + data: document, + }, + 30000, + undefined, + this.flowId, + ); + } + + /** + * Loads plain text into this flow for processing + */ + loadText( + text: string, // Text content + id?: string, + metadata?: Triple[], + charset?: string, // Character encoding + ) { + return this.api.makeRequest( + "text-load", + { + id: id, + metadata: metadata, + text: text, + charset: charset, + }, + 30000, + undefined, + this.flowId, + ); + } + + /** + * Executes a GraphQL query against structured row data + */ + rowsQuery( + query: string, + collection?: string, + variables?: Record, + operationName?: string, + ) { + return this.api + .makeRequest( + "rows", + { + query: query, + user: this.api.user, + collection: collection || "default", + variables: variables, + operation_name: operationName, + }, + 30000, + undefined, + this.flowId, + ) + .then((r) => { + // Return the GraphQL response structure directly + const result: Record = {}; + if (r.data !== undefined) result.data = r.data; + if (r.errors) result.errors = r.errors; + if (r.extensions) result.extensions = r.extensions; + return result; + }); + } + + /** + * Converts a natural language question to a GraphQL query + */ + nlpQuery(question: string, maxResults?: number) { + return this.api + .makeRequest( + "nlp-query", + { + question: question, + max_results: maxResults || 100, + }, + 30000, + undefined, + this.flowId, + ) + .then((r) => r); + } + + /** + * Executes a natural language question against structured data + * Combines NLP query conversion and GraphQL execution + */ + structuredQuery(question: string, collection?: string) { + return this.api + .makeRequest( + "structured-query", + { + question: question, + user: this.api.user, + collection: collection || "default", + }, + 30000, + undefined, + this.flowId, + ) + .then((r) => { + // Return the response structure directly + const result: Record = {}; + if (r.data !== undefined) result.data = r.data; + if (r.errors) result.errors = r.errors; + return result; + }); + } + + /** + * Performs semantic search on structured data indexes using embedding vectors + * @param vectors - Embedding vectors to search for + * @param schemaName - Name of the schema to search + * @param collection - Optional collection name + * @param indexName - Optional index name to filter results + * @param limit - Maximum number of results to return (default: 10) + */ + rowEmbeddingsQuery( + vector: number[], + schemaName: string, + collection?: string, + indexName?: string, + limit?: number, + ): Promise { + const request: RowEmbeddingsQueryRequest = { + vector: vector, + schema_name: schemaName, + user: this.api.user, + collection: collection || "default", + limit: limit || 10, + }; + + if (indexName) { + request.index_name = indexName; + } + + return this.api + .makeRequest( + "row-embeddings", + request, + 30000, + undefined, + this.flowId, + ) + .then((r) => { + if (r.error) { + throw new Error(r.error.message); + } + return r.matches || []; + }); + } +} + +/** + * ConfigApi - Dedicated configuration management interface + * Handles system configuration, prompts, and token cost tracking + */ +export class ConfigApi { + api: BaseApi; + + constructor(api: BaseApi) { + this.api = api; + } + + /** + * Retrieves complete configuration + */ + getConfigAll() { + return this.api.makeRequest( + "config", + { + operation: "config", + }, + 60000, + ); + } + + /** + * Retrieves specific configuration entries + */ + getConfig(keys: { type: string; key: string }[]) { + return this.api.makeRequest( + "config", + { + operation: "get", + keys: keys, + }, + 60000, + ); + } + + /** + * Updates configuration values + */ + putConfig(values: { type: string; key: string; value: string }[]) { + return this.api.makeRequest( + "config", + { + operation: "put", + values: values, + }, + 60000, + ); + } + + /** + * Deletes configuration entries + */ + deleteConfig(keys: { type: string; key: string }) { + return this.api.makeRequest( + "config", + { + operation: "delete", + keys: keys, + }, + 30000, + ); + } + + // Specialized prompt management methods + + /** + * Retrieves available prompt templates + */ + getPrompts() { + return this.getConfigAll().then((r) => { + const config = r as Record< + string, + Record> + >; + return JSON.parse(config.config.prompt["template-index"]); + }); + } + + /** + * Retrieves a specific prompt template + */ + getPrompt(id: string) { + return this.getConfigAll().then((r) => { + const config = r as Record< + string, + Record> + >; + return JSON.parse(config.config.prompt[`template.${id}`]); + }); + } + + /** + * Retrieves system prompt configuration + */ + getSystemPrompt() { + return this.getConfigAll().then((r) => { + const config = r as Record< + string, + Record> + >; + return JSON.parse(config.config.prompt.system); + }); + } + + /** + * Lists available configuration types + */ + list(type: string) { + return this.api + .makeRequest( + "config", + { + operation: "list", + type: type, + }, + 60000, + ) + .then((r) => r); + } + + /** + * Retrieves all key/values for a specific type + */ + getValues(type: string) { + return this.api + .makeRequest( + "config", + { + operation: "getvalues", + type: type, + }, + 60000, + ) + .then((r) => (r as RowsQueryResponse).values); + } + + /** + * Retrieves token cost information for different AI models + * Useful for cost tracking and optimization + */ + getTokenCosts() { + return this.api + .makeRequest( + "config", + { + operation: "getvalues", + type: "token-cost", + }, + 60000, + ) + .then((r) => { + // Parse JSON values and restructure data + const response = r as RowsQueryResponse; + return (response.values || []).map((x: unknown) => { + const item = x as Record; + return { key: item.key, value: JSON.parse(item.value) }; + }); + }) + .then((r) => + // Transform to more usable format + r.map((x: unknown) => { + const item = x as Record; + const value = item.value as Record; + return { + model: item.key, + input_price: value.input_price, // Cost per input token + output_price: value.output_price, // Cost per output token + }; + }), + ); + } +} + +/** + * KnowledgeApi - Manages knowledge graph cores and data + * Knowledge cores appear to be collections of processed knowledge graph data + */ +export class KnowledgeApi { + api: BaseApi; + + constructor(api: BaseApi) { + this.api = api; + } + + /** + * Retrieves list of available knowledge graph cores + */ + getKnowledgeCores() { + return this.api + .makeRequest( + "knowledge", + { + operation: "list-kg-cores", + user: this.api.user, + }, + 60000, + ) + .then((r) => r.ids || []); + } + + /** + * Deletes a knowledge graph core + */ + deleteKgCore(id: string, collection?: string) { + return this.api.makeRequest( + "knowledge", + { + operation: "delete-kg-core", + id: id, + user: this.api.user, + collection: collection || "default", + }, + 30000, + ); + } + + /** + * Deletes a knowledge graph core + */ + loadKgCore(id: string, flow: string, collection?: string) { + return this.api.makeRequest( + "knowledge", + { + operation: "load-kg-core", + id: id, + flow: flow, + user: this.api.user, + collection: collection || "default", + }, + 30000, + ); + } + + /** + * Retrieves a knowledge graph core with streaming data + * Uses multi-request pattern for large datasets + * @param receiver - Callback function to handle streaming data chunks + */ + getKgCore( + id: string, + collection: string | undefined, + receiver: (msg: unknown, eos: boolean) => void, + ) { + // Wrapper to handle end-of-stream detection + const recv = (msg: unknown) => { + const response = msg as Record; + if (response.eos) { + // End of stream - notify receiver and signal completion + receiver(msg, true); + return true; + } else { + // Regular message - continue streaming + receiver(msg, false); + return false; + } + }; + + return this.api.makeRequestMulti( + "knowledge", + { + operation: "get-kg-core", + id: id, + user: this.api.user, + collection: collection || "default", + }, + recv, // Stream handler + 30000, + ); + } +} + +/** + * CollectionManagementApi - Manages collections for organizing documents + * Provides operations for listing, creating, updating, and deleting collections + */ +export class CollectionManagementApi { + api: BaseApi; + + constructor(api: BaseApi) { + this.api = api; + } + + /** + * Lists all collections for the current user with optional tag filtering + * @param tagFilter - Optional array of tags to filter collections + * @returns Promise resolving to array of collection metadata + */ + listCollections(tagFilter?: string[]) { + const request: Record = { + operation: "list-collections", + user: this.api.user, + }; + + if (tagFilter && tagFilter.length > 0) { + request.tag_filter = tagFilter; + } + + return this.api + .makeRequest< + Record, + Record + >("collection-management", request, 30000) + .then((r) => r.collections || []); + } + + /** + * Creates or updates a collection for the current user + * @param collection - Collection ID (unique identifier) + * @param name - Display name for the collection + * @param description - Description of the collection + * @param tags - Array of tags for categorization + * @returns Promise resolving to updated collection metadata + */ + updateCollection( + collection: string, + name?: string, + description?: string, + tags?: string[], + ) { + const request: Record = { + operation: "update-collection", + user: this.api.user, + collection, + }; + + if (name !== undefined) { + request.name = name; + } + if (description !== undefined) { + request.description = description; + } + if (tags !== undefined) { + request.tags = tags; + } + + return this.api + .makeRequest< + Record, + Record + >("collection-management", request, 30000) + .then((r) => { + if ( + r.collections && + Array.isArray(r.collections) && + r.collections.length > 0 + ) { + return r.collections[0]; + } + throw new Error("Failed to update collection"); + }); + } + + /** + * Deletes a collection and all its data for the current user + * @param collection - Collection ID to delete + * @returns Promise resolving when deletion is complete + */ + deleteCollection(collection: string) { + return this.api.makeRequest< + Record, + Record + >( + "collection-management", + { + operation: "delete-collection", + user: this.api.user, + collection, + }, + 30000, + ); + } +} + +/** + * Factory function to create a new TrustGraph WebSocket connection + * This is the main entry point for using the TrustGraph API + * @param user - User identifier for API requests + * @param token - Optional authentication token for secure connections + * @param socketUrl - Optional WebSocket URL (defaults to /api/socket for browser, provide full URL for Node.js) + */ +export const createTrustGraphSocket = ( + user: string, + token?: string, + socketUrl?: string, +): BaseApi => { + return new BaseApi(user, token, socketUrl); +}; diff --git a/ai-context/trustgraph-client/src/types.ts b/ai-context/trustgraph-client/src/types.ts new file mode 100644 index 00000000..19bcb6bf --- /dev/null +++ b/ai-context/trustgraph-client/src/types.ts @@ -0,0 +1,3 @@ +// Type definitions for TrustGraph client + +export {}; diff --git a/ai-context/trustgraph-client/test-graphrag.js b/ai-context/trustgraph-client/test-graphrag.js new file mode 100755 index 00000000..308fef40 --- /dev/null +++ b/ai-context/trustgraph-client/test-graphrag.js @@ -0,0 +1,94 @@ +#!/usr/bin/env node + +/** + * Standalone test for GraphRAG streaming + * Tests the question "What is a cat?" using GraphRAG streaming mode + */ + +import { createTrustGraphSocket } from './dist/index.esm.js'; + +// Configuration +const USER = 'trustgraph'; +const SOCKET_URL = 'ws://localhost:8088/api/v1/socket'; +const QUESTION = 'What is a cat?'; + +console.log('GraphRAG Streaming Test'); +console.log('======================'); +console.log(`User: ${USER}`); +console.log(`Socket URL: ${SOCKET_URL}`); +console.log(`Question: "${QUESTION}"\n`); + +// Create socket connection +const socket = createTrustGraphSocket(USER, undefined, SOCKET_URL); + +// Wait for connection to establish +setTimeout(() => { + console.log('Starting GraphRAG query...\n'); + + let accumulated = ''; + let chunkCount = 0; + + // GraphRAG options + const options = { + entityLimit: 50, + tripleLimit: 30, + maxSubgraphSize: 1000, + pathLength: 2, + }; + + // Streaming receiver callback + const onChunk = (chunk, complete, metadata) => { + chunkCount++; + accumulated += chunk; + + if (chunk) { + process.stdout.write(chunk); + } + + if (complete) { + console.log('\n\n--- Streaming Complete ---'); + console.log(`Total chunks received: ${chunkCount}`); + console.log(`Total characters: ${accumulated.length}`); + + if (metadata) { + console.log('\nMetadata:'); + if (metadata.model) console.log(` Model: ${metadata.model}`); + if (metadata.in_token) console.log(` Input tokens: ${metadata.in_token}`); + if (metadata.out_token) console.log(` Output tokens: ${metadata.out_token}`); + } + + console.log('\n--- Full Response ---'); + console.log(accumulated); + + // Close socket and exit + socket.close(); + process.exit(0); + } + }; + + // Error callback + const onError = (error) => { + console.error('\n\nERROR:', error); + socket.close(); + process.exit(1); + }; + + // Execute GraphRAG streaming query + socket + .flow('default') + .graphRagStreaming( + QUESTION, + onChunk, + onError, + options, + 'default' // collection + ); + +}, 1000); // Wait 1 second for connection + +// Handle process termination +process.on('SIGINT', () => { + console.log('\n\nInterrupted. Closing socket...'); + socket.close(); + process.exit(0); +}); diff --git a/ai-context/trustgraph-client/test-streaming.js b/ai-context/trustgraph-client/test-streaming.js new file mode 100755 index 00000000..5ae6800a --- /dev/null +++ b/ai-context/trustgraph-client/test-streaming.js @@ -0,0 +1,111 @@ +#!/usr/bin/env node + +/** + * Test script for TrustGraph streaming APIs + * Tests both streaming and non-streaming text completion + * + * Usage: + * node test-streaming.js + * + * Requirements: + * - TrustGraph backend running on http://localhost:8088 + * - Built client library in ./dist/ + */ + +import { createTrustGraphSocket } from './dist/index.esm.js'; + +const USER = "test-user"; +const SYSTEM_PROMPT = "You are a helpful AI assistant."; +const TEST_PROMPT = "Explain what streaming is in one paragraph."; +const SOCKET_URL = "ws://localhost:8888/api/socket"; + +console.log("=".repeat(80)); +console.log("TrustGraph Streaming API Test"); +console.log("=".repeat(80)); +console.log(`Connecting to: ${SOCKET_URL}`); +console.log(`User: ${USER}`); +console.log("=".repeat(80)); + +// Create client connection with explicit WebSocket URL for Node.js +const client = createTrustGraphSocket(USER, undefined, SOCKET_URL); + +// Wait a bit for connection to establish +await new Promise(resolve => setTimeout(resolve, 1000)); + +console.log("\n[1/2] Testing NON-STREAMING text completion..."); +console.log("-".repeat(80)); + +try { + const flowApi = client.flow("default"); + const response = await flowApi.textCompletion(SYSTEM_PROMPT, TEST_PROMPT); + + console.log("✓ Non-streaming response received:"); + console.log(response); +} catch (error) { + console.error("✗ Non-streaming failed:", error.message); +} + +console.log("\n[2/2] Testing STREAMING text completion..."); +console.log("-".repeat(80)); + +try { + const flowApi = client.flow("default"); + + let accumulated = ""; + let chunkCount = 0; + const startTime = Date.now(); + + await new Promise((resolve, reject) => { + flowApi.textCompletionStreaming( + SYSTEM_PROMPT, + TEST_PROMPT, + (chunk, complete, metadata) => { + chunkCount++; + accumulated += chunk; + + // Show progress indicator + if (chunk) { + process.stdout.write(chunk); + } + + if (complete) { + const duration = Date.now() - startTime; + console.log("\n"); + console.log("-".repeat(80)); + console.log(`✓ Streaming complete!`); + console.log(` Chunks received: ${chunkCount}`); + console.log(` Total length: ${accumulated.length} chars`); + console.log(` Duration: ${duration}ms`); + console.log(` First chunk: ~${(startTime - Date.now() + duration) / chunkCount}ms`); + + // Display token usage and model info if available + if (metadata) { + console.log("\n Metadata:"); + if (metadata.model) console.log(` Model: ${metadata.model}`); + if (metadata.in_token !== undefined) console.log(` Input tokens: ${metadata.in_token}`); + if (metadata.out_token !== undefined) console.log(` Output tokens: ${metadata.out_token}`); + if (metadata.in_token && metadata.out_token) { + console.log(` Total tokens: ${metadata.in_token + metadata.out_token}`); + } + } + + resolve(); + } + }, + (error) => { + console.error("\n✗ Streaming error:", error); + reject(new Error(error)); + } + ); + }); +} catch (error) { + console.error("✗ Streaming failed:", error.message); +} + +console.log("\n" + "=".repeat(80)); +console.log("Test complete!"); +console.log("=".repeat(80)); + +// Close connection +client.close(); +process.exit(0); diff --git a/ai-context/trustgraph-client/tsconfig.json b/ai-context/trustgraph-client/tsconfig.json new file mode 100644 index 00000000..f69c5d78 --- /dev/null +++ b/ai-context/trustgraph-client/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "lib": ["ES2020", "DOM"], + "jsx": "react", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true + }, + "include": ["src"], + "exclude": ["node_modules", "dist"] +} diff --git a/ai-context/trustgraph-client/vitest.config.ts b/ai-context/trustgraph-client/vitest.config.ts new file mode 100644 index 00000000..1d10f07b --- /dev/null +++ b/ai-context/trustgraph-client/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + globals: true, + environment: "happy-dom", + }, +}); diff --git a/ai-context/trustgraph-templates/.github/workflows/cla.yml b/ai-context/trustgraph-templates/.github/workflows/cla.yml new file mode 100644 index 00000000..73f582cf --- /dev/null +++ b/ai-context/trustgraph-templates/.github/workflows/cla.yml @@ -0,0 +1,25 @@ +name: CLA Assistant + +on: + issue_comment: + types: [created] + pull_request_target: + types: [opened, synchronize, reopened] + +permissions: + actions: write + contents: write + pull-requests: write + statuses: write + +jobs: + CLAssistant: + runs-on: ubuntu-latest + steps: + - name: CLA Assistant + uses: trustgraph-ai/contributor-license-agreement/action@main + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PERSONAL_ACCESS_TOKEN: ${{ secrets.CLA_ASSISTANT_PAT }} + with: + allowlist: 'dependabot,dependabot[bot],github-actions,github-actions[bot]' diff --git a/ai-context/trustgraph-templates/.github/workflows/deploy-prod.yaml b/ai-context/trustgraph-templates/.github/workflows/deploy-prod.yaml new file mode 100644 index 00000000..cb902a52 --- /dev/null +++ b/ai-context/trustgraph-templates/.github/workflows/deploy-prod.yaml @@ -0,0 +1,76 @@ + +name: Deploy to prod + +on: + workflow_dispatch: + push: + # Deploys on master branch + branches: + - master + +permissions: + contents: read + id-token: 'write' + packages: read + +jobs: + + deploy: + + name: Deploy to prod + runs-on: ubuntu-latest + + steps: + + - name: Checkout + uses: actions/checkout@v3 + + - name: Get version + id: version + run: echo VERSION=sha-$(git rev-parse --short HEAD) >> $GITHUB_OUTPUT + + # Python package version MUST be a semantic version, but also doesn't + # matter, so just setting to 0.0.0. + # The container version MUST change on every push to get Cloud Run + # to re-deploy, so is based on git hash. + - name: Build container + run: make PACKAGE_VERSION=0.0.0 VERSION=${{ steps.version.outputs.VERSION }} + + - name: Log in to the container registry + uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - id: auth + name: Authenticate with Google Cloud + uses: google-github-actions/auth@v2 + with: + token_format: access_token + workload_identity_provider: projects/351149249312/locations/global/workloadIdentityPools/deploy/providers/github + service_account: deploy@trustgraph-ai.iam.gserviceaccount.com + access_token_lifetime: 900s + create_credentials_file: true + + - name: Login to Artifact Registry + uses: redhat-actions/podman-login@v1 + with: + registry: europe-west1-docker.pkg.dev + username: oauth2accesstoken + password: ${{ steps.auth.outputs.access_token }} + + - name: Install Pulumi + run: cd pulumi && npm install + + - name: Applying infrastructure 🚀🙏 + uses: pulumi/actions@v3 + with: + command: up + stack-name: prod + work-dir: pulumi + cloud-url: gs://trustgraph-ai-deploy/config-svc + env: + PULUMI_CONFIG_PASSPHRASE: "" + IMAGE_VERSION: ${{ steps.version.outputs.VERSION }} + diff --git a/ai-context/trustgraph-templates/.github/workflows/pull-request.yaml b/ai-context/trustgraph-templates/.github/workflows/pull-request.yaml new file mode 100644 index 00000000..dbe64b15 --- /dev/null +++ b/ai-context/trustgraph-templates/.github/workflows/pull-request.yaml @@ -0,0 +1,31 @@ + +name: Test pull request + +on: + pull_request: + +permissions: + contents: read + +jobs: + + container-push: + + name: Run tests + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup environment + run: | + python3 -m venv env + . env/bin/activate + pip install -e .[dev] + + - name: Run pytest tests + run: | + . env/bin/activate + pytest -v --tb=short + diff --git a/ai-context/trustgraph-templates/.github/workflows/undeploy-prod.yaml b/ai-context/trustgraph-templates/.github/workflows/undeploy-prod.yaml new file mode 100644 index 00000000..d5cd2628 --- /dev/null +++ b/ai-context/trustgraph-templates/.github/workflows/undeploy-prod.yaml @@ -0,0 +1,45 @@ + +name: Undeploy to prod + +on: + workflow_dispatch: + +permissions: + contents: read + id-token: 'write' + +jobs: + + deploy: + + name: Undeploy to prod + runs-on: ubuntu-latest + + steps: + + - name: Checkout + uses: actions/checkout@v3 + + - id: auth + name: Authenticate with Google Cloud + uses: google-github-actions/auth@v2 + with: + token_format: access_token + workload_identity_provider: projects/514167726704/locations/global/workloadIdentityPools/deploy/providers/deploy + service_account: deploy@kalntera-demo.iam.gserviceaccount.com + access_token_lifetime: 900s + create_credentials_file: true + + - name: Install Pulumi + run: cd pulumi && npm install + + - name: Destroy infrastructure ☠🔥 + uses: pulumi/actions@v3 + with: + command: destroy + stack-name: prod + work-dir: pulumi + cloud-url: gs://trustgraph-deploy/config-ui + env: + PULUMI_CONFIG_PASSPHRASE: "" + diff --git a/ai-context/trustgraph-templates/.gitignore b/ai-context/trustgraph-templates/.gitignore new file mode 100644 index 00000000..6a5e4738 --- /dev/null +++ b/ai-context/trustgraph-templates/.gitignore @@ -0,0 +1,22 @@ +*~ +__pycache__ +*.pyc +*.pyo +*.egg-info/ +build/ +dist/ +*.egg +.pytest_cache/ +.coverage +htmlcov/ +.eggs/ +*.so +*.dylib +.Python +env/ +venv/ +ENV/ +*.log +node_modules/ +trustgraph_configurator/version.py +INSTALLATION.md diff --git a/ai-context/trustgraph-templates/Containerfile b/ai-context/trustgraph-templates/Containerfile new file mode 100644 index 00000000..4eba036e --- /dev/null +++ b/ai-context/trustgraph-templates/Containerfile @@ -0,0 +1,38 @@ +# --- STAGE 1: Build --- +FROM python:3.14-slim AS build + +# Install build tools (Debian uses apt) +RUN apt-get update && apt-get install -y \ + build-essential \ + golang \ + git \ + && rm -rf /var/lib/apt/lists/* + +RUN mkdir -p /root/wheels /root/build + +# Build wheels +RUN pip wheel -w /root/wheels --no-deps gojsonnet + +COPY trustgraph_configurator/ /root/build/trustgraph_configurator/ +COPY pyproject.toml /root/build/pyproject.toml +COPY README.md /root/build/README.md + +RUN (cd /root/build && pip wheel -w /root/wheels --no-deps .) + +# --- STAGE 2: Runtime --- +FROM python:3.14-slim + +# No need to install libstdc++ manually, it's included in python-slim +RUN apt-get update && apt-get install -y \ + && rm -rf /var/lib/apt/lists/* + +# aiohttp and others will be pulled from PyPI or handled via wheels +COPY --from=build /root/wheels /root/wheels + +# Install your wheels plus regular dependencies +RUN pip install --no-cache-dir /root/wheels/* aiohttp pyyaml tabulate && \ + rm -rf /root/wheels + +CMD ["tg-config-svc"] +EXPOSE 8080 + diff --git a/ai-context/trustgraph-templates/DIALOG-FLOW.md b/ai-context/trustgraph-templates/DIALOG-FLOW.md new file mode 100644 index 00000000..e70ca46b --- /dev/null +++ b/ai-context/trustgraph-templates/DIALOG-FLOW.md @@ -0,0 +1,168 @@ +# TrustGraph Configuration Dialog Flow + +## Overview + +A configuration wizard system that guides users through TrustGraph deployment setup. Outputs deployment configuration and contextual documentation. + +Supports two interfaces: +- **Web UI**: Step-by-step wizard with visual cards +- **CLI**: Interactive terminal prompts + +## Architecture + +``` +┌─────────────────────┐ +│ trustgraph-flow │ State machine defining wizard steps +│ .yaml │ and transitions +└──────────┬──────────┘ + │ + ▼ +┌─────────────────────┐ +│ Flow Engine │ Executes state machine, collects user input, +│ (Web UI / CLI) │ manages state and backtracking +└──────────┬──────────┘ + │ + ▼ + ┌──────────────┐ + │ Wizard State │ Simple key/value object + │ (JSON) │ e.g., { platform: "gke", model_deployment: "ollama", ... } + └──────┬───────┘ + │ + ┌─────┴─────┐ + ▼ ▼ +┌─────────┐ ┌─────────────┐ +│ Output │ │Documentation│ +│Transform│ │ Assembler │ +└────┬────┘ └──────┬──────┘ + │ │ + ▼ ▼ +┌─────────┐ ┌─────────────┐ +│Component│ │ README.md │ +│ Array │ │ / Checklist │ +│ (JSON) │ │ (JSON) │ +└─────────┘ └─────────────┘ +``` + +## Key Design Decisions + +| Decision | Choice | Rationale | +|----------|--------|-----------| +| Flow structure | State machine | Explicit transitions, supports conditional branching | +| Expression language | JSONata | Single language for conditions and transforms | +| Documentation storage | Manifest + markdown fragments | Clean separation, easy to maintain | +| Platform variants | `in ['docker-compose', 'podman-compose']` | Explicit grouping, no hidden logic | +| Template variables | `{{var}}` syntax | Simple, familiar | + +## File Structure + +``` +config-dialog-flow/ +├── dialog-flow-schema.json # Schema: flow definitions +├── docs-manifest-schema.json # Schema: documentation manifests +├── trustgraph-flow.yaml # Flow: wizard state machine +├── trustgraph-output.jsonata # Transform: state → components +├── trustgraph-docs.yaml # Manifest: state → documentation +└── docs/ # Fragments: markdown content + ├── platform/ + ├── model/ + ├── storage/ + ├── gateway/ + ├── deploy/ + └── features/ +``` + +## Data Flow + +1. **Flow engine** loads `trustgraph-flow.yaml` +2. User progresses through steps, engine collects state +3. On completion: + - **Output transform** evaluates `trustgraph-output.jsonata` against state → component array + - **Doc assembler** evaluates `trustgraph-docs.yaml` conditions against state → filtered instructions → loads markdown fragments → outputs README or checklist JSON + +## Implementation Phases + +### Phase 1: Core Flow Engine + +- [ ] Parse YAML flow definition +- [ ] Implement state machine executor +- [ ] Handle step rendering (title, description, input) +- [ ] Evaluate JSONata transition conditions +- [ ] Manage wizard state (set values, navigate) +- [ ] Implement backtracking (previous step, state rollback) + +### Phase 2: Input Types + +- [ ] Select (single choice from options) +- [ ] Toggle (boolean) +- [ ] Number (with min/max/step) +- [ ] Text (free input) +- [ ] Skip single-option steps (UX decision) + +### Phase 3: Output Generation + +- [ ] Load JSONata transform +- [ ] Evaluate against wizard state +- [ ] Produce component array JSON +- [ ] Package as downloadable ZIP (existing functionality) + +### Phase 4: Documentation Assembly + +- [ ] Parse documentation manifest +- [ ] Evaluate `when` conditions (JSONata) +- [ ] Filter to matching instructions +- [ ] Load markdown fragments +- [ ] Substitute `{{var}}` placeholders +- [ ] Output as README.md (CLI) or checklist JSON (Web) + +### Phase 5: CLI Interface + +- [ ] Terminal prompt for each step +- [ ] Numbered option selection +- [ ] Progress indicator (`[3/12]`) +- [ ] Back command +- [ ] Review summary before generate + +### Phase 6: Web Interface + +- [ ] Step-by-step card UI +- [ ] Progress bar +- [ ] Option cards with icons/descriptions +- [ ] Back navigation +- [ ] Review screen with edit capability +- [ ] Generate button + +## Expression Language + +All conditions use JSONata: + +``` +platform = 'docker-compose' +platform in ['gke', 'eks', 'aks', 'minikube'] +model_deployment = 'ollama' and platform in ['docker-compose', 'podman-compose'] +version < '1.6.0' +ocr.enabled = true +``` + +## State Shape + +``` +{ + "version": "1.8.18", + "platform": "gke", + "k8s": { "namespace": "trustgraph" }, + "graph_store": "cassandra", + "vector_db": "qdrant", + "object_store": "cassandra", + "model_deployment": "ollama", + "max_output_tokens": 2048, + "ocr": { "enabled": true, "engine": "tesseract" }, + "embeddings": { "enabled": false } +} +``` + +## Out of Scope (Future) + +- Dual model mode (separate main/RAG models) +- Advanced settings (concurrency, chunking, memory profiles) +- Configuration import/export +- Validation beyond type checking diff --git a/ai-context/trustgraph-templates/Makefile b/ai-context/trustgraph-templates/Makefile new file mode 100644 index 00000000..ef0cdfec --- /dev/null +++ b/ai-context/trustgraph-templates/Makefile @@ -0,0 +1,22 @@ + +PACKAGE_VERSION=0.0.0 +VERSION=0.0.0 + +all: container + +package: update-package-versions + python3 -m build --sdist --outdir pkgs + +update-package-versions: + echo __version__ = \"${PACKAGE_VERSION}\" > trustgraph_configurator/version.py + +CONTAINER=localhost/config-svc +DOCKER=podman + +container: + ${DOCKER} build -f Containerfile -t ${CONTAINER}:${VERSION} \ + --format docker + +# On port 8081 +run-container: + ${DOCKER} run -i -t -p 8081:8080 ${CONTAINER}:${VERSION} diff --git a/ai-context/trustgraph-templates/README.md b/ai-context/trustgraph-templates/README.md new file mode 100644 index 00000000..a83bbbbc --- /dev/null +++ b/ai-context/trustgraph-templates/README.md @@ -0,0 +1,311 @@ +# TrustGraph Configuration Templates + +TrustGraph configurator is a Python-based tool that generates deployment configurations for TrustGraph AI systems. It supports multiple deployment platforms and provides templated configurations with versioning support. + +## Overview + +The configurator uses Jsonnet templates to generate deployment configurations for various platforms including Docker Compose, Podman, and multiple Kubernetes environments. It packages the generated configurations into ZIP files containing all necessary deployment resources. + +## Configuration Process + +The TrustGraph configuration system uses a multi-stage pipeline to generate deployment packages: + +``` +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ Dialog Flow │ │ JSONata │ │ Configuration │ │ Deployment │ +│ Configuration │────▶│ Transform │────▶│ Service │────▶│ Package │ +│ │ │ │ │ │ │ │ +│ (state object) │ │ (config object) │ │ (templates) │ │ (ZIP file) │ +└─────────────────┘ └─────────────────┘ └─────────────────┘ └─────────────────┘ + │ + │ ┌─────────────────┐ ┌─────────────────┐ + └──────────────▶│ Documentation │────▶│ Installation │ + │ Flow │ │ Guide │ + └─────────────────┘ └─────────────────┘ +``` + +### 1. Dialog Flow Configuration + +The dialog flow file (`trustgraph-flow.yaml`) describes configuration steps in a technology-neutral way. A UI wizard walks users through these steps, collecting choices about platform, model provider, storage backends, and features. The output is a **state object** - a simple key/value map representing all user selections. + +### 2. JSONata Transform + +The JSONata transform file (`trustgraph-output.jsonata`) converts the state object into a **configuration object**. This object understands how to invoke TrustGraph templates and contains the structured parameters needed by the template system. + +### 3. Configuration Service + +The configuration service receives the configuration object, invokes the appropriate Jsonnet templates, and runs the package builder. The output is a **deployment package** - a ZIP file containing all deployment resources (docker-compose.yaml or Kubernetes manifests, plus supporting files). + +### 4. Documentation Flow + +The original state object can also be used with the documentation manifest (`trustgraph-docs.yaml`) to generate a customised **installation guide** based on the user's specific configuration choices. + +## Installation + +The configurator is distributed as a Python package. To use it: + +```bash +export PYTHONPATH=. +# or install the package +pip install -e . +``` + +## Usage + +### List Available Configurations + +To see all available templates and platforms: + +```bash +tg-show-config-params +``` + +This will display: +- Available platforms (docker-compose, podman-compose, various Kubernetes options) +- Available templates with versions and stability status +- Latest version and latest stable version + +### Generate Configuration + +To generate a configuration package: + +```bash +scripts/tg-build-deployment --template --version \ + --input config.json --output output.zip --platform +``` + +Example: +```bash +scripts/tg-build-deployment --template 1.1 --version 1.1.9 \ + --input config.json --output deployment.zip --platform docker-compose +``` + +#### Output to stdout + +To output only the TrustGraph configuration: +```bash +scripts/tg-build-deployment --template 1.1 --latest-stable \ + --input config.json -O > trustgraph-config.json +``` + +To output only the platform resources (docker-compose.yaml or resources.yaml): +```bash +# For Docker Compose +scripts/tg-build-deployment --template 1.1 --latest-stable \ + --input config.json --platform docker-compose -R > docker-compose.yaml + +# For Kubernetes +scripts/tg-build-deployment --template 1.1 --latest-stable \ + --input config.json --platform gcp-k8s -R > resources.yaml +``` + +### Configuration Service API + +You can also run the configurator as a REST API service: + +```bash +scripts/tg-config-svc +``` + +This starts a web service on port 8080 that provides: +- REST API endpoints for configuration generation +- Programmatic access to version information +- Web-based configuration generation + +The service provides the same functionality as the command-line tool but through HTTP endpoints (see API Service section below for details). + +### Command Line Options + +- `-i, --input`: Input configuration file (default: config.json) +- `-o, --output`: Output ZIP file (default: output.zip) +- `-t, --template`: Template name (e.g., "1.1", "1.0", "0.23") +- `-v, --version`: Specific version to use +- `-p, --platform`: Target platform (default: docker-compose) +- `--latest`: Use the latest available version +- `--latest-stable`: Use the latest stable version +- `-O, --output-tg-config`: Output only TrustGraph configuration to stdout (no ZIP file) +- `-R, --output-resources`: Output only platform resources (docker-compose.yaml or resources.yaml) to stdout (no ZIP file) + +### Available Platforms + +- `docker-compose`: Local Docker deployment using docker-compose +- `podman-compose`: Local Podman deployment using podman-compose +- `minikube-k8s`: Minikube Kubernetes cluster +- `gcp-k8s`: Google Cloud Kubernetes (GKE) +- `aks-k8s`: Azure Kubernetes Service (AKS) +- `eks-k8s`: AWS Elastic Kubernetes Service (EKS) +- `scw-k8s`: Scaleway Kubernetes + +## Python Architecture + +### Module Structure + +The `trustgraph_configurator` package consists of several key modules: + +#### Core Modules + +1. **generator.py** (`Generator` class) + - Processes Jsonnet templates using the `_jsonnet` library + - Evaluates configuration snippets with custom import callbacks + - Returns processed JSON configurations + +2. **packager.py** (`Packager` class) + - Main orchestrator for configuration generation + - Handles template and resource file loading + - Generates platform-specific deployment packages + - Creates ZIP archives with all necessary files + - Supports both Docker Compose and Kubernetes outputs + +3. **index.py** (`Index` class) + - Manages template and platform metadata + - Reads from `templates/index.json` + - Provides version sorting and comparison + - Offers methods to get latest/stable versions + +4. **api.py** (`Api` class) + - REST API service for configuration generation + - Endpoints for version information and generation + - Validates input JSON before processing + - Returns generated configurations as binary data + +5. **service.py** + - Simple wrapper to run the API service + - Configures logging and starts the web server on port 8080 + +6. **run.py** + - Command-line interface implementation + - Argument parsing and validation + - Reads input configuration and writes output ZIP + +7. **list.py** + - Command-line tool to list available configurations + - Displays platforms, templates, and versions in tabular format + +### How Components Interact + +``` +User Input (config.json) + ↓ +run.py (CLI) or api.py (REST) + ↓ +Packager (orchestrator) + ├─→ Index (metadata/versions) + ├─→ Generator (Jsonnet processing) + └─→ Resource files (templates/) + ↓ + Platform-specific generation + (Docker Compose or Kubernetes) + ↓ + ZIP archive (output.zip) +``` + +### Key Design Patterns + +1. **Template Resolution**: The `Packager.fetch()` method implements a sophisticated file resolution system: + - Special handling for `trustgraph/config.json` and `version.jsonnet` + - Fallback search paths for templates and resources + - Version-specific template directories + +2. **Platform Abstraction**: Different platforms are handled through: + - Platform-specific Jsonnet templates (e.g., `config-to-docker-compose.jsonnet`) + - Conditional logic in `Packager.generate()` + - Unified output format (ZIP archives) + +3. **Version Management**: The system supports: + - Multiple template versions with different features + - Stability levels (alpha, beta, stable) + - Automatic version selection (latest/latest-stable) + +### Configuration Flow + +1. User provides a JSON configuration file +2. Packager validates and loads the appropriate template version +3. Generator processes Jsonnet templates with the configuration +4. Platform-specific resources are added (Grafana dashboards, Prometheus config) +5. Everything is packaged into a ZIP file for deployment + +### API Service + +The REST API service (`tg-config-svc`) provides programmatic access to the configurator functionality. Start the service with: + +```bash +scripts/tg-config-svc +``` + +The service runs on port 8080 and provides the following endpoints: + +``` +POST /api/generate/{platform}/{template} # Generate configuration +GET /api/latest # Get latest version info +GET /api/latest-stable # Get latest stable version info +GET /api/versions # List all available versions +``` + +#### Dialog Flow Resources + +These endpoints serve the dialog flow resources described in the Configuration Process section: + +``` +GET /api/dialog-flow # Dialog flow state machine (YAML) +GET /api/config-prepare # JSONata transform for config preparation +GET /api/docs-manifest # Documentation manifest (YAML) +GET /api/docs/{path} # Documentation markdown fragments +``` + +Example usage: +```bash +# Generate configuration via API +curl -X POST http://localhost:8080/api/generate/docker-compose/1.1 \ + -H "Content-Type: application/json" \ + -d @config.json \ + --output deployment.zip + +# Fetch dialog flow configuration +curl http://localhost:8080/api/dialog-flow + +# Fetch a documentation fragment +curl http://localhost:8080/api/docs/platform/docker-compose.md +``` + +## Output Structure + +### Docker Compose Output + +The generated ZIP file contains: +``` +docker-compose.yaml # Main deployment file +trustgraph/config.json # TrustGraph configuration +grafana/ # Grafana dashboards and provisioning +prometheus/ # Prometheus configuration +``` + +### Kubernetes Output + +The generated ZIP file contains: +``` +resources.yaml # All Kubernetes resources in a single file +``` + +## Development + +To extend or modify the configurator: + +1. Templates are in `trustgraph_configurator/templates//` +2. Add new platforms by creating appropriate Jsonnet templates +3. Update `templates/index.json` for new versions +4. Resources (dashboards, configs) go in `trustgraph_configurator/resources//` +5. Dialog flow resources are in `trustgraph_configurator/resources/dialog/`: + - `trustgraph-flow.yaml` - Dialog flow state machine + - `trustgraph-output.jsonata` - State-to-config transform + - `trustgraph-docs.yaml` - Documentation manifest + - `docs/` - Markdown documentation fragments + +## Error Handling + +The configurator includes error handling for: +- Missing or invalid templates +- Malformed input JSON +- File resolution failures +- Platform-specific generation errors + +Errors are logged with appropriate context for debugging. diff --git a/ai-context/trustgraph-templates/TEST_STRATEGY.md b/ai-context/trustgraph-templates/TEST_STRATEGY.md new file mode 100644 index 00000000..5896c3d5 --- /dev/null +++ b/ai-context/trustgraph-templates/TEST_STRATEGY.md @@ -0,0 +1,320 @@ +# TrustGraph Configurator Test Strategy + +## Executive Summary + +The TrustGraph configurator's primary risk is shipping broken configurations that fail to deploy properly. Unlike application crashes (which are obvious and easily fixable), configuration errors can cause silent failures, deployment issues, or runtime problems that are difficult to debug and significantly impact user experience. + +This test strategy prioritizes **configuration correctness** and **deployability** over code coverage metrics. + +## Risk Assessment + +### High Impact Risks +1. **Template Syntax Errors**: Jsonnet compilation failures that prevent configuration generation +2. **Invalid Configuration Structure**: Generated configs that don't match expected schemas +3. **Missing Dependencies**: Component configurations that reference non-existent services +4. **Platform-Specific Issues**: Configurations that work on one platform but fail on another +5. **Version Incompatibilities**: Template changes that break existing user configurations + +### Medium Impact Risks +1. **Performance Issues**: Inefficient template processing +2. **Resource Misconfigurations**: Incorrect memory/CPU limits +3. **Security Misconfigurations**: Missing security settings or exposed secrets + +### Low Impact Risks +1. **CLI Argument Parsing**: Easily debuggable and obvious failures +2. **Logging Issues**: Don't affect configuration correctness +3. **API Error Handling**: Obvious failures with clear error messages + +## Testing Strategy + +### 1. Configuration Validation Tests (Critical) + +#### Template Compilation Tests +- **Objective**: Ensure all Jsonnet templates compile without errors +- **Scope**: All templates in all versions (0.21, 0.22, 0.23, 1.0, 1.1) +- **Implementation**: + ```bash + # Test all platform configurations for each template version + for version in 0.21 0.22 0.23 1.0 1.1; do + for platform in docker-compose podman-compose minikube-k8s gcp-k8s aks-k8s eks-k8s scw-k8s; do + ./scripts/tg-build-deployment --template $version --platform $platform --input test-configs/minimal.json -O > /dev/null + done + done + ``` + +#### Schema Validation Tests +- **Objective**: Validate generated configurations against expected schemas +- **Docker Compose**: Validate against docker-compose schema +- **Kubernetes**: Validate against Kubernetes resource schemas +- **TrustGraph Config**: Validate against TrustGraph configuration schema + +#### Component Integration Tests +- **Objective**: Ensure component dependencies are correctly resolved +- **Test Cases**: + - LLM + Embeddings combinations (OpenAI + HF, Ollama + Ollama, etc.) + - RAG pipelines with all storage backends + - Graph databases with different embedding stores + - OCR + document processing chains + +### 2. Platform-Specific Deployment Tests (Critical) + +#### Docker Compose Validation +- **Objective**: Ensure generated docker-compose.yaml files are valid and deployable +- **Test Environment**: Local Docker daemon +- **Test Process**: + ```bash + # Generate configuration + ./scripts/tg-build-deployment --template 1.1 --platform docker-compose --input test-config.json -R > docker-compose.yaml + + # Validate syntax + docker-compose config -q + + # Test deployment (dry-run) + docker-compose up --no-start + + # Test actual deployment with minimal config + docker-compose up -d + timeout 60 docker-compose ps + docker-compose down + ``` + +#### Kubernetes Validation +- **Objective**: Ensure generated Kubernetes manifests are valid and deployable +- **Test Environment**: Minikube or kind cluster +- **Test Process**: + ```bash + # Generate configuration + ./scripts/tg-build-deployment --template 1.1 --platform gcp-k8s --input test-config.json -R > resources.yaml + + # Validate syntax + kubectl apply --dry-run=client -f resources.yaml + + # Test deployment + kubectl apply -f resources.yaml + kubectl wait --for=condition=Ready pod --all --timeout=300s + kubectl delete -f resources.yaml + ``` + +### 3. Configuration Matrix Tests (High Priority) + +#### Test Configuration Profiles +Create comprehensive test configurations covering: + +1. **Minimal Configuration** + ```json + { + "llm": {"engine": "openai", "model": "gpt-3.5-turbo"}, + "embeddings": {"engine": "hf", "model": "sentence-transformers/all-MiniLM-L6-v2"} + } + ``` + +2. **Complex RAG Configuration** + ```json + { + "llm": {"engine": "openai"}, + "embeddings": {"engine": "hf"}, + "vector_store": {"engine": "qdrant"}, + "graph_store": {"engine": "neo4j"}, + "document_store": {"engine": "cassandra"}, + "rag": {"enabled": true, "chunking": "recursive"} + } + ``` + +3. **Multi-Service Configuration** + ```json + { + "llm": [ + {"engine": "openai", "model": "gpt-4"}, + {"engine": "ollama", "model": "llama2"} + ], + "embeddings": {"engine": "fastembed"}, + "vector_store": {"engine": "milvus"}, + "monitoring": {"grafana": true, "prometheus": true} + } + ``` + +4. **Cloud-Specific Configurations** + - AWS Bedrock + EKS + - Azure OpenAI + AKS + - Google Vertex AI + GKE + +#### Cross-Platform Matrix +Test each configuration profile against all supported platforms: +- docker-compose +- podman-compose +- minikube-k8s +- gcp-k8s +- aks-k8s +- eks-k8s +- scw-k8s + +### 4. Version Compatibility Tests (Medium Priority) + +#### Backward Compatibility +- **Objective**: Ensure new template versions don't break existing configurations +- **Process**: + 1. Collect real-world configuration examples + 2. Test against new template versions + 3. Validate that outputs remain functionally equivalent + +#### Forward Compatibility +- **Objective**: Ensure template changes don't break when new features are added +- **Process**: + 1. Test configurations with unknown/future parameters + 2. Verify graceful degradation or clear error messages + +### 5. Integration Tests (Medium Priority) + +#### End-to-End Deployment Tests +- **Objective**: Verify complete deployment workflows +- **Test Cases**: + - Generate config → Deploy → Verify services start → Run basic functionality test + - Test with monitoring enabled (Grafana/Prometheus accessible) + - Test with different storage backends + +#### Resource Generation Tests +- **Objective**: Verify auxiliary resources are correctly generated +- **Test Cases**: + - Grafana dashboards are valid JSON + - Prometheus config is valid YAML + - All required secrets/configmaps are created + +### 6. Regression Tests (High Priority) + +#### Template Change Validation +- **Objective**: Detect when template changes break existing configurations +- **Process**: + 1. Maintain golden configurations for each version + 2. Generate outputs before and after changes + 3. Compare outputs for breaking changes + 4. Flag any structural differences for manual review + +#### Component Regression Tests +- **Objective**: Ensure component updates don't break integrations +- **Test Cases**: + - Component parameter changes + - New component additions + - Component deprecations + +## Test Implementation Framework + +### Test Infrastructure + +#### Test Configuration Repository +``` +tests/ +├── configs/ +│ ├── minimal.json +│ ├── complex-rag.json +│ ├── multi-service.json +│ └── cloud-specific/ +├── golden/ +│ ├── 1.1/ +│ │ ├── docker-compose/ +│ │ └── k8s/ +│ └── 1.0/ +├── schemas/ +│ ├── docker-compose.json +│ ├── k8s-resources.json +│ └── trustgraph-config.json +└── scripts/ + ├── test-all-platforms.sh + ├── validate-schemas.sh + └── deploy-test.sh +``` + +#### Automated Test Pipeline +```bash +#!/bin/bash +# test-all-platforms.sh + +set -e + +VERSIONS="0.21 0.22 0.23 1.0 1.1" +PLATFORMS="docker-compose podman-compose minikube-k8s gcp-k8s aks-k8s eks-k8s scw-k8s" +CONFIGS="minimal.json complex-rag.json multi-service.json" + +echo "Testing configuration generation..." +for version in $VERSIONS; do + for platform in $PLATFORMS; do + for config in $CONFIGS; do + echo "Testing $version/$platform/$config" + + # Test TrustGraph config generation + ./scripts/tg-build-deployment --template $version --platform $platform \ + --input tests/configs/$config -O > /tmp/tg-config.json + + # Validate TrustGraph config + ./tests/scripts/validate-tg-config.sh /tmp/tg-config.json + + # Test resource generation + ./scripts/tg-build-deployment --template $version --platform $platform \ + --input tests/configs/$config -R > /tmp/resources.yaml + + # Validate resources + ./tests/scripts/validate-resources.sh /tmp/resources.yaml $platform + + echo "✓ $version/$platform/$config passed" + done + done +done +``` + +### Test Execution Strategy + +#### Continuous Integration +1. **Pre-commit**: Template syntax validation +2. **Pull Request**: Full configuration matrix tests +3. **Release**: Deployment tests + regression tests + +#### Performance Testing +- Template processing time benchmarks +- Memory usage during large configuration generation +- Concurrent API request handling + +#### Security Testing +- Validate no secrets are exposed in generated configs +- Test secret injection mechanisms +- Validate security-related component configurations + +## Test Metrics and Success Criteria + +### Primary Success Metrics +1. **Configuration Validity**: 100% of generated configurations must be syntactically valid +2. **Deployment Success**: 95% of generated configurations must deploy successfully +3. **Regression Prevention**: Zero breaking changes to existing configurations without explicit versioning + +### Secondary Success Metrics +1. **Test Coverage**: All template versions × all platforms × all major components +2. **Performance**: Configuration generation < 10 seconds for complex configs +3. **Documentation**: All test failures must have clear error messages and remediation steps + +## Maintenance and Updates + +### Test Maintenance +- Update test configurations when new features are added +- Refresh golden configurations when intentional changes are made +- Review and update schemas when component APIs change + +### Test Infrastructure Updates +- Add new platforms when supported +- Update validation tools when dependencies change +- Maintain test environments (Docker, Kubernetes clusters) + +## Risk Mitigation + +### High-Risk Changes +Any changes to the following require full test suite execution: +- Jsonnet template files +- Component definitions +- Engine implementations +- Version configuration changes + +### Emergency Procedures +- Rollback plan for broken template releases +- Hotfix process for critical configuration issues +- Communication plan for notifying users of breaking changes + +## Conclusion + +This test strategy prioritizes what matters most: ensuring users receive working, deployable configurations. By focusing on configuration correctness over code coverage, we can prevent the high-impact failures that would significantly affect user experience while maintaining development velocity for lower-risk changes. \ No newline at end of file diff --git a/ai-context/trustgraph-templates/config-standalone-pulsar.jsonnet b/ai-context/trustgraph-templates/config-standalone-pulsar.jsonnet new file mode 100644 index 00000000..0ab4d03a --- /dev/null +++ b/ai-context/trustgraph-templates/config-standalone-pulsar.jsonnet @@ -0,0 +1,45 @@ +[ + { + "name": "triple-store-cassandra", + "parameters": {} + }, + { + "name": "object-store-cassandra", + "parameters": {} + }, + { + "name": "pulsar-standalone", + "parameters": {} + }, + { + "name": "vector-store-qdrant", + "parameters": {} + }, + { + "name": "grafana", + "parameters": {} + }, + { + "name": "trustgraph-base", + "parameters": {} + }, + { + "name": "override-recursive-chunker", + "parameters": { + "chunk-size": 2000, + "chunk-overlap": 100 + } + }, + { + "name": "embeddings-fastembed", + "parameters": { + "embeddings-model": "sentence-transformers/all-MiniLM-L6-v2" + } + }, + { + "name": "vertexai", + "parameters": { + "max-output-tokens": 8192 + } + } +] diff --git a/ai-context/trustgraph-templates/docs/garage-deployment.md b/ai-context/trustgraph-templates/docs/garage-deployment.md new file mode 100644 index 00000000..d00999bc --- /dev/null +++ b/ai-context/trustgraph-templates/docs/garage-deployment.md @@ -0,0 +1,244 @@ +# Garage S3-Compatible Object Storage + +## Overview + +Garage is a lightweight, self-hosted S3-compatible object storage system used as an alternative to Ceph for storing documents and other objects in the TrustGraph system. It provides a simpler deployment model while maintaining S3 API compatibility. + +## Features + +- **S3-Compatible API**: Full compatibility with AWS S3 SDK and CLI tools +- **Lightweight**: Minimal resource requirements compared to Ceph +- **Simple Deployment**: Single container with no complex dependencies +- **Distributed**: Supports multi-node clusters (though configured for single-node by default) + +## Architecture + +### Components + +1. **Garage Daemon** (`garage`) + - Main storage service container + - Provides S3 API on port 3900 + - Admin API on port 3903 + - RPC communication on port 3901 + +2. **Init Container** (`garage-init`) + - Runs once to initialize the cluster + - Configures cluster layout + - Creates S3 access credentials + - Uses remote RPC to communicate with daemon (no shared volumes) + +### Storage + +Garage uses two separate volumes: + +- **Metadata Volume** (`garage-meta`): Stores cluster metadata and LMDB database +- **Data Volume** (`garage-data`): Stores actual object data + +## Configuration Parameters + +All configuration is done via Jsonnet parameters with the `garage-` prefix: + +### S3 Credentials + +```jsonnet +"garage-access-key":: "GK000000000000000000000001", +"garage-secret-key":: "b171f00be9be4c32c734f4c05fe64c527a8ab5eb823b376cfa8c2531f70fc427", +``` + +**Format Requirements:** +- **Access Key ID**: Must start with `GK` followed by exactly 24 hex characters (0-9, a-f) +- **Secret Key**: Must be exactly 64 hex characters + +**Generate Secure Credentials:** + +```bash +# Generate Access Key ID (GK + 24 hex chars) +echo "GK$(openssl rand -hex 12)" + +# Generate Secret Key (64 hex chars) +openssl rand -hex 32 +``` + +### Cluster Configuration + +```jsonnet +"garage-rpc-secret":: "bbba746a9e289bad64a9e7a36a4299dac8d6e0b8cc2a6c2937fe756df4492008", +"garage-admin-token":: "batts-rockhearted-unpartially", +"garage-region":: "garage", +"garage-replication-factor":: "1", +``` + +- **rpc-secret**: 64 hex characters for node-to-node RPC authentication +- **admin-token**: Bearer token for Admin API access +- **region**: S3 region name +- **replication-factor**: Number of data replicas (set to 1 for single-node, 3+ for production) + +### Storage Volumes + +```jsonnet +"garage-meta-size":: "5G", +"garage-data-size":: "100G", +``` + +Both values can be overridden using the `.with()` function: + +```jsonnet +.with("meta-size", "10G") +.with("data-size", "500G") +``` + +## Integration with Librarian + +The librarian component automatically connects to Garage for object storage: + +```jsonnet +"--object-store-endpoint", url.object_store, +"--object-store-access-key", $["garage-access-key"], +"--object-store-secret-key", $["garage-secret-key"], +``` + +The object store endpoint is defined in `values/url.jsonnet`: + +```jsonnet +object_store: "http://garage:3900", +``` + +## Testing Garage with S3 Clients + +### Using AWS CLI + +```bash +# Set credentials +export AWS_ACCESS_KEY_ID="GK000000000000000000000001" +export AWS_SECRET_ACCESS_KEY="b171f00be9be4c32c734f4c05fe64c527a8ab5eb823b376cfa8c2531f70fc427" +export AWS_ENDPOINT_URL="http://localhost:3900" +export AWS_DEFAULT_REGION="garage" + +# Create a bucket +aws s3 mb s3://test-bucket + +# Upload a file +echo "Hello from Garage!" > test.txt +aws s3 cp test.txt s3://test-bucket/ + +# List files +aws s3 ls s3://test-bucket/ + +# Download a file +aws s3 cp s3://test-bucket/test.txt downloaded.txt + +# Verify +cat downloaded.txt +``` + +### Using s3cmd + +Create configuration file `~/.s3cfg`: + +```ini +[default] +access_key = GK000000000000000000000001 +secret_key = b171f00be9be4c32c734f4c05fe64c527a8ab5eb823b376cfa8c2531f70fc427 +host_base = localhost:3900 +host_bucket = localhost:3900 +use_https = False +``` + +Then use s3cmd: + +```bash +# List buckets +s3cmd ls + +# Create bucket +s3cmd mb s3://my-bucket + +# Upload file +s3cmd put file.txt s3://my-bucket/ + +# Download file +s3cmd get s3://my-bucket/file.txt +``` + +## Deployment Details + +### Initialization Process + +The init container performs these steps: + +1. **Wait for Garage daemon** - Polls `/health` endpoint until daemon is ready +2. **Get Node ID** - Queries `/v2/GetNodeInfo?node=self` via Admin API +3. **Configure Layout** - Assigns node to cluster with specified capacity via RPC +4. **Apply Layout** - Activates the cluster layout +5. **Import Credentials** - Creates S3 access key with provided credentials +6. **Grant Permissions** - Enables bucket creation for the key + +All operations are **idempotent** - the init container can be restarted safely and will skip already-configured items. + +### Network Architecture + +- **S3 API**: Port 3900 (HTTP) +- **RPC**: Port 3901 (internal cluster communication) +- **Web UI**: Port 3902 (optional web interface) +- **Admin API**: Port 3903 (cluster management) +- **K2V API**: Port 3904 (key-value store) + +### No Shared Volumes + +The init container communicates with the Garage daemon entirely over the network: + +- Admin API (HTTP) for status queries +- RPC (via garage CLI `-h` and `-s` flags) for cluster management + +This design works across all orchestrators (Kubernetes, Docker Compose) without requiring shared volume mounts. + +## Version Information + +Current deployment uses **Garage v2.1.0** + +- Image: `docker.io/dxflrs/garage:v2.1.0` +- Documentation: https://garagehq.deuxfleurs.fr/documentation/ + +## Troubleshooting + +### Init Container Fails + +Check logs: `podman logs ` + +Common issues: +- **403 Forbidden**: Check `garage-admin-token` is correct +- **Invalid layout version**: Cluster already initialized, init will retry +- **Node ID null**: Admin API not responding, check daemon logs + +### S3 Access Denied + +Verify credentials format: +- Access Key ID must start with `GK` + 24 hex chars +- Secret Key must be 64 hex chars +- Credentials must match what was imported during init + +### Check Garage Status + +```bash +# Query cluster status +curl -H "Authorization: Bearer " \ + http://localhost:3903/v2/GetClusterStatus + +# Check health +curl http://localhost:3903/health +``` + +## Production Considerations + +1. **Generate Secure Credentials**: Never use default credentials in production +2. **Set Replication Factor**: Use 3+ for redundancy in multi-node clusters +3. **Increase Storage Size**: Adjust `garage-data-size` based on expected usage +4. **Secure Admin Token**: Use a strong random token for `garage-admin-token` +5. **Monitor Storage**: Watch disk usage on data volume +6. **Backup Metadata**: The metadata volume contains critical cluster state + +## References + +- [Garage Documentation](https://garagehq.deuxfleurs.fr/documentation/) +- [Garage Admin API v2](https://garagehq.deuxfleurs.fr/api/garage-admin-v2.json) +- [Quick Start Guide](https://garagehq.deuxfleurs.fr/documentation/quick-start/) diff --git a/ai-context/trustgraph-templates/docs/tech-specs/tests.md b/ai-context/trustgraph-templates/docs/tech-specs/tests.md new file mode 100644 index 00000000..d695486a --- /dev/null +++ b/ai-context/trustgraph-templates/docs/tech-specs/tests.md @@ -0,0 +1,416 @@ +# Test Specification + +## Test Categories + +### Unit Tests +Test Python modules in isolation: +- **Generator** - Jsonnet template processing, import callbacks +- **Packager** - Zip file creation, configuration assembly +- **API** - Template listing, version resolution +- **CLI** - Argument parsing, error handling, exit codes + +### Integration Tests +Test full CLI workflow: +- Template compilation across version/platform/config matrix +- Output file generation (TrustGraph config + platform resources) +- Error propagation and reporting + +### Validation Tests +Verify correctness of generated outputs: +- Syntax validation (JSON/YAML parsing) +- Schema validation (structure compliance) +- Semantic validation (cross-references, consistency) +- Regression testing (golden files) + +## Test Matrix + +**Dimensions:** +- Versions: 1.6, 1.7, 1.8 +- Platforms: docker-compose, podman-compose, minikube-k8s, gcp-k8s, aks-k8s, eks-k8s, scw-k8s, ovh-k8s +- Configs: minimal.json, complex-rag.json, multi-service.json, cloud-aws.json + +**Total combinations:** 3 versions × 8 platforms × 4 configs = 96 combinations per output type = 192 tests + +## Validation Approaches + +### Syntax Validation +- **JSON**: Parse with `json.loads()`, check no exceptions +- **YAML**: Parse with `yaml.safe_load()`, check no exceptions +- **Docker Compose**: Validate with `docker-compose config` +- **Kubernetes**: Validate with `kubectl apply --dry-run=client` + +### Schema Validation +- **TrustGraph Config**: Define JSON schema, validate with `jsonschema` + - Required fields: services, modules, parameters + - Type checking for all configuration values + - Enum validation for fixed sets (llm providers, platforms) + +- **Kubernetes**: Check required fields + - apiVersion, kind, metadata present + - metadata.name, metadata.namespace defined + - spec structure matches resource kind + +- **Docker Compose**: Check required fields + - services defined with image/build + - Valid port mappings + - Valid volume definitions + +### Semantic Validation + +#### Kubernetes Resources +- **Label/Selector matching**: Deployment selectors match pod labels +- **Volume references**: volumeMounts reference defined volumes +- **Service targeting**: Service selectors match deployment labels +- **Port consistency**: containerPort matches service targetPort +- **ConfigMap/Secret references**: Referenced resources exist in manifest + +#### Docker Compose +- **Service dependencies**: depends_on references valid services +- **Volume references**: Volume names in bind mounts are defined +- **Network references**: Networks used by services are defined +- **Port conflicts**: No duplicate host port bindings +- **Environment variable references**: ${VAR} expansions are resolvable + +#### TrustGraph Config +- **Service references**: Configured services reference valid modules +- **Parameter validation**: Module parameters match schema +- **Storage consistency**: Graph/object/vector stores configured correctly +- **LLM configuration**: Valid model IDs, API configurations + +### Golden File Testing +Store reference outputs for each test case: +- **Location**: `tests/golden/{version}/{platform}/{config}/` +- **Files**: + - `tg-config.json` - Reference TrustGraph configuration + - `resources.yaml` - Reference platform resources +- **Comparison**: Use `pytest-golden` for automatic diff generation +- **Updates**: Explicit flag to regenerate golden files when intentional changes occur + +## Test Cases + +### Compilation Tests +For each (version, platform, config) combination: +1. Run `tg-build-deployment -t {version} -p {platform} -i {config} -O` +2. Assert exit code = 0 +3. Assert stdout contains valid JSON +4. Parse and validate TrustGraph config structure + +5. Run `tg-build-deployment -t {version} -p {platform} -i {config} -R` +6. Assert exit code = 0 +7. Assert stdout contains valid YAML +8. Parse and validate resource manifest structure + +### Error Handling Tests +- **Invalid config**: Malformed JSON input → exit code 1, error to stderr +- **Missing file**: Non-existent config file → exit code 1, error to stderr +- **Invalid template**: Non-existent version → exit code 1, error to stderr +- **Invalid platform**: Non-existent platform → exit code 1, error to stderr +- **Template errors**: Jsonnet compilation errors → exit code 1, error to stderr + +### CLI Interface Tests +- **Argument parsing**: Valid/invalid argument combinations +- **Help output**: `-h` flag displays usage +- **Version display**: Version flag shows package version +- **Output modes**: `-O` and `-R` flags produce correct output types +- **Default values**: Missing optional args use documented defaults + +### Module Unit Tests + +#### Generator +- `process(config)` - Valid jsonnet → parsed JSON +- `process(config)` - Invalid jsonnet → raises exception +- Import callback mechanism works correctly +- Template loading from package resources + +#### Packager +- `write(config, output)` - Creates valid zip file +- `write_tg_config(config)` - Outputs TrustGraph config to stdout +- `write_resources(config)` - Outputs platform resources to stdout +- Template version resolution (--latest, --latest-stable) +- Platform-specific template selection + +## Test Execution Methods + +### Direct Function Call (Primary Method) +Most tests call the Python entry point function directly rather than invoking the subprocess: + +```python +from trustgraph_configurator import run +import sys +import json + +def test_basic_compilation(monkeypatch, capsys): + """Test compilation by calling run() directly""" + # Mock sys.argv with CLI arguments + monkeypatch.setattr(sys, 'argv', [ + 'tg-build-deployment', + '-t', '1.8', + '-p', 'docker-compose', + '-i', 'tests/configs/minimal.json', + '-O' + ]) + + # Call the entry point directly + run.run() + + # Capture and validate output + captured = capsys.readouterr() + config = json.loads(captured.out) + assert 'services' in config +``` + +**Advantages:** +- **Fast**: No subprocess overhead (100x+ faster) +- **Easy stdout/stderr capture**: Use pytest's `capsys` fixture +- **Easy mocking**: Use `monkeypatch` for arguments, environment, file system +- **Better debugging**: Direct code path, breakpoints work naturally +- **Exit code testing**: Catch `SystemExit` exception to verify exit codes + +```python +def test_error_handling(monkeypatch): + """Test that errors exit with code 1""" + monkeypatch.setattr(sys, 'argv', [ + 'tg-build-deployment', + '-i', 'nonexistent.json' + ]) + + with pytest.raises(SystemExit) as exc_info: + run.run() + + assert exc_info.value.code == 1 +``` + +### Subprocess Invocation (Smoke Tests) +A small number of tests (1-2) should invoke the actual CLI executable to verify installation: + +```python +import subprocess + +def test_cli_executable_installed(): + """Verify the installed CLI entry point works""" + result = subprocess.run( + ['tg-build-deployment', '--help'], + capture_output=True, + text=True + ) + assert result.returncode == 0 + assert 'usage:' in result.stdout + +def test_cli_version_command(): + """Verify version command works from CLI""" + result = subprocess.run( + ['tg-build-deployment', '--version'], + capture_output=True, + text=True + ) + assert result.returncode == 0 +``` + +**Purpose:** +- Verify `pyproject.toml` entry point configuration is correct +- Verify CLI is accessible in PATH after installation +- End-to-end smoke test + +**Limitations:** +- Slower (subprocess overhead) +- Harder to mock/patch +- Less detailed error information + +### Fixture for Direct Execution +Create a reusable fixture for calling configurator: + +```python +# tests/conftest.py +import pytest +import sys +from io import StringIO + +@pytest.fixture +def run_configurator(monkeypatch, capsys): + """Fixture to run configurator with given arguments""" + def _run(args): + """ + Run configurator with args list. + Returns (stdout, stderr, exit_code) + """ + from trustgraph_configurator import run + + monkeypatch.setattr(sys, 'argv', ['tg-build-deployment'] + args) + + exit_code = 0 + try: + run.run() + except SystemExit as e: + exit_code = e.code or 0 + + captured = capsys.readouterr() + return captured.out, captured.err, exit_code + + return _run +``` + +**Usage:** +```python +def test_with_fixture(run_configurator): + stdout, stderr, code = run_configurator([ + '-t', '1.8', + '-p', 'docker-compose', + '-i', 'tests/configs/minimal.json', + '-O' + ]) + assert code == 0 + assert json.loads(stdout) +``` + +## Test Infrastructure + +### Pytest Configuration +```toml +[tool.pytest.ini_options] +testpaths = ["tests"] +python_files = ["test_*.py"] +python_classes = ["Test*"] +python_functions = ["test_*"] +addopts = [ + "-v", + "--strict-markers", + "--cov=trustgraph_configurator", + "--cov-report=term-missing", + "--cov-report=html", +] +markers = [ + "unit: Unit tests", + "integration: Integration tests", + "validation: Output validation tests", + "slow: Slow-running tests", +] +``` + +### Fixtures (`tests/conftest.py`) +- `test_config_dir` - Path to tests/configs/ +- `test_configs` - Dict of loaded test configurations +- `temp_output_dir` - Temporary directory for test outputs +- `run_configurator` - Function to execute configurator CLI +- `golden_dir` - Path to golden file directory for test case + +### Parametrization +Use `pytest.mark.parametrize` for matrix testing: +```python +@pytest.mark.parametrize("version", ["1.6", "1.7", "1.8"]) +@pytest.mark.parametrize("platform", ["docker-compose", "minikube-k8s", ...]) +@pytest.mark.parametrize("config", ["minimal.json", "complex-rag.json", ...]) +def test_compilation(version, platform, config, run_configurator): + ... +``` + +### Parallel Execution +Use pytest-xdist for parallel test execution: +```bash +pytest -n auto # Use all CPU cores +``` + +## Test File Organization + +``` +tests/ +├── conftest.py # Shared fixtures +├── unit/ +│ ├── test_generator.py +│ ├── test_packager.py +│ ├── test_api.py +│ └── test_run.py +├── integration/ +│ ├── test_compilation.py # Template compilation matrix +│ ├── test_cli.py # CLI interface tests +│ └── test_errors.py # Error handling tests +├── validation/ +│ ├── test_syntax.py # Syntax validation +│ ├── test_schema.py # Schema validation +│ ├── test_semantics_k8s.py +│ ├── test_semantics_docker.py +│ └── test_semantics_tg.py +├── configs/ # Test input configs (existing) +├── schemas/ # JSON schemas for validation +│ ├── trustgraph-config.schema.json +│ ├── kubernetes-deployment.schema.json +│ └── docker-compose.schema.json +├── golden/ # Reference outputs +│ └── {version}/{platform}/{config}/ +│ ├── tg-config.json +│ └── resources.yaml +└── validators/ # Validation helper modules + ├── kubernetes.py + ├── docker_compose.py + └── trustgraph.py +``` + +## Development Dependencies + +Add to pyproject.toml: +```toml +[project.optional-dependencies] +dev = [ + "pytest>=7.0", + "pytest-xdist>=3.0", # Parallel execution + "pytest-cov>=4.0", # Coverage reporting + "pytest-golden>=0.2", # Golden file testing + "jsonschema>=4.0", # Schema validation + "pyyaml>=6.0", # Already in main deps +] +``` + +Install for development: +```bash +pip install -e .[dev] +``` + +## CI/CD Integration + +Update `.github/workflows/pull-request.yaml`: +```yaml +- name: Install dependencies + run: | + python3 -m venv env + . env/bin/activate + pip install -e .[dev] + +- name: Run tests + run: | + . env/bin/activate + pytest -n auto --cov --cov-report=xml + +- name: Upload coverage + uses: codecov/codecov-action@v3 + with: + file: ./coverage.xml +``` + +## Running Tests + +```bash +# All tests +pytest + +# Specific category +pytest tests/unit/ +pytest tests/integration/ +pytest -m validation + +# Specific test file +pytest tests/unit/test_generator.py + +# Parallel execution +pytest -n auto + +# With coverage +pytest --cov=trustgraph_configurator --cov-report=html + +# Update golden files +pytest --update-golden + +# Verbose output +pytest -v + +# Stop on first failure +pytest -x +``` diff --git a/ai-context/trustgraph-templates/docs/templates-structure.md b/ai-context/trustgraph-templates/docs/templates-structure.md new file mode 100644 index 00000000..95bb04b8 --- /dev/null +++ b/ai-context/trustgraph-templates/docs/templates-structure.md @@ -0,0 +1,188 @@ +# How TrustGraph Templates Are Structured + +## Overview + +The TrustGraph template system is a Jsonnet-based configuration framework that generates deployment configurations for multiple platforms (Docker Compose, Kubernetes, etc.) from a single JSON configuration file. The system uses a component-based architecture with abstraction layers for different deployment targets. + +## Directory Structure + +``` +trustgraph_configurator/ +├── packager.py # Main entry point +├── generator.py # Jsonnet processor wrapper +├── templates/ +│ └── 1.3/ # Template version +│ ├── components.jsonnet # Component registry +│ ├── config-to-docker-compose.jsonnet # Docker Compose generator +│ ├── config-to-tg-configuration.jsonnet # TrustGraph config extractor +│ ├── components/ # Component definitions +│ ├── engine/ # Platform-specific engines +│ ├── util/ # Utility functions +│ ├── prompts/ # LLM prompt templates +│ └── values/ # Shared configuration values +└── resources/ + └── 1.3/ # Static resource files + ├── grafana/ # Grafana dashboards/configs + └── prometheus/ # Prometheus configs +``` + +## Core Concepts + +### 1. Components + +Components are the building blocks of the system. Each component: +- Defines configuration parameters with defaults +- Implements a `create` function that generates platform-specific resources +- Can compose with other components through Jsonnet's object composition (`+`) +- Lives in `components/` directory + +Example component structure (simplified `ollama.jsonnet`): +```jsonnet +{ + // Parameter with default value + "ollama-model":: "gemma2:9b", + + // Service definition + "text-completion" +: { + create:: function(engine) + // Use engine abstraction to create resources + local container = engine.container("text-completion") + .with_image(...) + .with_command([...]); + + engine.resources([container, ...]) + }, + + // Custom parameter setter + with:: function(key, value) + self + { ["ollama-" + key]:: value } +} +``` + +### 2. Engines + +Engines provide platform-specific implementations for resource creation. Each engine implements: +- `container()` - Create container definitions +- `service()` - Create service definitions +- `volume()` - Create volume definitions +- `resources()` - Aggregate resources for output + +The engine abstraction allows components to be platform-agnostic. Available engines: +- `docker-compose.jsonnet` - Docker Compose format +- `noop.jsonnet` - No-op engine for configuration extraction only +- Kubernetes engines (various cloud providers) + +### 3. Configuration Flow + +``` +config.json → decode → patterns → engine.create() → resources → output +``` + +1. **Input**: `config.json` contains a list of components to enable: +```json +[ + {"name": "trustgraph-base", "parameters": {}}, + {"name": "ollama", "parameters": {"model": "mixtral"}} +] +``` + +2. **Decode**: The `decode-config.jsonnet` utility: + - Loads each component from the registry + - Applies parameters using the `with_params` function + - Merges all components into a single "patterns" object + +3. **Engine Processing**: Platform-specific files like `config-to-docker-compose.jsonnet`: + - Call each component's `create(engine)` function + - Fold results into final resource structure + - Output platform-specific configuration + +### 4. Configuration Extraction + +The `config-to-tg-configuration.jsonnet` file extracts the TrustGraph runtime configuration from components. This includes: +- Prompt templates +- Flow definitions +- Model token costs +- Agent tools +- MCP server configurations + +This configuration is embedded into the deployment separately from the infrastructure resources. + +## Processing Pipeline + +### Entry Point: `packager.py` + +The Packager class orchestrates the entire process: + +1. **Template Selection**: Determines version and template based on user input +2. **Resource Generation**: + - For Docker Compose: Calls `config-to-docker-compose.jsonnet` + - For Kubernetes: Calls `config-to--k8s.jsonnet` +3. **Configuration Generation**: Calls `config-to-tg-configuration.jsonnet` for runtime config +4. **Packaging**: Creates ZIP file with all generated files and static resources + +### Jsonnet Processing: `generator.py` + +Simple wrapper around the Jsonnet library that: +- Evaluates Jsonnet templates +- Provides custom import callback for file resolution +- Returns parsed JSON output + +## Component Composition + +Components can be composed in several ways: + +### 1. Base Component Extension +Many components extend `trustgraph-base` which provides core services: +```jsonnet +local trustgraph = import "components/trustgraph.jsonnet"; +// Inherits all trustgraph services +{} + trustgraph + myCustomizations +``` + +### 2. Field Merging +Components use `+:` to merge with existing fields: +```jsonnet +"text-completion" +: { + // Adds to existing text-completion definition + create:: function(engine) ... +} +``` + +### 3. Parameter Injection +The `with` pattern allows runtime parameter injection: +```jsonnet +with:: function(key, value) + self + { [key]:: value } +``` + +## Hidden Fields and Configuration + +Jsonnet's `::` operator creates hidden fields that aren't included in JSON output by default. The template system uses this for: +- Default values that can be overridden +- Internal helper functions +- Configuration that needs special extraction + +For example, `trustgraph-base` has a hidden `configuration::` field containing runtime config that's extracted separately by `config-to-tg-configuration.jsonnet`. + +## Platform Abstraction + +The engine pattern provides clean separation between: +- **Component logic** - What services/containers to create +- **Platform specifics** - How to represent them (Docker Compose YAML, K8s manifests, etc.) + +This allows the same component definitions to generate configurations for multiple platforms without modification. + +## Static Resources + +Files in `resources/` are copied directly to the output package. These include: +- Grafana dashboard definitions +- Prometheus configuration +- Other platform-specific configs that don't need templating + +## Best Practices + +1. **Component Independence**: Components should be self-contained and not depend on specific ordering +2. **Parameter Namespacing**: Use prefixes (e.g., `ollama-model`) to avoid conflicts +3. **Hidden Fields for Defaults**: Use `::` for overridable defaults +4. **Engine Abstraction**: Always use engine methods rather than creating platform-specific structures directly +5. **Composition Over Inheritance**: Use Jsonnet's object composition (`+`) rather than complex inheritance hierarchies \ No newline at end of file diff --git a/ai-context/trustgraph-templates/examples/intel-battlemage-vllm.json b/ai-context/trustgraph-templates/examples/intel-battlemage-vllm.json new file mode 100644 index 00000000..fd7f8a6c --- /dev/null +++ b/ai-context/trustgraph-templates/examples/intel-battlemage-vllm.json @@ -0,0 +1,59 @@ +[ + { + "name": "triple-store-cassandra", + "parameters": {} + }, + { + "name": "object-store-cassandra", + "parameters": {} + }, + { + "name": "pulsar", + "parameters": {} + }, + { + "name": "vector-store-qdrant", + "parameters": {} + }, + { + "name": "grafana", + "parameters": {} + }, + { + "name": "trustgraph-base", + "parameters": { + "text-completion-concurrency": 16, + "text-completion-rag-concurrency": 16, + "prompt-concurrency": 16, + "prompt-rag-concurrency": 16, + "kg-extraction-concurrency": 16, + "embeddings-concurrency": 16, + } + }, + { + "name": "override-recursive-chunker", + "parameters": { + "chunk-size": 2000, + "chunk-overlap": 100 + } + }, + { + "name": "embeddings-fastembed", + "parameters": {} + }, + { + "name": "vllm", + "parameters": { + "max-output-tokens": 8192 + } + }, + { + "name": "hosting-intel-battlemage-vllm", + "parameters": { + "model": "mistralai/Mistral-Nemo-Instruct-2407", + "cpus": "32.0", + "memory": "48G", + "hf-token": "", + } + }, +] diff --git a/ai-context/trustgraph-templates/pulumi/Pulumi.prod.yaml b/ai-context/trustgraph-templates/pulumi/Pulumi.prod.yaml new file mode 100644 index 00000000..cc14f03c --- /dev/null +++ b/ai-context/trustgraph-templates/pulumi/Pulumi.prod.yaml @@ -0,0 +1,14 @@ +encryptionsalt: v1:vQGk98eEeYI=:v1:tHg+f1b66tEydgA9:J1RGVNI0FssyjSXVhcKU7bfBofNFTg== +config: + config-svc:artifact-name: config-svc + config-svc:artifact-repo: europe-west1-docker.pkg.dev/trustgraph-ai/config-svc + config-svc:artifact-repo-region: europe-west1 + config-svc:cloud-run-region: europe-west1 + config-svc:domain: app.trustgraph.ai + config-svc:environment: prod + config-svc:gcp-project: trustgraph-ai + config-svc:gcp-region: europe-west1 + config-svc:hostname: config-svc.app.trustgraph.ai + config-svc:managed-zone: app + config-svc:max-scale: "1" + config-svc:min-scale: "0" diff --git a/ai-context/trustgraph-templates/pulumi/Pulumi.yaml b/ai-context/trustgraph-templates/pulumi/Pulumi.yaml new file mode 100644 index 00000000..9898a5b1 --- /dev/null +++ b/ai-context/trustgraph-templates/pulumi/Pulumi.yaml @@ -0,0 +1,3 @@ +name: config-svc +runtime: nodejs +description: Config service diff --git a/ai-context/trustgraph-templates/pulumi/index.ts b/ai-context/trustgraph-templates/pulumi/index.ts new file mode 100644 index 00000000..394dfd68 --- /dev/null +++ b/ai-context/trustgraph-templates/pulumi/index.ts @@ -0,0 +1,323 @@ + +import * as pulumi from "@pulumi/pulumi"; +import * as gcp from "@pulumi/gcp"; +import { local } from "@pulumi/command"; +import * as fs from 'fs'; + +const cfg = new pulumi.Config(); + +function get(tag : string) { + + let val = cfg.get(tag); + + if (!val) { + console.log("ERROR: The '" + tag + "' config is mandatory"); + throw "The '" + tag + "' config is mandatory"; + } + + return val; + +} + +const imageVersion = process.env.IMAGE_VERSION; +if (!imageVersion) + throw Error("IMAGE_VERSION not defined"); + +const repo = get("artifact-repo"); +const artifactRepoRegion = get("artifact-repo-region"); +const artifactName = get("artifact-name"); +const hostname = get("hostname"); +const managedZone = get("managed-zone"); +const project = get("gcp-project"); +const region = get("gcp-region"); +const cloudRunRegion = get("cloud-run-region"); +const environment = get("environment"); +const domain = get("domain"); +const minScale = get("min-scale"); +const maxScale = get("max-scale"); + +const provider = new gcp.Provider( + "gcp", + { + project: project, + region: region, + } +); + +const artifactRepo = new gcp.artifactregistry.Repository( + "artifact-repo", + { + description: "repository for " + environment, + format: "DOCKER", + location: artifactRepoRegion, + repositoryId: artifactName, + cleanupPolicies: [ + { + id: "keep-minimum-versions", + action: "KEEP", + mostRecentVersions: { + keepCount: 5, + }, + } + ], + }, + { + provider: provider, + } +); + +const localImageName = "localhost/config-svc:" + imageVersion; + +const imageName = repo + "/config-svc:" + imageVersion; + +const taggedImage = new local.Command( + "podman-tag-command", + { + create: "podman tag " + localImageName + " " + imageName, + } +); + +const image = new local.Command( + "podman-push-command", + { + create: "podman push " + imageName, + }, + { + dependsOn: [taggedImage, artifactRepo], + } +); + +const svcAccount = new gcp.serviceaccount.Account( + "service-account", + { + accountId: "config-svc-" + environment, + displayName: "Config service", + description: "Config service", + }, + { + provider: provider, + } +); + +const service = new gcp.cloudrun.Service( + "service", + { + name: "config-svc-" + environment, + location: cloudRunRegion, + template: { + metadata: { + labels: { + version: "v" + imageVersion.replace(/\./g, "-"), + }, + annotations: { + + // Scale attributes + "autoscaling.knative.dev/minScale": minScale, + "autoscaling.knative.dev/maxScale": maxScale, + + // 2nd generation. Need to specify at least 512MB RAM. + // Going back to gen1 because faster cold starts + "run.googleapis.com/execution-environment": "gen1", + + } + }, + spec: { + containerConcurrency: 100, + timeoutSeconds: 300, + serviceAccountName: svcAccount.email, + containers: [ + { + image: imageName, + ports: [ + { + "name": "http1", // Must be http1 or h2c. + "containerPort": 8080, + } + ], + resources: { + limits: { + cpu: "1000m", + memory: "512Mi", + } + }, + } + ], + }, + }, + }, + { + provider: provider, + dependsOn: [image], + } +); + +const allUsersPolicy = gcp.organizations.getIAMPolicy( + { + bindings: [{ + role: "roles/run.invoker", + members: ["allUsers"], + }], + }, + { + provider: provider, + } +); + +const noAuthPolicy = new gcp.cloudrun.IamPolicy( + "no-auth-policy", + { + location: service.location, + project: service.project, + service: service.name, + policyData: allUsersPolicy.then(pol => pol.policyData), + }, + { + provider: provider, + } +); + +//////////////////////////////////////////////////////////////////////////// + +const domainMapping = new gcp.cloudrun.DomainMapping( + "domain-mapping", + { + name: hostname, + location: cloudRunRegion, + metadata: { + namespace: project, + }, + spec: { + routeName: service.name, + } + }, + { + provider: provider + } +); + +const zone = gcp.dns.getManagedZoneOutput( + { + name: managedZone, + }, + { + provider: provider, + } +); + +//////////////////////////////////////////////////////////////////////////// + +domainMapping.statuses.apply( + ss => ss[0].resourceRecords +).apply( + rrs => { + if (rrs) { + + let mapping : { [k : string] : string[] } = {}; + + for(var i = 0; i < rrs.length; i++) { + if (rrs[i].rrdata) { + + const rr = rrs[i].rrdata; + const tp = rrs[i].type; + + if (!rr || !tp) continue; + + if (mapping[tp]) + mapping[tp].push(rr); + else + mapping[tp] = [rr]; + + } + } + + for (let tp in mapping) { + + const recordSet = new gcp.dns.RecordSet( + "resource-record-" + tp, + { + name: hostname + ".", + managedZone: zone.name, + type: tp, + ttl: 300, + rrdatas: mapping[tp], + }, + { + provider: provider, + } + ); + + } + + } + + } +); + +//////////////////////////////////////////////////////////////////////////// + +const serviceMon = new gcp.monitoring.GenericService( + "service-monitoring", + { + basicService: { + serviceLabels: { + service_name: service.name, + location: cloudRunRegion, + }, + serviceType: "CLOUD_RUN", + }, + displayName: "Config service (" + environment + ")", + serviceId: "config-service-" + environment + "-mon", + userLabels: { + "service": service.name, + "application": "config-svc", + "environment": environment, + }, + }, + { + provider: provider, + } +); + +const latencySlo = new gcp.monitoring.Slo( + "latency-slo", + { + service: serviceMon.serviceId, + sloId: "config-service-" + environment + "-latency-slo", + displayName: "Config service latency (" + environment + ")", + goal: 0.95, + rollingPeriodDays: 5, + basicSli: { + latency: { + threshold: "2s" + } + }, + }, + { + provider: provider, + } +); + +const availabilitySlo = new gcp.monitoring.Slo( + "availability-slo", + { + service: serviceMon.serviceId, + sloId: "config-service-" + environment + "-availability-slo", + displayName: "Config service availability (" + environment + ")", + goal: 0.95, + rollingPeriodDays: 5, + windowsBasedSli: { + windowPeriod: "3600s", + goodTotalRatioThreshold: { + basicSliPerformance: { + availability: { + } + }, + threshold: 0.9, + } + } + }, + { + provider: provider, + } +); + diff --git a/ai-context/trustgraph-templates/pulumi/package-lock.json b/ai-context/trustgraph-templates/pulumi/package-lock.json new file mode 100644 index 00000000..6cd217dd --- /dev/null +++ b/ai-context/trustgraph-templates/pulumi/package-lock.json @@ -0,0 +1,4695 @@ +{ + "name": "pulumi", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "pulumi", + "dependencies": { + "@pulumi/command": "^1.1.3", + "@pulumi/gcp": "^9.10.0", + "@pulumi/pulumi": "^3.216.0" + } + }, + "node_modules/@gar/promise-retry": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@gar/promise-retry/-/promise-retry-1.0.2.tgz", + "integrity": "sha512-Lm/ZLhDZcBECta3TmCQSngiQykFdfw+QtI1/GYMsZd4l3nG+P8WLB16XuS7WaBGLQ+9E+cOcWQsth9cayuGt8g==", + "license": "MIT", + "dependencies": { + "retry": "^0.13.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.3.tgz", + "integrity": "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.8.0", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.0.tgz", + "integrity": "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==", + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.5.3", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@isaacs/string-locale-compare": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz", + "integrity": "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==", + "license": "ISC" + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@logdna/tail-file": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@logdna/tail-file/-/tail-file-2.2.0.tgz", + "integrity": "sha512-XGSsWDweP80Fks16lwkAUIr54ICyBs6PsI4mpfTLQaWgEJRtY9xEV+PeyDpJ+sJEGZxqINlpmAwe/6tS1pP8Ng==", + "license": "SEE LICENSE IN LICENSE", + "engines": { + "node": ">=10.3.0" + } + }, + "node_modules/@npmcli/agent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-4.0.0.tgz", + "integrity": "sha512-kAQTcEN9E8ERLVg5AsGwLNoFb+oEG6engbqAU2P43gD4JEIkNGMHdVQ096FsOAAYpZPB0RSt0zgInKIAS1l5QA==", + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^11.2.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/agent/node_modules/lru-cache": { + "version": "11.2.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", + "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@npmcli/arborist": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-9.4.0.tgz", + "integrity": "sha512-4Bm8hNixJG/sii1PMnag0V9i/sGOX9VRzFrUiZMSBJpGlLR38f+Btl85d07G9GL56xO0l0OZjvrGNYsDYp0xKA==", + "license": "ISC", + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/fs": "^5.0.0", + "@npmcli/installed-package-contents": "^4.0.0", + "@npmcli/map-workspaces": "^5.0.0", + "@npmcli/metavuln-calculator": "^9.0.2", + "@npmcli/name-from-folder": "^4.0.0", + "@npmcli/node-gyp": "^5.0.0", + "@npmcli/package-json": "^7.0.0", + "@npmcli/query": "^5.0.0", + "@npmcli/redact": "^4.0.0", + "@npmcli/run-script": "^10.0.0", + "bin-links": "^6.0.0", + "cacache": "^20.0.1", + "common-ancestor-path": "^2.0.0", + "hosted-git-info": "^9.0.0", + "json-stringify-nice": "^1.1.4", + "lru-cache": "^11.2.1", + "minimatch": "^10.0.3", + "nopt": "^9.0.0", + "npm-install-checks": "^8.0.0", + "npm-package-arg": "^13.0.0", + "npm-pick-manifest": "^11.0.1", + "npm-registry-fetch": "^19.0.0", + "pacote": "^21.0.2", + "parse-conflict-json": "^5.0.1", + "proc-log": "^6.0.0", + "proggy": "^4.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^3.0.1", + "semver": "^7.3.7", + "ssri": "^13.0.0", + "treeverse": "^3.0.0", + "walk-up-path": "^4.0.0" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/@npmcli/git": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-7.0.2.tgz", + "integrity": "sha512-oeolHDjExNAJAnlYP2qzNjMX/Xi9bmu78C9dIGr4xjobrSKbuMYCph8lTzn4vnW3NjIqVmw/f8BCfouqyJXlRg==", + "license": "ISC", + "dependencies": { + "@gar/promise-retry": "^1.0.0", + "@npmcli/promise-spawn": "^9.0.0", + "ini": "^6.0.0", + "lru-cache": "^11.2.1", + "npm-pick-manifest": "^11.0.1", + "proc-log": "^6.0.0", + "semver": "^7.3.5", + "which": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/@npmcli/package-json": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-7.0.5.tgz", + "integrity": "sha512-iVuTlG3ORq2iaVa1IWUxAO/jIp77tUKBhoMjuzYW2kL4MLN1bi/ofqkZ7D7OOwh8coAx1/S2ge0rMdGv8sLSOQ==", + "license": "ISC", + "dependencies": { + "@npmcli/git": "^7.0.0", + "glob": "^13.0.0", + "hosted-git-info": "^9.0.0", + "json-parse-even-better-errors": "^5.0.0", + "proc-log": "^6.0.0", + "semver": "^7.5.3", + "spdx-expression-parse": "^4.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/@npmcli/promise-spawn": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-9.0.1.tgz", + "integrity": "sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q==", + "license": "ISC", + "dependencies": { + "which": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/arborist/node_modules/hosted-git-info": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz", + "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==", + "license": "ISC", + "dependencies": { + "lru-cache": "^11.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/ini": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", + "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=20" + } + }, + "node_modules/@npmcli/arborist/node_modules/json-parse-even-better-errors": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-5.0.0.tgz", + "integrity": "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ==", + "license": "MIT", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/lru-cache": { + "version": "11.2.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", + "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@npmcli/arborist/node_modules/npm-pick-manifest": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-11.0.3.tgz", + "integrity": "sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ==", + "license": "ISC", + "dependencies": { + "npm-install-checks": "^8.0.0", + "npm-normalize-package-bin": "^5.0.0", + "npm-package-arg": "^13.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/arborist/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/spdx-expression-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", + "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "license": "ISC", + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/fs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-5.0.0.tgz", + "integrity": "sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og==", + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/git": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-6.0.3.tgz", + "integrity": "sha512-GUYESQlxZRAdhs3UhbB6pVRNUELQOHXwK9ruDkwmCv2aZ5y0SApQzUJCg02p3A7Ue2J5hxvlk1YI53c00NmRyQ==", + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^8.0.0", + "ini": "^5.0.0", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^10.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@npmcli/installed-package-contents": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-4.0.0.tgz", + "integrity": "sha512-yNyAdkBxB72gtZ4GrwXCM0ZUedo9nIbOMKfGjt6Cu6DXf0p8y1PViZAKDC8q8kv/fufx0WTjRBdSlyrvnP7hmA==", + "license": "ISC", + "dependencies": { + "npm-bundled": "^5.0.0", + "npm-normalize-package-bin": "^5.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/map-workspaces": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/map-workspaces/-/map-workspaces-5.0.3.tgz", + "integrity": "sha512-o2grssXo1e774E5OtEwwrgoszYRh0lqkJH+Pb9r78UcqdGJRDRfhpM8DvZPjzNLLNYeD/rNbjOKM3Ss5UABROw==", + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^4.0.0", + "@npmcli/package-json": "^7.0.0", + "glob": "^13.0.0", + "minimatch": "^10.0.3" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/map-workspaces/node_modules/@npmcli/git": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-7.0.2.tgz", + "integrity": "sha512-oeolHDjExNAJAnlYP2qzNjMX/Xi9bmu78C9dIGr4xjobrSKbuMYCph8lTzn4vnW3NjIqVmw/f8BCfouqyJXlRg==", + "license": "ISC", + "dependencies": { + "@gar/promise-retry": "^1.0.0", + "@npmcli/promise-spawn": "^9.0.0", + "ini": "^6.0.0", + "lru-cache": "^11.2.1", + "npm-pick-manifest": "^11.0.1", + "proc-log": "^6.0.0", + "semver": "^7.3.5", + "which": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/map-workspaces/node_modules/@npmcli/package-json": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-7.0.5.tgz", + "integrity": "sha512-iVuTlG3ORq2iaVa1IWUxAO/jIp77tUKBhoMjuzYW2kL4MLN1bi/ofqkZ7D7OOwh8coAx1/S2ge0rMdGv8sLSOQ==", + "license": "ISC", + "dependencies": { + "@npmcli/git": "^7.0.0", + "glob": "^13.0.0", + "hosted-git-info": "^9.0.0", + "json-parse-even-better-errors": "^5.0.0", + "proc-log": "^6.0.0", + "semver": "^7.5.3", + "spdx-expression-parse": "^4.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/map-workspaces/node_modules/@npmcli/promise-spawn": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-9.0.1.tgz", + "integrity": "sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q==", + "license": "ISC", + "dependencies": { + "which": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/map-workspaces/node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/map-workspaces/node_modules/hosted-git-info": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz", + "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==", + "license": "ISC", + "dependencies": { + "lru-cache": "^11.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/map-workspaces/node_modules/ini": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", + "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/map-workspaces/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=20" + } + }, + "node_modules/@npmcli/map-workspaces/node_modules/json-parse-even-better-errors": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-5.0.0.tgz", + "integrity": "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ==", + "license": "MIT", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/map-workspaces/node_modules/lru-cache": { + "version": "11.2.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", + "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@npmcli/map-workspaces/node_modules/npm-pick-manifest": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-11.0.3.tgz", + "integrity": "sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ==", + "license": "ISC", + "dependencies": { + "npm-install-checks": "^8.0.0", + "npm-normalize-package-bin": "^5.0.0", + "npm-package-arg": "^13.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/map-workspaces/node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/map-workspaces/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/map-workspaces/node_modules/spdx-expression-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", + "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/@npmcli/map-workspaces/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "license": "ISC", + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/metavuln-calculator": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/metavuln-calculator/-/metavuln-calculator-9.0.3.tgz", + "integrity": "sha512-94GLSYhLXF2t2LAC7pDwLaM4uCARzxShyAQKsirmlNcpidH89VA4/+K1LbJmRMgz5gy65E/QBBWQdUvGLe2Frg==", + "license": "ISC", + "dependencies": { + "cacache": "^20.0.0", + "json-parse-even-better-errors": "^5.0.0", + "pacote": "^21.0.0", + "proc-log": "^6.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/json-parse-even-better-errors": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-5.0.0.tgz", + "integrity": "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ==", + "license": "MIT", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/name-from-folder": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/name-from-folder/-/name-from-folder-4.0.0.tgz", + "integrity": "sha512-qfrhVlOSqmKM8i6rkNdZzABj8MKEITGFAY+4teqBziksCQAOLutiAxM1wY2BKEd8KjUSpWmWCYxvXr0y4VTlPg==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/node-gyp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-5.0.0.tgz", + "integrity": "sha512-uuG5HZFXLfyFKqg8QypsmgLQW7smiRjVc45bqD/ofZZcR/uxEjgQU8qDPv0s9TEeMUiAAU/GC5bR6++UdTirIQ==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/package-json": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-6.2.0.tgz", + "integrity": "sha512-rCNLSB/JzNvot0SEyXqWZ7tX2B5dD2a1br2Dp0vSYVo5jh8Z0EZ7lS9TsZ1UtziddB1UfNUaMCc538/HztnJGA==", + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", + "semver": "^7.5.3", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@npmcli/promise-spawn": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-8.0.3.tgz", + "integrity": "sha512-Yb00SWaL4F8w+K8YGhQ55+xE4RUNdMHV43WZGsiTM92gS+lC0mGsn7I4hLug7pbao035S6bj3Y3w0cUNGLfmkg==", + "license": "ISC", + "dependencies": { + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@npmcli/query": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/query/-/query-5.0.0.tgz", + "integrity": "sha512-8TZWfTQOsODpLqo9SVhVjHovmKXNpevHU0gO9e+y4V4fRIOneiXy0u0sMP9LmS71XivrEWfZWg50ReH4WRT4aQ==", + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/redact": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-4.0.0.tgz", + "integrity": "sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/run-script": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-10.0.4.tgz", + "integrity": "sha512-mGUWr1uMnf0le2TwfOZY4SFxZGXGfm4Jtay/nwAa2FLNAKXUoUwaGwBMNH36UHPtinWfTSJ3nqFQr0091CxVGg==", + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^5.0.0", + "@npmcli/package-json": "^7.0.0", + "@npmcli/promise-spawn": "^9.0.0", + "node-gyp": "^12.1.0", + "proc-log": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/@npmcli/git": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-7.0.2.tgz", + "integrity": "sha512-oeolHDjExNAJAnlYP2qzNjMX/Xi9bmu78C9dIGr4xjobrSKbuMYCph8lTzn4vnW3NjIqVmw/f8BCfouqyJXlRg==", + "license": "ISC", + "dependencies": { + "@gar/promise-retry": "^1.0.0", + "@npmcli/promise-spawn": "^9.0.0", + "ini": "^6.0.0", + "lru-cache": "^11.2.1", + "npm-pick-manifest": "^11.0.1", + "proc-log": "^6.0.0", + "semver": "^7.3.5", + "which": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/@npmcli/package-json": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-7.0.5.tgz", + "integrity": "sha512-iVuTlG3ORq2iaVa1IWUxAO/jIp77tUKBhoMjuzYW2kL4MLN1bi/ofqkZ7D7OOwh8coAx1/S2ge0rMdGv8sLSOQ==", + "license": "ISC", + "dependencies": { + "@npmcli/git": "^7.0.0", + "glob": "^13.0.0", + "hosted-git-info": "^9.0.0", + "json-parse-even-better-errors": "^5.0.0", + "proc-log": "^6.0.0", + "semver": "^7.5.3", + "spdx-expression-parse": "^4.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/@npmcli/promise-spawn": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-9.0.1.tgz", + "integrity": "sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q==", + "license": "ISC", + "dependencies": { + "which": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/run-script/node_modules/hosted-git-info": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz", + "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==", + "license": "ISC", + "dependencies": { + "lru-cache": "^11.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/ini": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", + "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=20" + } + }, + "node_modules/@npmcli/run-script/node_modules/json-parse-even-better-errors": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-5.0.0.tgz", + "integrity": "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ==", + "license": "MIT", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/lru-cache": { + "version": "11.2.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", + "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@npmcli/run-script/node_modules/npm-pick-manifest": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-11.0.3.tgz", + "integrity": "sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ==", + "license": "ISC", + "dependencies": { + "npm-install-checks": "^8.0.0", + "npm-normalize-package-bin": "^5.0.0", + "npm-package-arg": "^13.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/run-script/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/spdx-expression-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", + "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "license": "ISC", + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.55.0.tgz", + "integrity": "sha512-3cpa+qI45VHYcA5c0bHM6VHo9gicv3p5mlLHNG3rLyjQU8b7e0st1rWtrUn3JbZ3DwwCfhKop4eQ9UuYlC6Pkg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", + "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.28.0.tgz", + "integrity": "sha512-ZLwRMV+fNDpVmF2WYUdBHlq0eOWtEaUJSusrzjGnBt7iSRvfjFE3RXYUZJrqou/wIDWV0DwQ5KIfYe9WXg9Xqw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.55.0.tgz", + "integrity": "sha512-ohIkCLn2Wc3vhhFuf1bH8kOXHMEdcWiD847x7f3Qfygc+CGiatGLzQYscTcEYsWGMV22gVwB/kVcNcx5a3o8gA==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.55.0", + "@opentelemetry/otlp-transformer": "0.55.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/sdk-trace-base": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/resources": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.28.0.tgz", + "integrity": "sha512-cIyXSVJjGeTICENN40YSvLDAq4Y2502hGK3iN7tfdynQLKWb3XWZQEkPc+eSx47kiy11YeFAlYkEfXwR1w8kfw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.28.0.tgz", + "integrity": "sha512-ceUVWuCpIao7Y5xE02Xs3nQi0tOGmMea17ecBdwtCvdo9ekmO+ijc9RFDgfifMl7XCBf41zne/1POM3LqSTZDA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.30.1.tgz", + "integrity": "sha512-6S2QIMJahIquvFaaxmcwpvQQRD/YFaMTNoIxrfPIPOeITN+a8lfEcPDxNxn8JDAaxkg+4EnXhz8upVDYenoQjA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-trace-base": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.55.0.tgz", + "integrity": "sha512-YDCMlaQRZkziLL3t6TONRgmmGxDx6MyQDXRD0dknkkgUZtOK5+8MWft1OXzmNu6XfBOdT12MKN5rz+jHUkafKQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.55.0", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-grpc": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.55.0.tgz", + "integrity": "sha512-n2ZH4pRwOy0Vhag/3eKqiyDBwcpUnGgJI9iiIRX7vivE0FMncaLazWphNFezRRaM/LuKwq1TD8pVUvieP68mow==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "0.55.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.55.0.tgz", + "integrity": "sha512-iHQI0Zzq3h1T6xUJTVFwmFl5Dt5y1es+fl4kM+k5T/3YvmVyeYkSiF+wHCg6oKrlUAJfk+t55kaAu3sYmt7ZYA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/otlp-transformer": "0.55.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.55.0.tgz", + "integrity": "sha512-gebbjl9FiSp52igWXuGjcWQKfB6IBwFGt5z1VFwTcVZVeEZevB6bJIqoFrhH4A02m7OUlpJ7l4EfRi3UtkNANQ==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/otlp-exporter-base": "0.55.0", + "@opentelemetry/otlp-transformer": "0.55.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.55.0.tgz", + "integrity": "sha512-kVqEfxtp6mSN2Dhpy0REo1ghP4PYhC1kMHQJ2qVlO99Pc+aigELjZDfg7/YKmL71gR6wVGIeJfiql/eXL7sQPA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.55.0", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/sdk-logs": "0.55.0", + "@opentelemetry/sdk-metrics": "1.28.0", + "@opentelemetry/sdk-trace-base": "1.28.0", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.28.0.tgz", + "integrity": "sha512-cIyXSVJjGeTICENN40YSvLDAq4Y2502hGK3iN7tfdynQLKWb3XWZQEkPc+eSx47kiy11YeFAlYkEfXwR1w8kfw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.28.0.tgz", + "integrity": "sha512-ceUVWuCpIao7Y5xE02Xs3nQi0tOGmMea17ecBdwtCvdo9ekmO+ijc9RFDgfifMl7XCBf41zne/1POM3LqSTZDA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.30.1.tgz", + "integrity": "sha512-oATwWWDIJzybAZ4pO76ATN5N6FFbOA1otibAVlS8v90B4S1wClnhRUk7K+2CHAwN1JKYuj4jh/lpCEG5BAqFuQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/propagator-jaeger": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.30.1.tgz", + "integrity": "sha512-Pj/BfnYEKIOImirH76M4hDaBSx6HyZ2CXUqk+Kj02m6BB80c/yo4BdWkn/1gDFfU+YPY+bPR2U0DKBfdxCKwmg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/resources/node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/resources/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.55.0.tgz", + "integrity": "sha512-TSx+Yg/d48uWW6HtjS1AD5x6WPfLhDWLl/WxC7I2fMevaiBuKCuraxTB8MDXieCNnBI24bw9ytyXrDCswFfWgA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.55.0", + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.28.0.tgz", + "integrity": "sha512-cIyXSVJjGeTICENN40YSvLDAq4Y2502hGK3iN7tfdynQLKWb3XWZQEkPc+eSx47kiy11YeFAlYkEfXwR1w8kfw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.28.0.tgz", + "integrity": "sha512-43tqMK/0BcKTyOvm15/WQ3HLr0Vu/ucAl/D84NO7iSlv6O4eOprxSHa3sUtmYkaZWHqdDJV0AHVz/R6u4JALVQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/resources": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/resources": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.28.0.tgz", + "integrity": "sha512-cIyXSVJjGeTICENN40YSvLDAq4Y2502hGK3iN7tfdynQLKWb3XWZQEkPc+eSx47kiy11YeFAlYkEfXwR1w8kfw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.28.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", + "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.30.1.tgz", + "integrity": "sha512-cBjYOINt1JxXdpw1e5MlHmFRc5fgj4GW/86vsKFxJCJ8AL4PdVtYH41gWwl4qd4uQjqEL1oJVrXkSy5cnduAnQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/context-async-hooks": "1.30.1", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/propagator-b3": "1.30.1", + "@opentelemetry/propagator-jaeger": "1.30.1", + "@opentelemetry/sdk-trace-base": "1.30.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz", + "integrity": "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, + "node_modules/@pulumi/command": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@pulumi/command/-/command-1.2.1.tgz", + "integrity": "sha512-mutNDIUYP67yCBYOVIidQyxuTwZDY9v/sx9EGbgIv4PXfyfolOKGgGLeoHEbI1lxRwaw2wbTZ3VNIynDnA5VKA==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@pulumi/pulumi": "^3.142.0" + } + }, + "node_modules/@pulumi/gcp": { + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/@pulumi/gcp/-/gcp-9.14.0.tgz", + "integrity": "sha512-5QZelNAR1VkvbP9AFPCJUv2jQTgTAe0B/ed197ZucOpuL0HR/m27ggElW3RxE96qP5GXtd9KArU5SFcoVZ05yA==", + "license": "Apache-2.0", + "dependencies": { + "@npmcli/package-json": "^6.2.0", + "@pulumi/pulumi": "^3.142.0", + "@types/express": "^4.16.0" + } + }, + "node_modules/@pulumi/pulumi": { + "version": "3.225.0", + "resolved": "https://registry.npmjs.org/@pulumi/pulumi/-/pulumi-3.225.0.tgz", + "integrity": "sha512-dqlc+d7kd6srAEyLxhO/lHRj0AWSvaMYNbP2BWafXZuzqp/2zg0Ro+OPE2/dQbyJQwW3bD250DLzEU94qInlcw==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.10.1", + "@logdna/tail-file": "^2.0.6", + "@npmcli/arborist": "^9.0.0", + "@opentelemetry/api": "^1.9", + "@opentelemetry/exporter-trace-otlp-grpc": "^0.55", + "@opentelemetry/exporter-zipkin": "^1.28", + "@opentelemetry/instrumentation": "^0.55", + "@opentelemetry/instrumentation-grpc": "^0.55", + "@opentelemetry/resources": "^1.28", + "@opentelemetry/sdk-trace-base": "^1.28", + "@opentelemetry/sdk-trace-node": "^1.28", + "@types/google-protobuf": "^3.15.5", + "@types/semver": "^7.5.6", + "@types/tmp": "^0.2.6", + "execa": "^5.1.0", + "fdir": "^6.5.0", + "google-protobuf": "^3.21.4", + "got": "^11.8.6", + "ini": "^2.0.0", + "js-yaml": "^3.14.2", + "minimist": "^1.2.6", + "normalize-package-data": "^6.0.0", + "package-directory": "^8.1.0", + "picomatch": "^3.0.1", + "require-from-string": "^2.0.1", + "semver": "^7.5.2", + "source-map-support": "^0.5.6", + "tmp": "^0.2.4", + "upath": "^1.1.0" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "ts-node": ">= 7.0.1 < 12", + "typescript": ">= 3.8.3 < 6" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@pulumi/pulumi/node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/@sigstore/bundle": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-4.0.0.tgz", + "integrity": "sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A==", + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.5.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@sigstore/core": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-3.1.0.tgz", + "integrity": "sha512-o5cw1QYhNQ9IroioJxpzexmPjfCe7gzafd2RY3qnMpxr4ZEja+Jad/U8sgFpaue6bOaF+z7RVkyKVV44FN+N8A==", + "license": "Apache-2.0", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@sigstore/protobuf-specs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.5.0.tgz", + "integrity": "sha512-MM8XIwUjN2bwvCg1QvrMtbBmpcSHrkhFSCu1D11NyPvDQ25HEc4oG5/OcQfd/Tlf/OxmKWERDj0zGE23jQaMwA==", + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@sigstore/sign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-4.1.0.tgz", + "integrity": "sha512-Vx1RmLxLGnSUqx/o5/VsCjkuN5L7y+vxEEwawvc7u+6WtX2W4GNa7b9HEjmcRWohw/d6BpATXmvOwc78m+Swdg==", + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.1.0", + "@sigstore/protobuf-specs": "^0.5.0", + "make-fetch-happen": "^15.0.3", + "proc-log": "^6.1.0", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@sigstore/sign/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@sigstore/tuf": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-4.0.1.tgz", + "integrity": "sha512-OPZBg8y5Vc9yZjmWCHrlWPMBqW5yd8+wFNl+thMdtcWz3vjVSoJQutF8YkrzI0SLGnkuFof4HSsWUhXrf219Lw==", + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.5.0", + "tuf-js": "^4.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@sigstore/verify": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-3.1.0.tgz", + "integrity": "sha512-mNe0Iigql08YupSOGv197YdHpPPr+EzDZmfCgMc7RPNaZTw5aLN01nBl6CHJOh3BGtnMIj83EeN4butBchc8Ag==", + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.1.0", + "@sigstore/protobuf-specs": "^0.5.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", + "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==", + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-4.1.0.tgz", + "integrity": "sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww==", + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^10.1.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", + "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "^1" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.8", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.8.tgz", + "integrity": "sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/google-protobuf": { + "version": "3.15.12", + "resolved": "https://registry.npmjs.org/@types/google-protobuf/-/google-protobuf-3.15.12.tgz", + "integrity": "sha512-40um9QqwHjRS92qnOaDpL7RmDK15NuZYo9HihiJRbYkMQZlWnuH8AdvbMy8/o6lgLmKbDUKa+OALCltHdbOTpQ==", + "license": "MIT" + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", + "license": "MIT" + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "license": "MIT" + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.3.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.3.tgz", + "integrity": "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "license": "MIT" + }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", + "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "<1" + } + }, + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", + "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/shimmer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz", + "integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==", + "license": "MIT" + }, + "node_modules/@types/tmp": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.6.tgz", + "integrity": "sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA==", + "license": "MIT" + }, + "node_modules/abbrev": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", + "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/bin-links": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-6.0.0.tgz", + "integrity": "sha512-X4CiKlcV2GjnCMwnKAfbVWpHa++65th9TuzAEYtZoATiOE2DQKhSp4CJlyLoTqdhBKlXjpXjCTYPNNFS33Fi6w==", + "license": "ISC", + "dependencies": { + "cmd-shim": "^8.0.0", + "npm-normalize-package-bin": "^5.0.0", + "proc-log": "^6.0.0", + "read-cmd-shim": "^6.0.0", + "write-file-atomic": "^7.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/bin-links/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/cacache": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-20.0.3.tgz", + "integrity": "sha512-3pUp4e8hv07k1QlijZu6Kn7c9+ZpWWk4j3F8N3xPuCExULobqJydKYOTj1FTq58srkJsXvO7LbGAH4C0ZU3WGw==", + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^5.0.0", + "fs-minipass": "^3.0.0", + "glob": "^13.0.0", + "lru-cache": "^11.1.0", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^13.0.0", + "unique-filename": "^5.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "11.2.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", + "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/cacache/node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cmd-shim": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-8.0.0.tgz", + "integrity": "sha512-Jk/BK6NCapZ58BKUxlSI+ouKRbjH1NLZCgJkYoab+vEHUY3f6OzpNBN9u7HFSv9J6TRDGs4PLOHezoKGaFRSCA==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/common-ancestor-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-2.0.0.tgz", + "integrity": "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">= 18" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "license": "MIT" + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exponential-backoff": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", + "license": "Apache-2.0" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/find-up-simple": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", + "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs-minipass": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", + "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/google-protobuf": { + "version": "3.21.4", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.4.tgz", + "integrity": "sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ==", + "license": "(BSD-3-Clause AND Apache-2.0)" + }, + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz", + "integrity": "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==", + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "license": "BSD-2-Clause" + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ignore-walk": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-8.0.0.tgz", + "integrity": "sha512-FCeMZT4NiRQGh+YkeKMtWrOmBgWjHjMJ26WQWrRQyoyzqevdaGSakUaJW5xQYmjLlUVk2qUnCjYVBax9EKKg8A==", + "license": "ISC", + "dependencies": { + "minimatch": "^10.0.3" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/import-in-the-middle": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.15.0.tgz", + "integrity": "sha512-bpQy+CrsRmYmoPMAE/0G33iwRqwW4ouqdRg8jgbH3aKuCtOc8lxgmYXg2dMM92CRiGP660EtBcymH/eVUpCSaA==", + "license": "Apache-2.0", + "dependencies": { + "acorn": "^8.14.0", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/ini": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-5.0.0.tgz", + "integrity": "sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", + "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz", + "integrity": "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==", + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/json-stringify-nice": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz", + "integrity": "sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw==", + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/just-diff": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/just-diff/-/just-diff-6.0.2.tgz", + "integrity": "sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA==", + "license": "MIT" + }, + "node_modules/just-diff-apply": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/just-diff-apply/-/just-diff-apply-5.5.0.tgz", + "integrity": "sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw==", + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/make-fetch-happen": { + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-15.0.4.tgz", + "integrity": "sha512-vM2sG+wbVeVGYcCm16mM3d5fuem9oC28n436HjsGO3LcxoTI8LNVa4rwZDn3f76+cWyT4GGJDxjTYU1I2nr6zw==", + "license": "ISC", + "dependencies": { + "@gar/promise-retry": "^1.0.0", + "@npmcli/agent": "^4.0.0", + "cacache": "^20.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^5.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^6.0.0", + "ssri": "^13.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/make-fetch-happen/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT" + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-collect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", + "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-fetch": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-5.0.2.tgz", + "integrity": "sha512-2d0q2a8eCi2IRg/IGubCNRJoYbA1+YPXAzQVRFmB45gdGZafyivnZ5YSEfo3JikbjGxOdntGFvBQGqaSMXlAFQ==", + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^2.0.0", + "minizlib": "^3.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + }, + "optionalDependencies": { + "iconv-lite": "^0.7.2" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/minipass-sized": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-2.0.0.tgz", + "integrity": "sha512-zSsHhto5BcUVM2m1LurnXY6M//cGhVaegT71OfOXoprxT6o780GZd792ea6FfrQkuU4usHZIUczAQMRUE2plzA==", + "license": "ISC", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/module-details-from-path": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", + "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-gyp": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.2.0.tgz", + "integrity": "sha512-q23WdzrQv48KozXlr0U1v9dwO/k59NHeSzn6loGcasyf0UnSrtzs8kRxM+mfwJSf0DkX0s43hcqgnSO4/VNthQ==", + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^15.0.0", + "nopt": "^9.0.0", + "proc-log": "^6.0.0", + "semver": "^7.3.5", + "tar": "^7.5.4", + "tinyglobby": "^0.2.12", + "which": "^6.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/node-gyp/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=20" + } + }, + "node_modules/node-gyp/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "license": "ISC", + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/nopt": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", + "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==", + "license": "ISC", + "dependencies": { + "abbrev": "^4.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-bundled": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-5.0.0.tgz", + "integrity": "sha512-JLSpbzh6UUXIEoqPsYBvVNVmyrjVZ1fzEFbqxKkTJQkWBO3xFzFT+KDnSKQWwOQNbuWRwt5LSD6HOTLGIWzfrw==", + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^5.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-install-checks": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-8.0.0.tgz", + "integrity": "sha512-ScAUdMpyzkbpxoNekQ3tNRdFI8SJ86wgKZSQZdUxT+bj0wVFpsEMWnkXP0twVe1gJyNF5apBWDJhhIbgrIViRA==", + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-5.0.0.tgz", + "integrity": "sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-package-arg": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-13.0.2.tgz", + "integrity": "sha512-IciCE3SY3uE84Ld8WZU23gAPPV9rIYod4F+rc+vJ7h7cwAJt9Vk6TVsK60ry7Uj3SRS3bqRRIGuTp9YVlk6WNA==", + "license": "ISC", + "dependencies": { + "hosted-git-info": "^9.0.0", + "proc-log": "^6.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^7.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-package-arg/node_modules/hosted-git-info": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz", + "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==", + "license": "ISC", + "dependencies": { + "lru-cache": "^11.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-package-arg/node_modules/lru-cache": { + "version": "11.2.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", + "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/npm-package-arg/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-packlist": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.4.tgz", + "integrity": "sha512-uMW73iajD8hiH4ZBxEV3HC+eTnppIqwakjOYuvgddnalIw2lJguKviK1pcUJDlIWm1wSJkchpDZDSVVsZEYRng==", + "license": "ISC", + "dependencies": { + "ignore-walk": "^8.0.0", + "proc-log": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-packlist/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-pick-manifest": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-10.0.0.tgz", + "integrity": "sha512-r4fFa4FqYY8xaM7fHecQ9Z2nE9hgNfJR+EmoKv0+chvzWkBcORX3r0FpTByP+CbOVJDladMXnPQGVN8PBLGuTQ==", + "license": "ISC", + "dependencies": { + "npm-install-checks": "^7.1.0", + "npm-normalize-package-bin": "^4.0.0", + "npm-package-arg": "^12.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-pick-manifest/node_modules/npm-install-checks": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-7.1.2.tgz", + "integrity": "sha512-z9HJBCYw9Zr8BqXcllKIs5nI+QggAImbBdHphOzVYrz2CB4iQ6FzWyKmlqDZua+51nAu7FcemlbTc9VgQN5XDQ==", + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-pick-manifest/node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", + "integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-pick-manifest/node_modules/npm-package-arg": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-12.0.2.tgz", + "integrity": "sha512-f1NpFjNI9O4VbKMOlA5QoBq/vSQPORHcTZ2feJpFkTHJ9eQkdlmZEKSjcAhxTGInC7RlEyScT9ui67NaOsjFWA==", + "license": "ISC", + "dependencies": { + "hosted-git-info": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-pick-manifest/node_modules/validate-npm-package-name": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-6.0.2.tgz", + "integrity": "sha512-IUoow1YUtvoBBC06dXs8bR8B9vuA3aJfmQNKMoaPG/OFsPmoQvw8xh+6Ye25Gx9DQhoEom3Pcu9MKHerm/NpUQ==", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-registry-fetch": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-19.1.1.tgz", + "integrity": "sha512-TakBap6OM1w0H73VZVDf44iFXsOS3h+L4wVMXmbWOQroZgFhMch0juN6XSzBNlD965yIKvWg2dfu7NSiaYLxtw==", + "license": "ISC", + "dependencies": { + "@npmcli/redact": "^4.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^15.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^5.0.0", + "minizlib": "^3.0.1", + "npm-package-arg": "^13.0.0", + "proc-log": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-map": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-directory": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/package-directory/-/package-directory-8.2.0.tgz", + "integrity": "sha512-qJSu5Mo6tHmRxCy2KCYYKYgcfBdUpy9dwReaZD/xwf608AUk/MoRtIOWzgDtUeGeC7n/55yC3MI1Q+MbSoektw==", + "license": "MIT", + "dependencies": { + "find-up-simple": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/pacote": { + "version": "21.4.0", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-21.4.0.tgz", + "integrity": "sha512-DR7mn7HUOomAX1BORnpYy678qVIidbvOojkBscqy27dRKN+s/hLeQT1MeYYrx1Cxh62jyKjiWiDV7RTTqB+ZEQ==", + "license": "ISC", + "dependencies": { + "@gar/promise-retry": "^1.0.0", + "@npmcli/git": "^7.0.0", + "@npmcli/installed-package-contents": "^4.0.0", + "@npmcli/package-json": "^7.0.0", + "@npmcli/promise-spawn": "^9.0.0", + "@npmcli/run-script": "^10.0.0", + "cacache": "^20.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^13.0.0", + "npm-packlist": "^10.0.1", + "npm-pick-manifest": "^11.0.1", + "npm-registry-fetch": "^19.0.0", + "proc-log": "^6.0.0", + "sigstore": "^4.0.0", + "ssri": "^13.0.0", + "tar": "^7.4.3" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/pacote/node_modules/@npmcli/git": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-7.0.2.tgz", + "integrity": "sha512-oeolHDjExNAJAnlYP2qzNjMX/Xi9bmu78C9dIGr4xjobrSKbuMYCph8lTzn4vnW3NjIqVmw/f8BCfouqyJXlRg==", + "license": "ISC", + "dependencies": { + "@gar/promise-retry": "^1.0.0", + "@npmcli/promise-spawn": "^9.0.0", + "ini": "^6.0.0", + "lru-cache": "^11.2.1", + "npm-pick-manifest": "^11.0.1", + "proc-log": "^6.0.0", + "semver": "^7.3.5", + "which": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/pacote/node_modules/@npmcli/package-json": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-7.0.5.tgz", + "integrity": "sha512-iVuTlG3ORq2iaVa1IWUxAO/jIp77tUKBhoMjuzYW2kL4MLN1bi/ofqkZ7D7OOwh8coAx1/S2ge0rMdGv8sLSOQ==", + "license": "ISC", + "dependencies": { + "@npmcli/git": "^7.0.0", + "glob": "^13.0.0", + "hosted-git-info": "^9.0.0", + "json-parse-even-better-errors": "^5.0.0", + "proc-log": "^6.0.0", + "semver": "^7.5.3", + "spdx-expression-parse": "^4.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/pacote/node_modules/@npmcli/promise-spawn": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-9.0.1.tgz", + "integrity": "sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q==", + "license": "ISC", + "dependencies": { + "which": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/pacote/node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/pacote/node_modules/hosted-git-info": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz", + "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==", + "license": "ISC", + "dependencies": { + "lru-cache": "^11.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/pacote/node_modules/ini": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", + "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/pacote/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=20" + } + }, + "node_modules/pacote/node_modules/json-parse-even-better-errors": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-5.0.0.tgz", + "integrity": "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ==", + "license": "MIT", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/pacote/node_modules/lru-cache": { + "version": "11.2.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", + "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/pacote/node_modules/npm-pick-manifest": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-11.0.3.tgz", + "integrity": "sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ==", + "license": "ISC", + "dependencies": { + "npm-install-checks": "^8.0.0", + "npm-normalize-package-bin": "^5.0.0", + "npm-package-arg": "^13.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/pacote/node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/pacote/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/pacote/node_modules/spdx-expression-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", + "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/pacote/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "license": "ISC", + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/parse-conflict-json": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/parse-conflict-json/-/parse-conflict-json-5.0.1.tgz", + "integrity": "sha512-ZHEmNKMq1wyJXNwLxyHnluPfRAFSIliBvbK/UiOceROt4Xh9Pz0fq49NytIaeaCUf5VR86hwQ/34FCcNU5/LKQ==", + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^5.0.0", + "just-diff": "^6.0.0", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/parse-conflict-json/node_modules/json-parse-even-better-errors": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-5.0.0.tgz", + "integrity": "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ==", + "license": "MIT", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/picomatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", + "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/proc-log": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", + "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/proggy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/proggy/-/proggy-4.0.0.tgz", + "integrity": "sha512-MbA4R+WQT76ZBm/5JUpV9yqcJt92175+Y0Bodg3HgiXzrmKu7Ggq+bpn6y6wHH+gN9NcyKn3yg1+d47VaKwNAQ==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/promise-all-reject-late": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz", + "integrity": "sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==", + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/promise-call-limit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/promise-call-limit/-/promise-call-limit-3.0.2.tgz", + "integrity": "sha512-mRPQO2T1QQVw11E7+UdCJu7S61eJVWknzml9sC1heAdj1jxl0fWMBypIt9ZOcLFf8FkG995ZD7RnVk7HH72fZw==", + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/promise-retry/node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/protobufjs": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", + "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-cmd-shim": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-6.0.0.tgz", + "integrity": "sha512-1zM5HuOfagXCBWMN83fuFI/x+T/UhZ7k+KIzhrHXcQoeX5+7gmaDYjELQHmmzIodumBHeByBJT4QYS7ufAgs7A==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-in-the-middle": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.2.tgz", + "integrity": "sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "license": "MIT" + }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT", + "optional": true + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==", + "license": "BSD-2-Clause" + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/sigstore": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-4.1.0.tgz", + "integrity": "sha512-/fUgUhYghuLzVT/gaJoeVehLCgZiUxPCPMcyVNY0lIf/cTCz58K/WTI7PefDarXxp9nUKpEwg1yyz3eSBMTtgA==", + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.1.0", + "@sigstore/protobuf-specs": "^0.5.0", + "@sigstore/sign": "^4.1.0", + "@sigstore/tuf": "^4.0.1", + "@sigstore/verify": "^3.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "license": "CC0-1.0" + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, + "node_modules/ssri": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-13.0.1.tgz", + "integrity": "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tar": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.10.tgz", + "integrity": "sha512-8mOPs1//5q/rlkNSPcCegA6hiHJYDmSLEI8aMH/CdSQJNWztHC9WHNam5zdQlfpTwB9Xp7IBEsHfV5LKMJGVAw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tmp": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/treeverse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/treeverse/-/treeverse-3.0.0.tgz", + "integrity": "sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/tuf-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-4.1.0.tgz", + "integrity": "sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ==", + "license": "MIT", + "dependencies": { + "@tufjs/models": "4.1.0", + "debug": "^4.4.3", + "make-fetch-happen": "^15.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "license": "MIT" + }, + "node_modules/unique-filename": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-5.0.0.tgz", + "integrity": "sha512-2RaJTAvAb4owyjllTfXzFClJ7WsGxlykkPvCr9pA//LD9goVq+m4PPAeBgNodGZ7nSrntT/auWpJ6Y5IFXcfjg==", + "license": "ISC", + "dependencies": { + "unique-slug": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/unique-slug": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-6.0.0.tgz", + "integrity": "sha512-4Lup7Ezn8W3d52/xBhZBVdx323ckxa7DEvd9kPQHppTkLoJXw6ltrBCyj5pnrxj0qKDxYMJ56CoxNuFCscdTiw==", + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "license": "MIT", + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-7.0.2.tgz", + "integrity": "sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/walk-up-path": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-4.0.0.tgz", + "integrity": "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==", + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-7.0.1.tgz", + "integrity": "sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg==", + "license": "ISC", + "dependencies": { + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + } + } +} diff --git a/ai-context/trustgraph-templates/pulumi/package.json b/ai-context/trustgraph-templates/pulumi/package.json new file mode 100644 index 00000000..1bbb56b1 --- /dev/null +++ b/ai-context/trustgraph-templates/pulumi/package.json @@ -0,0 +1,7 @@ +{ + "dependencies": { + "@pulumi/command": "^1.1.3", + "@pulumi/gcp": "^9.10.0", + "@pulumi/pulumi": "^3.216.0" + } +} diff --git a/ai-context/trustgraph-templates/pulumi/tsconfig.json b/ai-context/trustgraph-templates/pulumi/tsconfig.json new file mode 100644 index 00000000..ab65afa6 --- /dev/null +++ b/ai-context/trustgraph-templates/pulumi/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "strict": true, + "outDir": "bin", + "target": "es2016", + "module": "commonjs", + "moduleResolution": "node", + "sourceMap": true, + "experimentalDecorators": true, + "pretty": true, + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.ts" + ] +} diff --git a/ai-context/trustgraph-templates/pyproject.toml b/ai-context/trustgraph-templates/pyproject.toml new file mode 100644 index 00000000..953cd0a1 --- /dev/null +++ b/ai-context/trustgraph-templates/pyproject.toml @@ -0,0 +1,68 @@ +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "trustgraph-configurator" +dynamic = ["version"] +authors = [ + {name = "trustgraph.ai", email = "security@trustgraph.ai"}, +] +description = "Configuration creator for trustgraph.ai" +readme = "README.md" +requires-python = ">=3.8" +license = {text = "Apache-2.0"} +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", +] +dependencies = [ + "aiohttp", + "gojsonnet", + "pyyaml", + "tabulate", +] + +[project.optional-dependencies] +dev = [ + "pytest>=7.0", + "pytest-xdist>=3.0", + "pytest-cov>=4.0", + "jsonschema>=4.0", +] + +[project.urls] +Homepage = "https://github.com/trustgraph-ai/trustgraph-configurator" + +[project.scripts] +tg-build-deployment = "trustgraph_configurator:run" +tg-config-svc = "trustgraph_configurator:run_service" +tg-show-config-params = "trustgraph_configurator:list" + +[tool.setuptools.packages.find] +include = ["trustgraph_configurator*"] + +[tool.setuptools.package-data] +trustgraph_configurator = ["templates/**", "resources/**"] + +[tool.setuptools.dynamic] +version = {attr = "trustgraph_configurator.__version__"} + +[tool.pytest.ini_options] +testpaths = ["tests"] +python_files = ["test_*.py"] +python_classes = ["Test*"] +python_functions = ["test_*"] +addopts = [ + "-v", + "--strict-markers", + "--tb=short", +] +markers = [ + "unit: Unit tests", + "integration: Integration tests", + "validation: Output validation tests", + "slow: Slow-running tests", +] + diff --git a/ai-context/trustgraph-templates/test-dialog-flow.py b/ai-context/trustgraph-templates/test-dialog-flow.py new file mode 100644 index 00000000..a26d31ff --- /dev/null +++ b/ai-context/trustgraph-templates/test-dialog-flow.py @@ -0,0 +1,493 @@ +#!/usr/bin/env python3 +""" +Test harness for dialog flow - walks through selecting all default options, +produces the resulting state object, and runs the JSONata transform. + +Supports a test matrix mode where each field is tested with all its options +while other fields use defaults. +""" + +import yaml +import json +import argparse +import zipfile +import io +from pathlib import Path +import jsonata +import requests + + +RESOURCES_DIR = Path(__file__).parent / "trustgraph_configurator/resources/dialog" + + +def load_flow(): + """Load the dialog flow YAML file.""" + with open(RESOURCES_DIR / "trustgraph-flow.yaml") as f: + return yaml.safe_load(f) + + +def load_jsonata_transform(): + """Load the JSONata transform file.""" + with open(RESOURCES_DIR / "trustgraph-output.jsonata") as f: + return f.read() + + +def run_transform(state, transform_expr): + """Run the JSONata transform on the state object.""" + expr = jsonata.Jsonata(transform_expr) + return expr.evaluate(state) + + +def call_config_service(config): + """ + Call the configuration service to generate a deployment package. + Returns (success, message, zip_contents) tuple. + + The API expects just the templates array, not the full config object. + """ + url = config["api_url"] + templates = config["templates"] + + try: + response = requests.post( + url, + json=templates, + headers={"Content-Type": "application/json"}, + timeout=30 + ) + + if response.status_code != 200: + return False, f"HTTP {response.status_code}: {response.text[:200]}", None + + # Verify it's a valid ZIP + try: + zip_data = io.BytesIO(response.content) + with zipfile.ZipFile(zip_data, 'r') as zf: + file_list = zf.namelist() + return True, f"ZIP with {len(file_list)} files", file_list + except zipfile.BadZipFile: + return False, "Response is not a valid ZIP file", None + + except requests.exceptions.ConnectionError: + return False, "Connection refused - is the service running?", None + except requests.exceptions.Timeout: + return False, "Request timed out", None + except Exception as e: + return False, f"Error: {e}", None + + +def get_all_options(step): + """Get all possible values for a step's input.""" + input_def = step.get("input", {}) + input_type = input_def.get("type") + + if input_type == "select": + return [opt["value"] for opt in input_def.get("options", [])] + elif input_type == "toggle": + return [True, False] + elif input_type == "number": + # For numbers, just test default, min, and max + default = input_def.get("default") + min_val = input_def.get("min") + max_val = input_def.get("max") + values = [] + if min_val is not None: + values.append(min_val) + if default is not None and default not in values: + values.append(default) + if max_val is not None and max_val not in values: + values.append(max_val) + return values + + return [] + + +def get_default_value(step): + """Get the default value for a step's input.""" + input_def = step.get("input", {}) + input_type = input_def.get("type") + + if input_type == "select": + options = input_def.get("options", []) + # Find recommended option, or use first + for opt in options: + if opt.get("recommended"): + return opt["value"] + return options[0]["value"] if options else None + + elif input_type == "number": + return input_def.get("default") + + elif input_type == "toggle": + return input_def.get("default", False) + + return None + + +def evaluate_condition(condition, state): + """ + Evaluate a simple condition against the state. + Supports: "key = value", "key = true/false", "key < 'version'" + """ + if not condition: + return True + + # Handle equality: "ocr.enabled = true" + if " = " in condition: + key, value = condition.split(" = ", 1) + key = key.strip() + value = value.strip() + + # Get nested key + state_value = state.get(key) + + # Parse value + if value == "true": + return state_value is True + elif value == "false": + return state_value is False + else: + return str(state_value) == value + + # Handle less-than for version comparisons: "version < '1.6.0'" + if " < " in condition: + key, value = condition.split(" < ", 1) + key = key.strip() + value = value.strip().strip("'\"") + state_value = state.get(key, "") + return str(state_value) < value + + return False + + +def get_next_step(step, state): + """Determine the next step based on transitions and current state.""" + transitions = step.get("transitions", []) + + for trans in transitions: + when = trans.get("when") + if when: + if evaluate_condition(when, state): + return trans.get("next") + else: + # Unconditional transition + return trans.get("next") + + return None # Terminal state + + +def walk_flow(flow_data, overrides=None, verbose=True): + """ + Walk through the flow, return the state object. + + Args: + flow_data: The parsed dialog flow YAML + overrides: Dict of {state_key: value} to override defaults + verbose: Whether to print progress + """ + state = {} + overrides = overrides or {} + steps = flow_data.get("steps", {}) + current = flow_data.get("flow", {}).get("start") + visited_steps = [] + + if verbose: + print(f"Starting at: {current}") + print("-" * 60) + + while current: + step = steps.get(current) + if not step: + if verbose: + print(f"ERROR: Step '{current}' not found!") + break + + visited_steps.append(current) + title = step.get("title", current) + state_key = step.get("state_key") + + # Get value - use override if present, otherwise default + if state_key: + if state_key in overrides: + value = overrides[state_key] + else: + value = get_default_value(step) + state[state_key] = value + + if verbose: + is_override = state_key in overrides + marker = " [OVERRIDE]" if is_override else "" + print(f"Step: {current}") + print(f" Title: {title}") + print(f" State key: {state_key} = {value}{marker}") + else: + if verbose: + print(f"Step: {current}") + print(f" Title: {title}") + print(f" (no state key - review/terminal step)") + + # Get next step + next_step = get_next_step(step, state) + if verbose: + if next_step: + print(f" -> Next: {next_step}") + else: + print(f" -> Terminal state") + print() + + current = next_step + + return state, visited_steps + + +def collect_fields_for_path(flow_data, overrides=None): + """ + Collect all fields and their possible values for a given path. + Returns a list of (step_name, state_key, options, default_value) tuples. + """ + fields = [] + steps = flow_data.get("steps", {}) + + # Walk with given overrides to find the path + _, visited = walk_flow(flow_data, overrides=overrides, verbose=False) + + for step_name in visited: + step = steps.get(step_name, {}) + state_key = step.get("state_key") + if state_key: + options = get_all_options(step) + default = get_default_value(step) + if len(options) > 1: # Only include fields with choices + fields.append((step_name, state_key, options, default)) + + return fields + + +def collect_all_fields(flow_data): + """ + Collect all fields from the baseline (default) path. + """ + return collect_fields_for_path(flow_data, overrides=None) + + +def run_single_test(flow_data, transform_expr, overrides, description, test_num, results, call_api=False): + """Run a single test case and record the result.""" + print("-" * 70) + print(f"Test {test_num}: {description}") + print("-" * 70) + + state, _ = walk_flow(flow_data, overrides=overrides, verbose=False) + + try: + config = run_transform(state, transform_expr) + result = { + "test": test_num, + "description": description, + "overrides": overrides, + "state": state, + "config": config + } + + print(f"State: {json.dumps(state, indent=2)}") + print(f"Templates: {[t['name'] for t in config['templates']]}") + + # Optionally call the configuration service + if call_api: + success, message, files = call_config_service(config) + result["api_success"] = success + result["api_message"] = message + if files: + result["api_files"] = files + + if success: + print(f"API: OK - {message}") + else: + print(f"API: FAILED - {message}") + result["error"] = f"API: {message}" + + results.append(result) + + except Exception as e: + results.append({ + "test": test_num, + "description": description, + "overrides": overrides, + "state": state, + "error": str(e) + }) + print(f"State: {json.dumps(state, indent=2)}") + print(f"ERROR: {e}") + print() + + return test_num + 1 + + +def run_test_matrix(flow_data, transform_expr, call_api=False): + """ + Run the test matrix - for each field, try all values while others use defaults. + When a toggle enables conditional fields, also test all options of those fields. + """ + baseline_fields = collect_all_fields(flow_data) + baseline_keys = {f[1] for f in baseline_fields} + + print("=" * 70) + print("TEST MATRIX" + (" (with API validation)" if call_api else "")) + print("=" * 70) + print() + print(f"Found {len(baseline_fields)} fields with multiple options on baseline path:") + for step_name, state_key, options, default in baseline_fields: + print(f" - {state_key}: {len(options)} options (default: {default})") + print() + + results = [] + test_num = 1 + + # First, run the baseline (all defaults) + test_num = run_single_test( + flow_data, transform_expr, + overrides={}, + description="BASELINE (all defaults)", + test_num=test_num, + results=results, + call_api=call_api + ) + + # For each field, try each non-default value + for step_name, state_key, options, default in baseline_fields: + for option in options: + if option == default: + continue # Skip default, already tested in baseline + + overrides = {state_key: option} + test_num = run_single_test( + flow_data, transform_expr, + overrides=overrides, + description=f"{state_key} = {option}", + test_num=test_num, + results=results, + call_api=call_api + ) + + # Check if this override unlocks new fields (conditional paths) + unlocked_fields = collect_fields_for_path(flow_data, overrides=overrides) + unlocked_keys = {f[1] for f in unlocked_fields} + new_keys = unlocked_keys - baseline_keys + + if new_keys: + # Test all non-default options of the newly unlocked fields + for uf_step, uf_key, uf_options, uf_default in unlocked_fields: + if uf_key not in new_keys: + continue + for uf_option in uf_options: + if uf_option == uf_default: + continue # Default already tested above + + combined_overrides = {state_key: option, uf_key: uf_option} + test_num = run_single_test( + flow_data, transform_expr, + overrides=combined_overrides, + description=f"{state_key} = {option}, {uf_key} = {uf_option}", + test_num=test_num, + results=results, + call_api=call_api + ) + + return results + + +def main(): + parser = argparse.ArgumentParser( + description="Test harness for dialog flow configuration" + ) + parser.add_argument( + "--matrix", "-m", + action="store_true", + help="Run test matrix (each field with all options)" + ) + parser.add_argument( + "--api", "-a", + action="store_true", + help="Call the configuration service API to validate each config" + ) + parser.add_argument( + "--summary", "-s", + action="store_true", + help="Show summary only (with --matrix)" + ) + args = parser.parse_args() + + flow_data = load_flow() + transform_expr = load_jsonata_transform() + + if args.matrix: + results = run_test_matrix(flow_data, transform_expr, call_api=args.api) + + # Summary + print("=" * 70) + print("SUMMARY") + print("=" * 70) + print() + passed = [r for r in results if "error" not in r] + failed = [r for r in results if "error" in r] + print(f"Total tests: {len(results)}") + print(f"Passed: {len(passed)}") + print(f"Failed: {len(failed)}") + + if args.api: + api_ok = [r for r in results if r.get("api_success")] + api_fail = [r for r in results if "api_success" in r and not r["api_success"]] + print(f"API OK: {len(api_ok)}") + print(f"API Failed: {len(api_fail)}") + + if failed: + print() + print("Failed tests:") + for r in failed: + print(f" - Test {r['test']}: {r['description']}") + print(f" Error: {r['error']}") + else: + print("=" * 60) + print("Dialog Flow Test Harness - Default Options") + print("=" * 60) + print() + + state, _ = walk_flow(flow_data) + + print("=" * 60) + print("Final State Object:") + print("=" * 60) + print() + print(json.dumps(state, indent=2)) + + # Run JSONata transform + print() + print("=" * 60) + print("Running JSONata Transform...") + print("=" * 60) + print() + + config = run_transform(state, transform_expr) + + print("=" * 60) + print("Configuration Object (output of transform):") + print("=" * 60) + print() + print(json.dumps(config, indent=2)) + + # Optionally call the API + if args.api: + print() + print("=" * 60) + print("Calling Configuration Service...") + print("=" * 60) + print() + success, message, files = call_config_service(config) + if success: + print(f"OK: {message}") + print(f"Files: {files}") + else: + print(f"FAILED: {message}") + + +if __name__ == "__main__": + main() diff --git a/ai-context/trustgraph-templates/test-docs-flow.py b/ai-context/trustgraph-templates/test-docs-flow.py new file mode 100644 index 00000000..487a75c9 --- /dev/null +++ b/ai-context/trustgraph-templates/test-docs-flow.py @@ -0,0 +1,416 @@ +#!/usr/bin/env python3 +""" +Test harness for documentation assembly - verifies that for each configuration +state, the documentation can be assembled from the manifest and markdown fragments. +""" + +import yaml +import json +import argparse +import re +from pathlib import Path +import jsonata + +RESOURCES_DIR = Path(__file__).parent / "trustgraph_configurator/resources/dialog" + + +def load_flow(): + """Load the dialog flow YAML file.""" + with open(RESOURCES_DIR / "trustgraph-flow.yaml") as f: + return yaml.safe_load(f) + + +def load_docs_manifest(): + """Load the documentation manifest YAML file.""" + with open(RESOURCES_DIR / "trustgraph-docs.yaml") as f: + return yaml.safe_load(f) + + +def load_doc_file(filename): + """Load a documentation markdown file.""" + path = RESOURCES_DIR / "docs" / filename + if path.exists(): + return path.read_text() + return None + + +def get_default_value(step): + """Get the default value for a step's input.""" + input_def = step.get("input", {}) + input_type = input_def.get("type") + + if input_type == "select": + options = input_def.get("options", []) + for opt in options: + if opt.get("recommended"): + return opt["value"] + return options[0]["value"] if options else None + elif input_type == "number": + return input_def.get("default") + elif input_type == "toggle": + return input_def.get("default", False) + return None + + +def get_all_options(step): + """Get all possible values for a step's input.""" + input_def = step.get("input", {}) + input_type = input_def.get("type") + + if input_type == "select": + return [opt["value"] for opt in input_def.get("options", [])] + elif input_type == "toggle": + return [True, False] + elif input_type == "number": + default = input_def.get("default") + min_val = input_def.get("min") + max_val = input_def.get("max") + values = [] + if min_val is not None: + values.append(min_val) + if default is not None and default not in values: + values.append(default) + if max_val is not None and max_val not in values: + values.append(max_val) + return values + return [] + + +def evaluate_condition(condition, state): + """Evaluate a simple condition against the state.""" + if not condition: + return True + + if " = " in condition: + key, value = condition.split(" = ", 1) + key = key.strip() + value = value.strip() + state_value = state.get(key) + if value == "true": + return state_value is True + elif value == "false": + return state_value is False + else: + return str(state_value) == value + + if " < " in condition: + key, value = condition.split(" < ", 1) + key = key.strip() + value = value.strip().strip("'\"") + state_value = state.get(key, "") + return str(state_value) < value + + return False + + +def get_next_step(step, state): + """Determine the next step based on transitions and current state.""" + transitions = step.get("transitions", []) + for trans in transitions: + when = trans.get("when") + if when: + if evaluate_condition(when, state): + return trans.get("next") + else: + return trans.get("next") + return None + + +def walk_flow(flow_data, overrides=None): + """Walk through the flow, return the state object.""" + state = {} + overrides = overrides or {} + steps = flow_data.get("steps", {}) + current = flow_data.get("flow", {}).get("start") + visited_steps = [] + + while current: + step = steps.get(current) + if not step: + break + + visited_steps.append(current) + state_key = step.get("state_key") + + if state_key: + if state_key in overrides: + value = overrides[state_key] + else: + value = get_default_value(step) + state[state_key] = value + + current = get_next_step(step, state) + + return state, visited_steps + + +def evaluate_when_condition(when_expr, state): + """ + Evaluate a 'when' condition from the docs manifest against the state. + Supports: equality, 'in' arrays, 'and' conditions. + """ + if not when_expr: + return False + + # Use jsonata for complex expressions + try: + expr = jsonata.Jsonata(when_expr) + result = expr.evaluate(state) + return bool(result) + except Exception as e: + # Fallback to simple parsing for basic cases + return evaluate_simple_when(when_expr, state) + + +def evaluate_simple_when(when_expr, state): + """Simple fallback parser for when expressions.""" + # Handle "and" conditions + if " and " in when_expr: + parts = when_expr.split(" and ") + return all(evaluate_simple_when(p.strip(), state) for p in parts) + + # Handle "in" conditions: "platform in ['docker-compose', 'podman-compose']" + in_match = re.match(r"(\w+)\s+in\s+\[([^\]]+)\]", when_expr) + if in_match: + key = in_match.group(1) + values_str = in_match.group(2) + values = [v.strip().strip("'\"") for v in values_str.split(",")] + return state.get(key) in values + + # Handle equality: "platform = 'docker-compose'" + eq_match = re.match(r"(\w+)\s*=\s*['\"]([^'\"]+)['\"]", when_expr) + if eq_match: + key = eq_match.group(1) + value = eq_match.group(2) + return state.get(key) == value + + return False + + +def assemble_docs(state, manifest): + """ + Assemble documentation for a given state. + Returns (success, matched_instructions, errors). + """ + instructions = manifest.get("documentation", {}).get("instructions", []) + matched = [] + errors = [] + + for instr in instructions: + # Check if instruction applies + always = instr.get("always", False) + when = instr.get("when") + + applies = always or (when and evaluate_when_condition(when, state)) + + if applies: + file_path = instr.get("file") + content = load_doc_file(file_path) + + if content is None: + errors.append(f"Missing file: {file_path}") + matched.append({ + "id": instr.get("id"), + "goal": instr.get("goal"), + "file": file_path, + "found": False + }) + else: + matched.append({ + "id": instr.get("id"), + "goal": instr.get("goal"), + "file": file_path, + "found": True, + "content_length": len(content) + }) + + success = len(errors) == 0 and len(matched) > 0 + return success, matched, errors + + +def collect_fields_for_path(flow_data, overrides=None): + """Collect all fields and their possible values for a given path.""" + fields = [] + steps = flow_data.get("steps", {}) + _, visited = walk_flow(flow_data, overrides=overrides) + + for step_name in visited: + step = steps.get(step_name, {}) + state_key = step.get("state_key") + if state_key: + options = get_all_options(step) + default = get_default_value(step) + if len(options) > 1: + fields.append((step_name, state_key, options, default)) + + return fields + + +def run_single_test(flow_data, manifest, overrides, description, test_num, results): + """Run a single documentation assembly test.""" + print("-" * 70) + print(f"Test {test_num}: {description}") + print("-" * 70) + + state, _ = walk_flow(flow_data, overrides=overrides) + success, matched, errors = assemble_docs(state, manifest) + + result = { + "test": test_num, + "description": description, + "overrides": overrides, + "state": state, + "matched_count": len(matched), + "matched": matched, + "success": success + } + + if errors: + result["errors"] = errors + + results.append(result) + + print(f"State: {json.dumps({k: v for k, v in state.items() if not k.startswith('ocr') and not k.startswith('embed')}, indent=2)}") + print(f"Matched instructions: {len(matched)}") + for m in matched: + status = "OK" if m["found"] else "MISSING" + print(f" - [{status}] {m['goal']} ({m['file']})") + + if errors: + print(f"ERRORS: {errors}") + else: + print("Docs: OK") + print() + + return test_num + 1 + + +def run_test_matrix(flow_data, manifest): + """Run the documentation test matrix.""" + baseline_fields = collect_fields_for_path(flow_data) + baseline_keys = {f[1] for f in baseline_fields} + + print("=" * 70) + print("DOCUMENTATION TEST MATRIX") + print("=" * 70) + print() + print(f"Found {len(baseline_fields)} fields with multiple options on baseline path:") + for step_name, state_key, options, default in baseline_fields: + print(f" - {state_key}: {len(options)} options (default: {default})") + print() + + results = [] + test_num = 1 + + # Baseline test + test_num = run_single_test( + flow_data, manifest, + overrides={}, + description="BASELINE (all defaults)", + test_num=test_num, + results=results + ) + + # Test each field variation + for step_name, state_key, options, default in baseline_fields: + for option in options: + if option == default: + continue + + overrides = {state_key: option} + test_num = run_single_test( + flow_data, manifest, + overrides=overrides, + description=f"{state_key} = {option}", + test_num=test_num, + results=results + ) + + # Check for unlocked fields + unlocked_fields = collect_fields_for_path(flow_data, overrides=overrides) + unlocked_keys = {f[1] for f in unlocked_fields} + new_keys = unlocked_keys - baseline_keys + + if new_keys: + for uf_step, uf_key, uf_options, uf_default in unlocked_fields: + if uf_key not in new_keys: + continue + for uf_option in uf_options: + if uf_option == uf_default: + continue + + combined_overrides = {state_key: option, uf_key: uf_option} + test_num = run_single_test( + flow_data, manifest, + overrides=combined_overrides, + description=f"{state_key} = {option}, {uf_key} = {uf_option}", + test_num=test_num, + results=results + ) + + return results + + +def main(): + parser = argparse.ArgumentParser( + description="Test harness for documentation assembly" + ) + parser.add_argument( + "--matrix", "-m", + action="store_true", + help="Run test matrix (each field with all options)" + ) + args = parser.parse_args() + + flow_data = load_flow() + manifest = load_docs_manifest() + + if args.matrix: + results = run_test_matrix(flow_data, manifest) + + # Summary + print("=" * 70) + print("SUMMARY") + print("=" * 70) + print() + passed = [r for r in results if r["success"]] + failed = [r for r in results if not r["success"]] + print(f"Total tests: {len(results)}") + print(f"Passed: {len(passed)}") + print(f"Failed: {len(failed)}") + + if failed: + print() + print("Failed tests:") + for r in failed: + print(f" - Test {r['test']}: {r['description']}") + if "errors" in r: + for err in r["errors"]: + print(f" Error: {err}") + else: + # Single test with defaults + print("=" * 60) + print("Documentation Assembly Test - Default Options") + print("=" * 60) + print() + + state, _ = walk_flow(flow_data) + success, matched, errors = assemble_docs(state, manifest) + + print(f"State: {json.dumps(state, indent=2)}") + print() + print(f"Matched {len(matched)} instructions:") + for m in matched: + status = "OK" if m["found"] else "MISSING" + print(f" [{status}] {m['goal']}") + print(f" File: {m['file']}") + print() + + if errors: + print(f"Errors: {errors}") + else: + print("All documentation files found!") + + +if __name__ == "__main__": + main() diff --git a/ai-context/trustgraph-templates/tests/README.md b/ai-context/trustgraph-templates/tests/README.md new file mode 100644 index 00000000..064c8237 --- /dev/null +++ b/ai-context/trustgraph-templates/tests/README.md @@ -0,0 +1,189 @@ +# TrustGraph Configurator Test Suite + +Comprehensive pytest-based test suite for trustgraph-configurator. + +## Installation + +Install with development dependencies: + +```bash +pip install -e .[dev] +``` + +## Running Tests + +```bash +# All tests +pytest + +# Specific category +pytest tests/unit/ +pytest tests/integration/ +pytest tests/validation/ + +# By marker +pytest -m unit +pytest -m integration +pytest -m validation + +# Specific test file +pytest tests/unit/test_generator.py + +# Parallel execution (faster) +pytest -n auto + +# Verbose output +pytest -v + +# Stop on first failure +pytest -x + +# With coverage +pytest --cov=trustgraph_configurator --cov-report=html +``` + +## Test Structure + +``` +tests/ +├── conftest.py # Shared fixtures +├── unit/ # Unit tests for Python modules +│ ├── test_generator.py +│ ├── test_packager.py +│ ├── test_api.py +│ └── test_run.py +├── integration/ # Full workflow tests +│ ├── test_compilation.py # Template compilation matrix +│ ├── test_cli.py # CLI interface tests +│ └── test_errors.py # Error handling tests +├── validation/ # Output validation tests +│ ├── test_syntax.py # Syntax validation +│ ├── test_schema.py # Schema validation +│ ├── test_semantics_k8s.py +│ ├── test_semantics_docker.py +│ └── test_semantics_tg.py +├── validators/ # Validation helper modules +│ ├── kubernetes.py +│ ├── docker_compose.py +│ └── trustgraph.py +├── schemas/ # JSON schemas +│ ├── trustgraph-config.schema.json +│ ├── kubernetes-resource.schema.json +│ └── docker-compose.schema.json +└── configs/ # Test input configs + ├── minimal.json + ├── complex-rag.json + ├── multi-service.json + └── cloud-aws.json +``` + +## Test Categories + +### Unit Tests (`tests/unit/`) +Test individual Python modules in isolation: +- Generator: Jsonnet template processing +- Packager: Configuration assembly and zip creation +- API: Template listing and version resolution +- Run: CLI entry point and argument parsing + +### Integration Tests (`tests/integration/`) +Test full workflow end-to-end: +- **Compilation**: Template compilation across all version/platform/config combinations (192 tests) +- **CLI**: Command line interface functionality +- **Errors**: Error handling and reporting + +### Validation Tests (`tests/validation/`) +Verify correctness of generated outputs: +- **Syntax**: JSON/YAML parsing validation +- **Schema**: JSON Schema compliance +- **Semantics**: Cross-references, consistency checks + +## Test Matrix + +Integration tests cover: +- **Versions**: 1.6, 1.7, 1.8 +- **Platforms**: docker-compose, podman-compose, minikube-k8s, gcp-k8s, aks-k8s, eks-k8s, scw-k8s, ovh-k8s +- **Configs**: minimal, complex-rag, multi-service, cloud-aws + +Total: 3 versions × 8 platforms × 4 configs × 2 outputs = 192 test combinations + +## Validation Layers + +### Syntax Validation +- JSON parsing with `json.loads()` +- YAML parsing with `yaml.safe_load()` + +### Schema Validation +- TrustGraph config against `trustgraph-config.schema.json` +- Docker Compose against `docker-compose.schema.json` +- Kubernetes resources against `kubernetes-resource.schema.json` + +### Semantic Validation + +**Kubernetes:** +- Deployment selectors match pod labels +- Service selectors match deployment labels +- volumeMounts reference defined volumes +- ConfigMap/Secret references exist +- Service targetPorts match container ports + +**Docker Compose:** +- depends_on references valid services +- Volume names are defined +- Network references are valid +- No port conflicts + +**TrustGraph Config:** +- Service references are valid +- Parameter types are reasonable +- Storage backends are consistent +- LLM configuration is present + +## Fixtures + +Available in `conftest.py`: +- `test_config_dir`: Path to test configs +- `test_configs`: Loaded test configurations +- `temp_output_dir`: Temporary directory for outputs +- `run_configurator`: Function to execute configurator +- `mock_config_file`: Create temporary config files + +## CI/CD + +Tests run automatically on pull requests via GitHub Actions. + +See `.github/workflows/pull-request.yaml` for CI configuration. + +## Development + +### Adding New Tests + +1. Create test file in appropriate directory +2. Use appropriate markers (`@pytest.mark.unit`, etc.) +3. Use fixtures from `conftest.py` +4. Follow naming convention: `test_*.py`, `test_*()` functions + +### Adding New Validation + +1. Add validation logic to `tests/validators/` +2. Create corresponding tests in `tests/validation/` +3. Update schemas in `tests/schemas/` if needed + +## Troubleshooting + +**Test failures:** +- Check stderr output for error messages +- Run with `-v` for verbose output +- Run with `--tb=long` for full tracebacks + +**Import errors:** +- Ensure package is installed: `pip install -e .[dev]` +- Check PYTHONPATH includes project root + +**Slow tests:** +- Use `-n auto` for parallel execution +- Run specific test subsets instead of full suite + +## Documentation + +See `docs/tech-specs/tests.md` for detailed test specification. diff --git a/ai-context/trustgraph-templates/tests/configs/cloud-aws.json b/ai-context/trustgraph-templates/tests/configs/cloud-aws.json new file mode 100644 index 00000000..f09029e1 --- /dev/null +++ b/ai-context/trustgraph-templates/tests/configs/cloud-aws.json @@ -0,0 +1,26 @@ +[ + { + "name": "bedrock", + "parameters": { + "model": "anthropic.claude-3-sonnet-20240229-v1:0" + } + }, + { + "name": "embeddings-hf", + "parameters": { + "embeddings-model": "sentence-transformers/all-MiniLM-L6-v2" + } + }, + { + "name": "vector-store-qdrant", + "parameters": {} + }, + { + "name": "trustgraph-base", + "parameters": {} + }, + { + "name": "pulsar", + "parameters": {} + } +] diff --git a/ai-context/trustgraph-templates/tests/configs/complex-rag.json b/ai-context/trustgraph-templates/tests/configs/complex-rag.json new file mode 100644 index 00000000..ca93a4dd --- /dev/null +++ b/ai-context/trustgraph-templates/tests/configs/complex-rag.json @@ -0,0 +1,42 @@ +[ + { + "name": "openai", + "parameters": { + "model": "gpt-4", + "temperature": 0.7 + } + }, + { + "name": "embeddings-hf", + "parameters": { + "embeddings-model": "sentence-transformers/all-MiniLM-L6-v2" + } + }, + { + "name": "vector-store-qdrant", + "parameters": {} + }, + { + "name": "triple-store-neo4j", + "parameters": {} + }, + { + "name": "triple-store-cassandra", + "parameters": {} + }, + { + "name": "override-recursive-chunker", + "parameters": { + "chunk-size": 2000, + "chunk-overlap": 100 + } + }, + { + "name": "trustgraph-base", + "parameters": {} + }, + { + "name": "pulsar", + "parameters": {} + } +] diff --git a/ai-context/trustgraph-templates/tests/configs/minimal.json b/ai-context/trustgraph-templates/tests/configs/minimal.json new file mode 100644 index 00000000..61e76a3f --- /dev/null +++ b/ai-context/trustgraph-templates/tests/configs/minimal.json @@ -0,0 +1,23 @@ +[ + { + "name": "openai", + "parameters": { + "model": "gpt-3.5-turbo", + "temperature": 0.7 + } + }, + { + "name": "embeddings-hf", + "parameters": { + "embeddings-model": "sentence-transformers/all-MiniLM-L6-v2" + } + }, + { + "name": "trustgraph-base", + "parameters": {} + }, + { + "name": "pulsar", + "parameters": {} + } +] diff --git a/ai-context/trustgraph-templates/tests/configs/multi-service.json b/ai-context/trustgraph-templates/tests/configs/multi-service.json new file mode 100644 index 00000000..f475fb75 --- /dev/null +++ b/ai-context/trustgraph-templates/tests/configs/multi-service.json @@ -0,0 +1,38 @@ +[ + { + "name": "ollama", + "parameters": { + "model": "llama2" + } + }, + { + "name": "embeddings-fastembed", + "parameters": { + "embeddings-model": "BAAI/bge-small-en-v1.5" + } + }, + { + "name": "vector-store-milvus", + "parameters": {} + }, + { + "name": "triple-store-memgraph", + "parameters": {} + }, + { + "name": "triple-store-cassandra", + "parameters": {} + }, + { + "name": "grafana", + "parameters": {} + }, + { + "name": "trustgraph-base", + "parameters": {} + }, + { + "name": "pulsar", + "parameters": {} + } +] diff --git a/ai-context/trustgraph-templates/tests/conftest.py b/ai-context/trustgraph-templates/tests/conftest.py new file mode 100644 index 00000000..76a04905 --- /dev/null +++ b/ai-context/trustgraph-templates/tests/conftest.py @@ -0,0 +1,125 @@ +""" +Pytest configuration and shared fixtures for trustgraph-configurator tests. +""" + +import pytest + +# ============================================================================= +# Version Configuration - Update these when adding new template versions +# ============================================================================= +TESTED_VERSIONS = ["1.8", "1.9", "2.0"] +PRIMARY_VERSION = "1.9" # Used when only one version is tested +import sys +import json +import tempfile +import shutil +from pathlib import Path + + +@pytest.fixture(scope="session") +def test_config_dir(): + """Path to the test configurations directory.""" + return Path(__file__).parent / "configs" + + +@pytest.fixture(scope="session") +def test_configs(test_config_dir): + """Dictionary of loaded test configurations.""" + configs = {} + for config_file in test_config_dir.glob("*.json"): + with open(config_file) as f: + configs[config_file.name] = json.load(f) + return configs + + +@pytest.fixture +def temp_output_dir(): + """Temporary directory for test outputs.""" + temp_dir = tempfile.mkdtemp() + yield Path(temp_dir) + shutil.rmtree(temp_dir) + + +@pytest.fixture +def run_configurator(monkeypatch, capsys): + """ + Fixture to run configurator with given arguments. + + Usage: + stdout, stderr, exit_code = run_configurator(['-t', '1.8', '-p', 'docker-compose', ...]) + + Returns: + tuple: (stdout, stderr, exit_code) + """ + def _run(args): + from trustgraph_configurator import run + + # Set sys.argv with the command and arguments + monkeypatch.setattr(sys, 'argv', ['tg-build-deployment'] + args) + + exit_code = 0 + try: + run() # run is already the function, not a module + except SystemExit as e: + exit_code = e.code if e.code is not None else 0 + + # Capture output + captured = capsys.readouterr() + return captured.out, captured.err, exit_code + + return _run + + +@pytest.fixture(scope="session") +def golden_dir(): + """Path to the golden files directory.""" + return Path(__file__).parent / "golden" + + +@pytest.fixture(scope="session") +def test_versions(): + """List of template versions to test.""" + return TESTED_VERSIONS + + +@pytest.fixture(scope="session") +def primary_version(): + """Primary version for tests that only need one version.""" + return PRIMARY_VERSION + + +@pytest.fixture(scope="session") +def test_platforms(): + """List of platforms to test.""" + return [ + "docker-compose", + "podman-compose", + "minikube-k8s", + "gcp-k8s", + "aks-k8s", + "eks-k8s", + "scw-k8s", + "ovh-k8s", + ] + + +@pytest.fixture(scope="session") +def test_config_names(): + """List of test configuration file names.""" + return [ + "minimal.json", + "complex-rag.json", + "multi-service.json", + "cloud-aws.json", + ] + + +@pytest.fixture +def mock_config_file(tmp_path): + """Create a temporary config file for testing.""" + def _create(config_data): + config_file = tmp_path / "test_config.json" + with open(config_file, 'w') as f: + json.dump(config_data, f) + return str(config_file) + return _create diff --git a/ai-context/trustgraph-templates/tests/integration/__init__.py b/ai-context/trustgraph-templates/tests/integration/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ai-context/trustgraph-templates/tests/integration/test_cli.py b/ai-context/trustgraph-templates/tests/integration/test_cli.py new file mode 100644 index 00000000..1666254c --- /dev/null +++ b/ai-context/trustgraph-templates/tests/integration/test_cli.py @@ -0,0 +1,89 @@ +""" +Integration tests for CLI interface. +""" + +import pytest +import subprocess + +from conftest import TESTED_VERSIONS + + +@pytest.mark.integration +class TestCLIInterface: + """Tests for CLI command line interface.""" + + def test_cli_executable_help(self): + """Test that CLI executable --help works.""" + result = subprocess.run( + ['tg-build-deployment', '--help'], + capture_output=True, + text=True + ) + assert result.returncode == 0 + assert 'usage' in result.stdout.lower() + + def test_cli_executable_exists(self): + """Test that tg-build-deployment is in PATH.""" + result = subprocess.run( + ['which', 'tg-build-deployment'], + capture_output=True, + text=True + ) + assert result.returncode == 0 + + def test_output_modes(self, run_configurator, test_config_dir, primary_version): + """Test -O and -R output modes.""" + config_file = str(test_config_dir / "minimal.json") + + # Test -O mode + stdout_o, _, code_o = run_configurator([ + '-t', primary_version, + '-p', 'docker-compose', + '-i', config_file, + '--latest-stable', + '-O' + ]) + assert code_o == 0 + assert len(stdout_o) > 0 + + # Test -R mode + stdout_r, _, code_r = run_configurator([ + '-t', primary_version, + '-p', 'docker-compose', + '-i', config_file, + '--latest-stable', + '-R' + ]) + assert code_r == 0 + assert len(stdout_r) > 0 + + # Outputs should be different + assert stdout_o != stdout_r + + def test_platform_argument(self, run_configurator, test_config_dir, primary_version): + """Test -p/--platform argument.""" + config_file = str(test_config_dir / "minimal.json") + + for platform in ['docker-compose', 'minikube-k8s']: + stdout, stderr, code = run_configurator([ + '-t', primary_version, + '-p', platform, + '-i', config_file, + '--latest-stable', + '-O' + ]) + assert code == 0, f"Failed for platform {platform}" + + def test_template_argument(self, run_configurator, test_config_dir): + """Test -t/--template argument.""" + config_file = str(test_config_dir / "minimal.json") + + for template in TESTED_VERSIONS: + stdout, stderr, code = run_configurator([ + '-t', template, + '-p', 'docker-compose', + '-i', config_file, + '--latest-stable', + '-O' + ]) + assert code == 0, f"Failed for template {template}" diff --git a/ai-context/trustgraph-templates/tests/integration/test_compilation.py b/ai-context/trustgraph-templates/tests/integration/test_compilation.py new file mode 100644 index 00000000..e38f27d2 --- /dev/null +++ b/ai-context/trustgraph-templates/tests/integration/test_compilation.py @@ -0,0 +1,163 @@ +""" +Integration tests for template compilation across all combinations. +""" + +import pytest +import json +import yaml + +from conftest import TESTED_VERSIONS + + +@pytest.mark.integration +@pytest.mark.parametrize("version", TESTED_VERSIONS) +@pytest.mark.parametrize("platform", [ + "docker-compose", + "podman-compose", + "minikube-k8s", + "gcp-k8s", + "aks-k8s", + "eks-k8s", + "scw-k8s", + "ovh-k8s", +]) +@pytest.mark.parametrize("config", [ + "minimal.json", + "complex-rag.json", + "multi-service.json", + "cloud-aws.json", +]) +def test_tg_config_generation(version, platform, config, run_configurator, test_config_dir): + """Test TrustGraph config generation for all combinations.""" + config_file = str(test_config_dir / config) + + stdout, stderr, code = run_configurator([ + '-t', version, + '-p', platform, + '-i', config_file, + '--latest-stable', + '-O' + ]) + + # Should succeed + assert code == 0, f"Failed for {version}/{platform}/{config}: {stderr}" + + # Should output valid JSON + try: + tg_config = json.loads(stdout) + except json.JSONDecodeError as e: + pytest.fail(f"Invalid JSON output for {version}/{platform}/{config}: {e}") + + # Basic structure checks + assert isinstance(tg_config, (dict, list)), "TrustGraph config should be dict or list" + + +@pytest.mark.integration +@pytest.mark.parametrize("version", TESTED_VERSIONS) +@pytest.mark.parametrize("platform", [ + "docker-compose", + "podman-compose", + "minikube-k8s", + "gcp-k8s", + "aks-k8s", + "eks-k8s", + "scw-k8s", + "ovh-k8s", +]) +@pytest.mark.parametrize("config", [ + "minimal.json", + "complex-rag.json", + "multi-service.json", + "cloud-aws.json", +]) +def test_resources_generation(version, platform, config, run_configurator, test_config_dir): + """Test platform resources generation for all combinations.""" + config_file = str(test_config_dir / config) + + stdout, stderr, code = run_configurator([ + '-t', version, + '-p', platform, + '-i', config_file, + '--latest-stable', + '-R' + ]) + + # Should succeed + assert code == 0, f"Failed for {version}/{platform}/{config}: {stderr}" + + # Should output valid YAML + try: + resources = yaml.safe_load(stdout) + except yaml.YAMLError as e: + pytest.fail(f"Invalid YAML output for {version}/{platform}/{config}: {e}") + + # Basic structure checks + if platform in ["docker-compose", "podman-compose"]: + assert "services" in resources, "Docker Compose should have services" + else: + # Kubernetes resources + assert resources is not None, "K8s resources should not be empty" + + +@pytest.mark.integration +def test_compilation_minimal_docker_compose(run_configurator, test_config_dir, primary_version): + """Smoke test: minimal config on docker-compose.""" + config_file = str(test_config_dir / "minimal.json") + + # Test TG config + stdout, stderr, code = run_configurator([ + '-t', primary_version, + '-p', 'docker-compose', + '-i', config_file, + '--latest-stable', + '-O' + ]) + + assert code == 0 + tg_config = json.loads(stdout) + assert tg_config is not None + + # Test resources + stdout, stderr, code = run_configurator([ + '-t', primary_version, + '-p', 'docker-compose', + '-i', config_file, + '--latest-stable', + '-R' + ]) + + assert code == 0 + resources = yaml.safe_load(stdout) + assert "services" in resources + + +@pytest.mark.integration +def test_compilation_minimal_k8s(run_configurator, test_config_dir, primary_version): + """Smoke test: minimal config on k8s.""" + config_file = str(test_config_dir / "minimal.json") + + # Test TG config + stdout, stderr, code = run_configurator([ + '-t', primary_version, + '-p', 'minikube-k8s', + '-i', config_file, + '--latest-stable', + '-O' + ]) + + assert code == 0 + tg_config = json.loads(stdout) + assert tg_config is not None + + # Test resources + stdout, stderr, code = run_configurator([ + '-t', primary_version, + '-p', 'minikube-k8s', + '-i', config_file, + '--latest-stable', + '-R' + ]) + + assert code == 0 + resources = yaml.safe_load(stdout) + assert resources is not None diff --git a/ai-context/trustgraph-templates/tests/integration/test_errors.py b/ai-context/trustgraph-templates/tests/integration/test_errors.py new file mode 100644 index 00000000..ad13ee4f --- /dev/null +++ b/ai-context/trustgraph-templates/tests/integration/test_errors.py @@ -0,0 +1,97 @@ +""" +Integration tests for error handling. +""" + +import pytest +import json + + +@pytest.mark.integration +class TestErrorHandling: + """Tests for error handling and reporting.""" + + def test_nonexistent_config_file(self, run_configurator, primary_version): + """Test error when config file doesn't exist.""" + stdout, stderr, code = run_configurator([ + '-t', primary_version, + '-p', 'docker-compose', + '-i', '/nonexistent/config.json', + '--latest-stable', + '-O' + ]) + assert code == 1 + assert len(stderr) > 0 # Error should be in stderr + + def test_invalid_json_config(self, run_configurator, tmp_path, primary_version): + """Test error when config file has invalid JSON.""" + invalid_config = tmp_path / "invalid.json" + invalid_config.write_text("{ invalid json") + + stdout, stderr, code = run_configurator([ + '-t', primary_version, + '-p', 'docker-compose', + '-i', str(invalid_config), + '--latest-stable', + '-O' + ]) + assert code == 1 + + def test_invalid_platform(self, run_configurator, test_config_dir, primary_version): + """Test error when platform is invalid.""" + config_file = str(test_config_dir / "minimal.json") + + stdout, stderr, code = run_configurator([ + '-t', primary_version, + '-p', 'nonexistent-platform', + '-i', config_file, + '--latest-stable', + '-R' # Use -R to trigger platform-specific generation + ]) + assert code == 1 + + def test_invalid_template_version(self, run_configurator, test_config_dir): + """Test error when template version doesn't exist.""" + config_file = str(test_config_dir / "minimal.json") + + stdout, stderr, code = run_configurator([ + '-t', '999.999', + '-p', 'docker-compose', + '-i', config_file, + '-O' + ]) + assert code == 1 + + def test_malformed_config_structure(self, run_configurator, tmp_path, primary_version): + """Test error when config structure is invalid.""" + # Valid JSON but wrong structure + invalid_config = tmp_path / "bad_structure.json" + invalid_config.write_text('{"wrong": "structure"}') + + stdout, stderr, code = run_configurator([ + '-t', primary_version, + '-p', 'docker-compose', + '-i', str(invalid_config), + '--latest-stable', + '-O' + ]) + # May succeed with warning or fail - either is acceptable + # The important thing is it doesn't crash + assert code in [0, 1] + + def test_missing_required_args(self, run_configurator): + """Test error when required arguments are missing.""" + # Missing template + stdout, stderr, code = run_configurator([ + '-p', 'docker-compose', + '-O' + ]) + assert code == 1 + + def test_error_goes_to_stderr(self, run_configurator): + """Test that errors are written to stderr, not stdout.""" + stdout, stderr, code = run_configurator([ + '-i', '/nonexistent/config.json' + ]) + assert code == 1 + # Errors should be in stderr + assert len(stderr) > 0 or 'Exception' in stderr or code == 1 diff --git a/ai-context/trustgraph-templates/tests/schemas/docker-compose.schema.json b/ai-context/trustgraph-templates/tests/schemas/docker-compose.schema.json new file mode 100644 index 00000000..ea7e6a27 --- /dev/null +++ b/ai-context/trustgraph-templates/tests/schemas/docker-compose.schema.json @@ -0,0 +1,103 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Docker Compose Configuration", + "description": "Basic schema for Docker Compose files", + "type": "object", + "required": ["services"], + "properties": { + "version": { + "type": "string", + "description": "Docker Compose version" + }, + "services": { + "type": "object", + "description": "Service definitions", + "minProperties": 1, + "additionalProperties": { + "type": "object", + "properties": { + "image": { + "type": "string", + "description": "Docker image" + }, + "build": { + "oneOf": [ + {"type": "string"}, + {"type": "object"} + ], + "description": "Build configuration" + }, + "ports": { + "type": "array", + "items": { + "oneOf": [ + {"type": "string"}, + {"type": "integer"} + ] + } + }, + "volumes": { + "type": "array", + "items": { + "type": "string" + } + }, + "environment": { + "oneOf": [ + { + "type": "object", + "additionalProperties": { + "oneOf": [ + {"type": "string"}, + {"type": "number"}, + {"type": "boolean"} + ] + } + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "depends_on": { + "oneOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "object" + } + ] + }, + "networks": { + "oneOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "object" + } + ] + } + } + } + }, + "volumes": { + "type": "object", + "description": "Named volumes" + }, + "networks": { + "type": "object", + "description": "Network definitions" + } + } +} diff --git a/ai-context/trustgraph-templates/tests/schemas/kubernetes-resource.schema.json b/ai-context/trustgraph-templates/tests/schemas/kubernetes-resource.schema.json new file mode 100644 index 00000000..943096ab --- /dev/null +++ b/ai-context/trustgraph-templates/tests/schemas/kubernetes-resource.schema.json @@ -0,0 +1,42 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Kubernetes Resource", + "description": "Basic schema for Kubernetes resources", + "type": "object", + "required": ["apiVersion", "kind", "metadata"], + "properties": { + "apiVersion": { + "type": "string", + "description": "Kubernetes API version" + }, + "kind": { + "type": "string", + "description": "Resource kind", + "enum": ["Deployment", "Service", "ConfigMap", "Secret", "PersistentVolumeClaim", "PersistentVolume", "Namespace", "StorageClass"] + }, + "metadata": { + "type": "object", + "required": ["name"], + "properties": { + "name": { + "type": "string", + "description": "Resource name" + }, + "namespace": { + "type": "string", + "description": "Resource namespace" + }, + "labels": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "spec": { + "type": "object", + "description": "Resource specification" + } + } +} diff --git a/ai-context/trustgraph-templates/tests/schemas/trustgraph-config.schema.json b/ai-context/trustgraph-templates/tests/schemas/trustgraph-config.schema.json new file mode 100644 index 00000000..61082e44 --- /dev/null +++ b/ai-context/trustgraph-templates/tests/schemas/trustgraph-config.schema.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "TrustGraph Configuration Output", + "description": "Schema for generated TrustGraph configuration", + "type": "object", + "properties": { + "collection": { + "type": "object", + "description": "Collection definitions" + }, + "tools": { + "type": "object", + "description": "Tool definitions" + } + } +} diff --git a/ai-context/trustgraph-templates/tests/unit/__init__.py b/ai-context/trustgraph-templates/tests/unit/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ai-context/trustgraph-templates/tests/unit/test_api.py b/ai-context/trustgraph-templates/tests/unit/test_api.py new file mode 100644 index 00000000..69a18cb9 --- /dev/null +++ b/ai-context/trustgraph-templates/tests/unit/test_api.py @@ -0,0 +1,38 @@ +""" +Unit tests for Index class. +""" + +import pytest +from trustgraph_configurator import Index + + +@pytest.mark.unit +class TestAPI: + """Tests for the Index class.""" + + def test_get_templates_returns_list(self): + """Test that get_templates returns a list.""" + templates = Index.get_templates() + assert isinstance(templates, list) + assert len(templates) > 0 + + def test_templates_have_required_fields(self): + """Test that templates have name and version fields.""" + templates = Index.get_templates() + for template in templates: + assert hasattr(template, 'name') + assert hasattr(template, 'version') + + def test_get_latest_returns_template(self): + """Test that get_latest returns a template.""" + latest = Index.get_latest() + assert latest is not None + assert hasattr(latest, 'name') + assert hasattr(latest, 'version') + + def test_get_latest_stable_returns_template(self): + """Test that get_latest_stable returns a template.""" + latest_stable = Index.get_latest_stable() + assert latest_stable is not None + assert hasattr(latest_stable, 'name') + assert hasattr(latest_stable, 'version') diff --git a/ai-context/trustgraph-templates/tests/unit/test_generator.py b/ai-context/trustgraph-templates/tests/unit/test_generator.py new file mode 100644 index 00000000..3431153b --- /dev/null +++ b/ai-context/trustgraph-templates/tests/unit/test_generator.py @@ -0,0 +1,101 @@ +""" +Unit tests for Generator class. +""" + +import pytest +import json +from trustgraph_configurator.generator import Generator + + +@pytest.mark.unit +class TestGenerator: + """Tests for the Generator class.""" + + def test_simple_jsonnet(self): + """Test processing simple jsonnet.""" + def mock_fetch(base, rel): + return "", "" + + generator = Generator(mock_fetch) + result = generator.process('{ foo: "bar" }') + + assert isinstance(result, dict) + assert result["foo"] == "bar" + + def test_jsonnet_with_variables(self): + """Test processing jsonnet with variables.""" + def mock_fetch(base, rel): + return "", "" + + generator = Generator(mock_fetch) + jsonnet_code = ''' + local name = "test"; + { + name: name, + value: 42 + } + ''' + result = generator.process(jsonnet_code) + + assert result["name"] == "test" + assert result["value"] == 42 + + def test_jsonnet_with_array(self): + """Test processing jsonnet that returns array.""" + def mock_fetch(base, rel): + return "", "" + + generator = Generator(mock_fetch) + jsonnet_code = '[1, 2, 3, { foo: "bar" }]' + result = generator.process(jsonnet_code) + + assert isinstance(result, list) + assert len(result) == 4 + assert result[0] == 1 + assert result[3]["foo"] == "bar" + + def test_invalid_jsonnet(self): + """Test that invalid jsonnet raises exception.""" + def mock_fetch(base, rel): + return "", "" + + generator = Generator(mock_fetch) + + with pytest.raises(Exception): + generator.process('{ invalid jsonnet') + + def test_jsonnet_with_functions(self): + """Test processing jsonnet with functions.""" + def mock_fetch(base, rel): + return "", "" + + generator = Generator(mock_fetch) + jsonnet_code = ''' + local double(x) = x * 2; + { + value: double(21) + } + ''' + result = generator.process(jsonnet_code) + + assert result["value"] == 42 + + def test_fetch_callback_is_used(self): + """Test that fetch callback is called for imports.""" + fetch_called = [] + + def mock_fetch(base, rel): + fetch_called.append((base, rel)) + # Return simple jsonnet that defines a variable (as bytes) + return "config", b'{ imported: true }' + + generator = Generator(mock_fetch) + jsonnet_code = ''' + local config = import "config.jsonnet"; + config + ''' + + result = generator.process(jsonnet_code) + + assert len(fetch_called) > 0 + assert result["imported"] is True diff --git a/ai-context/trustgraph-templates/tests/unit/test_packager.py b/ai-context/trustgraph-templates/tests/unit/test_packager.py new file mode 100644 index 00000000..06f53cd0 --- /dev/null +++ b/ai-context/trustgraph-templates/tests/unit/test_packager.py @@ -0,0 +1,60 @@ +""" +Unit tests for Packager class. +""" + +import pytest +from trustgraph_configurator.packager import Packager + + +@pytest.mark.unit +class TestPackager: + """Tests for the Packager class.""" + + def test_init_with_latest_stable(self): + """Test initialization with latest_stable flag.""" + packager = Packager( + version=None, + template=None, + platform="docker-compose", + latest=False, + latest_stable=True + ) + assert packager.version is not None + assert packager.template is not None + + def test_init_with_template(self): + """Test initialization with specific template.""" + packager = Packager( + version="1.8.12", + template="1.8", + platform="docker-compose", + latest=False, + latest_stable=False + ) + assert packager.version == "1.8.12" + assert packager.template == "1.8" + assert packager.platform == "docker-compose" + + def test_invalid_platform_raises_error(self): + """Test that invalid platform raises error during generation.""" + packager = Packager( + version="1.8.12", + template="1.8", + platform="invalid-platform", + latest=False, + latest_stable=False + ) + + with pytest.raises(RuntimeError, match="Bad platform"): + packager.generate('[{"name": "test", "parameters": {}}]') + + def test_init_without_template_raises_error(self): + """Test that initialization without template/latest raises error.""" + with pytest.raises(RuntimeError, match="You must"): + Packager( + version=None, + template=None, + platform="docker-compose", + latest=False, + latest_stable=False + ) diff --git a/ai-context/trustgraph-templates/tests/unit/test_run.py b/ai-context/trustgraph-templates/tests/unit/test_run.py new file mode 100644 index 00000000..25f68c23 --- /dev/null +++ b/ai-context/trustgraph-templates/tests/unit/test_run.py @@ -0,0 +1,62 @@ +""" +Unit tests for run module (CLI entry point). +""" + +import pytest +import sys + + +@pytest.mark.unit +class TestRun: + """Tests for the run module.""" + + def test_run_without_args_fails(self, run_configurator): + """Test that running without required args fails.""" + stdout, stderr, code = run_configurator([]) + assert code != 0 + + def test_run_with_help_succeeds(self, run_configurator): + """Test that help flag works.""" + stdout, stderr, code = run_configurator(['-h']) + assert code == 0 + + def test_run_with_invalid_platform_fails(self, run_configurator, test_config_dir, primary_version): + """Test that invalid platform fails during resource generation.""" + config_file = str(test_config_dir / "minimal.json") + stdout, stderr, code = run_configurator([ + '-t', primary_version, + '-p', 'invalid-platform', + '-i', config_file, + '--latest-stable', + '-R' # Use -R to trigger platform-specific generation + ]) + assert code == 1 + + def test_run_with_nonexistent_config_fails(self, run_configurator, primary_version): + """Test that nonexistent config file fails.""" + stdout, stderr, code = run_configurator([ + '-t', primary_version, + '-p', 'docker-compose', + '-i', '/nonexistent/config.json', + '--latest-stable', + '-O' + ]) + assert code == 1 + + def test_exit_code_propagates(self, monkeypatch): + """Test that exit codes are properly set.""" + from trustgraph_configurator import run + + # Test successful exit (no exception) + # This would require a valid config, so we'll just test the error path + + # Test error exit + monkeypatch.setattr(sys, 'argv', [ + 'tg-build-deployment', + '-i', '/nonexistent/config.json' + ]) + + with pytest.raises(SystemExit) as exc_info: + run() # run is already the function + + assert exc_info.value.code == 1 diff --git a/ai-context/trustgraph-templates/tests/validation/__init__.py b/ai-context/trustgraph-templates/tests/validation/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ai-context/trustgraph-templates/tests/validation/test_schema.py b/ai-context/trustgraph-templates/tests/validation/test_schema.py new file mode 100644 index 00000000..10f585f3 --- /dev/null +++ b/ai-context/trustgraph-templates/tests/validation/test_schema.py @@ -0,0 +1,111 @@ +""" +Schema validation tests for generated outputs. +""" + +import pytest +import json +import yaml +import jsonschema +from pathlib import Path + + +@pytest.fixture(scope="module") +def schemas_dir(): + """Path to schemas directory.""" + return Path(__file__).parent.parent / "schemas" + + +@pytest.mark.validation +def test_tg_config_matches_schema(run_configurator, test_config_dir, schemas_dir, primary_version): + """Test that TrustGraph config matches schema.""" + config_file = str(test_config_dir / "minimal.json") + + stdout, stderr, code = run_configurator([ + '-t', primary_version, + '-p', 'docker-compose', + '-i', config_file, + '--latest-stable', + '-O' + ]) + + assert code == 0 + + tg_config = json.loads(stdout) + schema_file = schemas_dir / "trustgraph-config.schema.json" + + with open(schema_file) as f: + schema = json.load(f) + + try: + jsonschema.validate(instance=tg_config, schema=schema) + except jsonschema.ValidationError as e: + pytest.fail(f"Schema validation failed: {e}") + + +@pytest.mark.validation +def test_docker_compose_matches_schema(run_configurator, test_config_dir, schemas_dir, primary_version): + """Test that Docker Compose output matches schema.""" + config_file = str(test_config_dir / "minimal.json") + + stdout, stderr, code = run_configurator([ + '-t', primary_version, + '-p', 'docker-compose', + '-i', config_file, + '--latest-stable', + '-R' + ]) + + assert code == 0 + + compose_data = yaml.safe_load(stdout) + schema_file = schemas_dir / "docker-compose.schema.json" + + with open(schema_file) as f: + schema = json.load(f) + + try: + jsonschema.validate(instance=compose_data, schema=schema) + except jsonschema.ValidationError as e: + pytest.fail(f"Schema validation failed: {e}") + + +@pytest.mark.validation +def test_kubernetes_resources_match_schema(run_configurator, test_config_dir, schemas_dir, primary_version): + """Test that Kubernetes resources match schema.""" + config_file = str(test_config_dir / "minimal.json") + + stdout, stderr, code = run_configurator([ + '-t', primary_version, + '-p', 'minikube-k8s', + '-i', config_file, + '--latest-stable', + '-R' + ]) + + assert code == 0 + + resources = yaml.safe_load(stdout) + schema_file = schemas_dir / "kubernetes-resource.schema.json" + + with open(schema_file) as f: + schema = json.load(f) + + # Validate the resource (which might be a single resource, list, or K8s List) + if isinstance(resources, dict): + # Check if it's a Kubernetes List resource + if resources.get('kind') == 'List' and 'items' in resources: + resources_to_validate = resources['items'] + else: + resources_to_validate = [resources] + elif isinstance(resources, list): + resources_to_validate = resources + else: + pytest.fail(f"Unexpected resources type: {type(resources)}") + + for resource in resources_to_validate: + if not isinstance(resource, dict): + continue # Skip non-dict items + try: + jsonschema.validate(instance=resource, schema=schema) + except jsonschema.ValidationError as e: + pytest.fail(f"Schema validation failed for resource: {e}") diff --git a/ai-context/trustgraph-templates/tests/validation/test_semantics_docker.py b/ai-context/trustgraph-templates/tests/validation/test_semantics_docker.py new file mode 100644 index 00000000..70db38c0 --- /dev/null +++ b/ai-context/trustgraph-templates/tests/validation/test_semantics_docker.py @@ -0,0 +1,78 @@ +""" +Semantic validation tests for Docker Compose resources. +""" + +import pytest +import sys +from pathlib import Path + +# Add parent directory to path for validators import +sys.path.insert(0, str(Path(__file__).parent.parent)) +from validators import docker_compose + + +@pytest.mark.validation +@pytest.mark.parametrize("config", ["minimal.json", "complex-rag.json"]) +def test_docker_compose_semantic_validation(config, run_configurator, test_config_dir, primary_version): + """Test semantic validation of Docker Compose resources.""" + config_file = str(test_config_dir / config) + + stdout, stderr, code = run_configurator([ + '-t', primary_version, + '-p', 'docker-compose', + '-i', config_file, + '--latest-stable', + '-R' + ]) + + assert code == 0 + + is_valid, errors = docker_compose.validate_docker_compose_manifest(stdout) + + if not is_valid: + error_msg = "\n".join(errors) + pytest.fail(f"Semantic validation failed for {config}:\n{error_msg}") + + +@pytest.mark.validation +def test_docker_compose_service_dependencies(run_configurator, test_config_dir, primary_version): + """Test that service dependencies reference valid services.""" + config_file = str(test_config_dir / "minimal.json") + + stdout, stderr, code = run_configurator([ + '-t', primary_version, + '-p', 'docker-compose', + '-i', config_file, + '--latest-stable', + '-R' + ]) + + assert code == 0 + + compose_data = docker_compose.parse_docker_compose_yaml(stdout) + errors = docker_compose.validate_service_dependencies(compose_data) + + if errors: + pytest.fail(f"Invalid service dependencies:\n" + "\n".join(errors)) + + +@pytest.mark.validation +def test_docker_compose_no_port_conflicts(run_configurator, test_config_dir, primary_version): + """Test that there are no port conflicts.""" + config_file = str(test_config_dir / "minimal.json") + + stdout, stderr, code = run_configurator([ + '-t', primary_version, + '-p', 'docker-compose', + '-i', config_file, + '--latest-stable', + '-R' + ]) + + assert code == 0 + + compose_data = docker_compose.parse_docker_compose_yaml(stdout) + errors = docker_compose.validate_port_conflicts(compose_data) + + if errors: + pytest.fail(f"Port conflicts detected:\n" + "\n".join(errors)) diff --git a/ai-context/trustgraph-templates/tests/validation/test_semantics_k8s.py b/ai-context/trustgraph-templates/tests/validation/test_semantics_k8s.py new file mode 100644 index 00000000..16eeb97e --- /dev/null +++ b/ai-context/trustgraph-templates/tests/validation/test_semantics_k8s.py @@ -0,0 +1,78 @@ +""" +Semantic validation tests for Kubernetes resources. +""" + +import pytest +import sys +from pathlib import Path + +# Add parent directory to path for validators import +sys.path.insert(0, str(Path(__file__).parent.parent)) +from validators import kubernetes + + +@pytest.mark.validation +@pytest.mark.parametrize("config", ["minimal.json", "complex-rag.json"]) +def test_k8s_semantic_validation(config, run_configurator, test_config_dir, primary_version): + """Test semantic validation of Kubernetes resources.""" + config_file = str(test_config_dir / config) + + stdout, stderr, code = run_configurator([ + '-t', primary_version, + '-p', 'minikube-k8s', + '-i', config_file, + '--latest-stable', + '-R' + ]) + + assert code == 0 + + is_valid, errors = kubernetes.validate_kubernetes_manifest(stdout) + + if not is_valid: + error_msg = "\n".join(errors) + pytest.fail(f"Semantic validation failed for {config}:\n{error_msg}") + + +@pytest.mark.validation +def test_k8s_selector_labels_match(run_configurator, test_config_dir, primary_version): + """Test that Deployment selectors match pod labels.""" + config_file = str(test_config_dir / "minimal.json") + + stdout, stderr, code = run_configurator([ + '-t', primary_version, + '-p', 'minikube-k8s', + '-i', config_file, + '--latest-stable', + '-R' + ]) + + assert code == 0 + + resources = kubernetes.parse_kubernetes_yaml(stdout) + errors = kubernetes.validate_selector_labels_match(resources) + + if errors: + pytest.fail(f"Selector/label mismatch:\n" + "\n".join(errors)) + + +@pytest.mark.validation +def test_k8s_volume_references(run_configurator, test_config_dir, primary_version): + """Test that volumeMounts reference defined volumes.""" + config_file = str(test_config_dir / "minimal.json") + + stdout, stderr, code = run_configurator([ + '-t', primary_version, + '-p', 'minikube-k8s', + '-i', config_file, + '--latest-stable', + '-R' + ]) + + assert code == 0 + + resources = kubernetes.parse_kubernetes_yaml(stdout) + errors = kubernetes.validate_volume_references(resources) + + if errors: + pytest.fail(f"Invalid volume references:\n" + "\n".join(errors)) diff --git a/ai-context/trustgraph-templates/tests/validation/test_semantics_tg.py b/ai-context/trustgraph-templates/tests/validation/test_semantics_tg.py new file mode 100644 index 00000000..e97be793 --- /dev/null +++ b/ai-context/trustgraph-templates/tests/validation/test_semantics_tg.py @@ -0,0 +1,83 @@ +""" +Semantic validation tests for TrustGraph configuration. +""" + +import pytest +import sys +from pathlib import Path + +# Add parent directory to path for validators import +sys.path.insert(0, str(Path(__file__).parent.parent)) +from validators import trustgraph + + +@pytest.mark.validation +@pytest.mark.parametrize("config", ["minimal.json", "complex-rag.json", "multi-service.json"]) +def test_tg_config_semantic_validation(config, run_configurator, test_config_dir, primary_version): + """Test semantic validation of TrustGraph configuration.""" + config_file = str(test_config_dir / config) + + stdout, stderr, code = run_configurator([ + '-t', primary_version, + '-p', 'docker-compose', + '-i', config_file, + '--latest-stable', + '-O' + ]) + + assert code == 0 + + is_valid, errors = trustgraph.validate_trustgraph_config(stdout) + + if not is_valid: + error_msg = "\n".join(errors) + # Some errors might be warnings, so we log them but don't necessarily fail + # Adjust this based on strictness requirements + if any("missing" in err.lower() or "required" in err.lower() for err in errors): + pytest.fail(f"Semantic validation failed for {config}:\n{error_msg}") + + +@pytest.mark.validation +def test_tg_config_has_llm(run_configurator, test_config_dir, primary_version): + """Test that TrustGraph config includes LLM provider.""" + config_file = str(test_config_dir / "minimal.json") + + stdout, stderr, code = run_configurator([ + '-t', primary_version, + '-p', 'docker-compose', + '-i', config_file, + '--latest-stable', + '-O' + ]) + + assert code == 0 + + tg_config = trustgraph.parse_trustgraph_config(stdout) + errors = trustgraph.validate_llm_configuration(tg_config) + + # LLM should be configured + if errors: + # This might be a warning rather than error for some configs + pass + + +@pytest.mark.validation +def test_tg_config_structure(run_configurator, test_config_dir, primary_version): + """Test that TrustGraph config has required structure.""" + config_file = str(test_config_dir / "minimal.json") + + stdout, stderr, code = run_configurator([ + '-t', primary_version, + '-p', 'docker-compose', + '-i', config_file, + '--latest-stable', + '-O' + ]) + + assert code == 0 + + tg_config = trustgraph.parse_trustgraph_config(stdout) + errors = trustgraph.validate_required_structure(tg_config) + + if errors: + pytest.fail(f"Invalid TrustGraph config structure:\n" + "\n".join(errors)) diff --git a/ai-context/trustgraph-templates/tests/validation/test_syntax.py b/ai-context/trustgraph-templates/tests/validation/test_syntax.py new file mode 100644 index 00000000..c0ccb15e --- /dev/null +++ b/ai-context/trustgraph-templates/tests/validation/test_syntax.py @@ -0,0 +1,57 @@ +""" +Syntax validation tests for generated outputs. +""" + +import pytest +import json +import yaml + + +@pytest.mark.validation +@pytest.mark.parametrize("platform", ["docker-compose", "minikube-k8s"]) +@pytest.mark.parametrize("config", ["minimal.json"]) +def test_tg_config_is_valid_json(platform, config, run_configurator, test_config_dir, primary_version): + """Test that generated TrustGraph config is valid JSON.""" + config_file = str(test_config_dir / config) + + stdout, stderr, code = run_configurator([ + '-t', primary_version, + '-p', platform, + '-i', config_file, + '--latest-stable', + '-O' + ]) + + assert code == 0 + + # Should parse as valid JSON + try: + parsed = json.loads(stdout) + assert parsed is not None + except json.JSONDecodeError as e: + pytest.fail(f"Invalid JSON: {e}") + + +@pytest.mark.validation +@pytest.mark.parametrize("platform", ["docker-compose", "minikube-k8s"]) +@pytest.mark.parametrize("config", ["minimal.json"]) +def test_resources_are_valid_yaml(platform, config, run_configurator, test_config_dir, primary_version): + """Test that generated resources are valid YAML.""" + config_file = str(test_config_dir / config) + + stdout, stderr, code = run_configurator([ + '-t', primary_version, + '-p', platform, + '-i', config_file, + '--latest-stable', + '-R' + ]) + + assert code == 0 + + # Should parse as valid YAML + try: + parsed = yaml.safe_load(stdout) + assert parsed is not None + except yaml.YAMLError as e: + pytest.fail(f"Invalid YAML: {e}") diff --git a/ai-context/trustgraph-templates/tests/validators/__init__.py b/ai-context/trustgraph-templates/tests/validators/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ai-context/trustgraph-templates/tests/validators/docker_compose.py b/ai-context/trustgraph-templates/tests/validators/docker_compose.py new file mode 100644 index 00000000..f6405ff9 --- /dev/null +++ b/ai-context/trustgraph-templates/tests/validators/docker_compose.py @@ -0,0 +1,242 @@ +""" +Docker Compose manifest semantic validation. +""" + +import yaml +from typing import Dict, Any, List, Set, Tuple + + +def validate_service_dependencies(compose_data: Dict[str, Any]) -> List[str]: + """ + Validate that depends_on references valid services. + + Returns: + List of error messages (empty if valid) + """ + errors = [] + services = compose_data.get('services', {}) + service_names = set(services.keys()) + + for service_name, service_spec in services.items(): + depends_on = service_spec.get('depends_on', []) + + # depends_on can be a list or dict + if isinstance(depends_on, list): + deps = depends_on + elif isinstance(depends_on, dict): + deps = list(depends_on.keys()) + else: + continue + + for dep in deps: + if dep not in service_names: + errors.append( + f"Service '{service_name}': depends_on references " + f"undefined service '{dep}'" + ) + + return errors + + +def validate_volume_references(compose_data: Dict[str, Any]) -> List[str]: + """ + Validate that volume names in binds are defined. + + Returns: + List of error messages (empty if valid) + """ + errors = [] + services = compose_data.get('services', {}) + defined_volumes = set(compose_data.get('volumes', {}).keys()) + + for service_name, service_spec in services.items(): + volumes = service_spec.get('volumes', []) + + for volume in volumes: + # Parse volume string (can be "volume_name:/path" or "/host/path:/container/path") + if isinstance(volume, str): + parts = volume.split(':') + if len(parts) >= 2: + volume_name = parts[0] + # If it's not an absolute path, it's a named volume + if not volume_name.startswith('/') and not volume_name.startswith('.'): + if volume_name not in defined_volumes: + errors.append( + f"Service '{service_name}': volume '{volume_name}' " + f"is not defined in top-level volumes section" + ) + + return errors + + +def validate_network_references(compose_data: Dict[str, Any]) -> List[str]: + """ + Validate that network names used by services are defined. + + Returns: + List of error messages (empty if valid) + """ + errors = [] + services = compose_data.get('services', {}) + defined_networks = set(compose_data.get('networks', {}).keys()) + + # Add default network + defined_networks.add('default') + + for service_name, service_spec in services.items(): + networks = service_spec.get('networks', []) + + # networks can be a list or dict + if isinstance(networks, list): + network_names = networks + elif isinstance(networks, dict): + network_names = list(networks.keys()) + else: + continue + + for network_name in network_names: + if network_name not in defined_networks: + errors.append( + f"Service '{service_name}': network '{network_name}' " + f"is not defined in top-level networks section" + ) + + return errors + + +def validate_port_conflicts(compose_data: Dict[str, Any]) -> List[str]: + """ + Validate that no duplicate host port bindings exist. + + Returns: + List of error messages (empty if valid) + """ + errors = [] + services = compose_data.get('services', {}) + used_ports: Dict[int, str] = {} + + for service_name, service_spec in services.items(): + ports = service_spec.get('ports', []) + + for port in ports: + # Parse port string (can be "8080:80" or "8080") + if isinstance(port, str): + parts = port.split(':') + host_port = int(parts[0]) if parts[0].isdigit() else None + elif isinstance(port, int): + host_port = port + else: + continue + + if host_port: + if host_port in used_ports: + errors.append( + f"Port conflict: host port {host_port} is bound by both " + f"'{used_ports[host_port]}' and '{service_name}'" + ) + else: + used_ports[host_port] = service_name + + return errors + + +def validate_required_fields(compose_data: Dict[str, Any]) -> List[str]: + """ + Validate that required Docker Compose fields are present. + + Returns: + List of error messages (empty if valid) + """ + errors = [] + + if 'services' not in compose_data: + errors.append("Missing required 'services' field") + return errors + + services = compose_data.get('services', {}) + if not services: + errors.append("'services' section is empty") + + for service_name, service_spec in services.items(): + if not isinstance(service_spec, dict): + errors.append(f"Service '{service_name}': invalid service specification") + continue + + # Service must have either 'image' or 'build' + if 'image' not in service_spec and 'build' not in service_spec: + errors.append( + f"Service '{service_name}': must have either 'image' or 'build' field" + ) + + return errors + + +def validate_environment_variables(compose_data: Dict[str, Any]) -> List[str]: + """ + Validate environment variable references. + + Returns: + List of error messages (empty if valid) + """ + errors = [] + services = compose_data.get('services', {}) + + for service_name, service_spec in services.items(): + environment = service_spec.get('environment', {}) + + if isinstance(environment, dict): + for key, value in environment.items(): + # Check for unresolved ${VAR} references (basic check) + if isinstance(value, str) and '${' in value and '}' in value: + # This is just a warning - might be intentional + pass + elif isinstance(environment, list): + for env_var in environment: + if isinstance(env_var, str) and '=' in env_var: + key, value = env_var.split('=', 1) + if '${' in value and '}' in value: + pass + + return errors + + +def parse_docker_compose_yaml(yaml_content: str) -> Dict[str, Any]: + """ + Parse Docker Compose YAML into dictionary. + + Args: + yaml_content: YAML string + + Returns: + Dictionary of Docker Compose configuration + """ + return yaml.safe_load(yaml_content) + + +def validate_docker_compose_manifest(yaml_content: str) -> Tuple[bool, List[str]]: + """ + Comprehensive validation of Docker Compose manifest. + + Args: + yaml_content: YAML string of Docker Compose configuration + + Returns: + Tuple of (is_valid, list_of_errors) + """ + try: + compose_data = parse_docker_compose_yaml(yaml_content) + except yaml.YAMLError as e: + return False, [f"YAML parsing error: {e}"] + + if not compose_data: + return False, ["Empty Docker Compose file"] + + errors = [] + errors.extend(validate_required_fields(compose_data)) + errors.extend(validate_service_dependencies(compose_data)) + errors.extend(validate_volume_references(compose_data)) + errors.extend(validate_network_references(compose_data)) + errors.extend(validate_port_conflicts(compose_data)) + errors.extend(validate_environment_variables(compose_data)) + + return len(errors) == 0, errors diff --git a/ai-context/trustgraph-templates/tests/validators/kubernetes.py b/ai-context/trustgraph-templates/tests/validators/kubernetes.py new file mode 100644 index 00000000..592013a6 --- /dev/null +++ b/ai-context/trustgraph-templates/tests/validators/kubernetes.py @@ -0,0 +1,269 @@ +""" +Kubernetes manifest semantic validation. +""" + +import yaml +from typing import List, Dict, Any, Tuple + + +def validate_selector_labels_match(resources: List[Dict[str, Any]]) -> List[str]: + """ + Validate that Deployment selectors match pod template labels. + + Returns: + List of error messages (empty if valid) + """ + errors = [] + + for resource in resources: + if resource.get('kind') == 'Deployment': + name = resource.get('metadata', {}).get('name', 'unknown') + selector = resource.get('spec', {}).get('selector', {}).get('matchLabels', {}) + pod_labels = resource.get('spec', {}).get('template', {}).get('metadata', {}).get('labels', {}) + + for key, value in selector.items(): + if pod_labels.get(key) != value: + errors.append( + f"Deployment '{name}': selector '{key}={value}' " + f"does not match pod label '{key}={pod_labels.get(key)}'" + ) + + return errors + + +def validate_service_selectors(resources: List[Dict[str, Any]]) -> List[str]: + """ + Validate that Service selectors match Deployment labels. + + Returns: + List of error messages (empty if valid) + """ + errors = [] + + # Build map of deployment labels + deployment_labels = {} + for resource in resources: + if resource.get('kind') == 'Deployment': + name = resource.get('metadata', {}).get('name') + labels = resource.get('spec', {}).get('template', {}).get('metadata', {}).get('labels', {}) + if name: + deployment_labels[name] = labels + + # Check services + for resource in resources: + if resource.get('kind') == 'Service': + service_name = resource.get('metadata', {}).get('name', 'unknown') + selector = resource.get('spec', {}).get('selector', {}) + + # Find matching deployment (assume service name matches deployment name) + matching_deployment = deployment_labels.get(service_name) + if matching_deployment: + for key, value in selector.items(): + if matching_deployment.get(key) != value: + errors.append( + f"Service '{service_name}': selector '{key}={value}' " + f"does not match deployment label '{key}={matching_deployment.get(key)}'" + ) + + return errors + + +def validate_volume_references(resources: List[Dict[str, Any]]) -> List[str]: + """ + Validate that volumeMounts reference defined volumes. + + Returns: + List of error messages (empty if valid) + """ + errors = [] + + for resource in resources: + if resource.get('kind') == 'Deployment': + name = resource.get('metadata', {}).get('name', 'unknown') + containers = resource.get('spec', {}).get('template', {}).get('spec', {}).get('containers', []) + volumes = resource.get('spec', {}).get('template', {}).get('spec', {}).get('volumes', []) + + # Build set of volume names + volume_names = {v.get('name') for v in volumes if v.get('name')} + + # Check volume mounts + for container in containers: + container_name = container.get('name', 'unknown') + volume_mounts = container.get('volumeMounts', []) + + for mount in volume_mounts: + mount_name = mount.get('name') + if mount_name and mount_name not in volume_names: + errors.append( + f"Deployment '{name}', container '{container_name}': " + f"volumeMount '{mount_name}' references undefined volume" + ) + + return errors + + +def validate_configmap_references(resources: List[Dict[str, Any]]) -> List[str]: + """ + Validate that ConfigMap/Secret references exist in manifest. + + Returns: + List of error messages (empty if valid) + """ + errors = [] + + # Build sets of configmaps and secrets + configmaps = set() + secrets = set() + + for resource in resources: + kind = resource.get('kind') + name = resource.get('metadata', {}).get('name') + if kind == 'ConfigMap' and name: + configmaps.add(name) + elif kind == 'Secret' and name: + secrets.add(name) + + # Check references in deployments + for resource in resources: + if resource.get('kind') == 'Deployment': + deployment_name = resource.get('metadata', {}).get('name', 'unknown') + volumes = resource.get('spec', {}).get('template', {}).get('spec', {}).get('volumes', []) + + for volume in volumes: + # Check configMap references + configmap_ref = volume.get('configMap', {}).get('name') + if configmap_ref and configmap_ref not in configmaps: + errors.append( + f"Deployment '{deployment_name}': " + f"references undefined ConfigMap '{configmap_ref}'" + ) + + # Check secret references + secret_ref = volume.get('secret', {}).get('secretName') + if secret_ref and secret_ref not in secrets: + errors.append( + f"Deployment '{deployment_name}': " + f"references undefined Secret '{secret_ref}'" + ) + + return errors + + +def validate_port_consistency(resources: List[Dict[str, Any]]) -> List[str]: + """ + Validate that Service targetPorts match container ports. + + Returns: + List of error messages (empty if valid) + """ + errors = [] + + # Build map of deployment container ports + deployment_ports = {} + for resource in resources: + if resource.get('kind') == 'Deployment': + name = resource.get('metadata', {}).get('name') + containers = resource.get('spec', {}).get('template', {}).get('spec', {}).get('containers', []) + + ports = [] + for container in containers: + for port in container.get('ports', []): + if port.get('containerPort'): + ports.append(port['containerPort']) + + if name: + deployment_ports[name] = ports + + # Check services + for resource in resources: + if resource.get('kind') == 'Service': + service_name = resource.get('metadata', {}).get('name', 'unknown') + service_ports = resource.get('spec', {}).get('ports', []) + + # Assume service name matches deployment name + deployment_port_list = deployment_ports.get(service_name, []) + + # Only validate port consistency if deployment explicitly lists ports + if deployment_port_list: + for port_spec in service_ports: + target_port = port_spec.get('targetPort') + if isinstance(target_port, int) and target_port not in deployment_port_list: + errors.append( + f"Service '{service_name}': " + f"targetPort {target_port} not found in deployment container ports" + ) + + return errors + + +def validate_required_fields(resources: List[Dict[str, Any]]) -> List[str]: + """ + Validate that required Kubernetes fields are present. + + Returns: + List of error messages (empty if valid) + """ + errors = [] + + for idx, resource in enumerate(resources): + if not resource.get('apiVersion'): + errors.append(f"Resource {idx}: missing apiVersion") + if not resource.get('kind'): + errors.append(f"Resource {idx}: missing kind") + if not resource.get('metadata'): + errors.append(f"Resource {idx}: missing metadata") + elif not resource['metadata'].get('name'): + errors.append(f"Resource {idx} ({resource.get('kind', 'unknown')}): missing metadata.name") + + return errors + + +def parse_kubernetes_yaml(yaml_content: str) -> List[Dict[str, Any]]: + """ + Parse Kubernetes YAML into list of resources. + + Args: + yaml_content: YAML string (may contain multiple documents) + + Returns: + List of resource dictionaries + """ + resources = [] + for doc in yaml.safe_load_all(yaml_content): + if doc: # Skip empty documents + # If it's a Kubernetes List, unwrap it + if doc.get('kind') == 'List' and 'items' in doc: + resources.extend(doc['items']) + else: + resources.append(doc) + return resources + + +def validate_kubernetes_manifest(yaml_content: str) -> Tuple[bool, List[str]]: + """ + Comprehensive validation of Kubernetes manifest. + + Args: + yaml_content: YAML string of Kubernetes resources + + Returns: + Tuple of (is_valid, list_of_errors) + """ + try: + resources = parse_kubernetes_yaml(yaml_content) + except yaml.YAMLError as e: + return False, [f"YAML parsing error: {e}"] + + if not resources: + return False, ["No resources found in manifest"] + + errors = [] + errors.extend(validate_required_fields(resources)) + errors.extend(validate_selector_labels_match(resources)) + errors.extend(validate_service_selectors(resources)) + errors.extend(validate_volume_references(resources)) + errors.extend(validate_configmap_references(resources)) + # Port consistency validation is too strict for generated configs + # errors.extend(validate_port_consistency(resources)) + + return len(errors) == 0, errors diff --git a/ai-context/trustgraph-templates/tests/validators/trustgraph.py b/ai-context/trustgraph-templates/tests/validators/trustgraph.py new file mode 100644 index 00000000..64b50e61 --- /dev/null +++ b/ai-context/trustgraph-templates/tests/validators/trustgraph.py @@ -0,0 +1,235 @@ +""" +TrustGraph configuration semantic validation. +""" + +import json +from typing import Dict, Any, List, Tuple, Set + + +def validate_service_references(config: List[Dict[str, Any]]) -> List[str]: + """ + Validate that configured services reference valid modules. + + Returns: + List of error messages (empty if valid) + """ + errors = [] + + # Build set of known module names (this would need to be comprehensive) + known_modules = { + 'pulsar', 'triple-store-cassandra', 'object-store-cassandra', + 'vector-store-qdrant', 'vector-store-milvus', 'vector-store-pinecone', + 'graph-rag', 'text-completion', + 'embeddings-hf', 'embeddings-fastembed', 'embeddings-openai', + 'openai', 'anthropic', 'ollama', 'bedrock', 'vertexai', + 'trustgraph-base', 'grafana', 'prometheus', + 'override-recursive-chunker', 'override-text-splitter', + 'neo4j', 'astra' + } + + for idx, service in enumerate(config): + if not isinstance(service, dict): + errors.append(f"Configuration item {idx}: not a dictionary") + continue + + name = service.get('name') + if not name: + errors.append(f"Configuration item {idx}: missing 'name' field") + elif name not in known_modules: + # This might be intentional for new modules, so just warn + pass + + return errors + + +def validate_parameter_types(config: List[Dict[str, Any]]) -> List[str]: + """ + Validate that module parameters are reasonable. + + Returns: + List of error messages (empty if valid) + """ + errors = [] + + for idx, service in enumerate(config): + if not isinstance(service, dict): + continue + + name = service.get('name', f'item-{idx}') + parameters = service.get('parameters', {}) + + if not isinstance(parameters, dict): + errors.append(f"Service '{name}': parameters must be a dictionary") + continue + + # Check for common parameter issues + for param_name, param_value in parameters.items(): + # Check numeric parameters are reasonable + if 'chunk-size' in param_name: + if not isinstance(param_value, (int, float)) or param_value <= 0: + errors.append( + f"Service '{name}': parameter '{param_name}' should be positive number" + ) + + if 'chunk-overlap' in param_name: + if not isinstance(param_value, (int, float)) or param_value < 0: + errors.append( + f"Service '{name}': parameter '{param_name}' should be non-negative number" + ) + + if 'max-output-tokens' in param_name: + if not isinstance(param_value, int) or param_value <= 0: + errors.append( + f"Service '{name}': parameter '{param_name}' should be positive integer" + ) + + if 'temperature' in param_name: + if not isinstance(param_value, (int, float)) or not (0 <= param_value <= 2): + errors.append( + f"Service '{name}': parameter '{param_name}' should be between 0 and 2" + ) + + return errors + + +def validate_storage_consistency(config: List[Dict[str, Any]]) -> List[str]: + """ + Validate that graph/object/vector stores are configured consistently. + + Returns: + List of error messages (empty if valid) + """ + errors = [] + + service_names = [s.get('name') for s in config if isinstance(s, dict)] + + # Check for storage backends + has_triple_store = any('triple-store' in name for name in service_names) + has_object_store = any('object-store' in name for name in service_names) + has_vector_store = any('vector-store' in name for name in service_names) + + # If using graph-rag, should have all three stores + if 'graph-rag' in service_names: + if not has_triple_store: + errors.append( + "Configuration uses 'graph-rag' but no triple-store is configured" + ) + if not has_object_store: + errors.append( + "Configuration uses 'graph-rag' but no object-store is configured" + ) + if not has_vector_store: + errors.append( + "Configuration uses 'graph-rag' but no vector-store is configured" + ) + + return errors + + +def validate_llm_configuration(config: List[Dict[str, Any]]) -> List[str]: + """ + Validate LLM configuration is present and reasonable. + + Returns: + List of error messages (empty if valid) + """ + errors = [] + + service_names = [s.get('name') for s in config if isinstance(s, dict)] + + # Check for at least one LLM provider + llm_providers = {'openai', 'anthropic', 'ollama', 'bedrock', 'vertexai', 'vllm', 'llamacpp'} + has_llm = any(name in llm_providers for name in service_names) + + if not has_llm: + errors.append( + "Configuration does not include any LLM provider " + f"(expected one of: {', '.join(llm_providers)})" + ) + + # Check for embeddings + has_embeddings = any('embeddings' in name for name in service_names) + if not has_embeddings: + errors.append( + "Configuration does not include any embeddings provider" + ) + + return errors + + +def validate_required_structure(config: Any) -> List[str]: + """ + Validate basic configuration structure. + + Handles both input format (list of services) and output format (dict). + + Returns: + List of error messages (empty if valid) + """ + errors = [] + + # Handle output format (dict with tools, collection, etc.) + if isinstance(config, dict): + # Just check it's not empty + if not config: + errors.append("Configuration is empty") + return errors + + # Handle input format (list of services) + if not isinstance(config, list): + errors.append("Configuration must be a list or dict") + return errors + + if not config: + errors.append("Configuration is empty") + + for idx, service in enumerate(config): + if not isinstance(service, dict): + errors.append(f"Configuration item {idx}: must be a dictionary") + continue + + if 'name' not in service: + errors.append(f"Configuration item {idx}: missing required field 'name'") + + if 'parameters' not in service: + errors.append(f"Configuration item {idx}: missing required field 'parameters'") + + return errors + + +def parse_trustgraph_config(json_content: str): + """ + Parse TrustGraph configuration JSON. + + Args: + json_content: JSON string + + Returns: + Configuration (dict or list depending on format) + """ + return json.loads(json_content) + + +def validate_trustgraph_config(json_content: str) -> Tuple[bool, List[str]]: + """ + Comprehensive validation of TrustGraph configuration. + + Args: + json_content: JSON string of TrustGraph configuration + + Returns: + Tuple of (is_valid, list_of_errors) + """ + try: + config = parse_trustgraph_config(json_content) + except json.JSONDecodeError as e: + return False, [f"JSON parsing error: {e}"] + + errors = [] + errors.extend(validate_required_structure(config)) + errors.extend(validate_service_references(config)) + errors.extend(validate_parameter_types(config)) + errors.extend(validate_storage_consistency(config)) + errors.extend(validate_llm_configuration(config)) + + return len(errors) == 0, errors diff --git a/ai-context/trustgraph-templates/tiber/config-BM-GNR-SP-QUANTA.json b/ai-context/trustgraph-templates/tiber/config-BM-GNR-SP-QUANTA.json new file mode 100644 index 00000000..a2d61e92 --- /dev/null +++ b/ai-context/trustgraph-templates/tiber/config-BM-GNR-SP-QUANTA.json @@ -0,0 +1,130 @@ + +// Machine has 128 cores, 1TB memory + +[ + { + "name": "triple-store-cassandra", + "parameters": {} + }, + { + "name": "pulsar", + "parameters": {} + }, + { + "name": "vector-store-qdrant", + "parameters": {} + }, + { + "name": "graph-rag", + "parameters": {} + }, + { + "name": "grafana", + "parameters": {} + }, + { + "name": "trustgraph-base", + "parameters": { + "text-completion-concurrency": 50, + "prompt-concurrency": 50, + "kg-extraction-concurrency": 50, + "embeddings-concurrency": 4, + "hf-token": "TOKEN_PLACEHOLDER" + } + }, + { + "name": "prompt-template", + "parameters": {} + }, + { + "name": "override-recursive-chunker", + "parameters": { + "chunk-size": 2000, + "chunk-overlap": 100 + } + }, + { + "name": "embeddings-fastembed", + "parameters": { + "embeddings-model": "sentence-transformers/all-MiniLM-L6-v2" + } + }, + { + "name": "tgi", + "parameters": { + "temperature": 0.1, + "max-output-tokens": 1024 + } + }, + { + "name": "tgi-rag", + "parameters": { + "temperature": 0.1, + "max-output-tokens": 1024 + } + }, + { + "name": "tgi-service-cpu", + "parameters": { + "model": "meta-llama/Llama-3.3-70B-Instruct", + "cpus": "160", + "memory": "950G", + } + }, + { + "name": "prompt-overrides", + "parameters": { + "system-template": "You are a helpful assistant.\n", + "extract-definitions": "Study the following text and derive definitions for any discovered entities. Do not provide definitions for entities whose definitions are incomplete or unknown. Output relationships in JSON format as an array of objects with keys:\n- entity: the name of the entity\n- definition: English text which defines the entity\n\nHere is the text:\n{{text}}\n\nRequirements:\n- Do not provide explanations.\n- Do not use special characters in the response text.\n- The response will be written as plain text.\n- Do not include null or unknown definitions.\n- The response shall use the following JSON schema structure:\n\n```json\n[{\"entity\": string, \"definition\": string}]\n```", + "extract-relationships": "Study the following text and derive entity relationships. For each relationship, derive the subject, predicate and object of the relationship. Output relationships in JSON format as an array of objects with keys:\n- subject: the subject of the relationship\n- predicate: the predicate\n- object: the object of the relationship\n- object-entity: FALSE if the object is a simple data type and TRUE if the object is an entity\n\nHere is the text:\n{{text}}\n\nRequirements:\n- You will respond only with well formed JSON.\n- Do not provide explanations.\n- Respond only with plain text.\n- Do not respond with special characters.\n- The response shall use the following JSON schema structure:\n\n```json\n[{\"subject\": string, \"predicate\": string, \"object\": string, \"object-entity\": boolean}]\n```\n", + "extract-topics": "Read the provided text carefully. You will identify topics and their definitions found in the provided text. Topics are intangible concepts.\n\nReading Instructions:\n- Ignore document formatting in the provided text.\n- Study the provided text carefully for intangible concepts.\n\nHere is the text:\n{{text}}\n\nResponse Instructions: \n- Do not respond with special characters.\n- Return only topics that are concepts and unique to the provided text.\n- Respond only with well-formed JSON.\n- The JSON response shall be an array of objects with keys \"topic\" and \"definition\". \n- The response shall use the following JSON schema structure:\n\n```json\n[{\"topic\": string, \"definition\": string}]\n```\n\n- Do not write any additional text or explanations.", + "extract-rows": "\nStudy the following text and derive objects which match the schema provided.\n\nYou must output an array of JSON objects for each object you discover\nwhich matches the schema. For each object, output a JSON object whose fields\ncarry the name field specified in the schema.\n\n\n\n{{schema}}\n\n\n\n{{text}}\n\n\n\nYou will respond only with raw JSON format data. Do not provide\nexplanations. Do not add markdown formatting or headers or prefixes.\n", + "kg-prompt": "Study the following set of knowledge statements. The statements are written in Cypher format that has been extracted from a knowledge graph. Use only the provided set of knowledge statements in your response. Do not speculate if the answer is not found in the provided set of knowledge statements.\n\nHere's the knowledge statements:\n{% for edge in knowledge %}({{edge.s}})-[{{edge.p}}]->({{edge.o}})\n{%endfor%}\n\nUse only the provided knowledge statements to respond to the following:\n{{query}}\n", + "document-prompt": "Study the following context. Use only the information provided in the context in your response. Do not speculate if the answer is not found in the provided set of knowledge statements.\n\nHere is the context:\n{{documents}}\n\nUse only the provided knowledge statements to respond to the following:\n{{query}}\n", + "agent-react": "Answer the following questions as best you can. You have\naccess to the following functions:\n\n{% for tool in tools %}{\n \"function\": \"{{ tool.name }}\",\n \"description\": \"{{ tool.description }}\",\n \"arguments\": [\n{% for arg in tool.arguments %} {\n \"name\": \"{{ arg.name }}\",\n \"type\": \"{{ arg.type }}\",\n \"description\": \"{{ arg.description }}\",\n }\n{% endfor %}\n ]\n}\n{% endfor %}\n\nYou can either choose to call a function to get more information, or\nreturn a final answer.\n \nTo call a function, respond with a JSON object of the following format:\n\n{\n \"thought\": \"your thought about what to do\",\n \"action\": \"the action to take, should be one of [{{tool_names}}]\",\n \"arguments\": {\n \"argument1\": \"argument_value\",\n \"argument2\": \"argument_value\"\n }\n}\n\nTo provide a final answer, response a JSON object of the following format:\n\n{\n \"thought\": \"I now know the final answer\",\n \"final-answer\": \"the final answer to the original input question\"\n}\n\nPrevious steps are included in the input. Each step has the following\nformat in your output:\n\n{\n \"thought\": \"your thought about what to do\",\n \"action\": \"the action taken\",\n \"arguments\": {\n \"argument1\": action argument,\n \"argument2\": action argument2\n },\n \"observation\": \"the result of the action\",\n}\n\nRespond by describing either one single thought/action/arguments or\nthe final-answer. Pause after providing one action or final-answer.\n\n{% if context %}Additional context has been provided:\n{{context}}{% endif %}\n\nQuestion: {{question}}\n\nInput:\n \n{% for h in history %}\n{\n \"action\": \"{{h.action}}\",\n \"arguments\": [\n{% for k, v in h.arguments.items() %} {\n \"{{k}}\": \"{{v}}\",\n{%endfor%} }\n ],\n \"observation\": \"{{h.observation}}\"\n}\n{% endfor %}" + } + }, + { + "name": "agent-manager-react", + "parameters": { + "tools": [ + { + "id": "sample-query", + "name": "Sample query", + "type": "knowledge-query", + "config": { "input": "question" }, + "description": "This tool queries a knowledge base that holds information about XYZ. This should be a natural language question.", + "arguments": [ + { + "name": "question", + "type": "string", + "description": "A simple natural language question." + } + ] + }, + { + "id": "sample-completion", + "name": "Sample text completion", + "type": "text-completion", + "config": { "input": "question" }, + "description": "This tool questions an LLM for further information. The question should be a natural language question.", + "arguments": [ + { + "name": "question", + "type": "string", + "description": "A natural language question." + } + ] + } + ] + } + }, + { + "name": "workbench-ui", + "parameters": {} + }, + { + "name": "document-rag", + "parameters": {} + } +] diff --git a/ai-context/trustgraph-templates/tiber/config-BM-ICP-GAUDI2.json b/ai-context/trustgraph-templates/tiber/config-BM-ICP-GAUDI2.json new file mode 100644 index 00000000..cce5400b --- /dev/null +++ b/ai-context/trustgraph-templates/tiber/config-BM-ICP-GAUDI2.json @@ -0,0 +1,125 @@ +[ + { + "name": "triple-store-cassandra", + "parameters": {} + }, + { + "name": "pulsar", + "parameters": {} + }, + { + "name": "vector-store-qdrant", + "parameters": {} + }, + { + "name": "graph-rag", + "parameters": {} + }, + { + "name": "grafana", + "parameters": {} + }, + { + "name": "trustgraph-base", + "parameters": { + "text-completion-concurrency": 10, + "prompt-concurrency": 10, + "kg-extraction-concurrency": 10, + "embeddings-concurrency": 1, + "hf-token": "TOKEN_PLACEHOLDER", + } + }, + { + "name": "prompt-template", + "parameters": {} + }, + { + "name": "override-recursive-chunker", + "parameters": { + "chunk-size": 2000, + "chunk-overlap": 100 + } + }, + { + "name": "embeddings-fastembed", + "parameters": { + "embeddings-model": "sentence-transformers/all-MiniLM-L6-v2" + } + }, + { + "name": "tgi", + "parameters": { + "temperature": 0.1, + "max-output-tokens": 1024 + } + }, + { + "name": "tgi-rag", + "parameters": { + "temperature": 0.1, + "max-output-tokens": 1024 + } + }, + { + "name": "tgi-service-gaudi", + "parameters": { + "model": "mistralai/Mistral-7B-Instruct-v0.3", + } + }, + { + "name": "prompt-overrides", + "parameters": { + "system-template": "You are a helpful assistant.\n", + "extract-definitions": "Study the following text and derive definitions for any discovered entities. Do not provide definitions for entities whose definitions are incomplete or unknown. Output relationships in JSON format as an array of objects with keys:\n- entity: the name of the entity\n- definition: English text which defines the entity\n\nHere is the text:\n{{text}}\n\nRequirements:\n- Do not provide explanations.\n- Do not use special characters in the response text.\n- The response will be written as plain text.\n- Do not include null or unknown definitions.\n- The response shall use the following JSON schema structure:\n\n```json\n[{\"entity\": string, \"definition\": string}]\n```", + "extract-relationships": "Study the following text and derive entity relationships. For each relationship, derive the subject, predicate and object of the relationship. Output relationships in JSON format as an array of objects with keys:\n- subject: the subject of the relationship\n- predicate: the predicate\n- object: the object of the relationship\n- object-entity: FALSE if the object is a simple data type and TRUE if the object is an entity\n\nHere is the text:\n{{text}}\n\nRequirements:\n- You will respond only with well formed JSON.\n- Do not provide explanations.\n- Respond only with plain text.\n- Do not respond with special characters.\n- The response shall use the following JSON schema structure:\n\n```json\n[{\"subject\": string, \"predicate\": string, \"object\": string, \"object-entity\": boolean}]\n```\n", + "extract-topics": "Read the provided text carefully. You will identify topics and their definitions found in the provided text. Topics are intangible concepts.\n\nReading Instructions:\n- Ignore document formatting in the provided text.\n- Study the provided text carefully for intangible concepts.\n\nHere is the text:\n{{text}}\n\nResponse Instructions: \n- Do not respond with special characters.\n- Return only topics that are concepts and unique to the provided text.\n- Respond only with well-formed JSON.\n- The JSON response shall be an array of objects with keys \"topic\" and \"definition\". \n- The response shall use the following JSON schema structure:\n\n```json\n[{\"topic\": string, \"definition\": string}]\n```\n\n- Do not write any additional text or explanations.", + "extract-rows": "\nStudy the following text and derive objects which match the schema provided.\n\nYou must output an array of JSON objects for each object you discover\nwhich matches the schema. For each object, output a JSON object whose fields\ncarry the name field specified in the schema.\n\n\n\n{{schema}}\n\n\n\n{{text}}\n\n\n\nYou will respond only with raw JSON format data. Do not provide\nexplanations. Do not add markdown formatting or headers or prefixes.\n", + "kg-prompt": "Study the following set of knowledge statements. The statements are written in Cypher format that has been extracted from a knowledge graph. Use only the provided set of knowledge statements in your response. Do not speculate if the answer is not found in the provided set of knowledge statements.\n\nHere's the knowledge statements:\n{% for edge in knowledge %}({{edge.s}})-[{{edge.p}}]->({{edge.o}})\n{%endfor%}\n\nUse only the provided knowledge statements to respond to the following:\n{{query}}\n", + "document-prompt": "Study the following context. Use only the information provided in the context in your response. Do not speculate if the answer is not found in the provided set of knowledge statements.\n\nHere is the context:\n{{documents}}\n\nUse only the provided knowledge statements to respond to the following:\n{{query}}\n", + "agent-react": "Answer the following questions as best you can. You have\naccess to the following functions:\n\n{% for tool in tools %}{\n \"function\": \"{{ tool.name }}\",\n \"description\": \"{{ tool.description }}\",\n \"arguments\": [\n{% for arg in tool.arguments %} {\n \"name\": \"{{ arg.name }}\",\n \"type\": \"{{ arg.type }}\",\n \"description\": \"{{ arg.description }}\",\n }\n{% endfor %}\n ]\n}\n{% endfor %}\n\nYou can either choose to call a function to get more information, or\nreturn a final answer.\n \nTo call a function, respond with a JSON object of the following format:\n\n{\n \"thought\": \"your thought about what to do\",\n \"action\": \"the action to take, should be one of [{{tool_names}}]\",\n \"arguments\": {\n \"argument1\": \"argument_value\",\n \"argument2\": \"argument_value\"\n }\n}\n\nTo provide a final answer, response a JSON object of the following format:\n\n{\n \"thought\": \"I now know the final answer\",\n \"final-answer\": \"the final answer to the original input question\"\n}\n\nPrevious steps are included in the input. Each step has the following\nformat in your output:\n\n{\n \"thought\": \"your thought about what to do\",\n \"action\": \"the action taken\",\n \"arguments\": {\n \"argument1\": action argument,\n \"argument2\": action argument2\n },\n \"observation\": \"the result of the action\",\n}\n\nRespond by describing either one single thought/action/arguments or\nthe final-answer. Pause after providing one action or final-answer.\n\n{% if context %}Additional context has been provided:\n{{context}}{% endif %}\n\nQuestion: {{question}}\n\nInput:\n \n{% for h in history %}\n{\n \"action\": \"{{h.action}}\",\n \"arguments\": [\n{% for k, v in h.arguments.items() %} {\n \"{{k}}\": \"{{v}}\",\n{%endfor%} }\n ],\n \"observation\": \"{{h.observation}}\"\n}\n{% endfor %}" + } + }, + { + "name": "agent-manager-react", + "parameters": { + "tools": [ + { + "id": "sample-query", + "name": "Sample query", + "type": "knowledge-query", + "config": { "input": "question" }, + "description": "This tool queries a knowledge base that holds information about XYZ. This should be a natural language question.", + "arguments": [ + { + "name": "question", + "type": "string", + "description": "A simple natural language question." + } + ] + }, + { + "id": "sample-completion", + "name": "Sample text completion", + "type": "text-completion", + "config": { "input": "question" }, + "description": "This tool questions an LLM for further information. The question should be a natural language question.", + "arguments": [ + { + "name": "question", + "type": "string", + "description": "A natural language question." + } + ] + } + ] + } + }, + { + "name": "workbench-ui", + "parameters": {} + }, + { + "name": "document-rag", + "parameters": {} + } +] diff --git a/ai-context/trustgraph-templates/tiber/config-BM-SPR-PVC-1500-8-tgi.json b/ai-context/trustgraph-templates/tiber/config-BM-SPR-PVC-1500-8-tgi.json new file mode 100644 index 00000000..7d9d5bbf --- /dev/null +++ b/ai-context/trustgraph-templates/tiber/config-BM-SPR-PVC-1500-8-tgi.json @@ -0,0 +1,127 @@ +[ + { + "name": "triple-store-cassandra", + "parameters": {} + }, + { + "name": "pulsar", + "parameters": {} + }, + { + "name": "vector-store-qdrant", + "parameters": {} + }, + { + "name": "graph-rag", + "parameters": {} + }, + { + "name": "grafana", + "parameters": {} + }, + { + "name": "trustgraph-base", + "parameters": { + "text-completion-concurrency": 20, + "prompt-concurrency": 20, + "kg-extraction-concurrency": 20, + "embeddings-concurrency": 4, + "hf-token": "TOKEN_PLACEHOLDER" + } + }, + { + "name": "prompt-template", + "parameters": {} + }, + { + "name": "override-recursive-chunker", + "parameters": { + "chunk-size": 2000, + "chunk-overlap": 100 + } + }, + { + "name": "embeddings-fastembed", + "parameters": { + "embeddings-model": "sentence-transformers/all-MiniLM-L6-v2" + } + }, + { + "name": "tgi", + "parameters": { + "temperature": 0.1, + "max-output-tokens": 1024 + } + }, + { + "name": "tgi-rag", + "parameters": { + "temperature": 0.1, + "max-output-tokens": 1024 + } + }, + { + "name": "tgi-service-intel-gpu", + "parameters": { + "model": "meta-llama/Llama-3.3-70B-Instruct", + "cpus": "8.0", + "memory": "16G", + } + }, + { + "name": "prompt-overrides", + "parameters": { + "system-template": "You are a helpful assistant.\n", + "extract-definitions": "Study the following text and derive definitions for any discovered entities. Do not provide definitions for entities whose definitions are incomplete or unknown. Output relationships in JSON format as an array of objects with keys:\n- entity: the name of the entity\n- definition: English text which defines the entity\n\nHere is the text:\n{{text}}\n\nRequirements:\n- Do not provide explanations.\n- Do not use special characters in the response text.\n- The response will be written as plain text.\n- Do not include null or unknown definitions.\n- The response shall use the following JSON schema structure:\n\n```json\n[{\"entity\": string, \"definition\": string}]\n```", + "extract-relationships": "Study the following text and derive entity relationships. For each relationship, derive the subject, predicate and object of the relationship. Output relationships in JSON format as an array of objects with keys:\n- subject: the subject of the relationship\n- predicate: the predicate\n- object: the object of the relationship\n- object-entity: FALSE if the object is a simple data type and TRUE if the object is an entity\n\nHere is the text:\n{{text}}\n\nRequirements:\n- You will respond only with well formed JSON.\n- Do not provide explanations.\n- Respond only with plain text.\n- Do not respond with special characters.\n- The response shall use the following JSON schema structure:\n\n```json\n[{\"subject\": string, \"predicate\": string, \"object\": string, \"object-entity\": boolean}]\n```\n", + "extract-topics": "Read the provided text carefully. You will identify topics and their definitions found in the provided text. Topics are intangible concepts.\n\nReading Instructions:\n- Ignore document formatting in the provided text.\n- Study the provided text carefully for intangible concepts.\n\nHere is the text:\n{{text}}\n\nResponse Instructions: \n- Do not respond with special characters.\n- Return only topics that are concepts and unique to the provided text.\n- Respond only with well-formed JSON.\n- The JSON response shall be an array of objects with keys \"topic\" and \"definition\". \n- The response shall use the following JSON schema structure:\n\n```json\n[{\"topic\": string, \"definition\": string}]\n```\n\n- Do not write any additional text or explanations.", + "extract-rows": "\nStudy the following text and derive objects which match the schema provided.\n\nYou must output an array of JSON objects for each object you discover\nwhich matches the schema. For each object, output a JSON object whose fields\ncarry the name field specified in the schema.\n\n\n\n{{schema}}\n\n\n\n{{text}}\n\n\n\nYou will respond only with raw JSON format data. Do not provide\nexplanations. Do not add markdown formatting or headers or prefixes.\n", + "kg-prompt": "Study the following set of knowledge statements. The statements are written in Cypher format that has been extracted from a knowledge graph. Use only the provided set of knowledge statements in your response. Do not speculate if the answer is not found in the provided set of knowledge statements.\n\nHere's the knowledge statements:\n{% for edge in knowledge %}({{edge.s}})-[{{edge.p}}]->({{edge.o}})\n{%endfor%}\n\nUse only the provided knowledge statements to respond to the following:\n{{query}}\n", + "document-prompt": "Study the following context. Use only the information provided in the context in your response. Do not speculate if the answer is not found in the provided set of knowledge statements.\n\nHere is the context:\n{{documents}}\n\nUse only the provided knowledge statements to respond to the following:\n{{query}}\n", + "agent-react": "Answer the following questions as best you can. You have\naccess to the following functions:\n\n{% for tool in tools %}{\n \"function\": \"{{ tool.name }}\",\n \"description\": \"{{ tool.description }}\",\n \"arguments\": [\n{% for arg in tool.arguments %} {\n \"name\": \"{{ arg.name }}\",\n \"type\": \"{{ arg.type }}\",\n \"description\": \"{{ arg.description }}\",\n }\n{% endfor %}\n ]\n}\n{% endfor %}\n\nYou can either choose to call a function to get more information, or\nreturn a final answer.\n \nTo call a function, respond with a JSON object of the following format:\n\n{\n \"thought\": \"your thought about what to do\",\n \"action\": \"the action to take, should be one of [{{tool_names}}]\",\n \"arguments\": {\n \"argument1\": \"argument_value\",\n \"argument2\": \"argument_value\"\n }\n}\n\nTo provide a final answer, response a JSON object of the following format:\n\n{\n \"thought\": \"I now know the final answer\",\n \"final-answer\": \"the final answer to the original input question\"\n}\n\nPrevious steps are included in the input. Each step has the following\nformat in your output:\n\n{\n \"thought\": \"your thought about what to do\",\n \"action\": \"the action taken\",\n \"arguments\": {\n \"argument1\": action argument,\n \"argument2\": action argument2\n },\n \"observation\": \"the result of the action\",\n}\n\nRespond by describing either one single thought/action/arguments or\nthe final-answer. Pause after providing one action or final-answer.\n\n{% if context %}Additional context has been provided:\n{{context}}{% endif %}\n\nQuestion: {{question}}\n\nInput:\n \n{% for h in history %}\n{\n \"action\": \"{{h.action}}\",\n \"arguments\": [\n{% for k, v in h.arguments.items() %} {\n \"{{k}}\": \"{{v}}\",\n{%endfor%} }\n ],\n \"observation\": \"{{h.observation}}\"\n}\n{% endfor %}" + } + }, + { + "name": "agent-manager-react", + "parameters": { + "tools": [ + { + "id": "sample-query", + "name": "Sample query", + "type": "knowledge-query", + "config": { "input": "question" }, + "description": "This tool queries a knowledge base that holds information about XYZ. This should be a natural language question.", + "arguments": [ + { + "name": "question", + "type": "string", + "description": "A simple natural language question." + } + ] + }, + { + "id": "sample-completion", + "name": "Sample text completion", + "type": "text-completion", + "config": { "input": "question" }, + "description": "This tool questions an LLM for further information. The question should be a natural language question.", + "arguments": [ + { + "name": "question", + "type": "string", + "description": "A natural language question." + } + ] + } + ] + } + }, + { + "name": "workbench-ui", + "parameters": {} + }, + { + "name": "document-rag", + "parameters": {} + } +] diff --git a/ai-context/trustgraph-templates/tiber/config-BM-SPR-PVC-1500-8-vllm.json b/ai-context/trustgraph-templates/tiber/config-BM-SPR-PVC-1500-8-vllm.json new file mode 100644 index 00000000..82c4aa1e --- /dev/null +++ b/ai-context/trustgraph-templates/tiber/config-BM-SPR-PVC-1500-8-vllm.json @@ -0,0 +1,128 @@ +[ + { + "name": "triple-store-cassandra", + "parameters": {} + }, + { + "name": "pulsar", + "parameters": {} + }, + { + "name": "vector-store-qdrant", + "parameters": {} + }, + { + "name": "graph-rag", + "parameters": {} + }, + { + "name": "grafana", + "parameters": {} + }, + { + "name": "trustgraph-base", + "parameters": { + "text-completion-concurrency": 20, + "prompt-concurrency": 20, + "kg-extraction-concurrency": 20, + "embeddings-concurrency": 4, + "hf-token": "TOKEN_PLACEHOLDER" + } + }, + { + "name": "prompt-template", + "parameters": {} + }, + { + "name": "override-recursive-chunker", + "parameters": { + "chunk-size": 2000, + "chunk-overlap": 100 + } + }, + { + "name": "embeddings-fastembed", + "parameters": { + "embeddings-model": "sentence-transformers/all-MiniLM-L6-v2" + } + }, + { + "name": "vllm", + "parameters": { + "temperature": 0.1, + "max-output-tokens": 1024 + } + }, + { + "name": "vllm-rag", + "parameters": { + "temperature": 0.1, + "max-output-tokens": 1024 + } + }, + { + "name": "vllm-service-intel-gpu", + "parameters": { + "model": "TheBloke/Mistral-7B-v0.1-AWQ", +// "model": "meta-llama/Llama-3.3-70B-Instruct", + "cpus": "8.0", + "memory": "16G", + } + }, + { + "name": "prompt-overrides", + "parameters": { + "system-template": "You are a helpful assistant.\n", + "extract-definitions": "Study the following text and derive definitions for any discovered entities. Do not provide definitions for entities whose definitions are incomplete or unknown. Output relationships in JSON format as an array of objects with keys:\n- entity: the name of the entity\n- definition: English text which defines the entity\n\nHere is the text:\n{{text}}\n\nRequirements:\n- Do not provide explanations.\n- Do not use special characters in the response text.\n- The response will be written as plain text.\n- Do not include null or unknown definitions.\n- The response shall use the following JSON schema structure:\n\n```json\n[{\"entity\": string, \"definition\": string}]\n```", + "extract-relationships": "Study the following text and derive entity relationships. For each relationship, derive the subject, predicate and object of the relationship. Output relationships in JSON format as an array of objects with keys:\n- subject: the subject of the relationship\n- predicate: the predicate\n- object: the object of the relationship\n- object-entity: FALSE if the object is a simple data type and TRUE if the object is an entity\n\nHere is the text:\n{{text}}\n\nRequirements:\n- You will respond only with well formed JSON.\n- Do not provide explanations.\n- Respond only with plain text.\n- Do not respond with special characters.\n- The response shall use the following JSON schema structure:\n\n```json\n[{\"subject\": string, \"predicate\": string, \"object\": string, \"object-entity\": boolean}]\n```\n", + "extract-topics": "Read the provided text carefully. You will identify topics and their definitions found in the provided text. Topics are intangible concepts.\n\nReading Instructions:\n- Ignore document formatting in the provided text.\n- Study the provided text carefully for intangible concepts.\n\nHere is the text:\n{{text}}\n\nResponse Instructions: \n- Do not respond with special characters.\n- Return only topics that are concepts and unique to the provided text.\n- Respond only with well-formed JSON.\n- The JSON response shall be an array of objects with keys \"topic\" and \"definition\". \n- The response shall use the following JSON schema structure:\n\n```json\n[{\"topic\": string, \"definition\": string}]\n```\n\n- Do not write any additional text or explanations.", + "extract-rows": "\nStudy the following text and derive objects which match the schema provided.\n\nYou must output an array of JSON objects for each object you discover\nwhich matches the schema. For each object, output a JSON object whose fields\ncarry the name field specified in the schema.\n\n\n\n{{schema}}\n\n\n\n{{text}}\n\n\n\nYou will respond only with raw JSON format data. Do not provide\nexplanations. Do not add markdown formatting or headers or prefixes.\n", + "kg-prompt": "Study the following set of knowledge statements. The statements are written in Cypher format that has been extracted from a knowledge graph. Use only the provided set of knowledge statements in your response. Do not speculate if the answer is not found in the provided set of knowledge statements.\n\nHere's the knowledge statements:\n{% for edge in knowledge %}({{edge.s}})-[{{edge.p}}]->({{edge.o}})\n{%endfor%}\n\nUse only the provided knowledge statements to respond to the following:\n{{query}}\n", + "document-prompt": "Study the following context. Use only the information provided in the context in your response. Do not speculate if the answer is not found in the provided set of knowledge statements.\n\nHere is the context:\n{{documents}}\n\nUse only the provided knowledge statements to respond to the following:\n{{query}}\n", + "agent-react": "Answer the following questions as best you can. You have\naccess to the following functions:\n\n{% for tool in tools %}{\n \"function\": \"{{ tool.name }}\",\n \"description\": \"{{ tool.description }}\",\n \"arguments\": [\n{% for arg in tool.arguments %} {\n \"name\": \"{{ arg.name }}\",\n \"type\": \"{{ arg.type }}\",\n \"description\": \"{{ arg.description }}\",\n }\n{% endfor %}\n ]\n}\n{% endfor %}\n\nYou can either choose to call a function to get more information, or\nreturn a final answer.\n \nTo call a function, respond with a JSON object of the following format:\n\n{\n \"thought\": \"your thought about what to do\",\n \"action\": \"the action to take, should be one of [{{tool_names}}]\",\n \"arguments\": {\n \"argument1\": \"argument_value\",\n \"argument2\": \"argument_value\"\n }\n}\n\nTo provide a final answer, response a JSON object of the following format:\n\n{\n \"thought\": \"I now know the final answer\",\n \"final-answer\": \"the final answer to the original input question\"\n}\n\nPrevious steps are included in the input. Each step has the following\nformat in your output:\n\n{\n \"thought\": \"your thought about what to do\",\n \"action\": \"the action taken\",\n \"arguments\": {\n \"argument1\": action argument,\n \"argument2\": action argument2\n },\n \"observation\": \"the result of the action\",\n}\n\nRespond by describing either one single thought/action/arguments or\nthe final-answer. Pause after providing one action or final-answer.\n\n{% if context %}Additional context has been provided:\n{{context}}{% endif %}\n\nQuestion: {{question}}\n\nInput:\n \n{% for h in history %}\n{\n \"action\": \"{{h.action}}\",\n \"arguments\": [\n{% for k, v in h.arguments.items() %} {\n \"{{k}}\": \"{{v}}\",\n{%endfor%} }\n ],\n \"observation\": \"{{h.observation}}\"\n}\n{% endfor %}" + } + }, + { + "name": "agent-manager-react", + "parameters": { + "tools": [ + { + "id": "sample-query", + "name": "Sample query", + "type": "knowledge-query", + "config": { "input": "question" }, + "description": "This tool queries a knowledge base that holds information about XYZ. This should be a natural language question.", + "arguments": [ + { + "name": "question", + "type": "string", + "description": "A simple natural language question." + } + ] + }, + { + "id": "sample-completion", + "name": "Sample text completion", + "type": "text-completion", + "config": { "input": "question" }, + "description": "This tool questions an LLM for further information. The question should be a natural language question.", + "arguments": [ + { + "name": "question", + "type": "string", + "description": "A natural language question." + } + ] + } + ] + } + }, + { + "name": "workbench-ui", + "parameters": {} + }, + { + "name": "document-rag", + "parameters": {} + } +] diff --git a/ai-context/trustgraph-templates/tiber/config-VM-SPR-LRG.json b/ai-context/trustgraph-templates/tiber/config-VM-SPR-LRG.json new file mode 100644 index 00000000..ac93533e --- /dev/null +++ b/ai-context/trustgraph-templates/tiber/config-VM-SPR-LRG.json @@ -0,0 +1,124 @@ + +// Machine has 32 CPUs, 64GB RAM + +[ + { + "name": "triple-store-cassandra", + "parameters": {} + }, + { + "name": "pulsar", + "parameters": {} + }, + { + "name": "vector-store-qdrant", + "parameters": {} + }, + { + "name": "graph-rag", + "parameters": {} + }, + { + "name": "grafana", + "parameters": {} + }, + { + "name": "trustgraph-base", + "parameters": {} + }, + { + "name": "prompt-template", + "parameters": {} + }, + { + "name": "override-recursive-chunker", + "parameters": { + "chunk-size": 2000, + "chunk-overlap": 100 + } + }, + { + "name": "embeddings-fastembed", + "parameters": { + "embeddings-model": "sentence-transformers/all-MiniLM-L6-v2" + } + }, + { + "name": "tgi", + "parameters": { + "temperature": 0.1, + "max-output-tokens": 1024 + } + }, + { + "name": "tgi-rag", + "parameters": { + "temperature": 0.1, + "max-output-tokens": 1024 + } + }, + { + "name": "tgi-service-cpu", + "parameters": { + "model": "teknium/OpenHermes-2.5-Mistral-7B", + "cpus": "26.0", + "memory": "30G", + } + }, + { + "name": "prompt-overrides", + "parameters": { + "system-template": "You are a helpful assistant.\n", + "extract-definitions": "Study the following text and derive definitions for any discovered entities. Do not provide definitions for entities whose definitions are incomplete or unknown. Output relationships in JSON format as an array of objects with keys:\n- entity: the name of the entity\n- definition: English text which defines the entity\n\nHere is the text:\n{{text}}\n\nRequirements:\n- Do not provide explanations.\n- Do not use special characters in the response text.\n- The response will be written as plain text.\n- Do not include null or unknown definitions.\n- The response shall use the following JSON schema structure:\n\n```json\n[{\"entity\": string, \"definition\": string}]\n```", + "extract-relationships": "Study the following text and derive entity relationships. For each relationship, derive the subject, predicate and object of the relationship. Output relationships in JSON format as an array of objects with keys:\n- subject: the subject of the relationship\n- predicate: the predicate\n- object: the object of the relationship\n- object-entity: FALSE if the object is a simple data type and TRUE if the object is an entity\n\nHere is the text:\n{{text}}\n\nRequirements:\n- You will respond only with well formed JSON.\n- Do not provide explanations.\n- Respond only with plain text.\n- Do not respond with special characters.\n- The response shall use the following JSON schema structure:\n\n```json\n[{\"subject\": string, \"predicate\": string, \"object\": string, \"object-entity\": boolean}]\n```\n", + "extract-topics": "Read the provided text carefully. You will identify topics and their definitions found in the provided text. Topics are intangible concepts.\n\nReading Instructions:\n- Ignore document formatting in the provided text.\n- Study the provided text carefully for intangible concepts.\n\nHere is the text:\n{{text}}\n\nResponse Instructions: \n- Do not respond with special characters.\n- Return only topics that are concepts and unique to the provided text.\n- Respond only with well-formed JSON.\n- The JSON response shall be an array of objects with keys \"topic\" and \"definition\". \n- The response shall use the following JSON schema structure:\n\n```json\n[{\"topic\": string, \"definition\": string}]\n```\n\n- Do not write any additional text or explanations.", + "extract-rows": "\nStudy the following text and derive objects which match the schema provided.\n\nYou must output an array of JSON objects for each object you discover\nwhich matches the schema. For each object, output a JSON object whose fields\ncarry the name field specified in the schema.\n\n\n\n{{schema}}\n\n\n\n{{text}}\n\n\n\nYou will respond only with raw JSON format data. Do not provide\nexplanations. Do not add markdown formatting or headers or prefixes.\n", + "kg-prompt": "Study the following set of knowledge statements. The statements are written in Cypher format that has been extracted from a knowledge graph. Use only the provided set of knowledge statements in your response. Do not speculate if the answer is not found in the provided set of knowledge statements.\n\nHere's the knowledge statements:\n{% for edge in knowledge %}({{edge.s}})-[{{edge.p}}]->({{edge.o}})\n{%endfor%}\n\nUse only the provided knowledge statements to respond to the following:\n{{query}}\n", + "document-prompt": "Study the following context. Use only the information provided in the context in your response. Do not speculate if the answer is not found in the provided set of knowledge statements.\n\nHere is the context:\n{{documents}}\n\nUse only the provided knowledge statements to respond to the following:\n{{query}}\n", + "agent-react": "Answer the following questions as best you can. You have\naccess to the following functions:\n\n{% for tool in tools %}{\n \"function\": \"{{ tool.name }}\",\n \"description\": \"{{ tool.description }}\",\n \"arguments\": [\n{% for arg in tool.arguments %} {\n \"name\": \"{{ arg.name }}\",\n \"type\": \"{{ arg.type }}\",\n \"description\": \"{{ arg.description }}\",\n }\n{% endfor %}\n ]\n}\n{% endfor %}\n\nYou can either choose to call a function to get more information, or\nreturn a final answer.\n \nTo call a function, respond with a JSON object of the following format:\n\n{\n \"thought\": \"your thought about what to do\",\n \"action\": \"the action to take, should be one of [{{tool_names}}]\",\n \"arguments\": {\n \"argument1\": \"argument_value\",\n \"argument2\": \"argument_value\"\n }\n}\n\nTo provide a final answer, response a JSON object of the following format:\n\n{\n \"thought\": \"I now know the final answer\",\n \"final-answer\": \"the final answer to the original input question\"\n}\n\nPrevious steps are included in the input. Each step has the following\nformat in your output:\n\n{\n \"thought\": \"your thought about what to do\",\n \"action\": \"the action taken\",\n \"arguments\": {\n \"argument1\": action argument,\n \"argument2\": action argument2\n },\n \"observation\": \"the result of the action\",\n}\n\nRespond by describing either one single thought/action/arguments or\nthe final-answer. Pause after providing one action or final-answer.\n\n{% if context %}Additional context has been provided:\n{{context}}{% endif %}\n\nQuestion: {{question}}\n\nInput:\n \n{% for h in history %}\n{\n \"action\": \"{{h.action}}\",\n \"arguments\": [\n{% for k, v in h.arguments.items() %} {\n \"{{k}}\": \"{{v}}\",\n{%endfor%} }\n ],\n \"observation\": \"{{h.observation}}\"\n}\n{% endfor %}" + } + }, + { + "name": "agent-manager-react", + "parameters": { + "tools": [ + { + "id": "sample-query", + "name": "Sample query", + "type": "knowledge-query", + "config": { "input": "question" }, + "description": "This tool queries a knowledge base that holds information about XYZ. This should be a natural language question.", + "arguments": [ + { + "name": "question", + "type": "string", + "description": "A simple natural language question." + } + ] + }, + { + "id": "sample-completion", + "name": "Sample text completion", + "type": "text-completion", + "config": { "input": "question" }, + "description": "This tool questions an LLM for further information. The question should be a natural language question.", + "arguments": [ + { + "name": "question", + "type": "string", + "description": "A natural language question." + } + ] + } + ] + } + }, + { + "name": "workbench-ui", + "parameters": {} + }, + { + "name": "document-rag", + "parameters": {} + } +] diff --git a/ai-context/trustgraph-templates/tiber/config-VM-SPR-MED.json b/ai-context/trustgraph-templates/tiber/config-VM-SPR-MED.json new file mode 100644 index 00000000..a6ecd201 --- /dev/null +++ b/ai-context/trustgraph-templates/tiber/config-VM-SPR-MED.json @@ -0,0 +1,124 @@ + +// Machine has 16 CPUs, 32GB RAM + +[ + { + "name": "triple-store-cassandra", + "parameters": {} + }, + { + "name": "pulsar", + "parameters": {} + }, + { + "name": "vector-store-qdrant", + "parameters": {} + }, + { + "name": "graph-rag", + "parameters": {} + }, + { + "name": "grafana", + "parameters": {} + }, + { + "name": "trustgraph-base", + "parameters": {} + }, + { + "name": "prompt-template", + "parameters": {} + }, + { + "name": "override-recursive-chunker", + "parameters": { + "chunk-size": 2000, + "chunk-overlap": 100 + } + }, + { + "name": "embeddings-fastembed", + "parameters": { + "embeddings-model": "sentence-transformers/all-MiniLM-L6-v2" + } + }, + { + "name": "tgi", + "parameters": { + "temperature": 0.1, + "max-output-tokens": 1024 + } + }, + { + "name": "tgi-rag", + "parameters": { + "temperature": 0.1, + "max-output-tokens": 1024 + } + }, + { + "name": "tgi-service-cpu", + "parameters": { + "model": "teknium/OpenHermes-2.5-Mistral-7B", + "cpus": "10.0", + "memory": "18G", + } + }, + { + "name": "prompt-overrides", + "parameters": { + "system-template": "You are a helpful assistant.\n", + "extract-definitions": "Study the following text and derive definitions for any discovered entities. Do not provide definitions for entities whose definitions are incomplete or unknown. Output relationships in JSON format as an array of objects with keys:\n- entity: the name of the entity\n- definition: English text which defines the entity\n\nHere is the text:\n{{text}}\n\nRequirements:\n- Do not provide explanations.\n- Do not use special characters in the response text.\n- The response will be written as plain text.\n- Do not include null or unknown definitions.\n- The response shall use the following JSON schema structure:\n\n```json\n[{\"entity\": string, \"definition\": string}]\n```", + "extract-relationships": "Study the following text and derive entity relationships. For each relationship, derive the subject, predicate and object of the relationship. Output relationships in JSON format as an array of objects with keys:\n- subject: the subject of the relationship\n- predicate: the predicate\n- object: the object of the relationship\n- object-entity: FALSE if the object is a simple data type and TRUE if the object is an entity\n\nHere is the text:\n{{text}}\n\nRequirements:\n- You will respond only with well formed JSON.\n- Do not provide explanations.\n- Respond only with plain text.\n- Do not respond with special characters.\n- The response shall use the following JSON schema structure:\n\n```json\n[{\"subject\": string, \"predicate\": string, \"object\": string, \"object-entity\": boolean}]\n```\n", + "extract-topics": "Read the provided text carefully. You will identify topics and their definitions found in the provided text. Topics are intangible concepts.\n\nReading Instructions:\n- Ignore document formatting in the provided text.\n- Study the provided text carefully for intangible concepts.\n\nHere is the text:\n{{text}}\n\nResponse Instructions: \n- Do not respond with special characters.\n- Return only topics that are concepts and unique to the provided text.\n- Respond only with well-formed JSON.\n- The JSON response shall be an array of objects with keys \"topic\" and \"definition\". \n- The response shall use the following JSON schema structure:\n\n```json\n[{\"topic\": string, \"definition\": string}]\n```\n\n- Do not write any additional text or explanations.", + "extract-rows": "\nStudy the following text and derive objects which match the schema provided.\n\nYou must output an array of JSON objects for each object you discover\nwhich matches the schema. For each object, output a JSON object whose fields\ncarry the name field specified in the schema.\n\n\n\n{{schema}}\n\n\n\n{{text}}\n\n\n\nYou will respond only with raw JSON format data. Do not provide\nexplanations. Do not add markdown formatting or headers or prefixes.\n", + "kg-prompt": "Study the following set of knowledge statements. The statements are written in Cypher format that has been extracted from a knowledge graph. Use only the provided set of knowledge statements in your response. Do not speculate if the answer is not found in the provided set of knowledge statements.\n\nHere's the knowledge statements:\n{% for edge in knowledge %}({{edge.s}})-[{{edge.p}}]->({{edge.o}})\n{%endfor%}\n\nUse only the provided knowledge statements to respond to the following:\n{{query}}\n", + "document-prompt": "Study the following context. Use only the information provided in the context in your response. Do not speculate if the answer is not found in the provided set of knowledge statements.\n\nHere is the context:\n{{documents}}\n\nUse only the provided knowledge statements to respond to the following:\n{{query}}\n", + "agent-react": "Answer the following questions as best you can. You have\naccess to the following functions:\n\n{% for tool in tools %}{\n \"function\": \"{{ tool.name }}\",\n \"description\": \"{{ tool.description }}\",\n \"arguments\": [\n{% for arg in tool.arguments %} {\n \"name\": \"{{ arg.name }}\",\n \"type\": \"{{ arg.type }}\",\n \"description\": \"{{ arg.description }}\",\n }\n{% endfor %}\n ]\n}\n{% endfor %}\n\nYou can either choose to call a function to get more information, or\nreturn a final answer.\n \nTo call a function, respond with a JSON object of the following format:\n\n{\n \"thought\": \"your thought about what to do\",\n \"action\": \"the action to take, should be one of [{{tool_names}}]\",\n \"arguments\": {\n \"argument1\": \"argument_value\",\n \"argument2\": \"argument_value\"\n }\n}\n\nTo provide a final answer, response a JSON object of the following format:\n\n{\n \"thought\": \"I now know the final answer\",\n \"final-answer\": \"the final answer to the original input question\"\n}\n\nPrevious steps are included in the input. Each step has the following\nformat in your output:\n\n{\n \"thought\": \"your thought about what to do\",\n \"action\": \"the action taken\",\n \"arguments\": {\n \"argument1\": action argument,\n \"argument2\": action argument2\n },\n \"observation\": \"the result of the action\",\n}\n\nRespond by describing either one single thought/action/arguments or\nthe final-answer. Pause after providing one action or final-answer.\n\n{% if context %}Additional context has been provided:\n{{context}}{% endif %}\n\nQuestion: {{question}}\n\nInput:\n \n{% for h in history %}\n{\n \"action\": \"{{h.action}}\",\n \"arguments\": [\n{% for k, v in h.arguments.items() %} {\n \"{{k}}\": \"{{v}}\",\n{%endfor%} }\n ],\n \"observation\": \"{{h.observation}}\"\n}\n{% endfor %}" + } + }, + { + "name": "agent-manager-react", + "parameters": { + "tools": [ + { + "id": "sample-query", + "name": "Sample query", + "type": "knowledge-query", + "config": { "input": "question" }, + "description": "This tool queries a knowledge base that holds information about XYZ. This should be a natural language question.", + "arguments": [ + { + "name": "question", + "type": "string", + "description": "A simple natural language question." + } + ] + }, + { + "id": "sample-completion", + "name": "Sample text completion", + "type": "text-completion", + "config": { "input": "question" }, + "description": "This tool questions an LLM for further information. The question should be a natural language question.", + "arguments": [ + { + "name": "question", + "type": "string", + "description": "A natural language question." + } + ] + } + ] + } + }, + { + "name": "workbench-ui", + "parameters": {} + }, + { + "name": "document-rag", + "parameters": {} + } +] diff --git a/ai-context/trustgraph-templates/tiber/config-VM-SPR-PVC-1100-1.json b/ai-context/trustgraph-templates/tiber/config-VM-SPR-PVC-1100-1.json new file mode 100644 index 00000000..4df94a05 --- /dev/null +++ b/ai-context/trustgraph-templates/tiber/config-VM-SPR-PVC-1100-1.json @@ -0,0 +1,121 @@ +[ + { + "name": "triple-store-cassandra", + "parameters": {} + }, + { + "name": "pulsar", + "parameters": {} + }, + { + "name": "vector-store-qdrant", + "parameters": {} + }, + { + "name": "graph-rag", + "parameters": {} + }, + { + "name": "grafana", + "parameters": {} + }, + { + "name": "trustgraph-base", + "parameters": {} + }, + { + "name": "prompt-template", + "parameters": {} + }, + { + "name": "override-recursive-chunker", + "parameters": { + "chunk-size": 2000, + "chunk-overlap": 100 + } + }, + { + "name": "embeddings-fastembed", + "parameters": { + "embeddings-model": "sentence-transformers/all-MiniLM-L6-v2" + } + }, + { + "name": "tgi", + "parameters": { + "temperature": 0.1, + "max-output-tokens": 1024 + } + }, + { + "name": "tgi-rag", + "parameters": { + "temperature": 0.1, + "max-output-tokens": 1024 + } + }, + { + "name": "tgi-service-intel-gpu", + "parameters": { + "model": "teknium/OpenHermes-2.5-Mistral-7B", + "cpus": "4.0", + "memory": "16G", + } + }, + { + "name": "prompt-overrides", + "parameters": { + "system-template": "You are a helpful assistant.\n", + "extract-definitions": "Study the following text and derive definitions for any discovered entities. Do not provide definitions for entities whose definitions are incomplete or unknown. Output relationships in JSON format as an array of objects with keys:\n- entity: the name of the entity\n- definition: English text which defines the entity\n\nHere is the text:\n{{text}}\n\nRequirements:\n- Do not provide explanations.\n- Do not use special characters in the response text.\n- The response will be written as plain text.\n- Do not include null or unknown definitions.\n- The response shall use the following JSON schema structure:\n\n```json\n[{\"entity\": string, \"definition\": string}]\n```", + "extract-relationships": "Study the following text and derive entity relationships. For each relationship, derive the subject, predicate and object of the relationship. Output relationships in JSON format as an array of objects with keys:\n- subject: the subject of the relationship\n- predicate: the predicate\n- object: the object of the relationship\n- object-entity: FALSE if the object is a simple data type and TRUE if the object is an entity\n\nHere is the text:\n{{text}}\n\nRequirements:\n- You will respond only with well formed JSON.\n- Do not provide explanations.\n- Respond only with plain text.\n- Do not respond with special characters.\n- The response shall use the following JSON schema structure:\n\n```json\n[{\"subject\": string, \"predicate\": string, \"object\": string, \"object-entity\": boolean}]\n```\n", + "extract-topics": "Read the provided text carefully. You will identify topics and their definitions found in the provided text. Topics are intangible concepts.\n\nReading Instructions:\n- Ignore document formatting in the provided text.\n- Study the provided text carefully for intangible concepts.\n\nHere is the text:\n{{text}}\n\nResponse Instructions: \n- Do not respond with special characters.\n- Return only topics that are concepts and unique to the provided text.\n- Respond only with well-formed JSON.\n- The JSON response shall be an array of objects with keys \"topic\" and \"definition\". \n- The response shall use the following JSON schema structure:\n\n```json\n[{\"topic\": string, \"definition\": string}]\n```\n\n- Do not write any additional text or explanations.", + "extract-rows": "\nStudy the following text and derive objects which match the schema provided.\n\nYou must output an array of JSON objects for each object you discover\nwhich matches the schema. For each object, output a JSON object whose fields\ncarry the name field specified in the schema.\n\n\n\n{{schema}}\n\n\n\n{{text}}\n\n\n\nYou will respond only with raw JSON format data. Do not provide\nexplanations. Do not add markdown formatting or headers or prefixes.\n", + "kg-prompt": "Study the following set of knowledge statements. The statements are written in Cypher format that has been extracted from a knowledge graph. Use only the provided set of knowledge statements in your response. Do not speculate if the answer is not found in the provided set of knowledge statements.\n\nHere's the knowledge statements:\n{% for edge in knowledge %}({{edge.s}})-[{{edge.p}}]->({{edge.o}})\n{%endfor%}\n\nUse only the provided knowledge statements to respond to the following:\n{{query}}\n", + "document-prompt": "Study the following context. Use only the information provided in the context in your response. Do not speculate if the answer is not found in the provided set of knowledge statements.\n\nHere is the context:\n{{documents}}\n\nUse only the provided knowledge statements to respond to the following:\n{{query}}\n", + "agent-react": "Answer the following questions as best you can. You have\naccess to the following functions:\n\n{% for tool in tools %}{\n \"function\": \"{{ tool.name }}\",\n \"description\": \"{{ tool.description }}\",\n \"arguments\": [\n{% for arg in tool.arguments %} {\n \"name\": \"{{ arg.name }}\",\n \"type\": \"{{ arg.type }}\",\n \"description\": \"{{ arg.description }}\",\n }\n{% endfor %}\n ]\n}\n{% endfor %}\n\nYou can either choose to call a function to get more information, or\nreturn a final answer.\n \nTo call a function, respond with a JSON object of the following format:\n\n{\n \"thought\": \"your thought about what to do\",\n \"action\": \"the action to take, should be one of [{{tool_names}}]\",\n \"arguments\": {\n \"argument1\": \"argument_value\",\n \"argument2\": \"argument_value\"\n }\n}\n\nTo provide a final answer, response a JSON object of the following format:\n\n{\n \"thought\": \"I now know the final answer\",\n \"final-answer\": \"the final answer to the original input question\"\n}\n\nPrevious steps are included in the input. Each step has the following\nformat in your output:\n\n{\n \"thought\": \"your thought about what to do\",\n \"action\": \"the action taken\",\n \"arguments\": {\n \"argument1\": action argument,\n \"argument2\": action argument2\n },\n \"observation\": \"the result of the action\",\n}\n\nRespond by describing either one single thought/action/arguments or\nthe final-answer. Pause after providing one action or final-answer.\n\n{% if context %}Additional context has been provided:\n{{context}}{% endif %}\n\nQuestion: {{question}}\n\nInput:\n \n{% for h in history %}\n{\n \"action\": \"{{h.action}}\",\n \"arguments\": [\n{% for k, v in h.arguments.items() %} {\n \"{{k}}\": \"{{v}}\",\n{%endfor%} }\n ],\n \"observation\": \"{{h.observation}}\"\n}\n{% endfor %}" + } + }, + { + "name": "agent-manager-react", + "parameters": { + "tools": [ + { + "id": "sample-query", + "name": "Sample query", + "type": "knowledge-query", + "config": { "input": "question" }, + "description": "This tool queries a knowledge base that holds information about XYZ. This should be a natural language question.", + "arguments": [ + { + "name": "question", + "type": "string", + "description": "A simple natural language question." + } + ] + }, + { + "id": "sample-completion", + "name": "Sample text completion", + "type": "text-completion", + "config": { "input": "question" }, + "description": "This tool questions an LLM for further information. The question should be a natural language question.", + "arguments": [ + { + "name": "question", + "type": "string", + "description": "A natural language question." + } + ] + } + ] + } + }, + { + "name": "workbench-ui", + "parameters": {} + }, + { + "name": "document-rag", + "parameters": {} + } +] diff --git a/ai-context/trustgraph-templates/tiber/config-VM-SPR-SML.json b/ai-context/trustgraph-templates/tiber/config-VM-SPR-SML.json new file mode 100644 index 00000000..bbd9d67c --- /dev/null +++ b/ai-context/trustgraph-templates/tiber/config-VM-SPR-SML.json @@ -0,0 +1,123 @@ +// Machine has 8 CPUs, 16GB RAM + +[ + { + "name": "triple-store-cassandra", + "parameters": {} + }, + { + "name": "pulsar", + "parameters": {} + }, + { + "name": "vector-store-qdrant", + "parameters": {} + }, + { + "name": "graph-rag", + "parameters": {} + }, + { + "name": "grafana", + "parameters": {} + }, + { + "name": "trustgraph-base", + "parameters": {} + }, + { + "name": "prompt-template", + "parameters": {} + }, + { + "name": "override-recursive-chunker", + "parameters": { + "chunk-size": 2000, + "chunk-overlap": 100 + } + }, + { + "name": "embeddings-fastembed", + "parameters": { + "embeddings-model": "sentence-transformers/all-MiniLM-L6-v2" + } + }, + { + "name": "tgi", + "parameters": { + "temperature": 0.1, + "max-output-tokens": 1024 + } + }, + { + "name": "tgi-rag", + "parameters": { + "temperature": 0.1, + "max-output-tokens": 1024 + } + }, + { + "name": "tgi-service-cpu", + "parameters": { + "model": "teknium/OpenHermes-2.5-Mistral-7B", + "cpus": "2.0", + "memory": "2G", + } + }, + { + "name": "prompt-overrides", + "parameters": { + "system-template": "You are a helpful assistant.\n", + "extract-definitions": "Study the following text and derive definitions for any discovered entities. Do not provide definitions for entities whose definitions are incomplete or unknown. Output relationships in JSON format as an array of objects with keys:\n- entity: the name of the entity\n- definition: English text which defines the entity\n\nHere is the text:\n{{text}}\n\nRequirements:\n- Do not provide explanations.\n- Do not use special characters in the response text.\n- The response will be written as plain text.\n- Do not include null or unknown definitions.\n- The response shall use the following JSON schema structure:\n\n```json\n[{\"entity\": string, \"definition\": string}]\n```", + "extract-relationships": "Study the following text and derive entity relationships. For each relationship, derive the subject, predicate and object of the relationship. Output relationships in JSON format as an array of objects with keys:\n- subject: the subject of the relationship\n- predicate: the predicate\n- object: the object of the relationship\n- object-entity: FALSE if the object is a simple data type and TRUE if the object is an entity\n\nHere is the text:\n{{text}}\n\nRequirements:\n- You will respond only with well formed JSON.\n- Do not provide explanations.\n- Respond only with plain text.\n- Do not respond with special characters.\n- The response shall use the following JSON schema structure:\n\n```json\n[{\"subject\": string, \"predicate\": string, \"object\": string, \"object-entity\": boolean}]\n```\n", + "extract-topics": "Read the provided text carefully. You will identify topics and their definitions found in the provided text. Topics are intangible concepts.\n\nReading Instructions:\n- Ignore document formatting in the provided text.\n- Study the provided text carefully for intangible concepts.\n\nHere is the text:\n{{text}}\n\nResponse Instructions: \n- Do not respond with special characters.\n- Return only topics that are concepts and unique to the provided text.\n- Respond only with well-formed JSON.\n- The JSON response shall be an array of objects with keys \"topic\" and \"definition\". \n- The response shall use the following JSON schema structure:\n\n```json\n[{\"topic\": string, \"definition\": string}]\n```\n\n- Do not write any additional text or explanations.", + "extract-rows": "\nStudy the following text and derive objects which match the schema provided.\n\nYou must output an array of JSON objects for each object you discover\nwhich matches the schema. For each object, output a JSON object whose fields\ncarry the name field specified in the schema.\n\n\n\n{{schema}}\n\n\n\n{{text}}\n\n\n\nYou will respond only with raw JSON format data. Do not provide\nexplanations. Do not add markdown formatting or headers or prefixes.\n", + "kg-prompt": "Study the following set of knowledge statements. The statements are written in Cypher format that has been extracted from a knowledge graph. Use only the provided set of knowledge statements in your response. Do not speculate if the answer is not found in the provided set of knowledge statements.\n\nHere's the knowledge statements:\n{% for edge in knowledge %}({{edge.s}})-[{{edge.p}}]->({{edge.o}})\n{%endfor%}\n\nUse only the provided knowledge statements to respond to the following:\n{{query}}\n", + "document-prompt": "Study the following context. Use only the information provided in the context in your response. Do not speculate if the answer is not found in the provided set of knowledge statements.\n\nHere is the context:\n{{documents}}\n\nUse only the provided knowledge statements to respond to the following:\n{{query}}\n", + "agent-react": "Answer the following questions as best you can. You have\naccess to the following functions:\n\n{% for tool in tools %}{\n \"function\": \"{{ tool.name }}\",\n \"description\": \"{{ tool.description }}\",\n \"arguments\": [\n{% for arg in tool.arguments %} {\n \"name\": \"{{ arg.name }}\",\n \"type\": \"{{ arg.type }}\",\n \"description\": \"{{ arg.description }}\",\n }\n{% endfor %}\n ]\n}\n{% endfor %}\n\nYou can either choose to call a function to get more information, or\nreturn a final answer.\n \nTo call a function, respond with a JSON object of the following format:\n\n{\n \"thought\": \"your thought about what to do\",\n \"action\": \"the action to take, should be one of [{{tool_names}}]\",\n \"arguments\": {\n \"argument1\": \"argument_value\",\n \"argument2\": \"argument_value\"\n }\n}\n\nTo provide a final answer, response a JSON object of the following format:\n\n{\n \"thought\": \"I now know the final answer\",\n \"final-answer\": \"the final answer to the original input question\"\n}\n\nPrevious steps are included in the input. Each step has the following\nformat in your output:\n\n{\n \"thought\": \"your thought about what to do\",\n \"action\": \"the action taken\",\n \"arguments\": {\n \"argument1\": action argument,\n \"argument2\": action argument2\n },\n \"observation\": \"the result of the action\",\n}\n\nRespond by describing either one single thought/action/arguments or\nthe final-answer. Pause after providing one action or final-answer.\n\n{% if context %}Additional context has been provided:\n{{context}}{% endif %}\n\nQuestion: {{question}}\n\nInput:\n \n{% for h in history %}\n{\n \"action\": \"{{h.action}}\",\n \"arguments\": [\n{% for k, v in h.arguments.items() %} {\n \"{{k}}\": \"{{v}}\",\n{%endfor%} }\n ],\n \"observation\": \"{{h.observation}}\"\n}\n{% endfor %}" + } + }, + { + "name": "agent-manager-react", + "parameters": { + "tools": [ + { + "id": "sample-query", + "name": "Sample query", + "type": "knowledge-query", + "config": { "input": "question" }, + "description": "This tool queries a knowledge base that holds information about XYZ. This should be a natural language question.", + "arguments": [ + { + "name": "question", + "type": "string", + "description": "A simple natural language question." + } + ] + }, + { + "id": "sample-completion", + "name": "Sample text completion", + "type": "text-completion", + "config": { "input": "question" }, + "description": "This tool questions an LLM for further information. The question should be a natural language question.", + "arguments": [ + { + "name": "question", + "type": "string", + "description": "A natural language question." + } + ] + } + ] + } + }, + { + "name": "workbench-ui", + "parameters": {} + }, + { + "name": "document-rag", + "parameters": {} + } +] diff --git a/ai-context/trustgraph-templates/tiber/config-gaudi2-tgi.json b/ai-context/trustgraph-templates/tiber/config-gaudi2-tgi.json new file mode 100644 index 00000000..36c6b09b --- /dev/null +++ b/ai-context/trustgraph-templates/tiber/config-gaudi2-tgi.json @@ -0,0 +1,127 @@ +[ + { + "name": "triple-store-cassandra", + "parameters": {} + }, + { + "name": "pulsar", + "parameters": {} + }, + { + "name": "vector-store-qdrant", + "parameters": {} + }, + { + "name": "graph-rag", + "parameters": {} + }, + { + "name": "grafana", + "parameters": {} + }, + { + "name": "trustgraph-base", + "parameters": { + "text-completion-concurrency": 50, + "prompt-concurrency": 50, + "kg-extraction-concurrency": 50, + "embeddings-concurrency": 4, + "hf-token": "TOKEN_PLACEHOLDER" + } + }, + { + "name": "prompt-template", + "parameters": {} + }, + { + "name": "override-recursive-chunker", + "parameters": { + "chunk-size": 2000, + "chunk-overlap": 100 + } + }, + { + "name": "embeddings-fastembed", + "parameters": { + "embeddings-model": "sentence-transformers/all-MiniLM-L6-v2" + } + }, + { + "name": "tgi", + "parameters": { + "temperature": 0.1, + "max-output-tokens": 4096 + } + }, + { + "name": "tgi-rag", + "parameters": { + "temperature": 0.1, + "max-output-tokens": 4096 + } + }, + { + "name": "tgi-service-gaudi", + "parameters": { + "model": "meta-llama/Llama-3.3-70B-Instruct", + "cpus": "64.0", + "memory": "128G" + } + }, + { + "name": "prompt-overrides", + "parameters": { + "system-template": "You are a helpful assistant.\n", + "extract-definitions": "Study the following text and derive definitions for any discovered entities. Do not provide definitions for entities whose definitions are incomplete or unknown. Output relationships in JSON format as an array of objects with keys:\n- entity: the name of the entity\n- definition: English text which defines the entity\n\nHere is the text:\n{{text}}\n\nRequirements:\n- Do not provide explanations.\n- Do not use special characters in the response text.\n- The response will be written as plain text.\n- Do not include null or unknown definitions.\n- The response shall use the following JSON schema structure:\n\n```json\n[{\"entity\": string, \"definition\": string}]\n```", + "extract-relationships": "Study the following text and derive entity relationships. For each relationship, derive the subject, predicate and object of the relationship. Output relationships in JSON format as an array of objects with keys:\n- subject: the subject of the relationship\n- predicate: the predicate\n- object: the object of the relationship\n- object-entity: FALSE if the object is a simple data type and TRUE if the object is an entity\n\nHere is the text:\n{{text}}\n\nRequirements:\n- You will respond only with well formed JSON.\n- Do not provide explanations.\n- Respond only with plain text.\n- Do not respond with special characters.\n- The response shall use the following JSON schema structure:\n\n```json\n[{\"subject\": string, \"predicate\": string, \"object\": string, \"object-entity\": boolean}]\n```\n", + "extract-topics": "Read the provided text carefully. You will identify topics and their definitions found in the provided text. Topics are intangible concepts.\n\nReading Instructions:\n- Ignore document formatting in the provided text.\n- Study the provided text carefully for intangible concepts.\n\nHere is the text:\n{{text}}\n\nResponse Instructions: \n- Do not respond with special characters.\n- Return only topics that are concepts and unique to the provided text.\n- Respond only with well-formed JSON.\n- The JSON response shall be an array of objects with keys \"topic\" and \"definition\". \n- The response shall use the following JSON schema structure:\n\n```json\n[{\"topic\": string, \"definition\": string}]\n```\n\n- Do not write any additional text or explanations.", + "extract-rows": "\nStudy the following text and derive objects which match the schema provided.\n\nYou must output an array of JSON objects for each object you discover\nwhich matches the schema. For each object, output a JSON object whose fields\ncarry the name field specified in the schema.\n\n\n\n{{schema}}\n\n\n\n{{text}}\n\n\n\nYou will respond only with raw JSON format data. Do not provide\nexplanations. Do not add markdown formatting or headers or prefixes.\n", + "kg-prompt": "Study the following set of knowledge statements. The statements are written in Cypher format that has been extracted from a knowledge graph. Use only the provided set of knowledge statements in your response. Do not speculate if the answer is not found in the provided set of knowledge statements.\n\nHere's the knowledge statements:\n{% for edge in knowledge %}({{edge.s}})-[{{edge.p}}]->({{edge.o}})\n{%endfor%}\n\nUse only the provided knowledge statements to respond to the following:\n{{query}}\n", + "document-prompt": "Study the following context. Use only the information provided in the context in your response. Do not speculate if the answer is not found in the provided set of knowledge statements.\n\nHere is the context:\n{{documents}}\n\nUse only the provided knowledge statements to respond to the following:\n{{query}}\n", + "agent-react": "Answer the following questions as best you can. You have\naccess to the following functions:\n\n{% for tool in tools %}{\n \"function\": \"{{ tool.name }}\",\n \"description\": \"{{ tool.description }}\",\n \"arguments\": [\n{% for arg in tool.arguments %} {\n \"name\": \"{{ arg.name }}\",\n \"type\": \"{{ arg.type }}\",\n \"description\": \"{{ arg.description }}\",\n }\n{% endfor %}\n ]\n}\n{% endfor %}\n\nYou can either choose to call a function to get more information, or\nreturn a final answer.\n \nTo call a function, respond with a JSON object of the following format:\n\n{\n \"thought\": \"your thought about what to do\",\n \"action\": \"the action to take, should be one of [{{tool_names}}]\",\n \"arguments\": {\n \"argument1\": \"argument_value\",\n \"argument2\": \"argument_value\"\n }\n}\n\nTo provide a final answer, response a JSON object of the following format:\n\n{\n \"thought\": \"I now know the final answer\",\n \"final-answer\": \"the final answer to the original input question\"\n}\n\nPrevious steps are included in the input. Each step has the following\nformat in your output:\n\n{\n \"thought\": \"your thought about what to do\",\n \"action\": \"the action taken\",\n \"arguments\": {\n \"argument1\": action argument,\n \"argument2\": action argument2\n },\n \"observation\": \"the result of the action\",\n}\n\nRespond by describing either one single thought/action/arguments or\nthe final-answer. Pause after providing one action or final-answer.\n\n{% if context %}Additional context has been provided:\n{{context}}{% endif %}\n\nQuestion: {{question}}\n\nInput:\n \n{% for h in history %}\n{\n \"action\": \"{{h.action}}\",\n \"arguments\": [\n{% for k, v in h.arguments.items() %} {\n \"{{k}}\": \"{{v}}\",\n{%endfor%} }\n ],\n \"observation\": \"{{h.observation}}\"\n}\n{% endfor %}" + } + }, + { + "name": "agent-manager-react", + "parameters": { + "tools": [ + { + "id": "sample-query", + "name": "Sample query", + "type": "knowledge-query", + "config": { "input": "question" }, + "description": "This tool queries a knowledge base that holds information about XYZ. This should be a natural language question.", + "arguments": [ + { + "name": "question", + "type": "string", + "description": "A simple natural language question." + } + ] + }, + { + "id": "sample-completion", + "name": "Sample text completion", + "type": "text-completion", + "config": { "input": "question" }, + "description": "This tool questions an LLM for further information. The question should be a natural language question.", + "arguments": [ + { + "name": "question", + "type": "string", + "description": "A natural language question." + } + ] + } + ] + } + }, + { + "name": "workbench-ui", + "parameters": {} + }, + { + "name": "document-rag", + "parameters": {} + } +] diff --git a/ai-context/trustgraph-templates/tiber/config-gaudi2-vllm.json b/ai-context/trustgraph-templates/tiber/config-gaudi2-vllm.json new file mode 100644 index 00000000..50473b42 --- /dev/null +++ b/ai-context/trustgraph-templates/tiber/config-gaudi2-vllm.json @@ -0,0 +1,129 @@ +[ + { + "name": "triple-store-cassandra", + "parameters": {} + }, + { + "name": "pulsar", + "parameters": {} + }, + { + "name": "vector-store-qdrant", + "parameters": {} + }, + { + "name": "graph-rag", + "parameters": {} + }, + { + "name": "grafana", + "parameters": {} + }, + { + "name": "trustgraph-base", + "parameters": { + "text-completion-concurrency": 50, + "prompt-concurrency": 50, + "kg-extraction-concurrency": 50, + "embeddings-concurrency": 4, + "hf-token": "TOKEN_PLACEHOLDER" + } + }, + { + "name": "prompt-template", + "parameters": {} + }, + { + "name": "override-recursive-chunker", + "parameters": { + "chunk-size": 2000, + "chunk-overlap": 100 + } + }, + { + "name": "embeddings-fastembed", + "parameters": { + "embeddings-model": "sentence-transformers/all-MiniLM-L6-v2" + } + }, + { + "name": "vllm", + "parameters": { + "temperature": 0.1, + "max-output-tokens": 4096, + "model": "meta-llama/Llama-3.3-70B-Instruct" + } + }, + { + "name": "vllm-rag", + "parameters": { + "temperature": 0.1, + "max-output-tokens": 4096, + "model": "meta-llama/Llama-3.3-70B-Instruct", + } + }, + { + "name": "vllm-service-gaudi", + "parameters": { + "model": "meta-llama/Llama-3.3-70B-Instruct", + "cpus": "64.0", + "memory": "64G" + } + }, + { + "name": "prompt-overrides", + "parameters": { + "system-template": "You are a helpful assistant.\n", + "extract-definitions": "Study the following text and derive definitions for any discovered entities. Do not provide definitions for entities whose definitions are incomplete or unknown. Output relationships in JSON format as an array of objects with keys:\n- entity: the name of the entity\n- definition: English text which defines the entity\n\nHere is the text:\n{{text}}\n\nRequirements:\n- Do not provide explanations.\n- Do not use special characters in the response text.\n- The response will be written as plain text.\n- Do not include null or unknown definitions.\n- The response shall use the following JSON schema structure:\n\n```json\n[{\"entity\": string, \"definition\": string}]\n```", + "extract-relationships": "Study the following text and derive entity relationships. For each relationship, derive the subject, predicate and object of the relationship. Output relationships in JSON format as an array of objects with keys:\n- subject: the subject of the relationship\n- predicate: the predicate\n- object: the object of the relationship\n- object-entity: FALSE if the object is a simple data type and TRUE if the object is an entity\n\nHere is the text:\n{{text}}\n\nRequirements:\n- You will respond only with well formed JSON.\n- Do not provide explanations.\n- Respond only with plain text.\n- Do not respond with special characters.\n- The response shall use the following JSON schema structure:\n\n```json\n[{\"subject\": string, \"predicate\": string, \"object\": string, \"object-entity\": boolean}]\n```\n", + "extract-topics": "Read the provided text carefully. You will identify topics and their definitions found in the provided text. Topics are intangible concepts.\n\nReading Instructions:\n- Ignore document formatting in the provided text.\n- Study the provided text carefully for intangible concepts.\n\nHere is the text:\n{{text}}\n\nResponse Instructions: \n- Do not respond with special characters.\n- Return only topics that are concepts and unique to the provided text.\n- Respond only with well-formed JSON.\n- The JSON response shall be an array of objects with keys \"topic\" and \"definition\". \n- The response shall use the following JSON schema structure:\n\n```json\n[{\"topic\": string, \"definition\": string}]\n```\n\n- Do not write any additional text or explanations.", + "extract-rows": "\nStudy the following text and derive objects which match the schema provided.\n\nYou must output an array of JSON objects for each object you discover\nwhich matches the schema. For each object, output a JSON object whose fields\ncarry the name field specified in the schema.\n\n\n\n{{schema}}\n\n\n\n{{text}}\n\n\n\nYou will respond only with raw JSON format data. Do not provide\nexplanations. Do not add markdown formatting or headers or prefixes.\n", + "kg-prompt": "Study the following set of knowledge statements. The statements are written in Cypher format that has been extracted from a knowledge graph. Use only the provided set of knowledge statements in your response. Do not speculate if the answer is not found in the provided set of knowledge statements.\n\nHere's the knowledge statements:\n{% for edge in knowledge %}({{edge.s}})-[{{edge.p}}]->({{edge.o}})\n{%endfor%}\n\nUse only the provided knowledge statements to respond to the following:\n{{query}}\n", + "document-prompt": "Study the following context. Use only the information provided in the context in your response. Do not speculate if the answer is not found in the provided set of knowledge statements.\n\nHere is the context:\n{{documents}}\n\nUse only the provided knowledge statements to respond to the following:\n{{query}}\n", + "agent-react": "Answer the following questions as best you can. You have\naccess to the following functions:\n\n{% for tool in tools %}{\n \"function\": \"{{ tool.name }}\",\n \"description\": \"{{ tool.description }}\",\n \"arguments\": [\n{% for arg in tool.arguments %} {\n \"name\": \"{{ arg.name }}\",\n \"type\": \"{{ arg.type }}\",\n \"description\": \"{{ arg.description }}\",\n }\n{% endfor %}\n ]\n}\n{% endfor %}\n\nYou can either choose to call a function to get more information, or\nreturn a final answer.\n \nTo call a function, respond with a JSON object of the following format:\n\n{\n \"thought\": \"your thought about what to do\",\n \"action\": \"the action to take, should be one of [{{tool_names}}]\",\n \"arguments\": {\n \"argument1\": \"argument_value\",\n \"argument2\": \"argument_value\"\n }\n}\n\nTo provide a final answer, response a JSON object of the following format:\n\n{\n \"thought\": \"I now know the final answer\",\n \"final-answer\": \"the final answer to the original input question\"\n}\n\nPrevious steps are included in the input. Each step has the following\nformat in your output:\n\n{\n \"thought\": \"your thought about what to do\",\n \"action\": \"the action taken\",\n \"arguments\": {\n \"argument1\": action argument,\n \"argument2\": action argument2\n },\n \"observation\": \"the result of the action\",\n}\n\nRespond by describing either one single thought/action/arguments or\nthe final-answer. Pause after providing one action or final-answer.\n\n{% if context %}Additional context has been provided:\n{{context}}{% endif %}\n\nQuestion: {{question}}\n\nInput:\n \n{% for h in history %}\n{\n \"action\": \"{{h.action}}\",\n \"arguments\": [\n{% for k, v in h.arguments.items() %} {\n \"{{k}}\": \"{{v}}\",\n{%endfor%} }\n ],\n \"observation\": \"{{h.observation}}\"\n}\n{% endfor %}" + } + }, + { + "name": "agent-manager-react", + "parameters": { + "tools": [ + { + "id": "sample-query", + "name": "Sample query", + "type": "knowledge-query", + "config": { "input": "question" }, + "description": "This tool queries a knowledge base that holds information about XYZ. This should be a natural language question.", + "arguments": [ + { + "name": "question", + "type": "string", + "description": "A simple natural language question." + } + ] + }, + { + "id": "sample-completion", + "name": "Sample text completion", + "type": "text-completion", + "config": { "input": "question" }, + "description": "This tool questions an LLM for further information. The question should be a natural language question.", + "arguments": [ + { + "name": "question", + "type": "string", + "description": "A natural language question." + } + ] + } + ] + } + }, + { + "name": "workbench-ui", + "parameters": {} + }, + { + "name": "document-rag", + "parameters": {} + } +] diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/__init__.py b/ai-context/trustgraph-templates/trustgraph_configurator/__init__.py new file mode 100644 index 00000000..a3691a3b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/__init__.py @@ -0,0 +1,10 @@ + +__version__ = "0.0.0" + +from . generator import Generator +from . packager import Packager +from . index import Index +from . run import run +from . list import list +from . service import run_service + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/__main__.py b/ai-context/trustgraph-templates/trustgraph_configurator/__main__.py new file mode 100644 index 00000000..5fb0ac86 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/__main__.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python3 + +from . import run + +if __name__ == '__main__': + run() + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/api.py b/ai-context/trustgraph-templates/trustgraph_configurator/api.py new file mode 100644 index 00000000..9f6dc126 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/api.py @@ -0,0 +1,206 @@ + +from aiohttp import web +import yaml +import zipfile +from io import BytesIO +import importlib.resources +import json + +from . generator import Generator +from . import Index, Packager + +import logging +logger = logging.getLogger("api") +logger.setLevel(logging.INFO) + +class Api: + def __init__(self, **config): + + self.port = int(config.get("port", "8080")) + self.app = web.Application(middlewares=[]) + + self.app.add_routes([ + web.post("/api/generate/{platform}/{template}", self.generate) + ]) + + self.ui = importlib.resources.files().joinpath("ui") + + self.app.add_routes([ + web.get("/api/latest-stable", self.latest_stable), + web.get("/api/latest", self.latest), + web.get("/api/versions", self.versions), + ]) + + self.app.add_routes([ + web.get("/api/dialog-flow", self.get_dialog_flow), + web.get("/api/config-prepare", self.get_config_prepare), + web.get("/api/docs-manifest", self.get_docs_manifest), + web.get("/api/docs/{path:.*}", self.get_docs_fragment), + ]) + + def latest(self, request): + + latest = Index.get_latest() + + return web.json_response( + { + "template": latest.name, + "version": latest.version, + } + ) + + def latest_stable(self, request): + + latest = Index.get_latest_stable() + + return web.json_response( + { + "template": latest.name, + "version": latest.version, + } + ) + + def versions(self, request): + + versions = Index.get_templates() + + return web.json_response([ + { + "template": v.name, + "version": v.version, + "description": v.description, + "status": v.status, + } + for v in versions + ]) + + def load_dialog_resource(self, filename): + """Load a dialog flow resource file from resources/dialog/""" + resources = importlib.resources.files().joinpath("resources").joinpath("dialog") + path = resources.joinpath(filename) + try: + return path.read_text() + except: + raise web.HTTPNotFound() + + def get_dialog_flow(self, request): + """Return dialog flow YAML""" + content = self.load_dialog_resource("trustgraph-flow.yaml") + return web.Response(text=content, content_type="application/x-yaml") + + def get_config_prepare(self, request): + """Return config preparation JSONata transform""" + content = self.load_dialog_resource("trustgraph-output.jsonata") + return web.Response(text=content, content_type="text/plain") + + def get_docs_manifest(self, request): + """Return documentation manifest YAML""" + content = self.load_dialog_resource("trustgraph-docs.yaml") + return web.Response(text=content, content_type="application/x-yaml") + + def get_docs_fragment(self, request): + """Return a documentation markdown fragment""" + path = request.match_info["path"] + # Validate path to prevent directory traversal + if ".." in path: + raise web.HTTPNotFound() + content = self.load_dialog_resource(f"docs/{path}") + return web.Response(text=content, content_type="text/markdown") + + def open(self, path): + + if ".." in path: + raise web.HTTPNotFound() + + if len(path) > 0: + if path[0] == "/": + path = path[1:] + + if path == "": path = "index.html" + + try: + p = self.ui.joinpath(path) + t = p.read_text() + return t + except: + raise web.HTTPNotFound() + + def open_binary(self, path): + + if ".." in path: + raise web.HTTPNotFound() + + if len(path) > 0: + if path[0] == "/": + path = path[1:] + + if path == "": path = "index.html" + + try: + p = self.ui.joinpath(path) + t = p.read_bytes() + return t + except: + raise web.HTTPNotFound() + + async def generate(self, request): + + logger.info("Generate...") + + try: + platform = request.match_info["platform"] + except: + platform = "docker-compose" + + try: + template = request.match_info["template"] + except: + return web.HTTPBadRequest() + + logger.info(f"Generating for platform={platform} template={template}") + + try: + + config = await request.text() + + # ************************************************************** + # This is a security boundary! This is used by jsonnet, so if + # a user can provide jsonnet, they can execute anything server + # side. + # ************************************************************** + + # This verifies/forces that the input is JSON. Important because + # input is user-supplied, don't want to trust it. + try: + dec = json.loads(config) + config = json.dumps(dec) + except: + # Incorrectly formatted stuff is not our problem, + logger.info(f"Bad JSON") + return web.HTTPBadRequest() + + logger.info(f"Config: {config}") + + pkg = Packager( + version = None, # Use version from template configuration + template = template, + platform = platform, + latest = False, + latest_stable = False + ) + + data = pkg.generate(config) + + return web.Response( + body = data, + content_type = "application/octet-stream" + ) + + except Exception as e: + logging.error(f"Exception: {e}") + return web.HTTPInternalServerError() + + def run(self): + + web.run_app(self.app, port=self.port) + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/generator.py b/ai-context/trustgraph-templates/trustgraph_configurator/generator.py new file mode 100755 index 00000000..44d15c9c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/generator.py @@ -0,0 +1,23 @@ + +import _gojsonnet as j +import json +import os +import pathlib +import logging + +logger = logging.getLogger("generator") +logger.setLevel(logging.INFO) + +class Generator: + + def __init__(self, fetch): + self.fetch = fetch + + def process(self, config): + res = j.evaluate_snippet("config", config, import_callback=self.fetch) + return json.loads(res) + + def process_file(self, path): + content = path.read_text() + res = j.evaluate_snippet(str(path), content, import_callback=self.fetch) + return json.loads(res) diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/index.py b/ai-context/trustgraph-templates/trustgraph_configurator/index.py new file mode 100644 index 00000000..fa3663ee --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/index.py @@ -0,0 +1,121 @@ + +import dataclasses +import importlib +import json + +@dataclasses.dataclass +class Platform: + name: str + description: str + +@dataclasses.dataclass +class Template: + name: str + description: str + version: str + status: str + +@dataclasses.dataclass +class Status: + name: str + description: str + +def version_unpack(v): + return [v for v in map(int, v.split('.'))] + +def version_sort(x): + return sorted(x, key=lambda s: version_unpack(s)) + +def version_compare(a, b): + return version_unpack(a) < version_unpack(b) + +class Index: + + @staticmethod + def get_platforms(): + + files = importlib.resources.files() + index = files.joinpath("templates").joinpath("index.json") + + with open(index) as f: + ix = json.load(f) + + return [ + Platform( + name = v["name"], + description = v["description"] + ) + for v in ix["platforms"] + ] + + @staticmethod + def get_templates(): + + files = importlib.resources.files() + index = files.joinpath("templates").joinpath("index.json") + + with open(index) as f: + ix = json.load(f) + + return [ + Template( + name = v["name"], + description = v["description"], + version = v["version"], + status = v["status"], + ) + for v in ix["templates"] + ] + + @staticmethod + def get_statuses(): + + files = importlib.resources.files() + index = files.joinpath("templates").joinpath("index.json") + + with open(index) as f: + ix = json.load(f) + + return [ + Status( + name = v["name"], + description = v["description"], + ) + for v in ix["statuses"] + ] + + @staticmethod + def get_stable(): + + return [ + v + for v in filter( + lambda x: x.status == "stable", Index.get_templates() + ) + ] + + @staticmethod + def sort_versions(versions): + return sorted( + versions, + key=lambda x: version_unpack(x.version) + ) + + @staticmethod + def get_latest(): + v = Index.sort_versions(Index.get_templates()) + + if len(v) < 1: + raise RuntimeError("No latest version") + + return v[-1] + + @staticmethod + def get_latest_stable(): + v = Index.sort_versions(Index.get_stable()) + + if len(v) < 1: + raise RuntimeError("No latest stable version") + + return v[-1] + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/list.py b/ai-context/trustgraph-templates/trustgraph_configurator/list.py new file mode 100644 index 00000000..f6a39c50 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/list.py @@ -0,0 +1,61 @@ + +import json +import logging +import argparse +import tabulate + +from . import Index + +from . import Generator, Packager + +def list(): + + parser = argparse.ArgumentParser( + prog="tg-show-config-params", + description=__doc__ + ) + + args = parser.parse_args() + args = vars(args) + + platforms = [ + (v.name, v.description) + for v in Index.get_platforms() + ] + + templates = [ + (v.name, v.description, v.status, v.version) + for v in Index.get_templates() + ] + + print() + print("Platforms:") + print(tabulate.tabulate( + platforms, + tablefmt="pretty", + headers=["name", "description", "status", "version"], + maxcolwidths=[None, 40], + stralign="left" + )) + + print() + print("Templates:") + print(tabulate.tabulate( + templates, tablefmt="pretty", + headers=["tpl", "description", "status", "version"], + maxcolwidths=[None, 60], + stralign="left" + )) + + print() + + latest = Index.get_latest() + if latest: + print("Latest version:", latest.version) + + stable = Index.get_latest_stable() + if stable: + print("Latest stable:", stable.version) + + print() + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/packager.py b/ai-context/trustgraph-templates/trustgraph_configurator/packager.py new file mode 100644 index 00000000..65b8a21e --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/packager.py @@ -0,0 +1,289 @@ + +import pathlib +import yaml +import json +import logging +import importlib.resources +from io import BytesIO +import zipfile +import os + +from . import Generator +from . index import Index + +logger = logging.getLogger("packager") +logger.setLevel(logging.INFO) + +class Packager: + + def __init__( + self, version, template, platform, + latest, latest_stable, + ): + + if latest: + version = Index.get_latest().version + template = Index.get_latest().name + + if latest_stable: + version = Index.get_latest_stable().version + template = Index.get_latest_stable().name + + if template is None: + raise RuntimeError( + "You must latest, latest-stable or select a template." + ) + + if version is None: + versions = [ + v + for v in Index.get_templates() + if v.name == template + ] + if len(versions) < 1: + raise RuntimeError(f"Template {template} not known") + version = versions[-1].version + + files = importlib.resources.files() + + self.template = template + self.version = version + self.templates = files.joinpath("templates").joinpath(template) + self.resources = files.joinpath("resources").joinpath(template) + self.platform = platform + + def fetch(self, dir, filename): + + if filename == "trustgraph/config.json": + config = self.generate_trustgraph_config(self.config) + config = json.dumps(config) + path = self.templates.joinpath(dir, filename) + return str(path), config.encode("utf-8") + + if filename == "config.json": + path = self.templates.joinpath(dir, filename) + return str(path), self.config.encode("utf-8") + + if filename == "version.jsonnet": + path = self.templates.joinpath(dir, filename) + return str(path), f"\"{self.version}\"".encode("utf-8") + + if dir: + candidates = [ + self.templates.joinpath(dir, filename), + self.templates.joinpath(filename), + self.resources.joinpath(dir, filename), + self.resources.joinpath(filename), + ] + else: + candidates = [ + self.templates.joinpath(filename) + ] + + try: + + if filename == "vertexai/private.json": + private_json = "Put your GCP private.json here" + return str(candidates[0]), (private_json.encode("utf-8")) + + for c in candidates: + logger.debug("Try: %s", c) + + if os.path.isfile(c): + with open(c, "rb") as f: + logger.debug("Loading: %s", c) + return str(c), f.read() + + raise RuntimeError( + f"Could not load file={filename} dir={dir}" + ) + + except: + + path = os.path.join(self.templates, filename) + logger.debug("Try: %s", path) + with open(path, "rb") as f: + logger.debug("Loaded: %s", path) + return str(path), f.read() + + def process_renderer(self, renderer_name): + gen = Generator(fetch=self.fetch) + renderers_dir = self.templates.joinpath("renderers") + if renderers_dir.exists(): + path = self.templates.joinpath(f"renderers/{renderer_name}") + return gen.process_file(path) + else: + path = self.templates.joinpath(renderer_name) + return gen.process(path.read_text()) + + def generate_trustgraph_config(self, config): + config = config.encode("utf-8") + return self.process_renderer("config-to-tg-configuration.jsonnet") + + def generate_additionals(self, config): + config = config.encode("utf-8") + return self.process_renderer("config-to-additionals.jsonnet") + + def generate_resources(self, config): + config = config.encode("utf-8") + return self.process_renderer(f"config-to-{self.platform}.jsonnet") + + def write(self, config, output): + + try: + + data = self.generate(config) + + print("Writing output file...") + + with open(output, "wb") as f: + f.write(data) + + print(f"Wrote {output}.") + + except Exception as e: + logging.error(f"Exception: {e}") + raise e + + def generate(self, config): + + self.config = config + + logger.info(f"Generating for platform={self.platform} " + f"template={self.template} " + f"version={self.version}") + + try: + + if self.platform in set(["docker-compose", "podman-compose"]): + data = self.generate_docker_compose( + "docker-compose", self.version, config + ) + elif self.platform in set([ + "minikube-k8s", "gcp-k8s", "aks-k8s", "eks-k8s", + "scw-k8s", "ovh-k8s" + ]): + data = self.generate_k8s( + self.platform, self.version, config + ) + else: + raise RuntimeError("Bad platform") + + return data + + except Exception as e: + logging.error(f"Exception: {e}") + raise e + + def write_tg_config(self, config): + """Output only the TrustGraph configuration to stdout""" + try: + self.config = config + tg_config_json = self.generate_trustgraph_config(config) + tg_config_file = json.dumps(tg_config_json, indent=4) + print(tg_config_file) + except Exception as e: + logging.error(f"Exception: {e}") + raise e + + def write_resources(self, config): + """Output only the platform resources to stdout""" + try: + self.config = config + + if self.platform in set(["docker-compose", "podman-compose"]): + compose_json = self.generate_resources(config) + compose_file = yaml.dump(compose_json) + print(compose_file) + elif self.platform in set([ + "minikube-k8s", "gcp-k8s", "aks-k8s", "eks-k8s", + "scw-k8s", "ovh-k8s" + ]): + processed = self.generate_resources(config) + y = yaml.dump(processed) + print(y) + else: + raise RuntimeError("Bad platform") + + except Exception as e: + logging.error(f"Exception: {e}") + raise e + + def generate_docker_compose(self, platform, version, config): + + compose_json = self.generate_resources(config) + compose_file = yaml.dump(compose_json) + + # Generate TG config for versions after 1.1... + if version[:2] != "0." and version[:3] != "1.0": + tg_config_json = self.generate_trustgraph_config(config) + tg_config_file = json.dumps(tg_config_json, indent=4) + + # Check if config-to-additionals.jsonnet exists for this version + renderers_dir = self.templates.joinpath("renderers") + if renderers_dir.exists(): + additionals_path = self.templates.joinpath("renderers/config-to-additionals.jsonnet") + else: + additionals_path = self.templates.joinpath("config-to-additionals.jsonnet") + has_additionals = os.path.isfile(additionals_path) + + # Generate additional config files from configVolume parts (if supported) + additionals = self.generate_additionals(config) if has_additionals else [] + + mem = BytesIO() + + with zipfile.ZipFile(mem, mode='w') as out: + + def output(name, content): + logger.info(f"Adding {name}...") + out.writestr(name, content) + + output("docker-compose.yaml", compose_file) + + # Add seperate TG config for versions after 1.1... + if version[:2] != "0." and version[:3] != "1.0": + output("trustgraph/config.json", tg_config_file) + + # Add generated config files from additionals (if available) + # Skip trustgraph/config.json since it's handled above + for item in additionals: + if item['path'] != 'trustgraph/config.json': + output(item['path'], item['content']) + + # Fallback: Walk resources directory if additionals not available + # This maintains backward compatibility with older versions + if not has_additionals and os.path.isdir(self.resources): + for root, dirs, files in os.walk(self.resources): + for file in files: + file_path = os.path.join(root, file) + rel_path = os.path.relpath(file_path, self.resources) + with open(file_path, 'r') as f: + content = f.read() + output(rel_path, content) + + logger.info("Generation complete.") + + return mem.getvalue() + + def generate_k8s(self, platform, version, config): + + processed = self.generate_resources(config) + + y = yaml.dump(processed) + + mem = BytesIO() + + with zipfile.ZipFile(mem, mode='w') as out: + + def output(name, content): + logger.info(f"Adding {name}...") + out.writestr(name, content) + + fname = "resources.yaml" + + output(fname, y) + + logger.info("Generation complete.") + + return mem.getvalue() + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.6/grafana/dashboards/dashboard.json b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.6/grafana/dashboards/dashboard.json new file mode 100644 index 00000000..185f9da9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.6/grafana/dashboards/dashboard.json @@ -0,0 +1,1457 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 2, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "editorMode": "code", + "expr": "producer_count_total{processor=\"chunker\",name=\"output\"}\n- ignoring(instance, job, processor, name, status)\nprocessing_count_total{processor=\"kg-extract-relationships\", name=\"input\"}", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Knowledge extraction backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "editorMode": "code", + "expr": "(producer_count_total{processor=\"graph-embeddings\",name=\"output\"} - ignoring(instance, job, processor, name, status) processing_count_total{processor=\"ge-write\",name=\"input\"} >= 2)\nor\n(0 * (producer_count_total{processor=\"graph-embeddings\",name=\"output\"} - ignoring(instance, job, processor, name, status) processing_count_total{processor=\"ge-write\",name=\"input\"}))", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Graph embeddings backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "editorMode": "code", + "expr": "(clamp_min(\n (\n producer_count_total{processor=\"kg-extract-definitions\",name=\"triples\"}\n + on(flow)\n producer_count_total{processor=\"kg-extract-relationships\",name=\"triples\"}\n )\n - on(flow) processing_count_total{processor=\"triples-write\",name=\"input\"},\n 0\n) >= 2) or (0 * clamp_min(\n (\n producer_count_total{processor=\"kg-extract-definitions\",name=\"triples\"}\n + on(flow)\n producer_count_total{processor=\"kg-extract-relationships\",name=\"triples\"}\n )\n - on(flow) processing_count_total{processor=\"triples-write\",name=\"input\"},\n 0\n))", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Triples backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 7 + }, + "id": 7, + "options": { + "calculate": false, + "cellGap": 1, + "color": { + "exponent": 0.5, + "fill": "dark-orange", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Oranges", + "steps": 64 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "tooltip": { + "mode": "single", + "showColorScale": false, + "yHistogram": false + }, + "yAxis": { + "axisPlacement": "left", + "reverse": false + } + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "sum by(le) (rate(text_completion_duration_bucket[$__rate_interval]))", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "99%", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "LLM latency", + "type": "heatmap" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 7 + }, + "id": 2, + "options": { + "calculate": false, + "cellGap": 5, + "cellValues": { + "unit": "" + }, + "color": { + "exponent": 0.5, + "fill": "dark-orange", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Oranges", + "steps": 64 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "tooltip": { + "mode": "single", + "showColorScale": false, + "yHistogram": false + }, + "yAxis": { + "axisLabel": "processing status", + "axisPlacement": "left", + "reverse": false + } + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "sum by(status) (rate(processing_count_total{status!=\"success\"}[$__rate_interval]))", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "", + "legendFormat": "{{status}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Error rate", + "type": "heatmap" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 15 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(processor) (rate(request_latency_count{processor!=\"config-svc\"}[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{job}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Request rate", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 15 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "pulsar_msg_backlog{topic!=\"persistent://tg/config/config\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{topic}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Pub/sub backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "semi-dark-green", + "mode": "palette-classic-by-name" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 10, + "options": { + "barRadius": 0, + "barWidth": 0.97, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "none", + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "max by(le) (chunk_size_bucket)", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": true, + "legendFormat": "{{le}}", + "range": false, + "refId": "A", + "useBackend": false + } + ], + "title": "Chunk size", + "type": "barchart" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 24 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "sum by(job) (increase(rate_limit_count_total[$__rate_interval]))", + "format": "time_series", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{instance}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Rate limit events", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "light-blue", + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 31 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(process_cpu_seconds_total[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{instance}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "CPU", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "GB", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 31 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "process_resident_memory_bytes / 1073741824", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{instance}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Memory", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "filterable": false, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 39 + }, + "id": 14, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "last_over_time(params_info[$__interval])", + "format": "table", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": true, + "legendFormat": "__auto", + "range": false, + "refId": "A", + "useBackend": false + } + ], + "title": "Model parameters", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "model", + "job" + ] + } + } + }, + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "equal", + "options": { + "value": "" + } + }, + "fieldName": "model" + } + ], + "match": "all", + "type": "exclude" + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 39 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(job) (rate(input_tokens_total[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "input {{job}}", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(job) (rate(output_tokens_total[$__rate_interval]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "output {{job}}", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "Tokens", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "$", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 39 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(job) (rate(input_cost_total[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "input {{job}}", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(job) (rate(output_cost_total[$__rate_interval]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "output {{job}}", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "Token cost", + "type": "timeseries" + } + ], + "preload": false, + "refresh": "5s", + "schemaVersion": 41, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Overview", + "uid": "b5c8abf8-fe79-496b-b028-10bde917d1f0", + "version": 12 +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.6/grafana/provisioning/dashboard.yml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.6/grafana/provisioning/dashboard.yml new file mode 100644 index 00000000..9b9e7450 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.6/grafana/provisioning/dashboard.yml @@ -0,0 +1,17 @@ + +apiVersion: 1 + +providers: + + - name: 'trustgraph.ai' + orgId: 1 + folder: 'TrustGraph' + folderUid: 'b6c5be90-d432-4df8-aeab-737c7b151228' + type: file + disableDeletion: false + updateIntervalSeconds: 30 + allowUiUpdates: true + options: + path: /var/lib/grafana/dashboards + foldersFromFilesStructure: false + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.6/grafana/provisioning/datasource.yml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.6/grafana/provisioning/datasource.yml new file mode 100644 index 00000000..3afdb9b7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.6/grafana/provisioning/datasource.yml @@ -0,0 +1,21 @@ +apiVersion: 1 + +prune: true + +datasources: + - name: Prometheus + type: prometheus + access: proxy + orgId: 1 + # Sets a custom UID to reference this + # data source in other parts of the configuration. + # If not specified, Grafana generates one. + uid: 'f6b18033-5918-4e05-a1ca-4cb30343b129' + + url: http://prometheus:9090 + + basicAuth: false + withCredentials: false + isDefault: true + editable: true + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.6/prometheus/prometheus.yml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.6/prometheus/prometheus.yml new file mode 100644 index 00000000..b5c8509d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.6/prometheus/prometheus.yml @@ -0,0 +1,247 @@ +global: + + scrape_interval: 15s # By default, scrape targets every 15 seconds. + + # Attach these labels to any time series or alerts when communicating with + # external systems (federation, remote storage, Alertmanager). + external_labels: + monitor: 'trustgraph' + +# A scrape configuration containing exactly one endpoint to scrape: +# Here it's Prometheus itself. +scrape_configs: + + # The job name is added as a label `job=` to any timeseries + # scraped from this config. + + # TrustGraph services + + - job_name: 'agent-manager' + scrape_interval: 5s + static_configs: + - targets: + - 'agent-manager:8000' + + - job_name: 'api-gateway' + scrape_interval: 5s + static_configs: + - targets: + - 'api-gateway:8000' + + - job_name: 'chunker' + scrape_interval: 5s + static_configs: + - targets: + - 'chunker:8000' + + - job_name: 'config-svc' + scrape_interval: 5s + static_configs: + - targets: + - 'config-svc:8000' + + - job_name: 'document-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'document-embeddings:8000' + + - job_name: 'document-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'document-rag:8000' + + - job_name: 'embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'embeddings:8000' + + - job_name: 'graph-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'graph-embeddings:8000' + + - job_name: 'graph-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'graph-rag:8000' + + - job_name: 'kg-extract-agent' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-agent:8000' + + - job_name: 'kg-extract-definitions' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-definitions:8000' + + - job_name: 'kg-extract-objects' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-objects:8000' + + - job_name: 'kg-extract-relationships' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-relationships:8000' + + - job_name: 'kg-extract-ontology' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-ontology:8000' + + - job_name: 'kg-manager' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-manager:8000' + + - job_name: 'kg-store' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-store:8000' + + - job_name: 'librarian' + scrape_interval: 5s + static_configs: + - targets: + - 'librarian:8000' + + - job_name: 'mcp-server' + scrape_interval: 5s + static_configs: + - targets: + - 'mcp-server:8000' + + - job_name: 'mcp-tool' + scrape_interval: 5s + static_configs: + - targets: + - 'mcp-tool:8000' + + - job_name: 'metering' + scrape_interval: 5s + static_configs: + - targets: + - 'metering:8000' + + - job_name: 'metering-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'metering-rag:8000' + + - job_name: 'nlp-query' + scrape_interval: 5s + static_configs: + - targets: + - 'nlp-query:8000' + + - job_name: 'pdf-decoder' + scrape_interval: 5s + static_configs: + - targets: + - 'pdf-decoder:8000' + + - job_name: 'prompt' + scrape_interval: 5s + static_configs: + - targets: + - 'prompt:8000' + + - job_name: 'prompt-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'prompt-rag:8000' + + - job_name: 'query-doc-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'query-doc-embeddings:8000' + + - job_name: 'query-graph-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'query-graph-embeddings:8000' + + - job_name: 'query-objects' + scrape_interval: 5s + static_configs: + - targets: + - 'query-objects:8000' + + - job_name: 'query-triples' + scrape_interval: 5s + static_configs: + - targets: + - 'query-triples:8000' + + - job_name: 'store-doc-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'store-doc-embeddings:8000' + + - job_name: 'store-graph-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'store-graph-embeddings:8000' + + - job_name: 'store-objects' + scrape_interval: 5s + static_configs: + - targets: + - 'store-objects:8000' + + - job_name: 'store-triples' + scrape_interval: 5s + static_configs: + - targets: + - 'store-triples:8000' + + - job_name: 'structured-query' + scrape_interval: 5s + static_configs: + - targets: + - 'structured-query:8000' + + - job_name: 'structured-diag' + scrape_interval: 5s + static_configs: + - targets: + - 'structured-query:8000' + + - job_name: 'text-completion' + scrape_interval: 5s + static_configs: + - targets: + - 'text-completion:8000' + + - job_name: 'text-completion-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'text-completion-rag:8000' + + # Non-Trustgraph services + - job_name: 'pulsar' + scrape_interval: 5s + static_configs: + - targets: + - 'pulsar:8080' + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.7/grafana/dashboards/log-dashboard.json b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.7/grafana/dashboards/log-dashboard.json new file mode 100644 index 00000000..2d40b195 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.7/grafana/dashboards/log-dashboard.json @@ -0,0 +1,178 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "loki", + "uid": "cf6m73l5rnvuod" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 33, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "barRadius": 0, + "barWidth": 0.97, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "orientation": "auto", + "showValue": "never", + "stacking": "normal", + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 100 + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "cf6m73l5rnvuod" + }, + "direction": "backward", + "editorMode": "code", + "expr": "topk(5, sum by (processor) (count_over_time({severity=~\"error|critical\"} [$__auto])))", + "queryType": "range", + "refId": "A" + } + ], + "title": "Error volume", + "type": "barchart" + }, + { + "datasource": { + "type": "loki", + "uid": "cf6m73l5rnvuod" + }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "gridPos": { + "h": 18, + "w": 24, + "x": 0, + "y": 6 + }, + "id": 1, + "options": { + "dedupStrategy": "none", + "enableInfiniteScrolling": false, + "enableLogDetails": true, + "prettifyLogMessage": false, + "showCommonLabels": false, + "showLabels": false, + "showTime": false, + "sortOrder": "Descending", + "wrapLogMessage": true + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "cf6m73l5rnvuod" + }, + "direction": "backward", + "editorMode": "builder", + "expr": "{severity=~\"error|critical\"} |= ``", + "queryType": "range", + "refId": "A" + } + ], + "title": "Log errors", + "type": "logs" + } + ], + "preload": false, + "schemaVersion": 41, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Logs", + "uid": "4bdce405-0dd3-4a14-9a8f-792152ebebba", + "version": 13 +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.7/grafana/dashboards/overview-dashboard.json b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.7/grafana/dashboards/overview-dashboard.json new file mode 100644 index 00000000..31e57fb3 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.7/grafana/dashboards/overview-dashboard.json @@ -0,0 +1,1403 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "expr": "producer_count_total{processor=\"chunker\",name=\"output\"}\n- ignoring(instance, job, processor, name, status)\nprocessing_count_total{processor=\"kg-extract-relationships\", name=\"input\"}", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Knowledge extraction backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "expr": "(producer_count_total{processor=\"graph-embeddings\",name=\"output\"} - ignoring(instance, job, processor, name, status) processing_count_total{processor=\"ge-write\",name=\"input\"} >= 2)\nor\n(0 * (producer_count_total{processor=\"graph-embeddings\",name=\"output\"} - ignoring(instance, job, processor, name, status) processing_count_total{processor=\"ge-write\",name=\"input\"}))", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Graph embeddings backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "expr": "(clamp_min(\n (\n producer_count_total{processor=\"kg-extract-definitions\",name=\"triples\"}\n + on(flow)\n producer_count_total{processor=\"kg-extract-relationships\",name=\"triples\"}\n )\n - on(flow) processing_count_total{processor=\"triples-write\",name=\"input\"},\n 0\n) >= 2) or (0 * clamp_min(\n (\n producer_count_total{processor=\"kg-extract-definitions\",name=\"triples\"}\n + on(flow)\n producer_count_total{processor=\"kg-extract-relationships\",name=\"triples\"}\n )\n - on(flow) processing_count_total{processor=\"triples-write\",name=\"input\"},\n 0\n))", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Triples backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 7 + }, + "id": 7, + "options": { + "calculate": false, + "cellGap": 1, + "color": { + "exponent": 0.5, + "fill": "dark-orange", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Oranges", + "steps": 64 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "tooltip": { + "mode": "single", + "showColorScale": false, + "yHistogram": false + }, + "yAxis": { + "axisPlacement": "left", + "reverse": false + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "sum by(le) (rate(text_completion_duration_bucket[$__rate_interval]))", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "99%", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "LLM latency", + "type": "heatmap" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 7 + }, + "id": 2, + "options": { + "calculate": false, + "cellGap": 5, + "cellValues": { + "unit": "" + }, + "color": { + "exponent": 0.5, + "fill": "dark-orange", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Oranges", + "steps": 64 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "tooltip": { + "mode": "single", + "showColorScale": false, + "yHistogram": false + }, + "yAxis": { + "axisLabel": "processing status", + "axisPlacement": "left", + "reverse": false + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "sum by(status) (rate(processing_count_total{status!=\"success\"}[$__rate_interval]))", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "", + "legendFormat": "{{status}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Error rate", + "type": "heatmap" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 15 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(processor) (rate(request_latency_count{processor!=\"config-svc\"}[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{job}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Request rate", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 15 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "pulsar_msg_backlog{topic!=\"persistent://tg/config/config\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{topic}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Pub/sub backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "semi-dark-green", + "mode": "palette-classic-by-name" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 10, + "options": { + "barRadius": 0, + "barWidth": 0.97, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "none", + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "max by(le) (chunk_size_bucket)", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": true, + "legendFormat": "{{le}}", + "range": false, + "refId": "A", + "useBackend": false + } + ], + "title": "Chunk size", + "type": "barchart" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 24 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "sum by(job) (increase(rate_limit_count_total[$__rate_interval]))", + "format": "time_series", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{instance}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Rate limit events", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "light-blue", + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 31 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(process_cpu_seconds_total[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{instance}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "CPU", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "GB", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 31 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "process_resident_memory_bytes / 1073741824", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{instance}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Memory", + "type": "timeseries" + }, + { + "datasource": { + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "footer": { + "reducers": [] + }, + "hideFrom": { + "viz": false + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 39 + }, + "id": 14, + "options": { + "cellHeight": "sm", + "showHeader": true + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "exemplar": false, + "expr": "sum by (model) (tokens_total)", + "format": "table", + "instant": true, + "legendFormat": "__auto", + "range": false, + "refId": "A" + } + ], + "title": "Models in use", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true + }, + "includeByName": {}, + "indexByName": {}, + "renameByName": {} + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 39 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(model, direction) (rate(tokens_total[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{model}} - {{direction}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Tokens", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "$", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 39 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(direction, model) (rate(tokens_total[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{model}} - {{direction}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Token cost", + "type": "timeseries" + } + ], + "preload": false, + "refresh": "5s", + "schemaVersion": 42, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Overview", + "uid": "b5c8abf8-fe79-496b-b028-10bde917d1f0", + "version": 1 +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.7/grafana/provisioning/dashboard.yml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.7/grafana/provisioning/dashboard.yml new file mode 100644 index 00000000..9b9e7450 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.7/grafana/provisioning/dashboard.yml @@ -0,0 +1,17 @@ + +apiVersion: 1 + +providers: + + - name: 'trustgraph.ai' + orgId: 1 + folder: 'TrustGraph' + folderUid: 'b6c5be90-d432-4df8-aeab-737c7b151228' + type: file + disableDeletion: false + updateIntervalSeconds: 30 + allowUiUpdates: true + options: + path: /var/lib/grafana/dashboards + foldersFromFilesStructure: false + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.7/grafana/provisioning/datasource.yml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.7/grafana/provisioning/datasource.yml new file mode 100644 index 00000000..1cf4bc3d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.7/grafana/provisioning/datasource.yml @@ -0,0 +1,36 @@ +apiVersion: 1 + +prune: true + +datasources: + - name: Prometheus + type: prometheus + access: proxy + orgId: 1 + # Sets a custom UID to reference this + # data source in other parts of the configuration. + # If not specified, Grafana generates one. + uid: 'f6b18033-5918-4e05-a1ca-4cb30343b129' + + url: http://prometheus:9090 + + basicAuth: false + withCredentials: false + isDefault: true + editable: true + + - name: Loki + type: loki + access: proxy + orgId: 1 + # Sets a custom UID to reference this + # data source in other parts of the configuration. + # If not specified, Grafana generates one. + uid: 'cf6m73l5rnvuod' + + url: http://loki:3100 + + basicAuth: false + withCredentials: false + editable: true + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.7/loki/local-config.yaml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.7/loki/local-config.yaml new file mode 100644 index 00000000..8c519ee9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.7/loki/local-config.yaml @@ -0,0 +1,63 @@ +auth_enabled: false + +server: + http_listen_port: 3100 + grpc_listen_port: 9096 + log_level: debug + grpc_server_max_concurrent_streams: 1000 + +common: + instance_addr: 127.0.0.1 + path_prefix: /tmp/loki + storage: + filesystem: + chunks_directory: /tmp/loki/chunks + rules_directory: /tmp/loki/rules + replication_factor: 1 + ring: + kvstore: + store: inmemory + +query_range: + results_cache: + cache: + embedded_cache: + enabled: true + max_size_mb: 100 + +limits_config: + metric_aggregation_enabled: true + +schema_config: + configs: + - from: 2020-10-24 + store: tsdb + object_store: filesystem + schema: v13 + index: + prefix: index_ + period: 24h + +pattern_ingester: + enabled: true + metric_aggregation: + loki_address: localhost:3100 + +ruler: + alertmanager_url: http://localhost:9093 + +frontend: + encoding: protobuf + +# By default, Loki will send anonymous, but uniquely-identifiable usage and configuration +# analytics to Grafana Labs. These statistics are sent to https://stats.grafana.org/ +# +# Statistics help us better understand how Loki is used, and they show us performance +# levels for most users. This helps us prioritize features and documentation. +# For more information on what's sent, look at +# https://github.com/grafana/loki/blob/main/pkg/analytics/stats.go +# Refer to the buildReport method to see what goes into a report. +# +# If you would like to disable reporting, uncomment the following lines: +analytics: + reporting_enabled: false diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.7/prometheus/prometheus.yml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.7/prometheus/prometheus.yml new file mode 100644 index 00000000..b5c8509d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.7/prometheus/prometheus.yml @@ -0,0 +1,247 @@ +global: + + scrape_interval: 15s # By default, scrape targets every 15 seconds. + + # Attach these labels to any time series or alerts when communicating with + # external systems (federation, remote storage, Alertmanager). + external_labels: + monitor: 'trustgraph' + +# A scrape configuration containing exactly one endpoint to scrape: +# Here it's Prometheus itself. +scrape_configs: + + # The job name is added as a label `job=` to any timeseries + # scraped from this config. + + # TrustGraph services + + - job_name: 'agent-manager' + scrape_interval: 5s + static_configs: + - targets: + - 'agent-manager:8000' + + - job_name: 'api-gateway' + scrape_interval: 5s + static_configs: + - targets: + - 'api-gateway:8000' + + - job_name: 'chunker' + scrape_interval: 5s + static_configs: + - targets: + - 'chunker:8000' + + - job_name: 'config-svc' + scrape_interval: 5s + static_configs: + - targets: + - 'config-svc:8000' + + - job_name: 'document-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'document-embeddings:8000' + + - job_name: 'document-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'document-rag:8000' + + - job_name: 'embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'embeddings:8000' + + - job_name: 'graph-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'graph-embeddings:8000' + + - job_name: 'graph-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'graph-rag:8000' + + - job_name: 'kg-extract-agent' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-agent:8000' + + - job_name: 'kg-extract-definitions' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-definitions:8000' + + - job_name: 'kg-extract-objects' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-objects:8000' + + - job_name: 'kg-extract-relationships' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-relationships:8000' + + - job_name: 'kg-extract-ontology' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-ontology:8000' + + - job_name: 'kg-manager' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-manager:8000' + + - job_name: 'kg-store' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-store:8000' + + - job_name: 'librarian' + scrape_interval: 5s + static_configs: + - targets: + - 'librarian:8000' + + - job_name: 'mcp-server' + scrape_interval: 5s + static_configs: + - targets: + - 'mcp-server:8000' + + - job_name: 'mcp-tool' + scrape_interval: 5s + static_configs: + - targets: + - 'mcp-tool:8000' + + - job_name: 'metering' + scrape_interval: 5s + static_configs: + - targets: + - 'metering:8000' + + - job_name: 'metering-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'metering-rag:8000' + + - job_name: 'nlp-query' + scrape_interval: 5s + static_configs: + - targets: + - 'nlp-query:8000' + + - job_name: 'pdf-decoder' + scrape_interval: 5s + static_configs: + - targets: + - 'pdf-decoder:8000' + + - job_name: 'prompt' + scrape_interval: 5s + static_configs: + - targets: + - 'prompt:8000' + + - job_name: 'prompt-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'prompt-rag:8000' + + - job_name: 'query-doc-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'query-doc-embeddings:8000' + + - job_name: 'query-graph-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'query-graph-embeddings:8000' + + - job_name: 'query-objects' + scrape_interval: 5s + static_configs: + - targets: + - 'query-objects:8000' + + - job_name: 'query-triples' + scrape_interval: 5s + static_configs: + - targets: + - 'query-triples:8000' + + - job_name: 'store-doc-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'store-doc-embeddings:8000' + + - job_name: 'store-graph-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'store-graph-embeddings:8000' + + - job_name: 'store-objects' + scrape_interval: 5s + static_configs: + - targets: + - 'store-objects:8000' + + - job_name: 'store-triples' + scrape_interval: 5s + static_configs: + - targets: + - 'store-triples:8000' + + - job_name: 'structured-query' + scrape_interval: 5s + static_configs: + - targets: + - 'structured-query:8000' + + - job_name: 'structured-diag' + scrape_interval: 5s + static_configs: + - targets: + - 'structured-query:8000' + + - job_name: 'text-completion' + scrape_interval: 5s + static_configs: + - targets: + - 'text-completion:8000' + + - job_name: 'text-completion-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'text-completion-rag:8000' + + # Non-Trustgraph services + - job_name: 'pulsar' + scrape_interval: 5s + static_configs: + - targets: + - 'pulsar:8080' + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.8/grafana/dashboards/log-dashboard.json b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.8/grafana/dashboards/log-dashboard.json new file mode 100644 index 00000000..2d40b195 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.8/grafana/dashboards/log-dashboard.json @@ -0,0 +1,178 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "loki", + "uid": "cf6m73l5rnvuod" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 33, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "barRadius": 0, + "barWidth": 0.97, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "orientation": "auto", + "showValue": "never", + "stacking": "normal", + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 100 + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "cf6m73l5rnvuod" + }, + "direction": "backward", + "editorMode": "code", + "expr": "topk(5, sum by (processor) (count_over_time({severity=~\"error|critical\"} [$__auto])))", + "queryType": "range", + "refId": "A" + } + ], + "title": "Error volume", + "type": "barchart" + }, + { + "datasource": { + "type": "loki", + "uid": "cf6m73l5rnvuod" + }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "gridPos": { + "h": 18, + "w": 24, + "x": 0, + "y": 6 + }, + "id": 1, + "options": { + "dedupStrategy": "none", + "enableInfiniteScrolling": false, + "enableLogDetails": true, + "prettifyLogMessage": false, + "showCommonLabels": false, + "showLabels": false, + "showTime": false, + "sortOrder": "Descending", + "wrapLogMessage": true + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "cf6m73l5rnvuod" + }, + "direction": "backward", + "editorMode": "builder", + "expr": "{severity=~\"error|critical\"} |= ``", + "queryType": "range", + "refId": "A" + } + ], + "title": "Log errors", + "type": "logs" + } + ], + "preload": false, + "schemaVersion": 41, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Logs", + "uid": "4bdce405-0dd3-4a14-9a8f-792152ebebba", + "version": 13 +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.8/grafana/dashboards/overview-dashboard.json b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.8/grafana/dashboards/overview-dashboard.json new file mode 100644 index 00000000..31e57fb3 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.8/grafana/dashboards/overview-dashboard.json @@ -0,0 +1,1403 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "expr": "producer_count_total{processor=\"chunker\",name=\"output\"}\n- ignoring(instance, job, processor, name, status)\nprocessing_count_total{processor=\"kg-extract-relationships\", name=\"input\"}", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Knowledge extraction backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "expr": "(producer_count_total{processor=\"graph-embeddings\",name=\"output\"} - ignoring(instance, job, processor, name, status) processing_count_total{processor=\"ge-write\",name=\"input\"} >= 2)\nor\n(0 * (producer_count_total{processor=\"graph-embeddings\",name=\"output\"} - ignoring(instance, job, processor, name, status) processing_count_total{processor=\"ge-write\",name=\"input\"}))", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Graph embeddings backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "expr": "(clamp_min(\n (\n producer_count_total{processor=\"kg-extract-definitions\",name=\"triples\"}\n + on(flow)\n producer_count_total{processor=\"kg-extract-relationships\",name=\"triples\"}\n )\n - on(flow) processing_count_total{processor=\"triples-write\",name=\"input\"},\n 0\n) >= 2) or (0 * clamp_min(\n (\n producer_count_total{processor=\"kg-extract-definitions\",name=\"triples\"}\n + on(flow)\n producer_count_total{processor=\"kg-extract-relationships\",name=\"triples\"}\n )\n - on(flow) processing_count_total{processor=\"triples-write\",name=\"input\"},\n 0\n))", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Triples backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 7 + }, + "id": 7, + "options": { + "calculate": false, + "cellGap": 1, + "color": { + "exponent": 0.5, + "fill": "dark-orange", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Oranges", + "steps": 64 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "tooltip": { + "mode": "single", + "showColorScale": false, + "yHistogram": false + }, + "yAxis": { + "axisPlacement": "left", + "reverse": false + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "sum by(le) (rate(text_completion_duration_bucket[$__rate_interval]))", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "99%", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "LLM latency", + "type": "heatmap" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 7 + }, + "id": 2, + "options": { + "calculate": false, + "cellGap": 5, + "cellValues": { + "unit": "" + }, + "color": { + "exponent": 0.5, + "fill": "dark-orange", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Oranges", + "steps": 64 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "tooltip": { + "mode": "single", + "showColorScale": false, + "yHistogram": false + }, + "yAxis": { + "axisLabel": "processing status", + "axisPlacement": "left", + "reverse": false + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "sum by(status) (rate(processing_count_total{status!=\"success\"}[$__rate_interval]))", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "", + "legendFormat": "{{status}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Error rate", + "type": "heatmap" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 15 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(processor) (rate(request_latency_count{processor!=\"config-svc\"}[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{job}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Request rate", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 15 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "pulsar_msg_backlog{topic!=\"persistent://tg/config/config\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{topic}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Pub/sub backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "semi-dark-green", + "mode": "palette-classic-by-name" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 10, + "options": { + "barRadius": 0, + "barWidth": 0.97, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "none", + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "max by(le) (chunk_size_bucket)", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": true, + "legendFormat": "{{le}}", + "range": false, + "refId": "A", + "useBackend": false + } + ], + "title": "Chunk size", + "type": "barchart" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 24 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "sum by(job) (increase(rate_limit_count_total[$__rate_interval]))", + "format": "time_series", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{instance}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Rate limit events", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "light-blue", + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 31 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(process_cpu_seconds_total[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{instance}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "CPU", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "GB", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 31 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "process_resident_memory_bytes / 1073741824", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{instance}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Memory", + "type": "timeseries" + }, + { + "datasource": { + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "footer": { + "reducers": [] + }, + "hideFrom": { + "viz": false + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 39 + }, + "id": 14, + "options": { + "cellHeight": "sm", + "showHeader": true + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "exemplar": false, + "expr": "sum by (model) (tokens_total)", + "format": "table", + "instant": true, + "legendFormat": "__auto", + "range": false, + "refId": "A" + } + ], + "title": "Models in use", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true + }, + "includeByName": {}, + "indexByName": {}, + "renameByName": {} + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 39 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(model, direction) (rate(tokens_total[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{model}} - {{direction}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Tokens", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "$", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 39 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(direction, model) (rate(tokens_total[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{model}} - {{direction}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Token cost", + "type": "timeseries" + } + ], + "preload": false, + "refresh": "5s", + "schemaVersion": 42, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Overview", + "uid": "b5c8abf8-fe79-496b-b028-10bde917d1f0", + "version": 1 +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.8/grafana/provisioning/dashboard.yml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.8/grafana/provisioning/dashboard.yml new file mode 100644 index 00000000..9b9e7450 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.8/grafana/provisioning/dashboard.yml @@ -0,0 +1,17 @@ + +apiVersion: 1 + +providers: + + - name: 'trustgraph.ai' + orgId: 1 + folder: 'TrustGraph' + folderUid: 'b6c5be90-d432-4df8-aeab-737c7b151228' + type: file + disableDeletion: false + updateIntervalSeconds: 30 + allowUiUpdates: true + options: + path: /var/lib/grafana/dashboards + foldersFromFilesStructure: false + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.8/grafana/provisioning/datasource.yml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.8/grafana/provisioning/datasource.yml new file mode 100644 index 00000000..1cf4bc3d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.8/grafana/provisioning/datasource.yml @@ -0,0 +1,36 @@ +apiVersion: 1 + +prune: true + +datasources: + - name: Prometheus + type: prometheus + access: proxy + orgId: 1 + # Sets a custom UID to reference this + # data source in other parts of the configuration. + # If not specified, Grafana generates one. + uid: 'f6b18033-5918-4e05-a1ca-4cb30343b129' + + url: http://prometheus:9090 + + basicAuth: false + withCredentials: false + isDefault: true + editable: true + + - name: Loki + type: loki + access: proxy + orgId: 1 + # Sets a custom UID to reference this + # data source in other parts of the configuration. + # If not specified, Grafana generates one. + uid: 'cf6m73l5rnvuod' + + url: http://loki:3100 + + basicAuth: false + withCredentials: false + editable: true + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.8/loki/local-config.yaml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.8/loki/local-config.yaml new file mode 100644 index 00000000..8c519ee9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.8/loki/local-config.yaml @@ -0,0 +1,63 @@ +auth_enabled: false + +server: + http_listen_port: 3100 + grpc_listen_port: 9096 + log_level: debug + grpc_server_max_concurrent_streams: 1000 + +common: + instance_addr: 127.0.0.1 + path_prefix: /tmp/loki + storage: + filesystem: + chunks_directory: /tmp/loki/chunks + rules_directory: /tmp/loki/rules + replication_factor: 1 + ring: + kvstore: + store: inmemory + +query_range: + results_cache: + cache: + embedded_cache: + enabled: true + max_size_mb: 100 + +limits_config: + metric_aggregation_enabled: true + +schema_config: + configs: + - from: 2020-10-24 + store: tsdb + object_store: filesystem + schema: v13 + index: + prefix: index_ + period: 24h + +pattern_ingester: + enabled: true + metric_aggregation: + loki_address: localhost:3100 + +ruler: + alertmanager_url: http://localhost:9093 + +frontend: + encoding: protobuf + +# By default, Loki will send anonymous, but uniquely-identifiable usage and configuration +# analytics to Grafana Labs. These statistics are sent to https://stats.grafana.org/ +# +# Statistics help us better understand how Loki is used, and they show us performance +# levels for most users. This helps us prioritize features and documentation. +# For more information on what's sent, look at +# https://github.com/grafana/loki/blob/main/pkg/analytics/stats.go +# Refer to the buildReport method to see what goes into a report. +# +# If you would like to disable reporting, uncomment the following lines: +analytics: + reporting_enabled: false diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.8/prometheus/prometheus.yml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.8/prometheus/prometheus.yml new file mode 100644 index 00000000..b5c8509d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.8/prometheus/prometheus.yml @@ -0,0 +1,247 @@ +global: + + scrape_interval: 15s # By default, scrape targets every 15 seconds. + + # Attach these labels to any time series or alerts when communicating with + # external systems (federation, remote storage, Alertmanager). + external_labels: + monitor: 'trustgraph' + +# A scrape configuration containing exactly one endpoint to scrape: +# Here it's Prometheus itself. +scrape_configs: + + # The job name is added as a label `job=` to any timeseries + # scraped from this config. + + # TrustGraph services + + - job_name: 'agent-manager' + scrape_interval: 5s + static_configs: + - targets: + - 'agent-manager:8000' + + - job_name: 'api-gateway' + scrape_interval: 5s + static_configs: + - targets: + - 'api-gateway:8000' + + - job_name: 'chunker' + scrape_interval: 5s + static_configs: + - targets: + - 'chunker:8000' + + - job_name: 'config-svc' + scrape_interval: 5s + static_configs: + - targets: + - 'config-svc:8000' + + - job_name: 'document-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'document-embeddings:8000' + + - job_name: 'document-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'document-rag:8000' + + - job_name: 'embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'embeddings:8000' + + - job_name: 'graph-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'graph-embeddings:8000' + + - job_name: 'graph-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'graph-rag:8000' + + - job_name: 'kg-extract-agent' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-agent:8000' + + - job_name: 'kg-extract-definitions' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-definitions:8000' + + - job_name: 'kg-extract-objects' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-objects:8000' + + - job_name: 'kg-extract-relationships' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-relationships:8000' + + - job_name: 'kg-extract-ontology' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-ontology:8000' + + - job_name: 'kg-manager' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-manager:8000' + + - job_name: 'kg-store' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-store:8000' + + - job_name: 'librarian' + scrape_interval: 5s + static_configs: + - targets: + - 'librarian:8000' + + - job_name: 'mcp-server' + scrape_interval: 5s + static_configs: + - targets: + - 'mcp-server:8000' + + - job_name: 'mcp-tool' + scrape_interval: 5s + static_configs: + - targets: + - 'mcp-tool:8000' + + - job_name: 'metering' + scrape_interval: 5s + static_configs: + - targets: + - 'metering:8000' + + - job_name: 'metering-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'metering-rag:8000' + + - job_name: 'nlp-query' + scrape_interval: 5s + static_configs: + - targets: + - 'nlp-query:8000' + + - job_name: 'pdf-decoder' + scrape_interval: 5s + static_configs: + - targets: + - 'pdf-decoder:8000' + + - job_name: 'prompt' + scrape_interval: 5s + static_configs: + - targets: + - 'prompt:8000' + + - job_name: 'prompt-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'prompt-rag:8000' + + - job_name: 'query-doc-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'query-doc-embeddings:8000' + + - job_name: 'query-graph-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'query-graph-embeddings:8000' + + - job_name: 'query-objects' + scrape_interval: 5s + static_configs: + - targets: + - 'query-objects:8000' + + - job_name: 'query-triples' + scrape_interval: 5s + static_configs: + - targets: + - 'query-triples:8000' + + - job_name: 'store-doc-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'store-doc-embeddings:8000' + + - job_name: 'store-graph-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'store-graph-embeddings:8000' + + - job_name: 'store-objects' + scrape_interval: 5s + static_configs: + - targets: + - 'store-objects:8000' + + - job_name: 'store-triples' + scrape_interval: 5s + static_configs: + - targets: + - 'store-triples:8000' + + - job_name: 'structured-query' + scrape_interval: 5s + static_configs: + - targets: + - 'structured-query:8000' + + - job_name: 'structured-diag' + scrape_interval: 5s + static_configs: + - targets: + - 'structured-query:8000' + + - job_name: 'text-completion' + scrape_interval: 5s + static_configs: + - targets: + - 'text-completion:8000' + + - job_name: 'text-completion-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'text-completion-rag:8000' + + # Non-Trustgraph services + - job_name: 'pulsar' + scrape_interval: 5s + static_configs: + - targets: + - 'pulsar:8080' + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.9/grafana/dashboards/log-dashboard.json b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.9/grafana/dashboards/log-dashboard.json new file mode 100644 index 00000000..2d40b195 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.9/grafana/dashboards/log-dashboard.json @@ -0,0 +1,178 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "loki", + "uid": "cf6m73l5rnvuod" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 33, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "barRadius": 0, + "barWidth": 0.97, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "orientation": "auto", + "showValue": "never", + "stacking": "normal", + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 100 + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "cf6m73l5rnvuod" + }, + "direction": "backward", + "editorMode": "code", + "expr": "topk(5, sum by (processor) (count_over_time({severity=~\"error|critical\"} [$__auto])))", + "queryType": "range", + "refId": "A" + } + ], + "title": "Error volume", + "type": "barchart" + }, + { + "datasource": { + "type": "loki", + "uid": "cf6m73l5rnvuod" + }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "gridPos": { + "h": 18, + "w": 24, + "x": 0, + "y": 6 + }, + "id": 1, + "options": { + "dedupStrategy": "none", + "enableInfiniteScrolling": false, + "enableLogDetails": true, + "prettifyLogMessage": false, + "showCommonLabels": false, + "showLabels": false, + "showTime": false, + "sortOrder": "Descending", + "wrapLogMessage": true + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "cf6m73l5rnvuod" + }, + "direction": "backward", + "editorMode": "builder", + "expr": "{severity=~\"error|critical\"} |= ``", + "queryType": "range", + "refId": "A" + } + ], + "title": "Log errors", + "type": "logs" + } + ], + "preload": false, + "schemaVersion": 41, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Logs", + "uid": "4bdce405-0dd3-4a14-9a8f-792152ebebba", + "version": 13 +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.9/grafana/dashboards/overview-dashboard.json b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.9/grafana/dashboards/overview-dashboard.json new file mode 100644 index 00000000..31e57fb3 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.9/grafana/dashboards/overview-dashboard.json @@ -0,0 +1,1403 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "expr": "producer_count_total{processor=\"chunker\",name=\"output\"}\n- ignoring(instance, job, processor, name, status)\nprocessing_count_total{processor=\"kg-extract-relationships\", name=\"input\"}", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Knowledge extraction backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "expr": "(producer_count_total{processor=\"graph-embeddings\",name=\"output\"} - ignoring(instance, job, processor, name, status) processing_count_total{processor=\"ge-write\",name=\"input\"} >= 2)\nor\n(0 * (producer_count_total{processor=\"graph-embeddings\",name=\"output\"} - ignoring(instance, job, processor, name, status) processing_count_total{processor=\"ge-write\",name=\"input\"}))", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Graph embeddings backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "expr": "(clamp_min(\n (\n producer_count_total{processor=\"kg-extract-definitions\",name=\"triples\"}\n + on(flow)\n producer_count_total{processor=\"kg-extract-relationships\",name=\"triples\"}\n )\n - on(flow) processing_count_total{processor=\"triples-write\",name=\"input\"},\n 0\n) >= 2) or (0 * clamp_min(\n (\n producer_count_total{processor=\"kg-extract-definitions\",name=\"triples\"}\n + on(flow)\n producer_count_total{processor=\"kg-extract-relationships\",name=\"triples\"}\n )\n - on(flow) processing_count_total{processor=\"triples-write\",name=\"input\"},\n 0\n))", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Triples backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 7 + }, + "id": 7, + "options": { + "calculate": false, + "cellGap": 1, + "color": { + "exponent": 0.5, + "fill": "dark-orange", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Oranges", + "steps": 64 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "tooltip": { + "mode": "single", + "showColorScale": false, + "yHistogram": false + }, + "yAxis": { + "axisPlacement": "left", + "reverse": false + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "sum by(le) (rate(text_completion_duration_bucket[$__rate_interval]))", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "99%", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "LLM latency", + "type": "heatmap" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 7 + }, + "id": 2, + "options": { + "calculate": false, + "cellGap": 5, + "cellValues": { + "unit": "" + }, + "color": { + "exponent": 0.5, + "fill": "dark-orange", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Oranges", + "steps": 64 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "tooltip": { + "mode": "single", + "showColorScale": false, + "yHistogram": false + }, + "yAxis": { + "axisLabel": "processing status", + "axisPlacement": "left", + "reverse": false + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "sum by(status) (rate(processing_count_total{status!=\"success\"}[$__rate_interval]))", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "", + "legendFormat": "{{status}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Error rate", + "type": "heatmap" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 15 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(processor) (rate(request_latency_count{processor!=\"config-svc\"}[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{job}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Request rate", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 15 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "pulsar_msg_backlog{topic!=\"persistent://tg/config/config\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{topic}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Pub/sub backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "semi-dark-green", + "mode": "palette-classic-by-name" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 10, + "options": { + "barRadius": 0, + "barWidth": 0.97, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "none", + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "max by(le) (chunk_size_bucket)", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": true, + "legendFormat": "{{le}}", + "range": false, + "refId": "A", + "useBackend": false + } + ], + "title": "Chunk size", + "type": "barchart" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 24 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "sum by(job) (increase(rate_limit_count_total[$__rate_interval]))", + "format": "time_series", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{instance}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Rate limit events", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "light-blue", + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 31 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(process_cpu_seconds_total[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{instance}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "CPU", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "GB", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 31 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "process_resident_memory_bytes / 1073741824", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{instance}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Memory", + "type": "timeseries" + }, + { + "datasource": { + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "footer": { + "reducers": [] + }, + "hideFrom": { + "viz": false + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 39 + }, + "id": 14, + "options": { + "cellHeight": "sm", + "showHeader": true + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "exemplar": false, + "expr": "sum by (model) (tokens_total)", + "format": "table", + "instant": true, + "legendFormat": "__auto", + "range": false, + "refId": "A" + } + ], + "title": "Models in use", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true + }, + "includeByName": {}, + "indexByName": {}, + "renameByName": {} + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 39 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(model, direction) (rate(tokens_total[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{model}} - {{direction}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Tokens", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "$", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 39 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(direction, model) (rate(tokens_total[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{model}} - {{direction}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Token cost", + "type": "timeseries" + } + ], + "preload": false, + "refresh": "5s", + "schemaVersion": 42, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Overview", + "uid": "b5c8abf8-fe79-496b-b028-10bde917d1f0", + "version": 1 +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.9/grafana/provisioning/dashboard.yml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.9/grafana/provisioning/dashboard.yml new file mode 100644 index 00000000..9b9e7450 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.9/grafana/provisioning/dashboard.yml @@ -0,0 +1,17 @@ + +apiVersion: 1 + +providers: + + - name: 'trustgraph.ai' + orgId: 1 + folder: 'TrustGraph' + folderUid: 'b6c5be90-d432-4df8-aeab-737c7b151228' + type: file + disableDeletion: false + updateIntervalSeconds: 30 + allowUiUpdates: true + options: + path: /var/lib/grafana/dashboards + foldersFromFilesStructure: false + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.9/grafana/provisioning/datasource.yml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.9/grafana/provisioning/datasource.yml new file mode 100644 index 00000000..1cf4bc3d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.9/grafana/provisioning/datasource.yml @@ -0,0 +1,36 @@ +apiVersion: 1 + +prune: true + +datasources: + - name: Prometheus + type: prometheus + access: proxy + orgId: 1 + # Sets a custom UID to reference this + # data source in other parts of the configuration. + # If not specified, Grafana generates one. + uid: 'f6b18033-5918-4e05-a1ca-4cb30343b129' + + url: http://prometheus:9090 + + basicAuth: false + withCredentials: false + isDefault: true + editable: true + + - name: Loki + type: loki + access: proxy + orgId: 1 + # Sets a custom UID to reference this + # data source in other parts of the configuration. + # If not specified, Grafana generates one. + uid: 'cf6m73l5rnvuod' + + url: http://loki:3100 + + basicAuth: false + withCredentials: false + editable: true + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.9/loki/local-config.yaml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.9/loki/local-config.yaml new file mode 100644 index 00000000..8c519ee9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.9/loki/local-config.yaml @@ -0,0 +1,63 @@ +auth_enabled: false + +server: + http_listen_port: 3100 + grpc_listen_port: 9096 + log_level: debug + grpc_server_max_concurrent_streams: 1000 + +common: + instance_addr: 127.0.0.1 + path_prefix: /tmp/loki + storage: + filesystem: + chunks_directory: /tmp/loki/chunks + rules_directory: /tmp/loki/rules + replication_factor: 1 + ring: + kvstore: + store: inmemory + +query_range: + results_cache: + cache: + embedded_cache: + enabled: true + max_size_mb: 100 + +limits_config: + metric_aggregation_enabled: true + +schema_config: + configs: + - from: 2020-10-24 + store: tsdb + object_store: filesystem + schema: v13 + index: + prefix: index_ + period: 24h + +pattern_ingester: + enabled: true + metric_aggregation: + loki_address: localhost:3100 + +ruler: + alertmanager_url: http://localhost:9093 + +frontend: + encoding: protobuf + +# By default, Loki will send anonymous, but uniquely-identifiable usage and configuration +# analytics to Grafana Labs. These statistics are sent to https://stats.grafana.org/ +# +# Statistics help us better understand how Loki is used, and they show us performance +# levels for most users. This helps us prioritize features and documentation. +# For more information on what's sent, look at +# https://github.com/grafana/loki/blob/main/pkg/analytics/stats.go +# Refer to the buildReport method to see what goes into a report. +# +# If you would like to disable reporting, uncomment the following lines: +analytics: + reporting_enabled: false diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.9/prometheus/prometheus.yml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.9/prometheus/prometheus.yml new file mode 100644 index 00000000..b5c8509d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/1.9/prometheus/prometheus.yml @@ -0,0 +1,247 @@ +global: + + scrape_interval: 15s # By default, scrape targets every 15 seconds. + + # Attach these labels to any time series or alerts when communicating with + # external systems (federation, remote storage, Alertmanager). + external_labels: + monitor: 'trustgraph' + +# A scrape configuration containing exactly one endpoint to scrape: +# Here it's Prometheus itself. +scrape_configs: + + # The job name is added as a label `job=` to any timeseries + # scraped from this config. + + # TrustGraph services + + - job_name: 'agent-manager' + scrape_interval: 5s + static_configs: + - targets: + - 'agent-manager:8000' + + - job_name: 'api-gateway' + scrape_interval: 5s + static_configs: + - targets: + - 'api-gateway:8000' + + - job_name: 'chunker' + scrape_interval: 5s + static_configs: + - targets: + - 'chunker:8000' + + - job_name: 'config-svc' + scrape_interval: 5s + static_configs: + - targets: + - 'config-svc:8000' + + - job_name: 'document-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'document-embeddings:8000' + + - job_name: 'document-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'document-rag:8000' + + - job_name: 'embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'embeddings:8000' + + - job_name: 'graph-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'graph-embeddings:8000' + + - job_name: 'graph-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'graph-rag:8000' + + - job_name: 'kg-extract-agent' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-agent:8000' + + - job_name: 'kg-extract-definitions' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-definitions:8000' + + - job_name: 'kg-extract-objects' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-objects:8000' + + - job_name: 'kg-extract-relationships' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-relationships:8000' + + - job_name: 'kg-extract-ontology' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-ontology:8000' + + - job_name: 'kg-manager' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-manager:8000' + + - job_name: 'kg-store' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-store:8000' + + - job_name: 'librarian' + scrape_interval: 5s + static_configs: + - targets: + - 'librarian:8000' + + - job_name: 'mcp-server' + scrape_interval: 5s + static_configs: + - targets: + - 'mcp-server:8000' + + - job_name: 'mcp-tool' + scrape_interval: 5s + static_configs: + - targets: + - 'mcp-tool:8000' + + - job_name: 'metering' + scrape_interval: 5s + static_configs: + - targets: + - 'metering:8000' + + - job_name: 'metering-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'metering-rag:8000' + + - job_name: 'nlp-query' + scrape_interval: 5s + static_configs: + - targets: + - 'nlp-query:8000' + + - job_name: 'pdf-decoder' + scrape_interval: 5s + static_configs: + - targets: + - 'pdf-decoder:8000' + + - job_name: 'prompt' + scrape_interval: 5s + static_configs: + - targets: + - 'prompt:8000' + + - job_name: 'prompt-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'prompt-rag:8000' + + - job_name: 'query-doc-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'query-doc-embeddings:8000' + + - job_name: 'query-graph-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'query-graph-embeddings:8000' + + - job_name: 'query-objects' + scrape_interval: 5s + static_configs: + - targets: + - 'query-objects:8000' + + - job_name: 'query-triples' + scrape_interval: 5s + static_configs: + - targets: + - 'query-triples:8000' + + - job_name: 'store-doc-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'store-doc-embeddings:8000' + + - job_name: 'store-graph-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'store-graph-embeddings:8000' + + - job_name: 'store-objects' + scrape_interval: 5s + static_configs: + - targets: + - 'store-objects:8000' + + - job_name: 'store-triples' + scrape_interval: 5s + static_configs: + - targets: + - 'store-triples:8000' + + - job_name: 'structured-query' + scrape_interval: 5s + static_configs: + - targets: + - 'structured-query:8000' + + - job_name: 'structured-diag' + scrape_interval: 5s + static_configs: + - targets: + - 'structured-query:8000' + + - job_name: 'text-completion' + scrape_interval: 5s + static_configs: + - targets: + - 'text-completion:8000' + + - job_name: 'text-completion-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'text-completion-rag:8000' + + # Non-Trustgraph services + - job_name: 'pulsar' + scrape_interval: 5s + static_configs: + - targets: + - 'pulsar:8080' + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.0/grafana/dashboards/log-dashboard.json b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.0/grafana/dashboards/log-dashboard.json new file mode 100644 index 00000000..2d40b195 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.0/grafana/dashboards/log-dashboard.json @@ -0,0 +1,178 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "loki", + "uid": "cf6m73l5rnvuod" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 33, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "barRadius": 0, + "barWidth": 0.97, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "orientation": "auto", + "showValue": "never", + "stacking": "normal", + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 100 + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "cf6m73l5rnvuod" + }, + "direction": "backward", + "editorMode": "code", + "expr": "topk(5, sum by (processor) (count_over_time({severity=~\"error|critical\"} [$__auto])))", + "queryType": "range", + "refId": "A" + } + ], + "title": "Error volume", + "type": "barchart" + }, + { + "datasource": { + "type": "loki", + "uid": "cf6m73l5rnvuod" + }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "gridPos": { + "h": 18, + "w": 24, + "x": 0, + "y": 6 + }, + "id": 1, + "options": { + "dedupStrategy": "none", + "enableInfiniteScrolling": false, + "enableLogDetails": true, + "prettifyLogMessage": false, + "showCommonLabels": false, + "showLabels": false, + "showTime": false, + "sortOrder": "Descending", + "wrapLogMessage": true + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "cf6m73l5rnvuod" + }, + "direction": "backward", + "editorMode": "builder", + "expr": "{severity=~\"error|critical\"} |= ``", + "queryType": "range", + "refId": "A" + } + ], + "title": "Log errors", + "type": "logs" + } + ], + "preload": false, + "schemaVersion": 41, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Logs", + "uid": "4bdce405-0dd3-4a14-9a8f-792152ebebba", + "version": 13 +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.0/grafana/dashboards/overview-dashboard.json b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.0/grafana/dashboards/overview-dashboard.json new file mode 100644 index 00000000..31e57fb3 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.0/grafana/dashboards/overview-dashboard.json @@ -0,0 +1,1403 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "expr": "producer_count_total{processor=\"chunker\",name=\"output\"}\n- ignoring(instance, job, processor, name, status)\nprocessing_count_total{processor=\"kg-extract-relationships\", name=\"input\"}", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Knowledge extraction backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "expr": "(producer_count_total{processor=\"graph-embeddings\",name=\"output\"} - ignoring(instance, job, processor, name, status) processing_count_total{processor=\"ge-write\",name=\"input\"} >= 2)\nor\n(0 * (producer_count_total{processor=\"graph-embeddings\",name=\"output\"} - ignoring(instance, job, processor, name, status) processing_count_total{processor=\"ge-write\",name=\"input\"}))", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Graph embeddings backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "expr": "(clamp_min(\n (\n producer_count_total{processor=\"kg-extract-definitions\",name=\"triples\"}\n + on(flow)\n producer_count_total{processor=\"kg-extract-relationships\",name=\"triples\"}\n )\n - on(flow) processing_count_total{processor=\"triples-write\",name=\"input\"},\n 0\n) >= 2) or (0 * clamp_min(\n (\n producer_count_total{processor=\"kg-extract-definitions\",name=\"triples\"}\n + on(flow)\n producer_count_total{processor=\"kg-extract-relationships\",name=\"triples\"}\n )\n - on(flow) processing_count_total{processor=\"triples-write\",name=\"input\"},\n 0\n))", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Triples backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 7 + }, + "id": 7, + "options": { + "calculate": false, + "cellGap": 1, + "color": { + "exponent": 0.5, + "fill": "dark-orange", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Oranges", + "steps": 64 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "tooltip": { + "mode": "single", + "showColorScale": false, + "yHistogram": false + }, + "yAxis": { + "axisPlacement": "left", + "reverse": false + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "sum by(le) (rate(text_completion_duration_bucket[$__rate_interval]))", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "99%", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "LLM latency", + "type": "heatmap" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 7 + }, + "id": 2, + "options": { + "calculate": false, + "cellGap": 5, + "cellValues": { + "unit": "" + }, + "color": { + "exponent": 0.5, + "fill": "dark-orange", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Oranges", + "steps": 64 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "tooltip": { + "mode": "single", + "showColorScale": false, + "yHistogram": false + }, + "yAxis": { + "axisLabel": "processing status", + "axisPlacement": "left", + "reverse": false + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "sum by(status) (rate(processing_count_total{status!=\"success\"}[$__rate_interval]))", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "", + "legendFormat": "{{status}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Error rate", + "type": "heatmap" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 15 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(processor) (rate(request_latency_count{processor!=\"config-svc\"}[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{job}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Request rate", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 15 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "pulsar_msg_backlog{topic!=\"persistent://tg/config/config\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{topic}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Pub/sub backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "semi-dark-green", + "mode": "palette-classic-by-name" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 10, + "options": { + "barRadius": 0, + "barWidth": 0.97, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "none", + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "max by(le) (chunk_size_bucket)", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": true, + "legendFormat": "{{le}}", + "range": false, + "refId": "A", + "useBackend": false + } + ], + "title": "Chunk size", + "type": "barchart" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 24 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "sum by(job) (increase(rate_limit_count_total[$__rate_interval]))", + "format": "time_series", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{instance}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Rate limit events", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "light-blue", + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 31 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(process_cpu_seconds_total[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{instance}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "CPU", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "GB", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 31 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "process_resident_memory_bytes / 1073741824", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{instance}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Memory", + "type": "timeseries" + }, + { + "datasource": { + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "footer": { + "reducers": [] + }, + "hideFrom": { + "viz": false + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 39 + }, + "id": 14, + "options": { + "cellHeight": "sm", + "showHeader": true + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "exemplar": false, + "expr": "sum by (model) (tokens_total)", + "format": "table", + "instant": true, + "legendFormat": "__auto", + "range": false, + "refId": "A" + } + ], + "title": "Models in use", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true + }, + "includeByName": {}, + "indexByName": {}, + "renameByName": {} + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 39 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(model, direction) (rate(tokens_total[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{model}} - {{direction}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Tokens", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "$", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 39 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(direction, model) (rate(tokens_total[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{model}} - {{direction}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Token cost", + "type": "timeseries" + } + ], + "preload": false, + "refresh": "5s", + "schemaVersion": 42, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Overview", + "uid": "b5c8abf8-fe79-496b-b028-10bde917d1f0", + "version": 1 +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.0/grafana/provisioning/dashboard.yml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.0/grafana/provisioning/dashboard.yml new file mode 100644 index 00000000..9b9e7450 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.0/grafana/provisioning/dashboard.yml @@ -0,0 +1,17 @@ + +apiVersion: 1 + +providers: + + - name: 'trustgraph.ai' + orgId: 1 + folder: 'TrustGraph' + folderUid: 'b6c5be90-d432-4df8-aeab-737c7b151228' + type: file + disableDeletion: false + updateIntervalSeconds: 30 + allowUiUpdates: true + options: + path: /var/lib/grafana/dashboards + foldersFromFilesStructure: false + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.0/grafana/provisioning/datasource.yml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.0/grafana/provisioning/datasource.yml new file mode 100644 index 00000000..1cf4bc3d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.0/grafana/provisioning/datasource.yml @@ -0,0 +1,36 @@ +apiVersion: 1 + +prune: true + +datasources: + - name: Prometheus + type: prometheus + access: proxy + orgId: 1 + # Sets a custom UID to reference this + # data source in other parts of the configuration. + # If not specified, Grafana generates one. + uid: 'f6b18033-5918-4e05-a1ca-4cb30343b129' + + url: http://prometheus:9090 + + basicAuth: false + withCredentials: false + isDefault: true + editable: true + + - name: Loki + type: loki + access: proxy + orgId: 1 + # Sets a custom UID to reference this + # data source in other parts of the configuration. + # If not specified, Grafana generates one. + uid: 'cf6m73l5rnvuod' + + url: http://loki:3100 + + basicAuth: false + withCredentials: false + editable: true + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.0/loki/local-config.yaml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.0/loki/local-config.yaml new file mode 100644 index 00000000..8c519ee9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.0/loki/local-config.yaml @@ -0,0 +1,63 @@ +auth_enabled: false + +server: + http_listen_port: 3100 + grpc_listen_port: 9096 + log_level: debug + grpc_server_max_concurrent_streams: 1000 + +common: + instance_addr: 127.0.0.1 + path_prefix: /tmp/loki + storage: + filesystem: + chunks_directory: /tmp/loki/chunks + rules_directory: /tmp/loki/rules + replication_factor: 1 + ring: + kvstore: + store: inmemory + +query_range: + results_cache: + cache: + embedded_cache: + enabled: true + max_size_mb: 100 + +limits_config: + metric_aggregation_enabled: true + +schema_config: + configs: + - from: 2020-10-24 + store: tsdb + object_store: filesystem + schema: v13 + index: + prefix: index_ + period: 24h + +pattern_ingester: + enabled: true + metric_aggregation: + loki_address: localhost:3100 + +ruler: + alertmanager_url: http://localhost:9093 + +frontend: + encoding: protobuf + +# By default, Loki will send anonymous, but uniquely-identifiable usage and configuration +# analytics to Grafana Labs. These statistics are sent to https://stats.grafana.org/ +# +# Statistics help us better understand how Loki is used, and they show us performance +# levels for most users. This helps us prioritize features and documentation. +# For more information on what's sent, look at +# https://github.com/grafana/loki/blob/main/pkg/analytics/stats.go +# Refer to the buildReport method to see what goes into a report. +# +# If you would like to disable reporting, uncomment the following lines: +analytics: + reporting_enabled: false diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.0/prometheus/prometheus.yml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.0/prometheus/prometheus.yml new file mode 100644 index 00000000..314fc618 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.0/prometheus/prometheus.yml @@ -0,0 +1,265 @@ +global: + + scrape_interval: 15s # By default, scrape targets every 15 seconds. + + # Attach these labels to any time series or alerts when communicating with + # external systems (federation, remote storage, Alertmanager). + external_labels: + monitor: 'trustgraph' + +# A scrape configuration containing exactly one endpoint to scrape: +# Here it's Prometheus itself. +scrape_configs: + + # The job name is added as a label `job=` to any timeseries + # scraped from this config. + + # TrustGraph services + + - job_name: 'agent-manager' + scrape_interval: 5s + static_configs: + - targets: + - 'agent-manager:8000' + + - job_name: 'api-gateway' + scrape_interval: 5s + static_configs: + - targets: + - 'api-gateway:8000' + + - job_name: 'chunker' + scrape_interval: 5s + static_configs: + - targets: + - 'chunker:8000' + + - job_name: 'config-svc' + scrape_interval: 5s + static_configs: + - targets: + - 'config-svc:8000' + + - job_name: 'document-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'document-embeddings:8000' + + - job_name: 'document-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'document-rag:8000' + + - job_name: 'embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'embeddings:8000' + + - job_name: 'graph-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'graph-embeddings:8000' + + - job_name: 'graph-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'graph-rag:8000' + + - job_name: 'kg-extract-agent' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-agent:8000' + + - job_name: 'kg-extract-definitions' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-definitions:8000' + + - job_name: 'kg-extract-objects' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-objects:8000' + + - job_name: 'kg-extract-relationships' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-relationships:8000' + + - job_name: 'kg-extract-ontology' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-ontology:8000' + + - job_name: 'kg-manager' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-manager:8000' + + - job_name: 'kg-store' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-store:8000' + + - job_name: 'librarian' + scrape_interval: 5s + static_configs: + - targets: + - 'librarian:8000' + + - job_name: 'mcp-server' + scrape_interval: 5s + static_configs: + - targets: + - 'mcp-server:8000' + + - job_name: 'mcp-tool' + scrape_interval: 5s + static_configs: + - targets: + - 'mcp-tool:8000' + + - job_name: 'metering' + scrape_interval: 5s + static_configs: + - targets: + - 'metering:8000' + + - job_name: 'metering-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'metering-rag:8000' + + - job_name: 'nlp-query' + scrape_interval: 5s + static_configs: + - targets: + - 'nlp-query:8000' + + - job_name: 'pdf-decoder' + scrape_interval: 5s + static_configs: + - targets: + - 'pdf-decoder:8000' + + - job_name: 'prompt' + scrape_interval: 5s + static_configs: + - targets: + - 'prompt:8000' + + - job_name: 'prompt-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'prompt-rag:8000' + + - job_name: 'query-doc-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'query-doc-embeddings:8000' + + - job_name: 'query-graph-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'query-graph-embeddings:8000' + + - job_name: 'query-row-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'query-row-embeddings:8000' + + - job_name: 'query-rows' + scrape_interval: 5s + static_configs: + - targets: + - 'query-rows:8000' + + - job_name: 'query-triples' + scrape_interval: 5s + static_configs: + - targets: + - 'query-triples:8000' + + - job_name: 'row-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'row-embeddings:8000' + + - job_name: 'store-doc-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'store-doc-embeddings:8000' + + - job_name: 'store-graph-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'store-graph-embeddings:8000' + + - job_name: 'store-row-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'store-row-embeddings:8000' + + - job_name: 'store-rows' + scrape_interval: 5s + static_configs: + - targets: + - 'store-rows:8000' + + - job_name: 'store-triples' + scrape_interval: 5s + static_configs: + - targets: + - 'store-triples:8000' + + - job_name: 'structured-query' + scrape_interval: 5s + static_configs: + - targets: + - 'structured-query:8000' + + - job_name: 'structured-diag' + scrape_interval: 5s + static_configs: + - targets: + - 'structured-diag:8000' + + - job_name: 'text-completion' + scrape_interval: 5s + static_configs: + - targets: + - 'text-completion:8000' + + - job_name: 'text-completion-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'text-completion-rag:8000' + + # Non-Trustgraph services + - job_name: 'pulsar' + scrape_interval: 5s + static_configs: + - targets: + - 'pulsar:8080' + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.1/grafana/dashboards/log-dashboard.json b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.1/grafana/dashboards/log-dashboard.json new file mode 100644 index 00000000..2d40b195 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.1/grafana/dashboards/log-dashboard.json @@ -0,0 +1,178 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "loki", + "uid": "cf6m73l5rnvuod" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 33, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "barRadius": 0, + "barWidth": 0.97, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "orientation": "auto", + "showValue": "never", + "stacking": "normal", + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 100 + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "cf6m73l5rnvuod" + }, + "direction": "backward", + "editorMode": "code", + "expr": "topk(5, sum by (processor) (count_over_time({severity=~\"error|critical\"} [$__auto])))", + "queryType": "range", + "refId": "A" + } + ], + "title": "Error volume", + "type": "barchart" + }, + { + "datasource": { + "type": "loki", + "uid": "cf6m73l5rnvuod" + }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "gridPos": { + "h": 18, + "w": 24, + "x": 0, + "y": 6 + }, + "id": 1, + "options": { + "dedupStrategy": "none", + "enableInfiniteScrolling": false, + "enableLogDetails": true, + "prettifyLogMessage": false, + "showCommonLabels": false, + "showLabels": false, + "showTime": false, + "sortOrder": "Descending", + "wrapLogMessage": true + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "cf6m73l5rnvuod" + }, + "direction": "backward", + "editorMode": "builder", + "expr": "{severity=~\"error|critical\"} |= ``", + "queryType": "range", + "refId": "A" + } + ], + "title": "Log errors", + "type": "logs" + } + ], + "preload": false, + "schemaVersion": 41, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Logs", + "uid": "4bdce405-0dd3-4a14-9a8f-792152ebebba", + "version": 13 +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.1/grafana/dashboards/overview-dashboard.json b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.1/grafana/dashboards/overview-dashboard.json new file mode 100644 index 00000000..31e57fb3 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.1/grafana/dashboards/overview-dashboard.json @@ -0,0 +1,1403 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "expr": "producer_count_total{processor=\"chunker\",name=\"output\"}\n- ignoring(instance, job, processor, name, status)\nprocessing_count_total{processor=\"kg-extract-relationships\", name=\"input\"}", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Knowledge extraction backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "expr": "(producer_count_total{processor=\"graph-embeddings\",name=\"output\"} - ignoring(instance, job, processor, name, status) processing_count_total{processor=\"ge-write\",name=\"input\"} >= 2)\nor\n(0 * (producer_count_total{processor=\"graph-embeddings\",name=\"output\"} - ignoring(instance, job, processor, name, status) processing_count_total{processor=\"ge-write\",name=\"input\"}))", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Graph embeddings backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "expr": "(clamp_min(\n (\n producer_count_total{processor=\"kg-extract-definitions\",name=\"triples\"}\n + on(flow)\n producer_count_total{processor=\"kg-extract-relationships\",name=\"triples\"}\n )\n - on(flow) processing_count_total{processor=\"triples-write\",name=\"input\"},\n 0\n) >= 2) or (0 * clamp_min(\n (\n producer_count_total{processor=\"kg-extract-definitions\",name=\"triples\"}\n + on(flow)\n producer_count_total{processor=\"kg-extract-relationships\",name=\"triples\"}\n )\n - on(flow) processing_count_total{processor=\"triples-write\",name=\"input\"},\n 0\n))", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Triples backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 7 + }, + "id": 7, + "options": { + "calculate": false, + "cellGap": 1, + "color": { + "exponent": 0.5, + "fill": "dark-orange", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Oranges", + "steps": 64 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "tooltip": { + "mode": "single", + "showColorScale": false, + "yHistogram": false + }, + "yAxis": { + "axisPlacement": "left", + "reverse": false + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "sum by(le) (rate(text_completion_duration_bucket[$__rate_interval]))", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "99%", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "LLM latency", + "type": "heatmap" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 7 + }, + "id": 2, + "options": { + "calculate": false, + "cellGap": 5, + "cellValues": { + "unit": "" + }, + "color": { + "exponent": 0.5, + "fill": "dark-orange", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Oranges", + "steps": 64 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "tooltip": { + "mode": "single", + "showColorScale": false, + "yHistogram": false + }, + "yAxis": { + "axisLabel": "processing status", + "axisPlacement": "left", + "reverse": false + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "sum by(status) (rate(processing_count_total{status!=\"success\"}[$__rate_interval]))", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "", + "legendFormat": "{{status}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Error rate", + "type": "heatmap" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 15 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(processor) (rate(request_latency_count{processor!=\"config-svc\"}[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{job}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Request rate", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 15 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "pulsar_msg_backlog{topic!=\"persistent://tg/config/config\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{topic}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Pub/sub backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "semi-dark-green", + "mode": "palette-classic-by-name" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 10, + "options": { + "barRadius": 0, + "barWidth": 0.97, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "none", + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "max by(le) (chunk_size_bucket)", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": true, + "legendFormat": "{{le}}", + "range": false, + "refId": "A", + "useBackend": false + } + ], + "title": "Chunk size", + "type": "barchart" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 24 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "sum by(job) (increase(rate_limit_count_total[$__rate_interval]))", + "format": "time_series", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{instance}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Rate limit events", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "light-blue", + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 31 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(process_cpu_seconds_total[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{instance}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "CPU", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "GB", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 31 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "process_resident_memory_bytes / 1073741824", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{instance}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Memory", + "type": "timeseries" + }, + { + "datasource": { + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "footer": { + "reducers": [] + }, + "hideFrom": { + "viz": false + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 39 + }, + "id": 14, + "options": { + "cellHeight": "sm", + "showHeader": true + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "exemplar": false, + "expr": "sum by (model) (tokens_total)", + "format": "table", + "instant": true, + "legendFormat": "__auto", + "range": false, + "refId": "A" + } + ], + "title": "Models in use", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true + }, + "includeByName": {}, + "indexByName": {}, + "renameByName": {} + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 39 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(model, direction) (rate(tokens_total[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{model}} - {{direction}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Tokens", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "$", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 39 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(direction, model) (rate(tokens_total[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{model}} - {{direction}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Token cost", + "type": "timeseries" + } + ], + "preload": false, + "refresh": "5s", + "schemaVersion": 42, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Overview", + "uid": "b5c8abf8-fe79-496b-b028-10bde917d1f0", + "version": 1 +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.1/grafana/provisioning/dashboard.yml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.1/grafana/provisioning/dashboard.yml new file mode 100644 index 00000000..9b9e7450 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.1/grafana/provisioning/dashboard.yml @@ -0,0 +1,17 @@ + +apiVersion: 1 + +providers: + + - name: 'trustgraph.ai' + orgId: 1 + folder: 'TrustGraph' + folderUid: 'b6c5be90-d432-4df8-aeab-737c7b151228' + type: file + disableDeletion: false + updateIntervalSeconds: 30 + allowUiUpdates: true + options: + path: /var/lib/grafana/dashboards + foldersFromFilesStructure: false + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.1/grafana/provisioning/datasource.yml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.1/grafana/provisioning/datasource.yml new file mode 100644 index 00000000..1cf4bc3d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.1/grafana/provisioning/datasource.yml @@ -0,0 +1,36 @@ +apiVersion: 1 + +prune: true + +datasources: + - name: Prometheus + type: prometheus + access: proxy + orgId: 1 + # Sets a custom UID to reference this + # data source in other parts of the configuration. + # If not specified, Grafana generates one. + uid: 'f6b18033-5918-4e05-a1ca-4cb30343b129' + + url: http://prometheus:9090 + + basicAuth: false + withCredentials: false + isDefault: true + editable: true + + - name: Loki + type: loki + access: proxy + orgId: 1 + # Sets a custom UID to reference this + # data source in other parts of the configuration. + # If not specified, Grafana generates one. + uid: 'cf6m73l5rnvuod' + + url: http://loki:3100 + + basicAuth: false + withCredentials: false + editable: true + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.1/loki/local-config.yaml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.1/loki/local-config.yaml new file mode 100644 index 00000000..8c519ee9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.1/loki/local-config.yaml @@ -0,0 +1,63 @@ +auth_enabled: false + +server: + http_listen_port: 3100 + grpc_listen_port: 9096 + log_level: debug + grpc_server_max_concurrent_streams: 1000 + +common: + instance_addr: 127.0.0.1 + path_prefix: /tmp/loki + storage: + filesystem: + chunks_directory: /tmp/loki/chunks + rules_directory: /tmp/loki/rules + replication_factor: 1 + ring: + kvstore: + store: inmemory + +query_range: + results_cache: + cache: + embedded_cache: + enabled: true + max_size_mb: 100 + +limits_config: + metric_aggregation_enabled: true + +schema_config: + configs: + - from: 2020-10-24 + store: tsdb + object_store: filesystem + schema: v13 + index: + prefix: index_ + period: 24h + +pattern_ingester: + enabled: true + metric_aggregation: + loki_address: localhost:3100 + +ruler: + alertmanager_url: http://localhost:9093 + +frontend: + encoding: protobuf + +# By default, Loki will send anonymous, but uniquely-identifiable usage and configuration +# analytics to Grafana Labs. These statistics are sent to https://stats.grafana.org/ +# +# Statistics help us better understand how Loki is used, and they show us performance +# levels for most users. This helps us prioritize features and documentation. +# For more information on what's sent, look at +# https://github.com/grafana/loki/blob/main/pkg/analytics/stats.go +# Refer to the buildReport method to see what goes into a report. +# +# If you would like to disable reporting, uncomment the following lines: +analytics: + reporting_enabled: false diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.1/prometheus/prometheus.yml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.1/prometheus/prometheus.yml new file mode 100644 index 00000000..314fc618 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.1/prometheus/prometheus.yml @@ -0,0 +1,265 @@ +global: + + scrape_interval: 15s # By default, scrape targets every 15 seconds. + + # Attach these labels to any time series or alerts when communicating with + # external systems (federation, remote storage, Alertmanager). + external_labels: + monitor: 'trustgraph' + +# A scrape configuration containing exactly one endpoint to scrape: +# Here it's Prometheus itself. +scrape_configs: + + # The job name is added as a label `job=` to any timeseries + # scraped from this config. + + # TrustGraph services + + - job_name: 'agent-manager' + scrape_interval: 5s + static_configs: + - targets: + - 'agent-manager:8000' + + - job_name: 'api-gateway' + scrape_interval: 5s + static_configs: + - targets: + - 'api-gateway:8000' + + - job_name: 'chunker' + scrape_interval: 5s + static_configs: + - targets: + - 'chunker:8000' + + - job_name: 'config-svc' + scrape_interval: 5s + static_configs: + - targets: + - 'config-svc:8000' + + - job_name: 'document-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'document-embeddings:8000' + + - job_name: 'document-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'document-rag:8000' + + - job_name: 'embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'embeddings:8000' + + - job_name: 'graph-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'graph-embeddings:8000' + + - job_name: 'graph-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'graph-rag:8000' + + - job_name: 'kg-extract-agent' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-agent:8000' + + - job_name: 'kg-extract-definitions' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-definitions:8000' + + - job_name: 'kg-extract-objects' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-objects:8000' + + - job_name: 'kg-extract-relationships' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-relationships:8000' + + - job_name: 'kg-extract-ontology' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-ontology:8000' + + - job_name: 'kg-manager' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-manager:8000' + + - job_name: 'kg-store' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-store:8000' + + - job_name: 'librarian' + scrape_interval: 5s + static_configs: + - targets: + - 'librarian:8000' + + - job_name: 'mcp-server' + scrape_interval: 5s + static_configs: + - targets: + - 'mcp-server:8000' + + - job_name: 'mcp-tool' + scrape_interval: 5s + static_configs: + - targets: + - 'mcp-tool:8000' + + - job_name: 'metering' + scrape_interval: 5s + static_configs: + - targets: + - 'metering:8000' + + - job_name: 'metering-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'metering-rag:8000' + + - job_name: 'nlp-query' + scrape_interval: 5s + static_configs: + - targets: + - 'nlp-query:8000' + + - job_name: 'pdf-decoder' + scrape_interval: 5s + static_configs: + - targets: + - 'pdf-decoder:8000' + + - job_name: 'prompt' + scrape_interval: 5s + static_configs: + - targets: + - 'prompt:8000' + + - job_name: 'prompt-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'prompt-rag:8000' + + - job_name: 'query-doc-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'query-doc-embeddings:8000' + + - job_name: 'query-graph-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'query-graph-embeddings:8000' + + - job_name: 'query-row-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'query-row-embeddings:8000' + + - job_name: 'query-rows' + scrape_interval: 5s + static_configs: + - targets: + - 'query-rows:8000' + + - job_name: 'query-triples' + scrape_interval: 5s + static_configs: + - targets: + - 'query-triples:8000' + + - job_name: 'row-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'row-embeddings:8000' + + - job_name: 'store-doc-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'store-doc-embeddings:8000' + + - job_name: 'store-graph-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'store-graph-embeddings:8000' + + - job_name: 'store-row-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'store-row-embeddings:8000' + + - job_name: 'store-rows' + scrape_interval: 5s + static_configs: + - targets: + - 'store-rows:8000' + + - job_name: 'store-triples' + scrape_interval: 5s + static_configs: + - targets: + - 'store-triples:8000' + + - job_name: 'structured-query' + scrape_interval: 5s + static_configs: + - targets: + - 'structured-query:8000' + + - job_name: 'structured-diag' + scrape_interval: 5s + static_configs: + - targets: + - 'structured-diag:8000' + + - job_name: 'text-completion' + scrape_interval: 5s + static_configs: + - targets: + - 'text-completion:8000' + + - job_name: 'text-completion-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'text-completion-rag:8000' + + # Non-Trustgraph services + - job_name: 'pulsar' + scrape_interval: 5s + static_configs: + - targets: + - 'pulsar:8080' + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.2/grafana/dashboards/log-dashboard.json b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.2/grafana/dashboards/log-dashboard.json new file mode 100644 index 00000000..2d40b195 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.2/grafana/dashboards/log-dashboard.json @@ -0,0 +1,178 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "loki", + "uid": "cf6m73l5rnvuod" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 33, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "barRadius": 0, + "barWidth": 0.97, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "orientation": "auto", + "showValue": "never", + "stacking": "normal", + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 100 + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "cf6m73l5rnvuod" + }, + "direction": "backward", + "editorMode": "code", + "expr": "topk(5, sum by (processor) (count_over_time({severity=~\"error|critical\"} [$__auto])))", + "queryType": "range", + "refId": "A" + } + ], + "title": "Error volume", + "type": "barchart" + }, + { + "datasource": { + "type": "loki", + "uid": "cf6m73l5rnvuod" + }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "gridPos": { + "h": 18, + "w": 24, + "x": 0, + "y": 6 + }, + "id": 1, + "options": { + "dedupStrategy": "none", + "enableInfiniteScrolling": false, + "enableLogDetails": true, + "prettifyLogMessage": false, + "showCommonLabels": false, + "showLabels": false, + "showTime": false, + "sortOrder": "Descending", + "wrapLogMessage": true + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "cf6m73l5rnvuod" + }, + "direction": "backward", + "editorMode": "builder", + "expr": "{severity=~\"error|critical\"} |= ``", + "queryType": "range", + "refId": "A" + } + ], + "title": "Log errors", + "type": "logs" + } + ], + "preload": false, + "schemaVersion": 41, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Logs", + "uid": "4bdce405-0dd3-4a14-9a8f-792152ebebba", + "version": 13 +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.2/grafana/dashboards/overview-dashboard-pulsar.json b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.2/grafana/dashboards/overview-dashboard-pulsar.json new file mode 100644 index 00000000..d4f0f1af --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.2/grafana/dashboards/overview-dashboard-pulsar.json @@ -0,0 +1,1403 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 5, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "expr": "producer_count_total{processor=\"chunker\",name=\"output\"}\n- ignoring(instance, job, processor, name, status)\nprocessing_count_total{processor=\"kg-extract-relationships\", name=\"input\"}", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Knowledge extraction backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "expr": "(producer_count_total{processor=\"graph-embeddings\",name=\"output\"} - ignoring(instance, job, processor, name, status) processing_count_total{processor=\"ge-write\",name=\"input\"} >= 2)\nor\n(0 * (producer_count_total{processor=\"graph-embeddings\",name=\"output\"} - ignoring(instance, job, processor, name, status) processing_count_total{processor=\"ge-write\",name=\"input\"}))", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Graph embeddings backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "expr": "(clamp_min(\n (\n producer_count_total{processor=\"kg-extract-definitions\",name=\"triples\"}\n + on(flow)\n producer_count_total{processor=\"kg-extract-relationships\",name=\"triples\"}\n )\n - on(flow) processing_count_total{processor=\"triples-write\",name=\"input\"},\n 0\n) >= 2) or (0 * clamp_min(\n (\n producer_count_total{processor=\"kg-extract-definitions\",name=\"triples\"}\n + on(flow)\n producer_count_total{processor=\"kg-extract-relationships\",name=\"triples\"}\n )\n - on(flow) processing_count_total{processor=\"triples-write\",name=\"input\"},\n 0\n))", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Triples backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 7 + }, + "id": 7, + "options": { + "calculate": false, + "cellGap": 1, + "color": { + "exponent": 0.5, + "fill": "dark-orange", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Oranges", + "steps": 64 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "tooltip": { + "mode": "single", + "showColorScale": false, + "yHistogram": false + }, + "yAxis": { + "axisPlacement": "left", + "reverse": false + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "sum by(le) (rate(text_completion_duration_bucket[$__rate_interval]))", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "99%", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "LLM latency", + "type": "heatmap" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 7 + }, + "id": 2, + "options": { + "calculate": false, + "cellGap": 5, + "cellValues": { + "unit": "" + }, + "color": { + "exponent": 0.5, + "fill": "dark-orange", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Oranges", + "steps": 64 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "tooltip": { + "mode": "single", + "showColorScale": false, + "yHistogram": false + }, + "yAxis": { + "axisLabel": "processing status", + "axisPlacement": "left", + "reverse": false + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "sum by(status) (rate(processing_count_total{status!=\"success\"}[$__rate_interval]))", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "", + "legendFormat": "{{status}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Error rate", + "type": "heatmap" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 15 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(processor) (rate(request_latency_count{processor!=\"config-svc\"}[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{job}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Request rate", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 15 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "pulsar_msg_backlog{topic!=\"persistent://tg/config/config\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{topic}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Pub/sub backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "semi-dark-green", + "mode": "palette-classic-by-name" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 10, + "options": { + "barRadius": 0, + "barWidth": 0.97, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "none", + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "max by(le) (chunk_size_bucket)", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": true, + "legendFormat": "{{le}}", + "range": false, + "refId": "A", + "useBackend": false + } + ], + "title": "Chunk size", + "type": "barchart" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 24 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "sum by(job) (increase(rate_limit_count_total[$__rate_interval]))", + "format": "time_series", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{instance}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Rate limit events", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "light-blue", + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 31 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(process_cpu_seconds_total[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{instance}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "CPU", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "GB", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 31 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "process_resident_memory_bytes / 1073741824", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{instance}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Memory", + "type": "timeseries" + }, + { + "datasource": { + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "footer": { + "reducers": [] + }, + "hideFrom": { + "viz": false + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 39 + }, + "id": 14, + "options": { + "cellHeight": "sm", + "showHeader": true + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "exemplar": false, + "expr": "sum by (model) (tokens_total)", + "format": "table", + "instant": true, + "legendFormat": "__auto", + "range": false, + "refId": "A" + } + ], + "title": "Models in use", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true + }, + "includeByName": {}, + "indexByName": {}, + "renameByName": {} + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 39 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(model, direction) (rate(tokens_total[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{model}} - {{direction}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Tokens", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "$", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 39 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(direction, model) (rate(tokens_total[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{model}} - {{direction}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Token cost", + "type": "timeseries" + } + ], + "preload": false, + "refresh": "5s", + "schemaVersion": 42, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Overview (Pulsar)", + "uid": "ad77jv9", + "version": 1 +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.2/grafana/dashboards/overview-dashboard-rabbitmq.json b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.2/grafana/dashboards/overview-dashboard-rabbitmq.json new file mode 100644 index 00000000..c3dee6d8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.2/grafana/dashboards/overview-dashboard-rabbitmq.json @@ -0,0 +1,1403 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 4, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "expr": "producer_count_total{processor=\"chunker\",name=\"output\"}\n- ignoring(instance, job, processor, name, status)\nprocessing_count_total{processor=\"kg-extract-relationships\", name=\"input\"}", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Knowledge extraction backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "expr": "(producer_count_total{processor=\"graph-embeddings\",name=\"output\"} - ignoring(instance, job, processor, name, status) processing_count_total{processor=\"ge-write\",name=\"input\"} >= 2)\nor\n(0 * (producer_count_total{processor=\"graph-embeddings\",name=\"output\"} - ignoring(instance, job, processor, name, status) processing_count_total{processor=\"ge-write\",name=\"input\"}))", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Graph embeddings backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "expr": "(clamp_min(\n (\n producer_count_total{processor=\"kg-extract-definitions\",name=\"triples\"}\n + on(flow)\n producer_count_total{processor=\"kg-extract-relationships\",name=\"triples\"}\n )\n - on(flow) processing_count_total{processor=\"triples-write\",name=\"input\"},\n 0\n) >= 2) or (0 * clamp_min(\n (\n producer_count_total{processor=\"kg-extract-definitions\",name=\"triples\"}\n + on(flow)\n producer_count_total{processor=\"kg-extract-relationships\",name=\"triples\"}\n )\n - on(flow) processing_count_total{processor=\"triples-write\",name=\"input\"},\n 0\n))", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Triples backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 7 + }, + "id": 7, + "options": { + "calculate": false, + "cellGap": 1, + "color": { + "exponent": 0.5, + "fill": "dark-orange", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Oranges", + "steps": 64 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "tooltip": { + "mode": "single", + "showColorScale": false, + "yHistogram": false + }, + "yAxis": { + "axisPlacement": "left", + "reverse": false + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "sum by(le) (rate(text_completion_duration_bucket[$__rate_interval]))", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "99%", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "LLM latency", + "type": "heatmap" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 7 + }, + "id": 2, + "options": { + "calculate": false, + "cellGap": 5, + "cellValues": { + "unit": "" + }, + "color": { + "exponent": 0.5, + "fill": "dark-orange", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Oranges", + "steps": 64 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "tooltip": { + "mode": "single", + "showColorScale": false, + "yHistogram": false + }, + "yAxis": { + "axisLabel": "processing status", + "axisPlacement": "left", + "reverse": false + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "sum by(status) (rate(processing_count_total{status!=\"success\"}[$__rate_interval]))", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "", + "legendFormat": "{{status}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Error rate", + "type": "heatmap" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 15 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(processor) (rate(request_latency_count{processor!=\"config-svc\"}[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{job}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Request rate", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 15 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": " label_replace( \n rabbitmq_queue_messages{queue!~\"tg.state.config.*|amq.gen-.*\"}, \n \"short\", \"$2:$1:$3\", \"queue\", \"^([^.]+)\\\\.([^.]+)\\\\.([^.]+)\\\\..+$\" \n ) \n", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{short}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Pub/sub backlog", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "semi-dark-green", + "mode": "palette-classic-by-name" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 10, + "options": { + "barRadius": 0, + "barWidth": 0.97, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "none", + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "max by(le) (chunk_size_bucket)", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": true, + "legendFormat": "{{le}}", + "range": false, + "refId": "A", + "useBackend": false + } + ], + "title": "Chunk size", + "type": "barchart" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 24 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "sum by(job) (increase(rate_limit_count_total[$__rate_interval]))", + "format": "time_series", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{instance}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Rate limit events", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "light-blue", + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 31 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(process_cpu_seconds_total[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{instance}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "CPU", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "GB", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 31 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "process_resident_memory_bytes / 1073741824", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{instance}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Memory", + "type": "timeseries" + }, + { + "datasource": { + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "footer": { + "reducers": [] + }, + "hideFrom": { + "viz": false + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 39 + }, + "id": 14, + "options": { + "cellHeight": "sm", + "showHeader": true + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "editorMode": "code", + "exemplar": false, + "expr": "sum by (model) (tokens_total)", + "format": "table", + "instant": true, + "legendFormat": "__auto", + "range": false, + "refId": "A" + } + ], + "title": "Models in use", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true + }, + "includeByName": {}, + "indexByName": {}, + "renameByName": {} + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 39 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(model, direction) (rate(tokens_total[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{model}} - {{direction}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Tokens", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "$", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 39 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "f6b18033-5918-4e05-a1ca-4cb30343b129" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(direction, model) (rate(tokens_total[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{model}} - {{direction}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Token cost", + "type": "timeseries" + } + ], + "preload": false, + "refresh": "5s", + "schemaVersion": 42, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-30m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Overview (RabbitMQ)", + "uid": "adkxj6x", + "version": 1 +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.2/grafana/provisioning/dashboard.yml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.2/grafana/provisioning/dashboard.yml new file mode 100644 index 00000000..9b9e7450 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.2/grafana/provisioning/dashboard.yml @@ -0,0 +1,17 @@ + +apiVersion: 1 + +providers: + + - name: 'trustgraph.ai' + orgId: 1 + folder: 'TrustGraph' + folderUid: 'b6c5be90-d432-4df8-aeab-737c7b151228' + type: file + disableDeletion: false + updateIntervalSeconds: 30 + allowUiUpdates: true + options: + path: /var/lib/grafana/dashboards + foldersFromFilesStructure: false + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.2/grafana/provisioning/datasource.yml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.2/grafana/provisioning/datasource.yml new file mode 100644 index 00000000..1cf4bc3d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.2/grafana/provisioning/datasource.yml @@ -0,0 +1,36 @@ +apiVersion: 1 + +prune: true + +datasources: + - name: Prometheus + type: prometheus + access: proxy + orgId: 1 + # Sets a custom UID to reference this + # data source in other parts of the configuration. + # If not specified, Grafana generates one. + uid: 'f6b18033-5918-4e05-a1ca-4cb30343b129' + + url: http://prometheus:9090 + + basicAuth: false + withCredentials: false + isDefault: true + editable: true + + - name: Loki + type: loki + access: proxy + orgId: 1 + # Sets a custom UID to reference this + # data source in other parts of the configuration. + # If not specified, Grafana generates one. + uid: 'cf6m73l5rnvuod' + + url: http://loki:3100 + + basicAuth: false + withCredentials: false + editable: true + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.2/loki/local-config.yaml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.2/loki/local-config.yaml new file mode 100644 index 00000000..8c519ee9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.2/loki/local-config.yaml @@ -0,0 +1,63 @@ +auth_enabled: false + +server: + http_listen_port: 3100 + grpc_listen_port: 9096 + log_level: debug + grpc_server_max_concurrent_streams: 1000 + +common: + instance_addr: 127.0.0.1 + path_prefix: /tmp/loki + storage: + filesystem: + chunks_directory: /tmp/loki/chunks + rules_directory: /tmp/loki/rules + replication_factor: 1 + ring: + kvstore: + store: inmemory + +query_range: + results_cache: + cache: + embedded_cache: + enabled: true + max_size_mb: 100 + +limits_config: + metric_aggregation_enabled: true + +schema_config: + configs: + - from: 2020-10-24 + store: tsdb + object_store: filesystem + schema: v13 + index: + prefix: index_ + period: 24h + +pattern_ingester: + enabled: true + metric_aggregation: + loki_address: localhost:3100 + +ruler: + alertmanager_url: http://localhost:9093 + +frontend: + encoding: protobuf + +# By default, Loki will send anonymous, but uniquely-identifiable usage and configuration +# analytics to Grafana Labs. These statistics are sent to https://stats.grafana.org/ +# +# Statistics help us better understand how Loki is used, and they show us performance +# levels for most users. This helps us prioritize features and documentation. +# For more information on what's sent, look at +# https://github.com/grafana/loki/blob/main/pkg/analytics/stats.go +# Refer to the buildReport method to see what goes into a report. +# +# If you would like to disable reporting, uncomment the following lines: +analytics: + reporting_enabled: false diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.2/prometheus/prometheus-pulsar.yml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.2/prometheus/prometheus-pulsar.yml new file mode 100644 index 00000000..2413fb41 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.2/prometheus/prometheus-pulsar.yml @@ -0,0 +1,271 @@ +global: + + scrape_interval: 15s # By default, scrape targets every 15 seconds. + + # Attach these labels to any time series or alerts when communicating with + # external systems (federation, remote storage, Alertmanager). + external_labels: + monitor: 'trustgraph' + +# A scrape configuration containing exactly one endpoint to scrape: +# Here it's Prometheus itself. +scrape_configs: + + # The job name is added as a label `job=` to any timeseries + # scraped from this config. + + # TrustGraph services + + - job_name: 'agent-manager' + scrape_interval: 5s + static_configs: + - targets: + - 'agent-manager:8000' + + - job_name: 'api-gateway' + scrape_interval: 5s + static_configs: + - targets: + - 'api-gateway:8000' + + - job_name: 'chunker' + scrape_interval: 5s + static_configs: + - targets: + - 'chunker:8000' + + - job_name: 'config-svc' + scrape_interval: 5s + static_configs: + - targets: + - 'config-svc:8000' + + - job_name: 'document-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'document-embeddings:8000' + + - job_name: 'document-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'document-rag:8000' + + - job_name: 'embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'embeddings:8000' + + - job_name: 'graph-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'graph-embeddings:8000' + + - job_name: 'graph-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'graph-rag:8000' + + - job_name: 'kg-extract-agent' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-agent:8000' + + - job_name: 'kg-extract-definitions' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-definitions:8000' + + - job_name: 'kg-extract-objects' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-objects:8000' + + - job_name: 'kg-extract-relationships' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-relationships:8000' + + - job_name: 'kg-extract-ontology' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-ontology:8000' + + - job_name: 'kg-manager' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-manager:8000' + + - job_name: 'kg-store' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-store:8000' + + - job_name: 'librarian' + scrape_interval: 5s + static_configs: + - targets: + - 'librarian:8000' + + - job_name: 'mcp-server' + scrape_interval: 5s + static_configs: + - targets: + - 'mcp-server:8000' + + - job_name: 'mcp-tool' + scrape_interval: 5s + static_configs: + - targets: + - 'mcp-tool:8000' + + - job_name: 'metering' + scrape_interval: 5s + static_configs: + - targets: + - 'metering:8000' + + - job_name: 'metering-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'metering-rag:8000' + + - job_name: 'nlp-query' + scrape_interval: 5s + static_configs: + - targets: + - 'nlp-query:8000' + + - job_name: 'pdf-decoder' + scrape_interval: 5s + static_configs: + - targets: + - 'pdf-decoder:8000' + + - job_name: 'prompt' + scrape_interval: 5s + static_configs: + - targets: + - 'prompt:8000' + + - job_name: 'prompt-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'prompt-rag:8000' + + - job_name: 'query-doc-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'query-doc-embeddings:8000' + + - job_name: 'query-graph-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'query-graph-embeddings:8000' + + - job_name: 'query-row-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'query-row-embeddings:8000' + + - job_name: 'query-rows' + scrape_interval: 5s + static_configs: + - targets: + - 'query-rows:8000' + + - job_name: 'query-triples' + scrape_interval: 5s + static_configs: + - targets: + - 'query-triples:8000' + + - job_name: 'row-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'row-embeddings:8000' + + - job_name: 'store-doc-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'store-doc-embeddings:8000' + + - job_name: 'store-graph-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'store-graph-embeddings:8000' + + - job_name: 'store-row-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'store-row-embeddings:8000' + + - job_name: 'store-rows' + scrape_interval: 5s + static_configs: + - targets: + - 'store-rows:8000' + + - job_name: 'store-triples' + scrape_interval: 5s + static_configs: + - targets: + - 'store-triples:8000' + + - job_name: 'structured-query' + scrape_interval: 5s + static_configs: + - targets: + - 'structured-query:8000' + + - job_name: 'structured-diag' + scrape_interval: 5s + static_configs: + - targets: + - 'structured-diag:8000' + + - job_name: 'text-completion' + scrape_interval: 5s + static_configs: + - targets: + - 'text-completion:8000' + + - job_name: 'text-completion-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'text-completion-rag:8000' + + # Non-Trustgraph services + - job_name: 'garage' + metrics_path: '/metrics' + scrape_interval: 5s + static_configs: + - targets: + - 'garage:3903' + + - job_name: 'pulsar' + scrape_interval: 5s + static_configs: + - targets: + - 'pulsar:8080' diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.2/prometheus/prometheus-rabbitmq.yml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.2/prometheus/prometheus-rabbitmq.yml new file mode 100644 index 00000000..08895dd7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/2.2/prometheus/prometheus-rabbitmq.yml @@ -0,0 +1,272 @@ +global: + + scrape_interval: 15s # By default, scrape targets every 15 seconds. + + # Attach these labels to any time series or alerts when communicating with + # external systems (federation, remote storage, Alertmanager). + external_labels: + monitor: 'trustgraph' + +# A scrape configuration containing exactly one endpoint to scrape: +# Here it's Prometheus itself. +scrape_configs: + + # The job name is added as a label `job=` to any timeseries + # scraped from this config. + + # TrustGraph services + + - job_name: 'agent-manager' + scrape_interval: 5s + static_configs: + - targets: + - 'agent-manager:8000' + + - job_name: 'api-gateway' + scrape_interval: 5s + static_configs: + - targets: + - 'api-gateway:8000' + + - job_name: 'chunker' + scrape_interval: 5s + static_configs: + - targets: + - 'chunker:8000' + + - job_name: 'config-svc' + scrape_interval: 5s + static_configs: + - targets: + - 'config-svc:8000' + + - job_name: 'document-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'document-embeddings:8000' + + - job_name: 'document-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'document-rag:8000' + + - job_name: 'embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'embeddings:8000' + + - job_name: 'graph-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'graph-embeddings:8000' + + - job_name: 'graph-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'graph-rag:8000' + + - job_name: 'kg-extract-agent' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-agent:8000' + + - job_name: 'kg-extract-definitions' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-definitions:8000' + + - job_name: 'kg-extract-objects' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-objects:8000' + + - job_name: 'kg-extract-relationships' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-relationships:8000' + + - job_name: 'kg-extract-ontology' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-extract-ontology:8000' + + - job_name: 'kg-manager' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-manager:8000' + + - job_name: 'kg-store' + scrape_interval: 5s + static_configs: + - targets: + - 'kg-store:8000' + + - job_name: 'librarian' + scrape_interval: 5s + static_configs: + - targets: + - 'librarian:8000' + + - job_name: 'mcp-server' + scrape_interval: 5s + static_configs: + - targets: + - 'mcp-server:8000' + + - job_name: 'mcp-tool' + scrape_interval: 5s + static_configs: + - targets: + - 'mcp-tool:8000' + + - job_name: 'metering' + scrape_interval: 5s + static_configs: + - targets: + - 'metering:8000' + + - job_name: 'metering-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'metering-rag:8000' + + - job_name: 'nlp-query' + scrape_interval: 5s + static_configs: + - targets: + - 'nlp-query:8000' + + - job_name: 'pdf-decoder' + scrape_interval: 5s + static_configs: + - targets: + - 'pdf-decoder:8000' + + - job_name: 'prompt' + scrape_interval: 5s + static_configs: + - targets: + - 'prompt:8000' + + - job_name: 'prompt-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'prompt-rag:8000' + + - job_name: 'query-doc-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'query-doc-embeddings:8000' + + - job_name: 'query-graph-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'query-graph-embeddings:8000' + + - job_name: 'query-row-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'query-row-embeddings:8000' + + - job_name: 'query-rows' + scrape_interval: 5s + static_configs: + - targets: + - 'query-rows:8000' + + - job_name: 'query-triples' + scrape_interval: 5s + static_configs: + - targets: + - 'query-triples:8000' + + - job_name: 'row-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'row-embeddings:8000' + + - job_name: 'store-doc-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'store-doc-embeddings:8000' + + - job_name: 'store-graph-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'store-graph-embeddings:8000' + + - job_name: 'store-row-embeddings' + scrape_interval: 5s + static_configs: + - targets: + - 'store-row-embeddings:8000' + + - job_name: 'store-rows' + scrape_interval: 5s + static_configs: + - targets: + - 'store-rows:8000' + + - job_name: 'store-triples' + scrape_interval: 5s + static_configs: + - targets: + - 'store-triples:8000' + + - job_name: 'structured-query' + scrape_interval: 5s + static_configs: + - targets: + - 'structured-query:8000' + + - job_name: 'structured-diag' + scrape_interval: 5s + static_configs: + - targets: + - 'structured-diag:8000' + + - job_name: 'text-completion' + scrape_interval: 5s + static_configs: + - targets: + - 'text-completion:8000' + + - job_name: 'text-completion-rag' + scrape_interval: 5s + static_configs: + - targets: + - 'text-completion-rag:8000' + + # Non-Trustgraph services + - job_name: 'garage' + metrics_path: '/metrics' + scrape_interval: 5s + static_configs: + - targets: + - 'garage:3903' + + - job_name: 'rabbitmq' + metrics_path: '/metrics/per-object' + scrape_interval: 5s + static_configs: + - targets: + - 'rabbitmq:15692' diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/deploy/docker-compose-deploy.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/deploy/docker-compose-deploy.md new file mode 100644 index 00000000..5a474399 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/deploy/docker-compose-deploy.md @@ -0,0 +1,25 @@ +When you download the deploy configuration, you will have a ZIP file containing all the configuration needed to launch TrustGraph in Docker Compose. Unzip the ZIP file: + +```bash +unzip deploy.zip +``` + +On MacOS, it may be necessary to specify a destination directory for the TrustGraph package: + +```bash +unzip deploy.zip -d deploy +``` + +Navigate to the `docker-compose` directory. From this directory, launch TrustGraph with: + +```bash +docker compose -f docker-compose.yaml up -d +``` + +If you are on Linux, running SELinux, you may need to change permissions on files in the deploy bundle so that they are accessible from within containers. This affects the `grafana` and `prometheus` directories. + +```bash +chcon -Rt svirt_sandbox_file_t grafana prometheus +chmod 755 prometheus/ grafana/ grafana/*/ +chmod 644 prometheus/* grafana/*/* +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/deploy/k8s-deploy.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/deploy/k8s-deploy.md new file mode 100644 index 00000000..ea442c1f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/deploy/k8s-deploy.md @@ -0,0 +1,13 @@ +To deploy to Kubernetes, you likely need to have Kubernetes credentials set up to connect to the Kubernetes management service. The mechanism to do this varies with the different kinds of Kubernetes services in use, check with your cloud provider documentation. + +When you download the deploy configuration, you will have a ZIP file containing all the configuration needed to launch TrustGraph on Kubernetes. Unzip the ZIP file: + +```bash +unzip deploy.zip +``` + +and launch: + +```bash +kubectl apply -f resources.yaml +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/deploy/podman-compose-deploy.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/deploy/podman-compose-deploy.md new file mode 100644 index 00000000..213664b8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/deploy/podman-compose-deploy.md @@ -0,0 +1,19 @@ +When you download the deploy configuration, you will have a ZIP file containing all the configuration needed to launch TrustGraph in Podman Compose. Unzip the ZIP file: + +```bash +unzip deploy.zip +``` + +Navigate to the `docker-compose` directory. From this directory, launch TrustGraph with: + +```bash +podman compose -f docker-compose.yaml up -d +``` + +If you are on Linux, running SELinux, you may need to change permissions on files in the deploy bundle so that they are accessible from within containers. This affects the `grafana` and `prometheus` directories. + +```bash +chcon -Rt svirt_sandbox_file_t grafana prometheus +chmod 755 prometheus/ grafana/ grafana/*/ +chmod 644 prometheus/* grafana/*/* +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/features/document-rag.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/features/document-rag.md new file mode 100644 index 00000000..2d7ac07f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/features/document-rag.md @@ -0,0 +1,5 @@ +Document RAG APIs are separate from GraphRAG. You can use `tg-invoke-document-rag` to test Document RAG processing once documents are loaded: + +```bash +tg-invoke-document-rag -q "Describe a cat" +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/features/workbench.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/features/workbench.md new file mode 100644 index 00000000..75f1f8e6 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/features/workbench.md @@ -0,0 +1,5 @@ +Once the system is running, you can access the Workbench on port 8888, or access using the following URL: + +[http://localhost:8888/](http://localhost:8888/) + +Once you have data loaded, you can present a Graph RAG query on the Chat tab. As well as answering the question, a list of semantic relationships which were used to answer the question are shown and these can be used to navigate the knowledge graph. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/gateway/gateway-compose.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/gateway/gateway-compose.md new file mode 100644 index 00000000..c499a823 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/gateway/gateway-compose.md @@ -0,0 +1,5 @@ +The API Gateway is a required component which supports the CLI and Workbench. The API Gateway must be configured with a secret key using an environment variable. The secret can be set to an empty string if no authentication is required. + +``` +GATEWAY_SECRET= +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/gateway/gateway-k8s.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/gateway/gateway-k8s.md new file mode 100644 index 00000000..ee3f4186 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/gateway/gateway-k8s.md @@ -0,0 +1,7 @@ +The API Gateway is a required component which supports the CLI and Workbench. The API Gateway must be configured with a secret key. However, that secret key can be empty if no authentication is required. The Workbench does not currently use keys for authentication. The below example shows how to set the API Gateway secret to be empty with no authentication. + +```bash +kubectl -n {{namespace}} create secret \ + generic gateway-secret \ + --from-literal=gateway-secret= +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/gateway/mcp-compose.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/gateway/mcp-compose.md new file mode 100644 index 00000000..61121d52 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/gateway/mcp-compose.md @@ -0,0 +1,6 @@ +The MCP server requires two secrets provided as environment variables. The MCP server secret is used by clients to authenticate to the MCP service. The gateway secret must match the value configured for the API Gateway, as the MCP server acts as a client of the gateway. Both can be set to empty strings to disable authentication. + +``` +MCP_SERVER_SECRET= +GATEWAY_SECRET= +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/gateway/mcp-k8s.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/gateway/mcp-k8s.md new file mode 100644 index 00000000..7b0119fd --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/gateway/mcp-k8s.md @@ -0,0 +1,8 @@ +The MCP server requires two secrets provided in a Kubernetes secret. The MCP server secret is used by clients to authenticate to the MCP service. The gateway secret must match the value configured for the API Gateway, as the MCP server acts as a client of the gateway. Both can be set to empty strings to disable authentication. + +```bash +kubectl -n {{namespace}} create secret \ + generic mcp-server-secret \ + --from-literal=mcp-server-secret= \ + --from-literal=gateway-secret= +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/azure-compose.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/azure-compose.md new file mode 100644 index 00000000..a34ae9d7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/azure-compose.md @@ -0,0 +1,6 @@ +To use Azure Serverless APIs, you need to have a serverless endpoint deployed, and you must also provide an endpoint token as an environment variable. + +``` +AZURE_ENDPOINT=https://ENDPOINT.API.HOST.GOES.HERE/ +AZURE_TOKEN=TOKEN-GOES-HERE +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/azure-k8s.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/azure-k8s.md new file mode 100644 index 00000000..b213fa20 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/azure-k8s.md @@ -0,0 +1,8 @@ +To use Azure Serverless APIs, you need to have a serverless endpoint deployed. You must also provide an Azure endpoint and token in a Kubernetes secret before launching the application. + +```bash +kubectl -n {{namespace}} create secret \ + generic azure-credentials \ + --from-literal=azure-endpoint=AZURE-ENDPOINT \ + --from-literal=azure-token=AZURE-TOKEN +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/azure-openai-compose.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/azure-openai-compose.md new file mode 100644 index 00000000..9b1fe641 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/azure-openai-compose.md @@ -0,0 +1,8 @@ +To use Azure's OpenAI APIs, you need to have a serverless OpenAI endpoint deployed, and you must also provide an endpoint token as an environment variable. In addition, the OpenAI API requires an API Version and Model Name to be set. The Model Name is set by the user during the deployment within AzureAI. + +``` +AZURE_ENDPOINT=https://ENDPOINT.API.HOST.GOES.HERE/ +AZURE_TOKEN=TOKEN-GOES-HERE +AZURE_API_VERSION=API-VERSION-GOES-HERE +AZURE_MODEL=MODEL-NAME-GOES-HERE +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/azure-openai-k8s.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/azure-openai-k8s.md new file mode 100644 index 00000000..fce4c097 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/azure-openai-k8s.md @@ -0,0 +1,10 @@ +To use Azure's OpenAI APIs, you need to have a serverless OpenAI endpoint deployed. You must also provide an endpoint token, API version, and model name in a Kubernetes secret. The Model Name is set by the user during the deployment within AzureAI. + +```bash +kubectl -n {{namespace}} create secret \ + generic azure-openai-credentials \ + --from-literal=azure-endpoint=https://ENDPOINT.API.HOST.GOES.HERE/ \ + --from-literal=azure-token=TOKEN-GOES-HERE \ + --from-literal=azure-api-version=API-VERSION-GOES-HERE \ + --from-literal=azure-model=MODEL-NAME-GOES-HERE +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/bedrock-compose.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/bedrock-compose.md new file mode 100644 index 00000000..d102d115 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/bedrock-compose.md @@ -0,0 +1,7 @@ +To use AWS Bedrock, you must have enabled models in the AWS Bedrock console. You must also provide an AWS access key ID and secret key. + +``` +AWS_ACCESS_KEY_ID=ID-KEY-HERE +AWS_SECRET_ACCESS_KEY=TOKEN-GOES-HERE +AWS_DEFAULT_REGION=AWS-REGION-HERE +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/bedrock-k8s.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/bedrock-k8s.md new file mode 100644 index 00000000..13a994e0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/bedrock-k8s.md @@ -0,0 +1,9 @@ +To use AWS Bedrock, you must have enabled models in the AWS Bedrock console. You must also provide an AWS access key ID and secret key as a Kubernetes secret before deploying the application. + +```bash +kubectl -n {{namespace}} create secret \ + generic bedrock-credentials \ + --from-literal=aws-id-key=AWS-ID-KEY \ + --from-literal=aws-secret=AWS-SECRET-KEY \ + --from-literal=aws-region=AWS-REGION-HERE +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/claude-compose.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/claude-compose.md new file mode 100644 index 00000000..02327a36 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/claude-compose.md @@ -0,0 +1,5 @@ +To use Anthropic Claude, you need a Claude API key. Provide the Claude API key in an environment variable when running the Docker Compose configuration. + +``` +CLAUDE_KEY=CLAUDE-KEY-GOES-HERE +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/claude-k8s.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/claude-k8s.md new file mode 100644 index 00000000..b07aec0c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/claude-k8s.md @@ -0,0 +1,7 @@ +To use Anthropic Claude, you need a Claude API key which must be provided in a Kubernetes secret. + +```bash +kubectl -n {{namespace}} create secret \ + generic claude-credentials \ + --from-literal=claude-key=CLAUDE-KEY-HERE +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/cohere-compose.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/cohere-compose.md new file mode 100644 index 00000000..7a29337a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/cohere-compose.md @@ -0,0 +1,5 @@ +To use Cohere APIs, you need an API token which must be provided in an environment variable. + +``` +COHERE_KEY=TOKEN-GOES-HERE +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/cohere-k8s.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/cohere-k8s.md new file mode 100644 index 00000000..0be46578 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/cohere-k8s.md @@ -0,0 +1,7 @@ +To use Cohere APIs, you need an API token which must be provided in a Kubernetes secret. + +```bash +kubectl -n {{namespace}} create secret \ + generic cohere-credentials \ + --from-literal=cohere-key=COHERE-KEY +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/google-ai-studio-compose.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/google-ai-studio-compose.md new file mode 100644 index 00000000..ec4acb75 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/google-ai-studio-compose.md @@ -0,0 +1,5 @@ +To use Google AI Studio APIs, you need an API token which must be provided in an environment variable. + +``` +GOOGLE_AI_STUDIO_KEY=TOKEN-GOES-HERE +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/google-ai-studio-k8s.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/google-ai-studio-k8s.md new file mode 100644 index 00000000..5a3e77cf --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/google-ai-studio-k8s.md @@ -0,0 +1,7 @@ +To use Google AI Studio APIs, you need an API token which must be provided in a Kubernetes secret. + +```bash +kubectl -n {{namespace}} create secret \ + generic googleaistudio-credentials \ + --from-literal=google-ai-studio-key=GOOGLEAISTUDIO-KEY +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/llamafile-compose.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/llamafile-compose.md new file mode 100644 index 00000000..c0ceb3b0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/llamafile-compose.md @@ -0,0 +1,5 @@ +To use Llamafile, you must have a Llamafile service running on an accessible host. The Llamafile host must be provided in an environment variable. + +``` +LLAMAFILE_URL=LLAMAFILE-URL +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/llamafile-k8s.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/llamafile-k8s.md new file mode 100644 index 00000000..93e5d88b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/llamafile-k8s.md @@ -0,0 +1,7 @@ +To use Llamafile, you must have a Llamafile service running on an accessible host. The Llamafile host must be provided in a Kubernetes secret. + +```bash +kubectl -n {{namespace}} create secret \ + generic llamafile-credentials \ + --from-literal=llamafile-url=http://llamafile:1234/ +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/lm-studio-compose.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/lm-studio-compose.md new file mode 100644 index 00000000..291a46a5 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/lm-studio-compose.md @@ -0,0 +1,9 @@ +LMStudio allows you to run models locally, with a nice UX. The LMStudio application or service must be running, and have the REST API enabled, and model made available by pulling from the model repository. + +Note that LMStudio is a commercial product - a licence is needed for non-personal usage. See [lmstudio.ai/work](https://lmstudio.ai/work). + +``` +LMSTUDIO_URL=http://localhost:1234 +``` + +Replace the URL with the URL of your LMStudio API service. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/lm-studio-k8s.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/lm-studio-k8s.md new file mode 100644 index 00000000..6e27c233 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/lm-studio-k8s.md @@ -0,0 +1,9 @@ +LMStudio service URL must be provided in a Kubernetes secret. + +```bash +kubectl -n {{namespace}} \ + create secret generic lmstudio-credentials \ + --from-literal=lmstudio-url=http://lmstudio:11434/ +``` + +Replace the URL with the URL of your LMStudio service. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/mistral-compose.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/mistral-compose.md new file mode 100644 index 00000000..9dce9c86 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/mistral-compose.md @@ -0,0 +1,5 @@ +To use Mistral, you need a Mistral API key. Provide the Mistral API key in an environment variable when running the Docker Compose configuration. + +``` +MISTRAL_TOKEN=TOKEN-GOES-HERE +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/mistral-k8s.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/mistral-k8s.md new file mode 100644 index 00000000..c19e7324 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/mistral-k8s.md @@ -0,0 +1,7 @@ +To use Mistral, you need a Mistral API key which must be provided in a Kubernetes secret. + +```bash +kubectl -n {{namespace}} create secret \ + generic mistral-credentials \ + --from-literal=mistral-key=MISTRAL-TOKEN +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/ollama-compose.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/ollama-compose.md new file mode 100644 index 00000000..0804b848 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/ollama-compose.md @@ -0,0 +1,9 @@ +The power of Ollama is the flexibility it provides in Language Model deployments. Being able to run LMs with Ollama enables fully secure AI TrustGraph pipelines that aren't relying on any external APIs. No data is leaving the host environment or network. + +The Ollama service must be running, and have required models available using `ollama pull`. The Ollama service URL must be provided in an environment variable. + +``` +OLLAMA_HOST=http://ollama-host:11434 +``` + +Replace the URL with the URL of your Ollama service. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/ollama-k8s.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/ollama-k8s.md new file mode 100644 index 00000000..5ccabea4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/ollama-k8s.md @@ -0,0 +1,9 @@ +The Ollama service URL must be provided in a Kubernetes secret. + +```bash +kubectl -n {{namespace}} \ + create secret generic ollama-credentials \ + --from-literal=ollama-host=http://ollama:11434/ +``` + +Replace the URL with the URL of your Ollama service. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/openai-compose.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/openai-compose.md new file mode 100644 index 00000000..98136288 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/openai-compose.md @@ -0,0 +1,5 @@ +To use OpenAI APIs, you need an API token which must be provided in an environment variable. + +``` +OPENAI_TOKEN=TOKEN-GOES-HERE +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/openai-k8s.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/openai-k8s.md new file mode 100644 index 00000000..040d98a6 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/openai-k8s.md @@ -0,0 +1,7 @@ +To use OpenAI APIs, you need an API token which must be provided in a Kubernetes secret. + +```bash +kubectl -n {{namespace}} create secret \ + generic openai-credentials \ + --from-literal=openai-token=OPENAI-TOKEN-HERE +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/tgi-compose.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/tgi-compose.md new file mode 100644 index 00000000..3c9807fe --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/tgi-compose.md @@ -0,0 +1,9 @@ +Text Generation Inference (TGI) is Hugging Face's production-ready inference server for LLMs. It provides high-performance text generation with features like continuous batching, tensor parallelism, and optimized attention mechanisms. + +The TGI service must be running with the required model loaded. The TGI service URL must be provided in an environment variable. + +``` +TGI_BASE_URL=http://tgi-host:8080/v1 +``` + +Replace the URL with the URL of your TGI service, noting the `v1` suffix for OpenAI-compatible API. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/tgi-k8s.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/tgi-k8s.md new file mode 100644 index 00000000..ac78434a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/tgi-k8s.md @@ -0,0 +1,9 @@ +The TGI service URL must be provided in a Kubernetes secret. + +```bash +kubectl -n {{namespace}} \ + create secret generic tgi-credentials \ + --from-literal=tgi-url=http://tgi:8080/v1 +``` + +Replace the URL with the URL of your TGI service. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/vertex-ai-compose.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/vertex-ai-compose.md new file mode 100644 index 00000000..03fcc98d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/vertex-ai-compose.md @@ -0,0 +1,7 @@ +To use VertexAI, you need to have a Google Cloud credential file provisioned for a service account which has access to the VertexAI services. This means signing up to GCP and using an existing, or launching a new GCP project. The GCP credential will be a JSON file which should be stored in `vertexai/private.json`. + +The credential file is mounted as a volume in Docker Compose, which can cause issues with SELinux if you are running on Linux. Make sure that Docker has access to volume files if this affects you. + +```bash +chcon -Rt svirt_sandbox_file_t vertexai/ +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/vertex-ai-k8s.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/vertex-ai-k8s.md new file mode 100644 index 00000000..5d067e36 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/vertex-ai-k8s.md @@ -0,0 +1,10 @@ +To use VertexAI, you need to have a Google Cloud credential file provisioned for a service account which has access to the VertexAI services. This means signing up to GCP and using an existing, or launching a new GCP project. The GCP credential will be a JSON file which would arrive in a file called `private.json`. + +The private.json file should be loaded into Kubernetes as a secret. + +```bash +kubectl -n {{namespace}} create secret \ + generic vertexai-creds --from-file=private.json=private.json +``` + +> **Warning:** Google Cloud private.json files are secrets which potentially provide access to all of your Google Cloud resources. Take great care to ensure that the permissions of the account are minimal, ideally scoped to just AI services. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/vllm-compose.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/vllm-compose.md new file mode 100644 index 00000000..cd41cca1 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/vllm-compose.md @@ -0,0 +1,9 @@ +vLLM is a high-throughput, memory-efficient inference and serving engine for LLMs. Using PagedAttention and continuous batching, vLLM enables fully secure AI TrustGraph pipelines that aren't relying on any external APIs. No data is leaving the host environment or network. + +The vLLM service must be running with the required model loaded using `vllm serve`. The vLLM service URL must be provided in an environment variable. + +``` +VLLM_BASE_URL=http://vllm-host:8000/v1 +``` + +Replace the URL with the URL of your vLLM service, noting the `v1` suffix. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/vllm-k8s.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/vllm-k8s.md new file mode 100644 index 00000000..2168b378 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/model/vllm-k8s.md @@ -0,0 +1,9 @@ +The vLLM service URL must be provided in a Kubernetes secret. + +```bash +kubectl -n {{namespace}} \ + create secret generic vllm-credentials \ + --from-literal=vllm-url=http://vllm:8000/v1 +``` + +Replace the URL with the URL of your vLLM service. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/platform/aks.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/platform/aks.md new file mode 100644 index 00000000..4ab0e703 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/platform/aks.md @@ -0,0 +1 @@ +You need to have an Azure account, and a running AKS cluster. You also need to be authenticated with the cluster and be able to see the cluster state. See [Azure Kubernetes Service (AKS)](https://azure.microsoft.com/en-us/products/kubernetes-service). diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/platform/docker-compose.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/platform/docker-compose.md new file mode 100644 index 00000000..c7b5afde --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/platform/docker-compose.md @@ -0,0 +1 @@ +You need to have Docker Compose installed. See [Installing Docker Compose](https://docs.docker.com/compose/install/). diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/platform/eks.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/platform/eks.md new file mode 100644 index 00000000..39c55649 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/platform/eks.md @@ -0,0 +1 @@ +You need to have an AWS account, and a running EKS cluster. You also need to be authenticated with the cluster and be able to see the cluster state. See [Amazon Elastic Kubernetes Service](https://aws.amazon.com/eks/). diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/platform/gke.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/platform/gke.md new file mode 100644 index 00000000..12b2e516 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/platform/gke.md @@ -0,0 +1 @@ +You need to have a Google Cloud account, and a running GKE cluster. You also need to be authenticated with the cluster and be able to see the cluster state. See [Google Kubernetes Engine (GKE)](https://cloud.google.com/kubernetes-engine). diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/platform/minikube.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/platform/minikube.md new file mode 100644 index 00000000..a036aea8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/platform/minikube.md @@ -0,0 +1 @@ +You need to have the Minikube cluster installed and running. See [Minikube - Get Started!](https://minikube.sigs.k8s.io/docs/start). There is TrustGraph documentation on Minikube [here](https://trustgraph.ai/docs/running/minikube). diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/platform/ovh.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/platform/ovh.md new file mode 100644 index 00000000..8bd0e1eb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/platform/ovh.md @@ -0,0 +1 @@ +You need to have an OVHcloud account, and a running Managed Kubernetes cluster. You also need to be authenticated with the cluster and be able to see the cluster state. See [OVHcloud Managed Kubernetes](https://www.ovhcloud.com/en/public-cloud/kubernetes/). diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/platform/podman-compose.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/platform/podman-compose.md new file mode 100644 index 00000000..903b73ba --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/platform/podman-compose.md @@ -0,0 +1 @@ +You need to have the Podman environment and Podman Compose installed. This should be available with your Linux distribution. See [Beginner's Guide to Using Podman Compose](https://linuxhandbook.com/podman-compose/). diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/platform/scw.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/platform/scw.md new file mode 100644 index 00000000..38328a3e --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/platform/scw.md @@ -0,0 +1 @@ +You need to have a Scaleway account, and a running Kubernetes Kapsule cluster. You also need to be authenticated with the cluster and be able to see the cluster state. See [Scaleway Kubernetes Kapsule](https://www.scaleway.com/en/kubernetes-kapsule/). diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/storage/falkordb.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/storage/falkordb.md new file mode 100644 index 00000000..fe773d4f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/storage/falkordb.md @@ -0,0 +1,3 @@ +FalkorDB is licensed under the [Server Side Public License (SSPLv1)](https://github.com/FalkorDB/FalkorDB/blob/master/LICENSE.txt). + +> "The Server Side Public License (SSPLv1) is designed to ensure that if you use FalkorDB as part of a service you make available to others (e.g., in the cloud or as an API), you are required to make the source code of your complete service available under the SSPLv1 license. This is similar to GPL but extends to server use." diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/storage/pinecone-compose.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/storage/pinecone-compose.md new file mode 100644 index 00000000..41be8b0b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/storage/pinecone-compose.md @@ -0,0 +1,5 @@ +To use Pinecone, you need an API token which must be provided in an environment variable. The API token can be created in the Pinecone console of your account. + +``` +PINECONE_API_KEY=TOKEN-GOES-HERE +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/storage/pinecone-k8s.md b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/storage/pinecone-k8s.md new file mode 100644 index 00000000..3a99ddd5 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/docs/storage/pinecone-k8s.md @@ -0,0 +1,7 @@ +To use Pinecone, you need an API token which must be provided in a Kubernetes secret. + +```bash +kubectl -n {{namespace}} create secret \ + generic pinecone-api-key \ + --from-literal=pinecone-api-key=PINECONE-API-KEY +``` diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/trustgraph-docs.yaml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/trustgraph-docs.yaml new file mode 100644 index 00000000..2a419ba2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/trustgraph-docs.yaml @@ -0,0 +1,431 @@ +# TrustGraph Documentation Manifest +# +# Maps configuration state to documentation fragments. +# Assembler evaluates 'when' conditions (JSONata) against wizard state, +# selects matching instructions, groups by category, orders by priority. +# +# Output formats: +# - Web/checklist: Structured JSON with completion tracking +# - CLI: Concatenated README.md + +documentation: + id: "trustgraph-deployment-guide" + title: "TrustGraph Deployment Guide" + version: "1.0" + + # Categories define grouping and display order + categories: + - id: platform + title: "Platform Setup" + priority: 1 + - id: model + title: "Model Configuration" + priority: 2 + - id: storage + title: "Storage Setup" + priority: 3 + - id: gateway + title: "API Gateway" + priority: 4 + - id: deployment + title: "Deployment" + priority: 5 + - id: verification + title: "Verification & Testing" + priority: 6 + + instructions: + + # ───────────────────────────────────────────────────────────── + # Platform Setup + # ───────────────────────────────────────────────────────────── + - id: platform-setup + goal: "Install and configure Docker Compose" + category: platform + priority: 1 + file: platform/docker-compose.md + when: "platform = 'docker-compose'" + + - id: platform-setup + goal: "Install and configure Podman Compose" + category: platform + priority: 1 + file: platform/podman-compose.md + when: "platform = 'podman-compose'" + + - id: platform-setup + goal: "Install and configure Minikube" + category: platform + priority: 1 + file: platform/minikube.md + when: "platform = 'minikube'" + + - id: platform-setup + goal: "Configure Google Kubernetes Engine" + category: platform + priority: 1 + file: platform/gke.md + when: "platform = 'gke'" + + - id: platform-setup + goal: "Configure AWS EKS" + category: platform + priority: 1 + file: platform/eks.md + when: "platform = 'eks'" + + - id: platform-setup + goal: "Configure Azure AKS" + category: platform + priority: 1 + file: platform/aks.md + when: "platform = 'aks'" + + - id: platform-setup + goal: "Configure Scaleway Kubernetes" + category: platform + priority: 1 + file: platform/scw.md + when: "platform = 'scw'" + + - id: platform-setup + goal: "Configure OVHcloud Kubernetes" + category: platform + priority: 1 + file: platform/ovh.md + when: "platform = 'ovh'" + + # ───────────────────────────────────────────────────────────── + # Model Configuration - Compose variants + # ───────────────────────────────────────────────────────────── + - id: model-setup + goal: "Set up Ollama model server" + category: model + priority: 1 + file: model/ollama-compose.md + when: "model_deployment = 'ollama' and platform in ['docker-compose', 'podman-compose']" + + - id: model-setup + goal: "Configure OpenAI integration" + category: model + priority: 1 + file: model/openai-compose.md + when: "model_deployment = 'openai' and platform in ['docker-compose', 'podman-compose']" + + - id: model-setup + goal: "Configure Claude (Anthropic) integration" + category: model + priority: 1 + file: model/claude-compose.md + when: "model_deployment = 'claude' and platform in ['docker-compose', 'podman-compose']" + + - id: model-setup + goal: "Configure Mistral integration" + category: model + priority: 1 + file: model/mistral-compose.md + when: "model_deployment = 'mistral' and platform in ['docker-compose', 'podman-compose']" + + - id: model-setup + goal: "Configure Cohere integration" + category: model + priority: 1 + file: model/cohere-compose.md + when: "model_deployment = 'cohere' and platform in ['docker-compose', 'podman-compose']" + + - id: model-setup + goal: "Configure Azure AI integration" + category: model + priority: 1 + file: model/azure-compose.md + when: "model_deployment = 'azure' and platform in ['docker-compose', 'podman-compose']" + + - id: model-setup + goal: "Configure Azure OpenAI integration" + category: model + priority: 1 + file: model/azure-openai-compose.md + when: "model_deployment = 'azure-openai' and platform in ['docker-compose', 'podman-compose']" + + - id: model-setup + goal: "Configure Amazon Bedrock integration" + category: model + priority: 1 + file: model/bedrock-compose.md + when: "model_deployment = 'bedrock' and platform in ['docker-compose', 'podman-compose']" + + - id: model-setup + goal: "Configure Google AI Studio integration" + category: model + priority: 1 + file: model/google-ai-studio-compose.md + when: "model_deployment = 'google-ai-studio' and platform in ['docker-compose', 'podman-compose']" + + - id: model-setup + goal: "Configure Vertex AI integration" + category: model + priority: 1 + file: model/vertex-ai-compose.md + when: "model_deployment = 'vertex-ai' and platform in ['docker-compose', 'podman-compose']" + + - id: model-setup + goal: "Set up Llamafile" + category: model + priority: 1 + file: model/llamafile-compose.md + when: "model_deployment = 'llamafile' and platform in ['docker-compose', 'podman-compose']" + + - id: model-setup + goal: "Set up LM Studio" + category: model + priority: 1 + file: model/lm-studio-compose.md + when: "model_deployment = 'lm-studio' and platform in ['docker-compose', 'podman-compose']" + + - id: model-setup + goal: "Set up vLLM" + category: model + priority: 1 + file: model/vllm-compose.md + when: "model_deployment = 'vllm' and platform in ['docker-compose', 'podman-compose']" + + - id: model-setup + goal: "Set up Text Generation Inference" + category: model + priority: 1 + file: model/tgi-compose.md + when: "model_deployment = 'tgi' and platform in ['docker-compose', 'podman-compose']" + + # ───────────────────────────────────────────────────────────── + # Model Configuration - Kubernetes variants + # ───────────────────────────────────────────────────────────── + - id: model-setup + goal: "Set up Ollama model server" + category: model + priority: 1 + file: model/ollama-k8s.md + when: "model_deployment = 'ollama' and platform in ['gke', 'eks', 'aks', 'minikube', 'scw', 'ovh']" + vars: + namespace: "k8s.namespace" + + - id: model-setup + goal: "Configure OpenAI integration" + category: model + priority: 1 + file: model/openai-k8s.md + when: "model_deployment = 'openai' and platform in ['gke', 'eks', 'aks', 'minikube', 'scw', 'ovh']" + vars: + namespace: "k8s.namespace" + + - id: model-setup + goal: "Configure Claude (Anthropic) integration" + category: model + priority: 1 + file: model/claude-k8s.md + when: "model_deployment = 'claude' and platform in ['gke', 'eks', 'aks', 'minikube', 'scw', 'ovh']" + vars: + namespace: "k8s.namespace" + + - id: model-setup + goal: "Configure Mistral integration" + category: model + priority: 1 + file: model/mistral-k8s.md + when: "model_deployment = 'mistral' and platform in ['gke', 'eks', 'aks', 'minikube', 'scw', 'ovh']" + vars: + namespace: "k8s.namespace" + + - id: model-setup + goal: "Configure Cohere integration" + category: model + priority: 1 + file: model/cohere-k8s.md + when: "model_deployment = 'cohere' and platform in ['gke', 'eks', 'aks', 'minikube', 'scw', 'ovh']" + vars: + namespace: "k8s.namespace" + + - id: model-setup + goal: "Configure Azure AI integration" + category: model + priority: 1 + file: model/azure-k8s.md + when: "model_deployment = 'azure' and platform in ['gke', 'eks', 'aks', 'minikube', 'scw', 'ovh']" + vars: + namespace: "k8s.namespace" + + - id: model-setup + goal: "Configure Azure OpenAI integration" + category: model + priority: 1 + file: model/azure-openai-k8s.md + when: "model_deployment = 'azure-openai' and platform in ['gke', 'eks', 'aks', 'minikube', 'scw', 'ovh']" + vars: + namespace: "k8s.namespace" + + - id: model-setup + goal: "Configure Amazon Bedrock integration" + category: model + priority: 1 + file: model/bedrock-k8s.md + when: "model_deployment = 'bedrock' and platform in ['gke', 'eks', 'aks', 'minikube', 'scw', 'ovh']" + vars: + namespace: "k8s.namespace" + + - id: model-setup + goal: "Configure Google AI Studio integration" + category: model + priority: 1 + file: model/google-ai-studio-k8s.md + when: "model_deployment = 'google-ai-studio' and platform in ['gke', 'eks', 'aks', 'minikube', 'scw', 'ovh']" + vars: + namespace: "k8s.namespace" + + - id: model-setup + goal: "Configure Vertex AI integration" + category: model + priority: 1 + file: model/vertex-ai-k8s.md + when: "model_deployment = 'vertex-ai' and platform in ['gke', 'eks', 'aks', 'minikube', 'scw', 'ovh']" + vars: + namespace: "k8s.namespace" + + - id: model-setup + goal: "Set up Llamafile" + category: model + priority: 1 + file: model/llamafile-k8s.md + when: "model_deployment = 'llamafile' and platform in ['gke', 'eks', 'aks', 'minikube', 'scw', 'ovh']" + vars: + namespace: "k8s.namespace" + + - id: model-setup + goal: "Set up LM Studio" + category: model + priority: 1 + file: model/lm-studio-k8s.md + when: "model_deployment = 'lm-studio' and platform in ['gke', 'eks', 'aks', 'minikube', 'scw', 'ovh']" + vars: + namespace: "k8s.namespace" + + - id: model-setup + goal: "Set up vLLM" + category: model + priority: 1 + file: model/vllm-k8s.md + when: "model_deployment = 'vllm' and platform in ['gke', 'eks', 'aks', 'minikube', 'scw', 'ovh']" + vars: + namespace: "k8s.namespace" + + - id: model-setup + goal: "Set up Text Generation Inference" + category: model + priority: 1 + file: model/tgi-k8s.md + when: "model_deployment = 'tgi' and platform in ['gke', 'eks', 'aks', 'minikube', 'scw', 'ovh']" + vars: + namespace: "k8s.namespace" + + # ───────────────────────────────────────────────────────────── + # Storage Setup + # ───────────────────────────────────────────────────────────── + - id: vector-db-setup + goal: "Configure Pinecone connection" + category: storage + priority: 1 + file: storage/pinecone-compose.md + when: "vector_db = 'pinecone' and platform in ['docker-compose', 'podman-compose']" + + - id: vector-db-setup + goal: "Configure Pinecone connection" + category: storage + priority: 1 + file: storage/pinecone-k8s.md + when: "vector_db = 'pinecone' and platform in ['gke', 'eks', 'aks', 'minikube', 'scw', 'ovh']" + vars: + namespace: "k8s.namespace" + + - id: graph-store-setup + goal: "Understand FalkorDB license" + category: storage + priority: 2 + file: storage/falkordb.md + when: "graph_store = 'falkordb'" + + # ───────────────────────────────────────────────────────────── + # API Gateway + # ───────────────────────────────────────────────────────────── + - id: gateway-setup + goal: "Configure API gateway secret" + category: gateway + priority: 1 + file: gateway/gateway-compose.md + when: "platform in ['docker-compose', 'podman-compose']" + + - id: gateway-setup + goal: "Configure API gateway secret" + category: gateway + priority: 1 + file: gateway/gateway-k8s.md + when: "platform in ['gke', 'eks', 'aks', 'minikube', 'scw', 'ovh']" + vars: + namespace: "k8s.namespace" + + # ───────────────────────────────────────────────────────────── + # MCP Server + # ───────────────────────────────────────────────────────────── + - id: mcp-setup + goal: "Configure MCP server secrets" + category: gateway + priority: 2 + file: gateway/mcp-compose.md + when: "platform in ['docker-compose', 'podman-compose']" + + - id: mcp-setup + goal: "Configure MCP server secrets" + category: gateway + priority: 2 + file: gateway/mcp-k8s.md + when: "platform in ['gke', 'eks', 'aks', 'minikube', 'scw', 'ovh']" + vars: + namespace: "k8s.namespace" + + # ───────────────────────────────────────────────────────────── + # Deployment + # ───────────────────────────────────────────────────────────── + - id: deploy + goal: "Deploy with Docker Compose" + category: deployment + priority: 1 + file: deploy/docker-compose-deploy.md + when: "platform = 'docker-compose'" + + - id: deploy + goal: "Deploy with Podman Compose" + category: deployment + priority: 1 + file: deploy/podman-compose-deploy.md + when: "platform = 'podman-compose'" + + - id: deploy + goal: "Deploy to Kubernetes cluster" + category: deployment + priority: 1 + file: deploy/k8s-deploy.md + when: "platform in ['gke', 'eks', 'aks', 'minikube', 'scw', 'ovh']" + + # ───────────────────────────────────────────────────────────── + # Verification & Testing + # ───────────────────────────────────────────────────────────── + - id: workbench + goal: "Access the TrustGraph Workbench" + category: verification + priority: 1 + file: features/workbench.md + always: true + + - id: document-rag + goal: "Test Document RAG" + category: verification + priority: 2 + file: features/document-rag.md + always: true diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/trustgraph-flow.yaml b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/trustgraph-flow.yaml new file mode 100644 index 00000000..9d12c6d5 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/trustgraph-flow.yaml @@ -0,0 +1,343 @@ +# TrustGraph Configuration Builder - Dialog Flow (State Machine) +# Each step declares its own transitions to the next state(s) + +flow: + id: "trustgraph-config" + title: "TrustGraph Configuration Builder" + version: "1.0" + start: version + +steps: + + # ───────────────────────────────────────────────────────────── + # Version Selection + # ───────────────────────────────────────────────────────────── + version: + title: "Which TrustGraph version?" + state_key: version + input: + type: select + options: + - label: "TrustGraph 2.2" + value: "2.2" + description: "Additional document format support" + badge: pre-release + recommended: false + - label: "TrustGraph 2.1" + value: "2.1" + description: "Explainability and provenance support" + badge: stable + recommended: true + - label: "TrustGraph 1.9" + value: "1.9" + description: "Improvements to ontology and knowledge extraction prompts" + badge: pre-release + recommended: false + - label: "TrustGraph 1.8" + value: "1.8" + description: "Pluggable messaging fabric, switched to Garage for object store" + badge: stable + recommended: false + transitions: + - next: platform + + # ───────────────────────────────────────────────────────────── + # Platform Selection + # ───────────────────────────────────────────────────────────── + platform: + title: "Which platform?" + description: "Choose how you'll run the TrustGraph containers" + state_key: platform + input: + type: select + options: + - label: "Docker Compose" + value: "docker-compose" + icon: "docker" + description: "Easy to install on MacOS and Linux. Great for evaluation and learning. May not be well suited to production deployments." + recommended: true + - label: "Podman Compose" + value: "podman-compose" + icon: "podman" + description: "Feature-compatible with Docker Compose, default for most Linux systems. May not be well suited to production deployments." + - label: "Minikube" + value: "minikube" + icon: "minikube" + description: "Stand-alone Kubernetes for small-to-medium deployments. Good for learning Kubernetes. Single node cluster limits production use." + - label: "Google Kubernetes Engine (GKE)" + value: "gke" + icon: "google-cloud" + description: "Managed Kubernetes on Google Cloud. Production-ready with full observability and scalability." + - label: "AWS EKS" + value: "eks" + icon: "aws" + description: "Elastic Kubernetes Service on AWS infrastructure. Suitable for production deployment." + - label: "Azure AKS" + value: "aks" + icon: "azure" + description: "Azure Kubernetes Service on Microsoft infrastructure. Suitable for production deployment." + - label: "Scaleway Kubernetes" + value: "scw" + icon: "scaleway" + description: "Managed Kubernetes on Scaleway European cloud infrastructure. Suitable for production deployment." + - label: "OVHCloud Kubernetes" + value: "ovh" + icon: "ovhcloud" + description: "Managed Kubernetes on OVHCloud European infrastructure. Suitable for production deployment." + transitions: + - next: graph-store + + # ───────────────────────────────────────────────────────────── + # Graph Store + # ───────────────────────────────────────────────────────────── + graph-store: + title: "Which graph store?" + description: "Where TrustGraph stores the knowledge graph" + state_key: graph_store + input: + type: select + options: + - label: "Apache Cassandra" + value: "cassandra" + icon: "cassandra" + description: "NoSQL database with fast read/write at scale. TrustGraph integrates a graph schema." + recommended: true + # - label: "Neo4j" + # value: "neo4j" + # icon: "neo4j" + # description: "Native graph database with Cypher query language" + # - label: "Memgraph" + # value: "memgraph" + # icon: "memgraph" + # description: "In-memory graph database with Cypher query language. High performance for real-time analytics." + # - label: "FalkorDB" + # value: "falkordb" + # icon: "falkordb" + # description: "High-performance graph database optimized for low-latency queries. Based on GraphBLAS." + transitions: + - next: vector-db + + # ───────────────────────────────────────────────────────────── + # Vector Database + # ───────────────────────────────────────────────────────────── + vector-db: + title: "Which vector database?" + description: "Stores embeddings for semantic search and RAG" + state_key: vector_db + input: + type: select + options: + - label: "Qdrant" + value: "qdrant" + icon: "qdrant" + description: "High RPS, minimal latency, fast indexing with accuracy control" + recommended: true + # - label: "Milvus" + # value: "milvus" + # icon: "milvus" + # description: "Open-source vector database built for scale" + # - label: "Pinecone" + # value: "pinecone" + # icon: "pinecone" + # description: "Managed vector database service (requires API key)" + transitions: + - next: row-store + + # ───────────────────────────────────────────────────────────── + # Row Store + # ───────────────────────────────────────────────────────────── + row-store: + title: "Which row data store?" + description: "Storage for row/tabular data" + state_key: row_store + input: + type: select + options: + - label: "Apache Cassandra" + value: "cassandra" + icon: "cassandra" + description: "NoSQL database with fast read/write performance at scale. Used for row storage in TrustGraph." + transitions: + - next: model-deployment + + # ───────────────────────────────────────────────────────────── + # Model Deployment + # ───────────────────────────────────────────────────────────── + model-deployment: + title: "How will you run the LLM?" + description: "Choose where the language model runs" + state_key: model_deployment + input: + type: select + options: + # Local / Self-hosted + - label: "Ollama" + value: "ollama" + icon: "ollama" + description: "Run LLMs locally. Bundles model weights, configs, and data into a single package. No external API dependencies." + - label: "Llamafile" + value: "llamafile" + icon: "llamafile" + description: "Single-file LLM distribution with embedded inference server. Runs on most computers with no dependencies." + - label: "LM Studio" + value: "lm-studio" + icon: "lm-studio" + description: "Easy local LLM running. Very usable, great support. Commercial, free for personal use." + # Cloud API Providers + - label: "OpenAI" + value: "openai" + icon: "openai" + description: "Industry-leading models (GPT-4, etc). Requires API key." + - label: "Claude (Anthropic)" + value: "claude" + icon: "anthropic" + description: "Helpful, honest, and harmless AI models. Designed to be trustworthy and reliable. Requires API key." + - label: "Mistral" + value: "mistral" + icon: "mistral" + description: "Efficient models excelling in multilingual tasks, code generation, and reasoning. Strong performance relative to size. Requires API key." + - label: "Cohere" + value: "cohere" + icon: "cohere" + description: "Cloud-agnostic AI with high security, privacy, and customization. Supports on-premises and private cloud. Requires API key." + # Cloud Platform Services + - label: "Azure AI" + value: "azure" + icon: "azure" + description: "Azure Endpoint Services for building, deploying, and managing AI applications. Requires Azure subscription." + - label: "Azure OpenAI" + value: "azure-openai" + icon: "azure" + description: "OpenAI models hosted on Azure infrastructure. Requires Azure subscription." + - label: "Amazon Bedrock" + value: "bedrock" + icon: "aws" + description: "Fully managed service for generative AI on AWS. Multiple model providers available. Requires AWS account." + - label: "Google AI Studio" + value: "google-ai-studio" + icon: "google" + description: "Integrated environment to prototype and experiment with Google's generative AI models. Requires Google account." + - label: "Vertex AI" + value: "vertex-ai" + icon: "google-cloud" + description: "Google Cloud platform for training and deploying ML models. Requires GCP account." + # Self-hosted inference servers + - label: "vLLM" + value: "vllm" + icon: "vllm" + description: "High-throughput LLM serving engine. Self-hosted with OpenAI-compatible API." + - label: "Text Generation Inference (TGI)" + value: "tgi" + icon: "huggingface" + description: "Hugging Face's production-ready inference server. Self-hosted with high performance." + transitions: + - next: max-output-tokens + + # ───────────────────────────────────────────────────────────── + # Max Output Tokens + # ───────────────────────────────────────────────────────────── + max-output-tokens: + title: "Maximum output tokens?" + description: "Limits the length of LLM responses" + state_key: max_output_tokens + input: + type: number + default: 4096 + min: 256 + max: 65536 + step: 256 + transitions: + - next: ocr-enabled + + # ───────────────────────────────────────────────────────────── + # OCR Pipelines Toggle + # ───────────────────────────────────────────────────────────── + ocr-enabled: + title: "Enable OCR processing?" + description: "Replace standard PDF decoding with Optical Character Recognition for scanned documents and images" + state_key: ocr.enabled + input: + type: toggle + default: false + transitions: + - when: "ocr.enabled = true" + next: ocr-engine + - next: embeddings-enabled + + # ───────────────────────────────────────────────────────────── + # OCR Engine (conditional) + # ───────────────────────────────────────────────────────────── + ocr-engine: + title: "Which OCR engine?" + state_key: ocr.engine + input: + type: select + options: + - label: "PDF Decode" + value: "pdf-decode" + description: "Default configuration. Extracts text from PDFs with structured text, but does not perform OCR on images or scanned documents." + recommended: true + - label: "Tesseract" + value: "tesseract" + description: "Free, open-source embedded OCR engine. Best-in-class for free/open-source. Use with PDF documents containing image scans." + - label: "Mistral" + value: "mistral" + description: "Best-in-class commercial OCR service. Requires a Mistral subscription. Use with PDF documents containing image scans." + transitions: + - next: embeddings-enabled + + # ───────────────────────────────────────────────────────────── + # Embeddings Configuration Toggle + # ───────────────────────────────────────────────────────────── + embeddings-enabled: + title: "Configure embeddings engine?" + description: "Customize the embeddings model used for semantic search" + state_key: embeddings.enabled + input: + type: toggle + default: false + transitions: + # Version gate: embeddings config not available before 1.6.0 + - when: "version < '1.6.0'" + next: review + - when: "embeddings.enabled = true" + next: embeddings-engine + - next: review + + # ───────────────────────────────────────────────────────────── + # Embeddings Engine (conditional) + # ───────────────────────────────────────────────────────────── + embeddings-engine: + title: "Which embeddings engine?" + state_key: embeddings.engine + input: + type: select + options: + - label: "FastEmbed" + value: "fastembed" + icon: "fastembed" + description: "Lightweight, fast Python library for embedding generation. Small container image and quick start time." + recommended: true + - label: "HuggingFace sentence-transformers" + value: "huggingface-sentence-transformers" + icon: "huggingface" + description: "Support for a large number of open/community models. Use for non-standard models. Larger container image and longer start time due to PyTorch dependencies." + - label: "Ollama" + value: "ollama" + icon: "ollama" + description: "Use Ollama for embeddings. Requires Ollama to be running with an embedding model loaded." + transitions: + - next: review + + # ───────────────────────────────────────────────────────────── + # Review & Generate (terminal) + # ───────────────────────────────────────────────────────────── + review: + title: "Review your configuration" + type: review + actions: + - id: generate + label: "Generate" + icon: "rocket" + description: "Generate the configuration package as a ZIP file" + transitions: [] # terminal state diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/trustgraph-output.jsonata b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/trustgraph-output.jsonata new file mode 100644 index 00000000..402826aa --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/resources/dialog/trustgraph-output.jsonata @@ -0,0 +1,175 @@ +( + /* + * TrustGraph Configuration Output Transform + * + * Input: State object collected from wizard + * Output: Configuration object with version, platform, and templates array + * + * Example input: + * { + * "version": "1.8.18", + * "platform": "docker-compose", + * "graph_store": "cassandra", + * "vector_db": "qdrant", + * "row_store": "cassandra", + * "model_deployment": "ollama", + * "max_output_tokens": 2048, + * "ocr": { "enabled": true, "engine": "tesseract" }, + * "embeddings": { "enabled": true, "engine": "fastembed" }, + * "k8s": { "namespace": "trustgraph" } + * } + */ + + /* ───────────────────────────────────────────────────────────── + * Helper: Build a component object + * ───────────────────────────────────────────────────────────── */ + $component := function($name, $params) { + { + "name": $name, + "parameters": $params ? $params : {} + } + }; + + /* ───────────────────────────────────────────────────────────── + * Core Infrastructure Components + * ───────────────────────────────────────────────────────────── */ + $infrastructure := [ + /* Graph store */ + $component("triple-store-" & graph_store), + + /* Row store */ + $component("row-store-" & row_store), + + /* Vector database */ + $component("vector-store-" & vector_db), + + /* Messaging - always included */ + $component("pulsar"), + + /* Monitoring - always included */ + $component("grafana") + ]; + + + /* ───────────────────────────────────────────────────────────── + * Model Deployment Components + * ───────────────────────────────────────────────────────────── */ + $model_components := ( + /* Map deployment choice to component name */ + $deployment_map := { + "ollama": "ollama", + "llamafile": "llamafile", + "lm-studio": "lmstudio", + "openai": "openai", + "claude": "claude", + "mistral": "mistral", + "cohere": "cohere", + "azure": "azure", + "azure-openai": "azure-openai", + "bedrock": "bedrock", + "google-ai-studio": "googleaistudio", + "vertex-ai": "vertexai", + "vllm": "vllm", + "tgi": "tgi" + }; + + $component_name := $lookup($deployment_map, model_deployment); + + [ + $component($component_name, { + "max-output-tokens": max_output_tokens + }) + ] + ); + + /* ───────────────────────────────────────────────────────────── + * OCR Components (conditional) + * ───────────────────────────────────────────────────────────── */ + $ocr_map := { + "tesseract": "ocr", + "mistral": "mistral-ocr" + }; + + $ocr_components := ( + ocr.enabled and ocr.engine != "pdf-decode" + ? [ + $component($lookup($ocr_map, ocr.engine)) + ] + : [] + ); + + /* ───────────────────────────────────────────────────────────── + * Embeddings Components (conditional) + * ───────────────────────────────────────────────────────────── */ + $embeddings_map := { + "fastembed": "fastembed", + "huggingface-sentence-transformers": "hf", + "ollama": "ollama" + }; + + $embeddings_components := ( + embeddings.enabled + ? [ + $component("embeddings-" & $lookup($embeddings_map, embeddings.engine)) + ] + : [ + /* Default embeddings if not customized */ + $component("embeddings-fastembed") + ] + ); + + /* ───────────────────────────────────────────────────────────── + * Base Configuration + * ───────────────────────────────────────────────────────────── */ + $base_components := [ + $component("trustgraph-base") + ]; + + /* ───────────────────────────────────────────────────────────── + * Assemble Templates Array + * ───────────────────────────────────────────────────────────── */ + $templates := $append( + $append( + $append( + $append( + $base_components, + $infrastructure + ), + $model_components + ), + $ocr_components + ), + $embeddings_components + ); + + /* ───────────────────────────────────────────────────────────── + * Platform Mapping (dialog values to API values) + * ───────────────────────────────────────────────────────────── */ + $platform_map := { + "docker-compose": "docker-compose", + "podman-compose": "podman-compose", + "minikube": "minikube-k8s", + "gke": "gcp-k8s", + "eks": "eks-k8s", + "aks": "aks-k8s", + "scw": "scw-k8s", + "ovh": "ovh-k8s" + }; + $api_platform := $lookup($platform_map, platform); + + /* ───────────────────────────────────────────────────────────── + * API Configuration + * ───────────────────────────────────────────────────────────── */ + $api_base := "https://config-ui.demo.trustgraph.ai/api/generate"; + $api_url := $api_base & "/" & $api_platform & "/" & version; + + /* ───────────────────────────────────────────────────────────── + * Final Output + * ───────────────────────────────────────────────────────────── */ + { + "version": version, + "platform": $api_platform, + "api_url": $api_url, + "templates": $templates + } +) diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/run.py b/ai-context/trustgraph-templates/trustgraph_configurator/run.py new file mode 100644 index 00000000..544f165d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/run.py @@ -0,0 +1,107 @@ + +import json +import logging +import argparse +import sys + +from . import Generator, Packager + +def run(): + + parser = argparse.ArgumentParser( + prog="tg-build-deployment", + description=__doc__ + ) + + parser.add_argument( + '-v', '--version', + help=f'Version' + ) + + parser.add_argument( + '-i', '--input', + default="config.json", + help=f'Input configuration name (default: config.json)' + ) + + parser.add_argument( + '-o', '--output', + default="deploy.zip", + help=f'Output file name (default: deploy.zip)' + ) + + parser.add_argument( + '-t', '--template', + help=f'Template to use' + ) + + parser.add_argument( + '-p', '--platform', + default="docker-compose", + help=f'Platform (default: docker-compose)' + ) + + parser.add_argument( + '--latest', + action='store_true', + help="Latest version", + ) + + parser.add_argument( + '--latest-stable', + action='store_true', + help="Latest stable version", + ) + + parser.add_argument( + '-O', '--output-tg-config', + action='store_true', + help="Output only TrustGraph configuration to stdout", + ) + + parser.add_argument( + '-R', '--output-resources', + action='store_true', + help="Output only resources (docker-compose.yaml or resources.yaml) to stdout", + ) + + try: + + args = parser.parse_args() + args = vars(args) + + input = args["input"] + + with open(input) as f: + config = f.read() + + output = args["output"] + output_tg_config = args.get("output_tg_config", False) + output_resources = args.get("output_resources", False) + + # Configure logging only if not outputting to stdout + if not output_tg_config and not output_resources: + logging.basicConfig(level=logging.INFO, format='%(message)s') + else: + # Suppress all logging when outputting to stdout + logging.basicConfig(level=logging.CRITICAL) + + del args["input"] + del args["output"] + del args["output_tg_config"] + del args["output_resources"] + + a = Packager(**args) + + if output_tg_config: + a.write_tg_config(config) + elif output_resources: + a.write_resources(config) + else: + a.write(config, output) + + except Exception as e: + + print(f"Exception: {e}", file=sys.stderr) + sys.exit(1) + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/service.py b/ai-context/trustgraph-templates/trustgraph_configurator/service.py new file mode 100644 index 00000000..f5b8d6be --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/service.py @@ -0,0 +1,18 @@ + +import logging + +from . api import Api + +def run_service(): + + logging.basicConfig( + level=logging.DEBUG, + format="%(asctime)s %(levelname)s %(message)s" + ) + + logging.info("Starting...") + + a = Api(port=8080) + + a.run() + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/README.md b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/README.md new file mode 100644 index 00000000..23039e9a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/README.md @@ -0,0 +1,125 @@ + +# TrustGraph template generation + +There are two utilities here: + +- `generate`: Generates a single Docker Compose launch configuration + based on configuration you provide. +- `generate-all`: Generates the release bundle for releases. You won't + need to use this unless you are managing releases. + +## `generate-all` + +Previously, this generates a full set of all vector DB / triple store / LLM +combinations, and put them in a single ZIP file. But this got out of +hand, so at the time of writing, this generates a single configuraton +using Qdrant vector DB, Ollama LLM support and Cassandra for a triple store. + +The combinations are contained withing the code, it takes two arguments: +- output ZIP file (is over-written) +- TrustGraph version number + +``` +templates/generate-all output.zip 0.18.11 +``` + +## `generate` + +This utility takes a configuration file describing the components to bundle, +and outputs a Docker Compose YAML file. + +### Input configuration + +The input configuration is a JSON file, an array of components to pull into +the configuration. For each component, there is a name and a (possibly empty) +object describing addtional parameters for that component. + +Example: + +``` +[ + { + "name": "cassandra", + "parameters": {} + }, + { + "name": "pulsar", + "parameters": {} + }, + { + "name": "qdrant", + "parameters": {} + }, + { + "name": "embeddings-hf", + "parameters": {} + }, + { + "name": "graph-rag", + "parameters": {} + }, + { + "name": "grafana", + "parameters": {} + }, + { + "name": "trustgraph", + "parameters": {} + }, + { + "name": "googleaistudio", + "parameters": { + "googleaistudio-temperature": 0.3, + "googleaistudio-max-output-tokens": 2048, + "googleaistudio-model": "gemini-1.5-pro-002" + } + }, + { + "name": "prompt-template", + "parameters": {} + }, + { + "name": "override-recursive-chunker", + "parameters": { + "chunk-size": 1000, + "chunk-overlap": 50 + } + }, + { + "name": "workbench-ui", + "parameters": {} + }, + { + "name": "agent-manager-react", + "parameters": {} + } +] +``` + +If you want to make your own configuration you could try changing the +configuration above: +- Components which are essential: pulsar, trustgraph, graph-rag, grafana, + agent-manager-react +- You need a triple store, one of: cassandra, memgraph, falkordb, neo4j +- You need a vector store, one of: qdrant, pinecone +- You need an LLM, one of: azure, azure-openai, bedrock, claude, cohere, + llamafile, ollama, openai, vertexai. +- You need an embeddings implementation, one of: embeddings-hf, + embeddings-ollama +- Optionally add the Workbench tool: workbench-ui + +Components have over-ridable parameters, look in the component definition +in `templates/components/` to see what you can override. + +### Invocation + +Two parameters: +- The output ZIP file +- The version number + +The configuration file described above is provided on standard input + +``` +templates/generate out.zip 0.18.9 < config.json +``` + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/base/base.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/base/base.jsonnet new file mode 100644 index 00000000..9f82efb2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/base/base.jsonnet @@ -0,0 +1,3 @@ +{ + restart: "on-failure:100", +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components.jsonnet new file mode 100644 index 00000000..41391a9e --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components.jsonnet @@ -0,0 +1,98 @@ +{ + + // Essentials + "trustgraph-base": import "components/trustgraph.jsonnet", + "rev-gateway": import "components/rev-gateway.jsonnet", + "pulsar": import "components/pulsar.jsonnet", + + // LLMs + "azure": import "components/azure.jsonnet", + "azure-openai": import "components/azure-openai.jsonnet", + "bedrock": import "components/bedrock.jsonnet", + "claude": import "components/claude.jsonnet", + "cohere": import "components/cohere.jsonnet", + "googleaistudio": import "components/googleaistudio.jsonnet", + "llamafile": import "components/llamafile.jsonnet", + "lmstudio": import "components/lmstudio.jsonnet", + "mistral": import "components/mistral.jsonnet", + "ollama": import "components/ollama.jsonnet", + "openai": import "components/openai.jsonnet", + "vertexai": import "components/vertexai.jsonnet", + "tgi": import "components/tgi.jsonnet", + "vllm": import "components/vllm.jsonnet", + + // LLMs for RAG. RAG components have been collapsed into the core + // component, so gone away. + "azure-rag": {}, + "azure-openai-rag": {}, + "bedrock-rag": {}, + "claude-rag": {}, + "cohere-rag": {}, + "googleaistudio-rag": {}, + "llamafile-rag": {}, + "lmstudio-rag": {}, + "mistral-rag": {}, + "ollama-rag": {}, + "openai-rag": {}, + "vertexai-rag": {}, + "tgi-rag": import "components/tgi-rag.jsonnet", + "vllm-rag": {}, + + "tgi-service-cpu": import "components/tgi-service-cpu.jsonnet", + "tgi-service-intel-gpu": import "components/tgi-service-intel-gpu.jsonnet", + "tgi-service-gaudi": import "components/tgi-service-gaudi.jsonnet", + + "vllm-service-intel-gpu": import "components/vllm-service-intel-gpu.jsonnet", + "vllm-service-gaudi": import "components/vllm-service-gaudi.jsonnet", + "vllm-service-nvidia": import "components/vllm-service-nvidia.jsonnet", + + // Embeddings + "embeddings-ollama": import "components/embeddings-ollama.jsonnet", + "embeddings-hf": import "components/embeddings-hf.jsonnet", + "embeddings-fastembed": import "components/embeddings-fastembed.jsonnet", + + // OCR options + "ocr": import "components/ocr.jsonnet", + "mistral-ocr": import "components/mistral-ocr.jsonnet", + + // Vector stores + "vector-store-milvus": import "components/vector-store-milvus.jsonnet", + "vector-store-qdrant": import "components/vector-store-qdrant.jsonnet", + "vector-store-pinecone": import "components/vector-store-pinecone.jsonnet", + + // Triples stores + "triple-store-cassandra": import "components/triple-store-cassandra.jsonnet", + "triple-store-neo4j": import "components/triple-store-neo4j.jsonnet", + "triple-store-falkordb": import "components/triple-store-falkordb.jsonnet", + "triple-store-memgraph": import "components/triple-store-memgraph.jsonnet", + + // Object stores + "row-store-cassandra": import "components/row-store-cassandra.jsonnet", + + // Observability support + "grafana": import "components/grafana.jsonnet", + + // Pulsar manager is a UI for Pulsar. Uses a LOT of memory + "pulsar-manager": import "components/pulsar-manager.jsonnet", + + "override-recursive-chunker": import "components/chunker-recursive.jsonnet", + + // The prompt manager + "prompt-overrides": import "components/prompt-overrides.jsonnet", + + // Archaic - part of core system, just making sure these don't + // cause a failure + "workbench-ui": {}, + "prompt-template": {}, + "agent-manager-react": {}, + "graph-rag": {}, + "document-rag": {}, + "librarian": {}, + + // Extra MCP services + "ddg-mcp-server": import "mcp/ddg-mcp-server.jsonnet", + + // Does nothing. But, can be a hack to overwrite parameters + "null": {}, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/agent-manager-react.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/agent-manager-react.jsonnet new file mode 100644 index 00000000..d99ae88c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/agent-manager-react.jsonnet @@ -0,0 +1,40 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "agent-manager" +: { + + create:: function(engine) + + local container = + engine.container("agent-manager") + .with_image(images.trustgraph_flow) + .with_command([ + "agent-manager-react", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "agent-manager", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/azure-openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/azure-openai.jsonnet new file mode 100644 index 00000000..4d5a70e8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/azure-openai.jsonnet @@ -0,0 +1,113 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/azure-openai.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["azure-openai-" + key]:: value, + }, + +// Strategy is to specify the model with the AZURE_MODEL environment +// variable. This isn't something that can just be specified dynamically, +// it has to match what was provisioned in Azure. + + "azure-openai-max-output-tokens":: 4192, + "azure-openai-temperature":: 0.0, + "azure-openai-models":: models, + + "llm-models" +:: $["azure-openai-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-openai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_MODEL", "azure-model") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure-openai", + "-p", + url.pulsar, + "-x", + std.toString($["azure-openai-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-openai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_MODEL", "azure-model") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure-openai", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["azure-openai-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/azure.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/azure.jsonnet new file mode 100644 index 00000000..f4db7500 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/azure.jsonnet @@ -0,0 +1,106 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/azure.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["azure-" + key]:: value, + }, + + "azure-max-output-tokens":: 4096, + "azure-temperature":: 0.0, + "azure-models":: models, + + "llm-models" +:: $["azure-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-ai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure", + "-p", + url.pulsar, + "-x", + std.toString($["azure-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-ai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["azure-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/bedrock.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/bedrock.jsonnet new file mode 100644 index 00000000..19bf1459 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/bedrock.jsonnet @@ -0,0 +1,106 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local chunker = import "chunker-recursive.jsonnet"; +local models = import "parameters/bedrock.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["bedrock-" + key]:: value, + }, + + "bedrock-max-output-tokens":: 4096, + "bedrock-temperature":: 0.0, + "bedrock-models":: models, + + "llm-models" +:: $["bedrock-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("bedrock-credentials") + .with_env_var("AWS_ACCESS_KEY_ID", "aws-id-key") + .with_env_var("AWS_SECRET_ACCESS_KEY", "aws-secret") + .with_env_var("AWS_DEFAULT_REGION", "aws-region"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_bedrock) + .with_command([ + "text-completion-bedrock", + "-p", + url.pulsar, + "-x", + std.toString($["bedrock-max-output-tokens"]), + "-t", + "%0.3f" % $["bedrock-temperature"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("bedrock-credentials") + .with_env_var("AWS_ACCESS_KEY_ID", "aws-id-key") + .with_env_var("AWS_SECRET_ACCESS_KEY", "aws-secret") + .with_env_var("AWS_DEFAULT_REGION", "aws-region"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_bedrock) + .with_command([ + "text-completion-bedrock", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["bedrock-max-output-tokens"]), + "-t", + "%0.3f" % $["bedrock-temperature"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + chunker + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/chunker-recursive.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/chunker-recursive.jsonnet new file mode 100644 index 00000000..a9f31d73 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/chunker-recursive.jsonnet @@ -0,0 +1,48 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; + +{ + + "chunk-size":: 2000, + "chunk-overlap":: 100, + + "chunker" +: { + + create:: function(engine) + + local container = + engine.container("chunker") + .with_image(images.trustgraph_flow) + .with_command([ + "chunker-recursive", + "-p", + url.pulsar, + "--chunk-size", + std.toString($["chunk-size"]), + "--chunk-overlap", + std.toString($["chunk-overlap"]), + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "chunker", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/claude.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/claude.jsonnet new file mode 100644 index 00000000..f2dcc7e1 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/claude.jsonnet @@ -0,0 +1,105 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/claude.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["claude-" + key]:: value, + }, + + "claude-max-output-tokens":: 4096, + "claude-temperature":: 0.0, + "claude-models":: models, + + "llm-models" +:: $["claude-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("claude-credentials") + .with_env_var("CLAUDE_KEY", "claude-key"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-claude", + "-p", + url.pulsar, + "-x", + std.toString($["claude-max-output-tokens"]), + "-t", + "%0.3f" % $["claude-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("claude-credentials") + .with_env_var("CLAUDE_KEY", "claude-key"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-claude", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["claude-max-output-tokens"]), + "-t", + "%0.3f" % $["claude-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/cohere.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/cohere.jsonnet new file mode 100644 index 00000000..644b7b4a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/cohere.jsonnet @@ -0,0 +1,98 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/cohere.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["cohere-" + key]:: value, + }, + + "cohere-temperature":: 0.0, + "cohere-models":: models, + + "llm-models" +:: $["cohere-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("cohere-credentials") + .with_env_var("COHERE_KEY", "cohere-key"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-cohere", + "-p", + url.pulsar, + "-t", + "%0.3f" % $["cohere-temperature"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("cohere-credentials") + .with_env_var("COHERE_KEY", "cohere-key"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-cohere", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-t", + "%0.3f" % $["cohere-temperature"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/configuration.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/configuration.jsonnet new file mode 100644 index 00000000..b2499ff6 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/configuration.jsonnet @@ -0,0 +1,50 @@ + +// This puts the default configuration together. References many things, +// flow classes, a default flow, token costs, prompts, agent tools + +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "init-trustgraph" +: { + + create:: function(engine) + + local cfgVol = engine.configVolume( + "trustgraph-cfg", "trustgraph", + { + "config.json": importstr "trustgraph/config.json", + } + ); + + local container = + engine.container("init-trustgraph") + .with_image(images.trustgraph_flow) + .with_command( + [ + "tg-init-trustgraph", + "-p", + url.pulsar_admin, + "--config-file", + "/trustgraph/config.json", + ] + ) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M") + .with_volume_mount(cfgVol, "/trustgraph/"); + + local containerSet = engine.containers( + "init-trustgraph", [ container ] + ); + + engine.resources([ + cfgVol, + containerSet, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/document-rag.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/document-rag.jsonnet new file mode 100644 index 00000000..239598b7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/document-rag.jsonnet @@ -0,0 +1,77 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; + +{ + + "document-rag-doc-limit":: 20, + + "document-rag" +: { + + create:: function(engine) + + local container = + engine.container("document-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "document-rag", + "-p", + url.pulsar, + "--doc-limit", + std.toString($["document-rag-doc-limit"]), + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "document-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "document-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("document-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "document-embeddings", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits("1.0", "512M") + .with_reservations("0.5", "512M"); + + local containerSet = engine.containers( + "document-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/embeddings-fastembed.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/embeddings-fastembed.jsonnet new file mode 100644 index 00000000..1e4d6cb9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/embeddings-fastembed.jsonnet @@ -0,0 +1,48 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/embeddings-fastembed.jsonnet"; + +{ + + "fastembed-models":: models, + + "embeddings-models" +:: $["fastembed-models"], + + embeddings +: { + + create:: function(engine) + + local container = + engine.container("embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "embeddings-fastembed", + "-p", + url.pulsar, + "--concurrency", + std.toString($["embeddings-concurrency"]), + "--log-level", + $["log-level"], + ]) + .with_limits("1.0", "400M") + .with_reservations("0.5", "400M"); + + local containerSet = engine.containers( + "embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/embeddings-hf.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/embeddings-hf.jsonnet new file mode 100644 index 00000000..29008129 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/embeddings-hf.jsonnet @@ -0,0 +1,46 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/embeddings-huggingface.jsonnet"; + +{ + + "huggingface-embeddings-models":: models, + + "embeddings-models" +:: $["huggingface-embeddings-models"], + + embeddings +: { + + create:: function(engine) + + local container = + engine.container("embeddings") + .with_image(images.trustgraph_hf) + .with_command([ + "embeddings-hf", + "-p", + url.pulsar, + "--concurrency", + std.toString($["embeddings-concurrency"]), + ]) + .with_limits("1.0", "400M") + .with_reservations("0.5", "400M"); + + local containerSet = engine.containers( + "embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/embeddings-ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/embeddings-ollama.jsonnet new file mode 100644 index 00000000..8422b646 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/embeddings-ollama.jsonnet @@ -0,0 +1,51 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local models = import "parameters/embeddings-ollama.jsonnet"; + +{ + + "ollama-url":: "${OLLAMA_HOST}", + + "ollama-models":: models, + + "embeddings-models" +:: $["ollama-models"], + + embeddings +: { + + create:: function(engine) + + local container = + engine.container("embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "embeddings-ollama", + "-p", + url.pulsar, + "--concurrency", + std.toString($["embeddings-concurrency"]), + "-r", + $["ollama-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/googleaistudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/googleaistudio.jsonnet new file mode 100644 index 00000000..9b6ff6c5 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/googleaistudio.jsonnet @@ -0,0 +1,105 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/googleaistudio.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["googleaistudio-" + key]:: value, + }, + + "googleaistudio-max-output-tokens":: 4096, + "googleaistudio-temperature":: 0.0, + "googleaistudio-models":: models, + + "llm-models" +:: $["googleaistudio-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("googleaistudio-credentials") + .with_env_var("GOOGLE_AI_STUDIO_KEY", "googleaistudio-key"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-googleaistudio", + "-p", + url.pulsar, + "-x", + std.toString($["googleaistudio-max-output-tokens"]), + "-t", + "%0.3f" % $["googleaistudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("googleaistudio-credentials") + .with_env_var("GOOGLE_AI_STUDIO_KEY", "googleaistudio-key"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-googleaistudio", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["googleaistudio-max-output-tokens"]), + "-t", + "%0.3f" % $["googleaistudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/grafana.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/grafana.jsonnet new file mode 100644 index 00000000..e968faec --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/grafana.jsonnet @@ -0,0 +1,122 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + "prometheus" +: { + + create:: function(engine) + + local vol = engine.volume("prometheus-data").with_size("20G"); + + local cfgVol = engine.configVolume( + "prometheus-cfg", "prometheus", + { + "prometheus.yml": importstr "prometheus/prometheus.yml", + } + ); + + local container = + engine.container("prometheus") + .with_image(images.prometheus) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M") + .with_port(9090, 9090, "http") + .with_volume_mount(cfgVol, "/etc/prometheus/") + .with_volume_mount(vol, "/prometheus"); + + local containerSet = engine.containers( + "prometheus", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9090, 9090, "http"); + + engine.resources([ + cfgVol, + vol, + containerSet, + service, + ]) + + }, + + "grafana" +: { + + create:: function(engine) + + local vol = engine.volume("grafana-storage").with_size("20G"); + + local provDashVol = engine.configVolume( + "prov-dash", "grafana/provisioning/", + { + "dashboard.yml": + importstr "grafana/provisioning/dashboard.yml", + } + + ); + + local provDataVol = engine.configVolume( + "prov-data", "grafana/provisioning/", + { + "datasource.yml": + importstr "grafana/provisioning/datasource.yml", + } + + ); + + local dashVol = engine.configVolume( + "dashboards", "grafana/dashboards/", + { + "dashboard.json": + importstr "grafana/dashboards/dashboard.json", + } + + ); + + local container = + engine.container("grafana") + .with_image(images.grafana) + .with_environment({ + // GF_AUTH_ANONYMOUS_ORG_ROLE: "Admin", + // GF_AUTH_ANONYMOUS_ENABLED: "true", + // GF_ORG_ROLE: "Admin", + GF_ORG_NAME: "trustgraph.ai", + // GF_SERVER_ROOT_URL: "https://example.com", + }) + .with_limits("1.0", "256M") + .with_reservations("0.5", "256M") + .with_port(3000, 3000, "cassandra") + .with_volume_mount(vol, "/var/lib/grafana") + .with_volume_mount( + provDashVol, "/etc/grafana/provisioning/dashboards/" + ) + .with_volume_mount( + provDataVol, "/etc/grafana/provisioning/datasources/" + ) + .with_volume_mount( + dashVol, "/var/lib/grafana/dashboards/" + ); + + local containerSet = engine.containers( + "grafana", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(3000, 3000, "http"); + + engine.resources([ + vol, + provDashVol, + provDataVol, + dashVol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/graph-rag.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/graph-rag.jsonnet new file mode 100644 index 00000000..64ab0130 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/graph-rag.jsonnet @@ -0,0 +1,223 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "graph-rag-entity-limit":: 50, + "graph-rag-triple-limit":: 30, + "graph-rag-max-subgraph-size":: 400, + "graph-rag-max-path-length":: 2, + + "kg-extract-definitions" +: { + + create:: function(engine) + + local container = + engine.container("kg-extract-definitions") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-definitions", + "-p", + url.pulsar, + "--concurrency", + std.toString($["kg-extraction-concurrency"]), + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "kg-extract-definitions", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-relationships" +: { + + create:: function(engine) + + local container = + engine.container("kg-extract-relationships") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-relationships", + "-p", + url.pulsar, + "--concurrency", + std.toString($["kg-extraction-concurrency"]), + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "kg-extract-relationships", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-agent" +: { + + create:: function(engine) + + local container = + engine.container("kg-extract-agent") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-agent", + "-p", + url.pulsar, + "--concurrency", + std.toString($["kg-extraction-concurrency"]), + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "kg-extract-agent", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-ontology" +: { + + create:: function(engine) + + local container = + engine.container("kg-extract-ontology") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-ontology", + "-p", + url.pulsar, + "--concurrency", + std.toString($["kg-extraction-concurrency"]), + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "300M") + .with_reservations("0.1", "300M"); + + local containerSet = engine.containers( + "kg-extract-ontology", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "graph-rag" +: { + + create:: function(engine) + + local container = + engine.container("graph-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "graph-rag", + "-p", + url.pulsar, +// "--concurrency", +// std.toString($["graph-rag-concurrency"]), + "--entity-limit", + std.toString($["graph-rag-entity-limit"]), + "--triple-limit", + std.toString($["graph-rag-triple-limit"]), + "--max-subgraph-size", + std.toString($["graph-rag-max-subgraph-size"]), + "--max-path-length", + std.toString($["graph-rag-max-path-length"]), + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "graph-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "graph-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "graph-embeddings", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits("1.0", "512M") + .with_reservations("0.5", "512M"); + + local containerSet = engine.containers( + "graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/librarian.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/librarian.jsonnet new file mode 100644 index 00000000..cfcbcff0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/librarian.jsonnet @@ -0,0 +1,43 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local minio = import "stores/minio.jsonnet"; +local cassandra = import "stores/cassandra.jsonnet"; + +{ + + "librarian" +: { + + create:: function(engine) + + local container = + engine.container("librarian") + .with_image(images.trustgraph_flow) + .with_command([ + "librarian", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M"); + + local containerSet = engine.containers( + "librarian", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +// Minio and Cassandra are used by the Librarian +} + minio + cassandra + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/llamafile.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/llamafile.jsonnet new file mode 100644 index 00000000..bb6c55fd --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/llamafile.jsonnet @@ -0,0 +1,95 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/slm.jsonnet"; +local models = import "parameters/llamafile.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["llamafile-" + key]:: value, + }, + + "llamafile-models":: models, + + "llm-models" +:: $["llamafile-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("llamafile-credentials") + .with_env_var("LLAMAFILE_URL", "llamafile-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-llamafile", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("llamafile-credentials") + .with_env_var("LLAMAFILE_URL", "llamafile-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-llamafile", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/lmstudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/lmstudio.jsonnet new file mode 100644 index 00000000..a578b694 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/lmstudio.jsonnet @@ -0,0 +1,105 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/lmstudio.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["lmstudio-" + key]:: value, + }, + + "lmstudio-max-output-tokens":: 4096, + "lmstudio-temperature":: 0.0, + "lmstudio-models":: models, + + "llm-models" +:: $["lmstudio-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("lmstudio-credentials") + .with_env_var("LMSTUDIO_URL", "lmstudio-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-lmstudio", + "-p", + url.pulsar, + "-x", + std.toString($["lmstudio-max-output-tokens"]), + "-t", + "%0.3f" % $["lmstudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("lmstudio-credentials") + .with_env_var("LMSTUDIO_URL", "lmstudio-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-lmstudio", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["lmstudio-max-output-tokens"]), + "-t", + "%0.3f" % $["lmstudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/mcp-server.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/mcp-server.jsonnet new file mode 100644 index 00000000..7769889e --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/mcp-server.jsonnet @@ -0,0 +1,48 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "mcp-server-port":: 8000, + + "mcp-server" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("mcp-server-secret") + .with_env_var("MCP_SERVER_SECRET", "mcp-server-secret"); + + local port = $["mcp-server-port"]; + + local container = + engine.container("mcp-server") + .with_image(images.trustgraph_mcp) + .with_command([ + "mcp-server", + "--port", + std.toString(port), + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_port(port, port, "mcp"); + + local containerSet = engine.containers( + "mcp-server", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(port, port, "mcp"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/mistral-ocr.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/mistral-ocr.jsonnet new file mode 100644 index 00000000..f70e0cd6 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/mistral-ocr.jsonnet @@ -0,0 +1,49 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["mistral-" + key]:: value, + }, + + "pdf-decoder" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("mistral-credentials") + .with_env_var("MISTRAL_TOKEN", "mistral-token"); + + local container = + engine.container("mistral-ocr") + .with_image(images.trustgraph_flow) + .with_command([ + "pdf-ocr-mistral", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "mistral-ocr", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/mistral.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/mistral.jsonnet new file mode 100644 index 00000000..6d01fbc9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/mistral.jsonnet @@ -0,0 +1,105 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/mistral.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["mistral-" + key]:: value, + }, + + "mistral-max-output-tokens":: 4096, + "mistral-temperature":: 0.0, + "mistral-models":: models, + + "llm-models" +:: $["mistral-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("mistral-credentials") + .with_env_var("MISTRAL_TOKEN", "mistral-token"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-mistral", + "-p", + url.pulsar, + "-x", + std.toString($["mistral-max-output-tokens"]), + "-t", + "%0.3f" % $["mistral-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("mistral-credentials") + .with_env_var("MISTRAL_TOKEN", "mistral-token"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-mistral", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["mistral-max-output-tokens"]), + "-t", + "%0.3f" % $["mistral-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/null.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/null.jsonnet new file mode 100644 index 00000000..2c63c085 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/null.jsonnet @@ -0,0 +1,2 @@ +{ +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/object-store-cassandra.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/object-store-cassandra.jsonnet new file mode 100644 index 00000000..8362b5c4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/object-store-cassandra.jsonnet @@ -0,0 +1,78 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local cassandra = import "stores/cassandra.jsonnet"; + +cassandra + { + + "store-objects" +: { + + create:: function(engine) + + local container = + engine.container("store-objects") + .with_image(images.trustgraph_flow) + .with_command([ + "objects-write-cassandra", + "-p", + url.pulsar, + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-objects", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-objects" +: { + + create:: function(engine) + + local container = + engine.container("query-objects") + .with_image(images.trustgraph_flow) + .with_command([ + "objects-query-cassandra", + "-p", + url.pulsar, + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "512M") + .with_reservations("0.1", "512M"); + + local containerSet = engine.containers( + "query-objects", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/ocr.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/ocr.jsonnet new file mode 100644 index 00000000..4f53aa5a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/ocr.jsonnet @@ -0,0 +1,37 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "pdf-decoder" +: { + + create:: function(engine) + + local container = + engine.container("pdf-ocr") + .with_image(images.trustgraph_ocr) + .with_command([ + "pdf-ocr", + "-p", + url.pulsar, + ]) + .with_limits("1.0", "512M") + .with_reservations("0.1", "512M"); + + local containerSet = engine.containers( + "pdf-ocr", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/ollama.jsonnet new file mode 100644 index 00000000..bccfd0f1 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/ollama.jsonnet @@ -0,0 +1,99 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/ollama.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["ollama-" + key]:: value, + }, + + "ollama-models":: models, + + "llm-models" +:: $["ollama-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("ollama-credentials") + .with_env_var("OLLAMA_HOST", "ollama-host"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-ollama", + "-p", + url.pulsar, + "--concurrency", + std.toString($["text-completion-concurrency"]), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("ollama-credentials") + .with_env_var("OLLAMA_HOST", "ollama-host"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-ollama", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "--concurrency", + std.toString($["text-completion-rag-concurrency"]), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/openai.jsonnet new file mode 100644 index 00000000..c8754280 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/openai.jsonnet @@ -0,0 +1,107 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/openai.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["openai-" + key]:: value, + }, + + "openai-max-output-tokens":: 4096, + "openai-temperature":: 0.0, + "openai-models":: models, + + "llm-models" +:: $["openai-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("openai-credentials") + .with_env_var("OPENAI_TOKEN", "openai-token") + .with_env_var("OPENAI_BASE_URL", "openai-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-openai", + "-p", + url.pulsar, + "-x", + std.toString($["openai-max-output-tokens"]), + "-t", + "%0.3f" % $["openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("openai-credentials") + .with_env_var("OPENAI_TOKEN", "openai-token") + .with_env_var("OPENAI_BASE_URL", "openai-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-openai", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["openai-max-output-tokens"]), + "-t", + "%0.3f" % $["openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/prompt-overrides.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/prompt-overrides.jsonnet new file mode 100644 index 00000000..852ec09d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/prompt-overrides.jsonnet @@ -0,0 +1,24 @@ +local default_prompts = import "prompts/default-prompts.jsonnet"; + +{ + + with:: function(key, value) + if (key == "system-template") then + self + { + prompts +:: { + "system-template": value, + } + } + else + self + { + prompts +:: { + templates +:: { + [key] +:: { + prompt: value + } + } + } + }, + +} + default_prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/prompt-template.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/prompt-template.jsonnet new file mode 100644 index 00000000..5d1af35b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/prompt-template.jsonnet @@ -0,0 +1,78 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "prompt" +: { + + create:: function(engine) + + local container = + engine.container("prompt") + .with_image(images.trustgraph_flow) + .with_command([ + "prompt-template", + "-p", + url.pulsar, + "--concurrency", + std.toString($["prompt-concurrency"]), + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "prompt", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "prompt-rag" +: { + + create:: function(engine) + + local container = + engine.container("prompt-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "prompt-template", + "-p", + url.pulsar, + "--id", + "prompt-rag", + "--concurrency", + std.toString($["prompt-rag-concurrency"]), + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "prompt-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/pulsar-manager.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/pulsar-manager.jsonnet new file mode 100644 index 00000000..9a0b59b2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/pulsar-manager.jsonnet @@ -0,0 +1,41 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + "pulsar" +: { + + create:: function(engine) + +// FIXME: Should persist something? +// local volume = engine.volume(...) + + local container = + engine.container("pulsar") + .with_image(images.pulsar_manager) + .with_environment({ + SPRING_CONFIGURATION_FILE: "/pulsar-manager/pulsar-manager/application.properties", + }) + .with_limits("0.5", "1.4G") + .with_reservations("0.1", "1.4G") + .with_port(9527, 9527, "api") + .with_port(7750, 7750, "api2"); + + local containerSet = engine.containers( + "pulsar", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9527, 9527, "api") + .with_port(7750, 7750, "api2); + + engine.resources([ + containerSet, + service, + ]) + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/pulsar.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/pulsar.jsonnet new file mode 100644 index 00000000..0cfd1854 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/pulsar.jsonnet @@ -0,0 +1,171 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +// This is a Pulsar configuration. Non-standalone mode so we deploy +// individual components: bookkeeper, broker and zookeeper. +// +// This also deploys the TrustGraph 'admin' container which initialises +// TrustGraph-specific namespaces etc. + +{ + + "pulsar" +: { + + create:: function(engine) + + // Zookeeper volume + local zkVolume = engine.volume("zookeeper").with_size("1G"); + + // Zookeeper container + local zkContainer = + engine.container("zookeeper") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "bin/apply-config-from-env.py conf/zookeeper.conf && bin/generate-zookeeper-config.sh conf/zookeeper.conf && exec bin/pulsar zookeeper" + ]) + .with_limits("1", "400M") + .with_reservations("0.05", "400M") + .with_user("0:1000") + .with_volume_mount(zkVolume, "/pulsar/data/zookeeper") + .with_environment({ + "metadataStoreUrl": "zk:zookeeper:2181", + "PULSAR_MEM": "-Xms256m -Xmx256m -XX:MaxDirectMemorySize=256m", + }) + .with_port(2181, 2181, "zookeeper") + .with_port(2888, 2888, "zookeeper2") + .with_port(3888, 3888, "zookeeper3"); + + // Pulsar cluster init container + local initContainer = + engine.container("pulsar-init") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "sleep 10 && bin/pulsar initialize-cluster-metadata --cluster cluster-a --zookeeper zookeeper:2181 --configuration-store zookeeper:2181 --web-service-url http://pulsar:8080 --broker-service-url pulsar://pulsar:6650", + ]) + .with_limits("1", "512M") + .with_reservations("0.05", "512M") + .with_environment({ + "PULSAR_MEM": "-Xms256m -Xmx256m -XX:MaxDirectMemorySize=256m", + }); + + + // Bookkeeper volume + local bookieVolume = engine.volume("bookie").with_size("20G"); + + // Bookkeeper container + local bookieContainer = + engine.container("bookie") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "bin/apply-config-from-env.py conf/bookkeeper.conf && exec bin/pulsar bookie" + // false ^ causes this to be a 'failure' exit. + ]) + .with_limits("1", "1024M") + .with_reservations("0.1", "1024M") + .with_user("0:1000") + .with_volume_mount(bookieVolume, "/pulsar/data/bookkeeper") + .with_environment({ + "clusterName": "cluster-a", + "zkServers": "zookeeper:2181", + "bookieId": "bookie", + "metadataStoreUri": "metadata-store:zk:zookeeper:2181", + "advertisedAddress": "bookie", + "BOOKIE_MEM": "-Xms512m -Xmx512m -XX:MaxDirectMemorySize=256m", + }) + .with_port(3181, 3181, "bookie"); + + // Pulsar broker, stateless (uses ZK and Bookkeeper for state) + local brokerContainer = + engine.container("pulsar") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "bin/apply-config-from-env.py conf/broker.conf && exec bin/pulsar broker" + ]) + .with_limits("1", "800M") + .with_reservations("0.1", "800M") + .with_environment({ + "metadataStoreUrl": "zk:zookeeper:2181", + "zookeeperServers": "zookeeper:2181", + "clusterName": "cluster-a", + "managedLedgerDefaultEnsembleSize": "1", + "managedLedgerDefaultWriteQuorum": "1", + "managedLedgerDefaultAckQuorum": "1", + "advertisedAddress": "pulsar", + "advertisedListeners": "external:pulsar://pulsar:6650,localhost:pulsar://localhost:6650", + "PULSAR_MEM": "-Xms512m -Xmx512m -XX:MaxDirectMemorySize=256m", + }) + .with_port(6650, 6650, "pulsar") + .with_port(8080, 8080, "admin"); + + // Container sets + local zkContainerSet = engine.containers( + "zookeeper", + [ + zkContainer, + ] + ); + + local initContainerSet = engine.containers( + "init-pulsar", + [ + initContainer, + ] + ); + + local bookieContainerSet = engine.containers( + "bookie", + [ + bookieContainer, + ] + ); + + local brokerContainerSet = engine.containers( + "pulsar", + [ + brokerContainer, + ] + ); + + // Zookeeper service + local zkService = + engine.service(zkContainerSet) + .with_port(2181, 2181, "zookeeper") + .with_port(2888, 2888, "zookeeper2") + .with_port(3888, 3888, "zookeeper3"); + + // Bookkeeper service + local bookieService = + engine.service(bookieContainerSet) + .with_port(3181, 3181, "bookie"); + + // Pulsar broker service + local brokerService = + engine.service(brokerContainerSet) + .with_port(6650, 6650, "pulsar") + .with_port(8080, 8080, "admin"); + + engine.resources([ + zkVolume, + bookieVolume, + zkContainerSet, + initContainerSet, + bookieContainerSet, + brokerContainerSet, + zkService, + bookieService, + brokerService, + ]) + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/rev-gateway.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/rev-gateway.jsonnet new file mode 100644 index 00000000..b549f402 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/rev-gateway.jsonnet @@ -0,0 +1,54 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + // Invalid, but at least means the rev-gateway won't connect to anything + // it shouldn't. + "rev-gateway-token":: "INVALID_TOKEN", + "rev-gateway-uri":: "wss://127.0.0.1/api/v1/relay?token=" + $["rev-gateway-token"], + + "rev-gateway" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("rev-gateway-secret") + .with_env_var("REV_GATEWAY_SECRET", "rev-gateway-secret"); + + local container = + engine.container("api-gateway") + .with_image(images.trustgraph_flow) + .with_command([ + "rev-gateway", + "-p", + url.pulsar, + "--websocket-uri", + std.toString($["rev-gateway-uri"]), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_port(8000, 8000, "metrics") + .with_port(port, port, "api"); + + local containerSet = engine.containers( + "api-gateway", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics") + .with_port(port, port, "api"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/structured-data.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/structured-data.jsonnet new file mode 100644 index 00000000..33d0ef06 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/structured-data.jsonnet @@ -0,0 +1,137 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "nlp-query" +: { + + create:: function(engine) + + local container = + engine.container("nlp-query") + .with_image(images.trustgraph_flow) + .with_command([ + "nlp-query", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "nlp-query", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "structured-query" +: { + + create:: function(engine) + + local container = + engine.container("structured-query") + .with_image(images.trustgraph_flow) + .with_command([ + "structured-query", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "structured-query", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "structured-diag" +: { + + create:: function(engine) + + local container = + engine.container("structured-diag") + .with_image(images.trustgraph_flow) + .with_command([ + "structured-diag", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "96M") + .with_reservations("0.1", "96M"); + + local containerSet = engine.containers( + "structured-diag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-objects" +: { + + create:: function(engine) + + local container = + engine.container("kg-extract-objects") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-objects", + "-p", + url.pulsar, + "--concurrency", + std.toString($["kg-extraction-concurrency"]), + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "kg-extract-objects", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/tgi-rag.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/tgi-rag.jsonnet new file mode 100644 index 00000000..0a8a7af9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/tgi-rag.jsonnet @@ -0,0 +1,62 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-rag-" + key]:: value, + }, + + "tgi-rag-max-output-tokens":: 1024, + "tgi-rag-temperature":: 0.0, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("tgi-credentials") + .with_env_var("TGI_BASE_URL", "tgi-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-tgi", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "--concurrency", + std.toString($["text-completion-rag-concurrency"]), + "-x", + std.toString($["tgi-rag-max-output-tokens"]), + "-t", + "%0.3f" % $["tgi-rag-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/tgi-service-cpu.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/tgi-service-cpu.jsonnet new file mode 100644 index 00000000..0a02f210 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/tgi-service-cpu.jsonnet @@ -0,0 +1,65 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-service-" + key]:: value, + }, + + "tgi-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "tgi-service-cpus":: "8.0", + "tgi-service-memory":: "16G", + + "tgi-service" +: { + + create:: function(engine) + + local vol = engine.volume("tgi-storage").with_size("20G"); + + local container = + engine.container("tgi-service") + .with_image(images["tgi-service-cpu"]) + .with_command([ + "--model-id", + $["tgi-service-model"], + "--cuda-graphs", + "0", + "--port", + "8899" + ]) + .with_privileged(true) + .with_device("/dev/dri", "/dev/dri") + .with_environment({ + HF_TOKEN: $["hf-token"], + }) + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_reservations( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_port(8899, 8899, "tgi") + .with_volume_mount(vol, "/data"); + + local containerSet = engine.containers( + "tgi-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(8899, 8899, "tgi"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/tgi-service-gaudi.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/tgi-service-gaudi.jsonnet new file mode 100644 index 00000000..9c52ef3d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/tgi-service-gaudi.jsonnet @@ -0,0 +1,92 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-service-" + key]:: value, + }, + + "tgi-service-model":: "meta-llama/Llama-3.3-70B-Instruct", + "tgi-service-cpus":: "64.0", + "tgi-service-memory":: "64G", + + "tgi-service" +: { + + create:: function(engine) + + local vol = engine.volume("tgi-storage").with_size("50G"); + + local container = + engine.container("tgi-service") + .with_image(images["tgi-service-gaudi"]) + .with_command([ + "--model-id", + $["tgi-service-model"], + "--sharded", + "true", + "--num-shard", + "8", + "--max-input-tokens", + "4096", + "--max-total-tokens", + "4096", + "--max-batch-size", + "128", +// "--max-batch-prefill-tokens", +// "16384", + "--max-waiting-tokens", + "7", +// "--waiting-served-ratio", +// "1.2", + "--max-concurrent-requests", + "512", + "--cuda-graphs", + "0", + "--port", + "8899" + ]) + .with_runtime("habana") + .with_environment({ + HABANA_VISIBLE_DEVICES: "all", + OMPI_MCA_btl_vader_single_copy_mechanism: "none", + HF_TOKEN: $["hf-token"], + ENABLE_HPU_GRAPH: 'true', + LIMIT_HPU_GRAPH: 'true', + USE_FLASH_ATTENTION: 'true', + FLASH_ATTENTION_RECOMPUTE: 'true', +// PT_HPU_ENABLE_LAZY_COLLECTIVES: 'true', +// PREFILL_BATCH_BUCKET_SIZE: "1", +// BATCH_BUCKET_SIZE: "1", + + }) + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_reservations( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_port(8899, 8899, "tgi") + .with_volume_mount(vol, "/data"); + + local containerSet = engine.containers( + "tgi-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(8899, 8899, "tgi"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/tgi-service-intel-gpu.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/tgi-service-intel-gpu.jsonnet new file mode 100644 index 00000000..ef223288 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/tgi-service-intel-gpu.jsonnet @@ -0,0 +1,65 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-service-" + key]:: value, + }, + + "tgi-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "tgi-service-cpus":: "8.0", + "tgi-service-memory":: "16G", + + "tgi-service" +: { + + create:: function(engine) + + local vol = engine.volume("tgi-storage").with_size("20G"); + + local container = + engine.container("tgi-service") + .with_image(images["tgi-service-intel-xpu"]) + .with_command([ + "--model-id", + $["tgi-service-model"], + "--cuda-graphs", + "0", + "--port", + "8899" + ]) + .with_environment({ + HF_TOKEN: $["hf-token"], + }) + .with_privileged(true) + .with_device("/dev/dri", "/dev/dri") + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_reservations( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_port(8899, 8899, "tgi") + .with_volume_mount(vol, "/data"); + + local containerSet = engine.containers( + "tgi-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(8899, 8899, "tgi"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/tgi.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/tgi.jsonnet new file mode 100644 index 00000000..f62266a2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/tgi.jsonnet @@ -0,0 +1,60 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-" + key]:: value, + }, + + "tgi-max-output-tokens":: 1024, + "tgi-temperature":: 0.0, + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("tgi-credentials") + .with_env_var("TGI_BASE_URL", "tgi-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-tgi", + "-p", + url.pulsar, + "--concurrency", + std.toString($["text-completion-concurrency"]), + "-x", + std.toString($["tgi-max-output-tokens"]), + "-t", + "%0.3f" % $["tgi-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/triple-store-cassandra.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/triple-store-cassandra.jsonnet new file mode 100644 index 00000000..a7106cb5 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/triple-store-cassandra.jsonnet @@ -0,0 +1,78 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local cassandra = import "stores/cassandra.jsonnet"; + +cassandra + { + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-cassandra", + "-p", + url.pulsar, + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-cassandra", + "-p", + url.pulsar, + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "512M") + .with_reservations("0.1", "512M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/triple-store-falkordb.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/triple-store-falkordb.jsonnet new file mode 100644 index 00000000..f9177a18 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/triple-store-falkordb.jsonnet @@ -0,0 +1,80 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local falkordb = import "stores/falkordb.jsonnet"; + +falkordb + { + + "falkordb-url":: "falkor://falkordb:6379", + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-falkordb", + "-p", + url.pulsar, + "-g", + $["falkordb-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-falkordb", + "-p", + url.pulsar, + "-g", + $["falkordb-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/triple-store-memgraph.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/triple-store-memgraph.jsonnet new file mode 100644 index 00000000..bc86a5a7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/triple-store-memgraph.jsonnet @@ -0,0 +1,85 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local memgraph = import "stores/memgraph.jsonnet"; + +memgraph + { + + "memgraph-url":: "bolt://memgraph:7687", + "memgraph-database":: "memgraph", + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-memgraph", + "-p", + url.pulsar, + "-g", + $["memgraph-url"], + "--database", + $["memgraph-database"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-memgraph", + "-p", + url.pulsar, + "-g", + $["memgraph-url"], + "--database", + $["memgraph-database"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/triple-store-neo4j.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/triple-store-neo4j.jsonnet new file mode 100644 index 00000000..f4a93d9c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/triple-store-neo4j.jsonnet @@ -0,0 +1,80 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local neo4j = import "stores/neo4j.jsonnet"; + +neo4j + { + + "neo4j-url":: "bolt://neo4j:7687", + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-neo4j", + "-p", + url.pulsar, + "-g", + $["neo4j-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-neo4j", + "-p", + url.pulsar, + "-g", + $["neo4j-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/trustgraph.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/trustgraph.jsonnet new file mode 100644 index 00000000..1eac5dd8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/trustgraph.jsonnet @@ -0,0 +1,349 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +local config_initialiser = import "configuration.jsonnet"; +local config = import "trustgraph-config.jsonnet"; +local librarian = import "librarian.jsonnet"; +local mcp_server = import "mcp-server.jsonnet"; +local workbench = import "workbench-ui.jsonnet"; +local graphrag = import "graph-rag.jsonnet"; +local documentrag = import "document-rag.jsonnet"; +local prompt_template = import "prompt-template.jsonnet"; +local agent_manager = import "agent-manager-react.jsonnet"; +local structured_data = import "structured-data.jsonnet"; +local ddg = import "mcp/ddg-mcp-server.jsonnet"; + +{ + + "log-level":: "INFO", + + "api-gateway-port":: 8088, + "api-gateway-timeout":: 600, + + "chunk-size":: 250, + "chunk-overlap":: 15, + + "prompt-concurrency":: 1, + "prompt-rag-concurrency":: 1, + + "text-completion-concurrency":: 1, + "text-completion-rag-concurrency":: 1, + + "kg-extraction-concurrency":: 1, + "graph-rag-concurrency":: 1, + + "embeddings-concurrency":: 1, + + "api-gateway" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("gateway-secret") + .with_env_var("GATEWAY_SECRET", "gateway-secret"); + + local port = $["api-gateway-port"]; + + local container = + engine.container("api-gateway") + .with_image(images.trustgraph_flow) + .with_command([ + "api-gateway", + "-p", + url.pulsar, + "--timeout", + std.toString($["api-gateway-timeout"]), + "--port", + std.toString(port), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_port(port, port, "api"); + + local containerSet = engine.containers( + "api-gateway", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics") + .with_port(port, port, "api"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "chunker" +: { + + create:: function(engine) + + local container = + engine.container("chunker") + .with_image(images.trustgraph_flow) + .with_command([ + "chunker-token", + "-p", + url.pulsar, + "--chunk-size", + std.toString($["chunk-size"]), + "--chunk-overlap", + std.toString($["chunk-overlap"]), + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "chunker", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "config-svc" +: { + + create:: function(engine) + + local container = + engine.container("config-svc") + .with_image(images.trustgraph_flow) + .with_command([ + "config-svc", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "config-svc", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "pdf-decoder" +: { + + create:: function(engine) + + local container = + engine.container("pdf-decoder") + .with_image(images.trustgraph_flow) + .with_command([ + "pdf-decoder", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "512M") + .with_reservations("0.1", "512M"); + + local containerSet = engine.containers( + "pdf-decoder", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "mcp-tool" +: { + + create:: function(engine) + + local container = + engine.container("mcp-tool") + .with_image(images.trustgraph_flow) + .with_command([ + "mcp-tool", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "mcp-tool", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "metering" +: { + + create:: function(engine) + + local container = + engine.container("metering") + .with_image(images.trustgraph_flow) + .with_command([ + "metering", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "metering", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "metering-rag" +: { + + create:: function(engine) + + local container = + engine.container("metering-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "metering", + "-p", + url.pulsar, + "--id", + "metering-rag", + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "metering-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-store" +: { + + create:: function(engine) + + local container = + engine.container("kg-store") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-store", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "kg-store", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-manager" +: { + + create:: function(engine) + + local container = + engine.container("kg-manager") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-manager", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "kg-manager", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + librarian + mcp_server + workbench + graphrag + + documentrag + prompt_template + agent_manager + structured_data + + config_initialiser + config + + ddg + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/vector-store-milvus.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/vector-store-milvus.jsonnet new file mode 100644 index 00000000..32d6fab4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/vector-store-milvus.jsonnet @@ -0,0 +1,147 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local milvus = import "stores/milvus.jsonnet"; + +milvus + { + + "store-graph-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("store-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-write-milvus", + "-p", + url.pulsar, + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-graph-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("query-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-query-milvus", + "-p", + url.pulsar, + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "store-doc-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("store-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-write-milvus", + "-p", + url.pulsar, + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-doc-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("query-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-query-milvus", + "-p", + url.pulsar, + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/vector-store-pinecone.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/vector-store-pinecone.jsonnet new file mode 100644 index 00000000..0f8d6a33 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/vector-store-pinecone.jsonnet @@ -0,0 +1,161 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; + +{ + + "pinecone-cloud":: "aws", + "pinecone-region":: "us-east-1", + + "store-graph-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("store-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-write-pinecone", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "query-graph-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("query-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-query-pinecone", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "store-doc-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("store-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-write-pinecone", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "query-doc-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("query-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-query-pinecone", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/vector-store-qdrant.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/vector-store-qdrant.jsonnet new file mode 100644 index 00000000..0582cd2f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/vector-store-qdrant.jsonnet @@ -0,0 +1,147 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local qdrant = import "stores/qdrant.jsonnet"; + +qdrant + { + + "store-graph-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("store-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-write-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-graph-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("query-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-query-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "store-doc-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("store-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-write-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-doc-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("query-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-query-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/vertexai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/vertexai.jsonnet new file mode 100644 index 00000000..05f608ce --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/vertexai.jsonnet @@ -0,0 +1,121 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/vertexai.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vertexai-" + key]:: value, + }, + + "vertexai-private-key":: "/vertexai/private.json", + "vertexai-region":: "us-central1", + "vertexai-max-output-tokens":: 4096, + "vertexai-temperature":: 0.0, + "vertexai-models":: models, + + "llm-models" +:: $["vertexai-models"], + + "text-completion" +: { + + create:: function(engine) + + local cfgVol = engine.secretVolume( + "vertexai-creds", + "./vertexai", + { + "private.json": importstr "vertexai/private.json", + } + ); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_vertexai) + .with_command([ + "text-completion-vertexai", + "-p", + url.pulsar, + "-k", + $["vertexai-private-key"], + "-r", + $["vertexai-region"], + "-x", + std.toString($["vertexai-max-output-tokens"]), + "-t", + "%0.3f" % $["vertexai-temperature"], + ]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_volume_mount(cfgVol, "/vertexai"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + cfgVol, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local cfgVol = engine.secretVolume( + "vertexai-creds", + "./vertexai", + { + "private.json": importstr "vertexai/private.json", + } + ); + + local container = + engine.container("text-completion-rag") + .with_image(images.trustgraph_vertexai) + .with_command([ + "text-completion-vertexai", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-k", + $["vertexai-private-key"], + "-r", + $["vertexai-region"], + "-x", + std.toString($["vertexai-max-output-tokens"]), + "-t", + "%0.3f" % $["vertexai-temperature"], + ]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_volume_mount(cfgVol, "/vertexai"); + + local containerSet = engine.containers( + "text-completion-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + cfgVol, + containerSet, + service, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/vllm-service-gaudi.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/vllm-service-gaudi.jsonnet new file mode 100644 index 00000000..25c1d921 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/vllm-service-gaudi.jsonnet @@ -0,0 +1,66 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-service-" + key]:: value, + }, + + "vllm-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "vllm-service-cpus":: "64.0", + "vllm-service-memory":: "64G", + + "vllm-service" +: { + + create:: function(engine) + + local vol = engine.volume("vllm-storage").with_size("50G"); + + local container = + engine.container("vllm-service") + .with_image(images["vllm-service-gaudi"]) + .with_command([ + "--model", + $["vllm-service-model"], + "--tensor-parallel-size=8", + "--port", + "8899", + ]) + .with_runtime("habana") + .with_environment({ + VLLM_SKIP_WARMUP: "true", + HUGGING_FACE_HUB_TOKEN: $["hf-token"], + HABANA_VISIBLE_DEVICES: "all", + VLLM_CACHE_ROOT: "/data", + }) + .with_privileged(true) + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_reservations( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_port(8899, 8899, "vllm") + .with_volume_mount(vol, "/data"); + + local containerSet = engine.containers( + "vllm-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(8899, 8899, "vllm"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/vllm-service-intel-gpu.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/vllm-service-intel-gpu.jsonnet new file mode 100644 index 00000000..cdd98abc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/vllm-service-intel-gpu.jsonnet @@ -0,0 +1,77 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-service-" + key]:: value, + }, + + "vllm-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "vllm-service-cpus":: "8.0", + "vllm-service-memory":: "16G", + + "vllm-service" +: { + + create:: function(engine) + + local vol = engine.volume("vllm-storage").with_size("20G"); + + local container = + engine.container("vllm-service") + .with_image(images["vllm-service-intel-xpu"]) + .with_command([ + "python", + "-m", + "vllm.entrypoints.openai.api_server", + "--model", + $["vllm-service-model"], + "--dtype=float16", + "--device=xpu", + "--enforce-eager", + "--port", + "8899", + "--block-size", + "64", + "--gpu-memory-util", + "0.85", + "--trust-remote-code", + "--disable-sliding-window", + ]) + .with_environment({ + HF_TOKEN: $["hf-token"], + VLLM_USE_V1: "1", + W_LONG_MAX_MODEL_LEN: "1", + VLLM_WORKER_MULTIPROC_METHOD: "spawn", + }) + .with_privileged(true) + .with_device("/dev/dri", "/dev/dri") + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_reservations( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_port(8899, 8899, "vllm") + .with_volume_mount(vol, "/data"); + + local containerSet = engine.containers( + "vllm-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(8899, 8899, "vllm"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/vllm-service-nvidia.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/vllm-service-nvidia.jsonnet new file mode 100644 index 00000000..42f23530 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/vllm-service-nvidia.jsonnet @@ -0,0 +1,64 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-service-" + key]:: value, + }, + + "vllm-service-model":: "mistralai/Mistral-7B-Instruct-v0.3", + "vllm-service-cpus":: "0.5", + "vllm-service-memory":: "1G", + + "vllm-service" +: { + + create:: function(engine) + + local vol = engine.volume("vllm-storage").with_size("50G"); + + local container = + engine.container("vllm-service") + .with_image(images["vllm-service-nvidia"]) + .with_command([ + "--model", + $["vllm-service-model"], + "--port", + "8899", + ]) + .with_runtime("nvidia") + .with_environment({ + VLLM_SKIP_WARMUP: "true", + HUGGING_FACE_HUB_TOKEN: $["hf-token"], + VLLM_CACHE_ROOT: "/data", + }) + .with_privileged(true) + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_reservations( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_port(8899, 8899, "vllm") + .with_volume_mount(vol, "/data"); + + local containerSet = engine.containers( + "vllm-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(8899, 8899, "vllm"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/vllm.jsonnet new file mode 100644 index 00000000..63dfe84b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/vllm.jsonnet @@ -0,0 +1,110 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/vllm.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-" + key]:: value, + }, + + "vllm-models":: models, + + "llm-models" +:: $["vllm-models"], + + "vllm-max-output-tokens":: 1024, + "vllm-temperature":: 0.0, + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("vllm-credentials") + .with_env_var("VLLM_BASE_URL", "vllm-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-vllm", + "-p", + url.pulsar, + "--concurrency", + std.toString($["text-completion-concurrency"]), + "-x", + std.toString($["vllm-max-output-tokens"]), + "-t", + "%0.3f" % $["vllm-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("vllm-credentials") + .with_env_var("VLLM_BASE_URL", "vllm-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-vllm", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "--concurrency", + std.toString($["text-completion-rag-concurrency"]), + "-x", + std.toString($["vllm-max-output-tokens"]), + "-t", + "%0.3f" % $["vllm-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/workbench-ui.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/workbench-ui.jsonnet new file mode 100644 index 00000000..f2048e47 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/components/workbench-ui.jsonnet @@ -0,0 +1,32 @@ +local images = import "values/images.jsonnet"; + +{ + + "workbench-ui" +: { + + create:: function(engine) + + local container = + engine.container("workbench-ui") + .with_image(images["workbench-ui"]) + .with_limits("0.1", "256M") + .with_reservations("0.1", "256M") + .with_port(8888, 8888, "ui"); + + local containerSet = engine.containers( + "workbench-ui", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8888, 8888, "ui"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-aks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-aks-k8s.jsonnet new file mode 100644 index 00000000..c603a0d8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-aks-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "engine/aks-k8s.jsonnet"; +local decode = import "util/decode-config.jsonnet"; +local components = import "components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-docker-compose.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-docker-compose.jsonnet new file mode 100644 index 00000000..442d2cb7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-docker-compose.jsonnet @@ -0,0 +1,20 @@ + +local engine = import "engine/docker-compose.jsonnet"; +local decode = import "util/decode-config.jsonnet"; +local components = import "components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resources = std.foldl( + function(state, p) state + p.create(engine), + std.objectValues(patterns), + {} +); + +resources + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-eks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-eks-k8s.jsonnet new file mode 100644 index 00000000..af8f8ed8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-eks-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "engine/eks-k8s.jsonnet"; +local decode = import "util/decode-config.jsonnet"; +local components = import "components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-gcp-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-gcp-k8s.jsonnet new file mode 100644 index 00000000..3d089a24 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-gcp-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "engine/gcp-k8s.jsonnet"; +local decode = import "util/decode-config.jsonnet"; +local components = import "components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-minikube-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-minikube-k8s.jsonnet new file mode 100644 index 00000000..073358cb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-minikube-k8s.jsonnet @@ -0,0 +1,26 @@ + +local engine = import "engine/minikube-k8s.jsonnet"; +local decode = import "util/decode-config.jsonnet"; +local components = import "components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +// Extract resources using the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-noop.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-noop.jsonnet new file mode 100644 index 00000000..cae548f7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-noop.jsonnet @@ -0,0 +1,20 @@ + +local engine = import "engine/noop.jsonnet"; +local decode = import "util/decode-config.jsonnet"; +local components = import "components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resources = std.foldl( + function(state, p) state + p.create(engine), + std.objectValues(patterns), + {} +); + +resources + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-ovh-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-ovh-k8s.jsonnet new file mode 100644 index 00000000..9c18f449 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-ovh-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "engine/ovh-k8s.jsonnet"; +local decode = import "util/decode-config.jsonnet"; +local components = import "components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-podman-compose.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-podman-compose.jsonnet new file mode 100644 index 00000000..442d2cb7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-podman-compose.jsonnet @@ -0,0 +1,20 @@ + +local engine = import "engine/docker-compose.jsonnet"; +local decode = import "util/decode-config.jsonnet"; +local components = import "components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resources = std.foldl( + function(state, p) state + p.create(engine), + std.objectValues(patterns), + {} +); + +resources + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-scw-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-scw-k8s.jsonnet new file mode 100644 index 00000000..98b79254 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-scw-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "engine/scw-k8s.jsonnet"; +local decode = import "util/decode-config.jsonnet"; +local components = import "components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-tg-configuration.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-tg-configuration.jsonnet new file mode 100644 index 00000000..4d4c6711 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config-to-tg-configuration.jsonnet @@ -0,0 +1,15 @@ + +local engine = import "engine/noop.jsonnet"; +local decode = import "util/decode-config.jsonnet"; +local components = import "components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract configuration directly from patterns +patterns.configuration.configuration + + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config/config-composer.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config/config-composer.jsonnet new file mode 100644 index 00000000..0e8db5b0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config/config-composer.jsonnet @@ -0,0 +1,93 @@ +// Configuration Composer Module +// Orchestrates the complete configuration building process +// Combines all components into the final TrustGraph configuration + +local flow_builder = import "flow-builder.jsonnet"; +local interface_builder = import "interface-builder.jsonnet"; + +{ + // Main function to build the complete configuration + build: function(config_spec) + // Extract configuration parameters + local flow_classes = config_spec.flow_classes; + local default_flow_class = config_spec.default_flow_class; + local default_flow_id = config_spec.default_flow_id; + local flow_init_parameters = config_spec.flow_init_parameters; + + // Build all processors for the default flow + local class_processors = flow_builder.build_class_processors( + flow_classes, + default_flow_class, + flow_init_parameters + ); + + local flow_processors = flow_builder.build_flow_processors( + flow_classes, + default_flow_class, + default_flow_id, + flow_init_parameters + ); + + // Combine processors into flow objects + local processor_array = class_processors + flow_processors; + local flow_objects = flow_builder.build_flow_objects(processor_array); + local flows_active = flow_builder.merge_flow_objects(flow_objects); + + // Build interfaces for the default flow + local default_flow_interfaces = interface_builder.build_interfaces( + flow_classes, + default_flow_class, + default_flow_id, + flow_init_parameters + ); + + // Return object with nested configuration (for backwards compatibility) + { + // Create function (for backwards compatibility) + create: function(engine) {}, + + // The actual configuration object + configuration: { + // Prompts configuration + prompt: { + "system": config_spec.prompts["system-template"], + "template-index": std.objectFieldsAll(config_spec.prompts.templates), + } + { + ["template." + template.key]: template.value + for template in std.objectKeysValuesAll(config_spec.prompts.templates) + }, + + // Tools configuration + tool: { + [tool.id]: tool + for tool in config_spec.tools + }, + + // MCP configuration + mcp: config_spec.mcp, + + // Flow classes reference + "flow-classes": flow_classes, + + // Interface descriptions + "interface-descriptions": config_spec.interface_descriptions, + + // Flow instances + "flows": { + [default_flow_id]: { + "description": "Default processing flow", + "class-name": default_flow_class, + "interfaces": default_flow_interfaces, + "parameters": flow_init_parameters, + }, + }, + + // Active flow processors + "flows-active": flows_active, + + // Token costs and parameter types + "token-costs": config_spec.token_costs, + "parameter-types": config_spec.parameter_types, + }, + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config/flow-builder.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config/flow-builder.jsonnet new file mode 100644 index 00000000..eff7c893 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config/flow-builder.jsonnet @@ -0,0 +1,72 @@ +// Flow Builder Module +// Processes flow classes and builds complete flow configurations +// Handles {class}, {id}, and parameter substitutions + +local param_processor = import "parameter-processor.jsonnet"; + +{ + // Builds class-level processors with parameter substitution + // Processes the 'class' section of flow classes + build_class_processors: function(flow_classes, class_name, parameters) + [ + [ + // Replace {class} in the processor key + local key = std.strReplace(processor.key, "{class}", class_name); + local parts = std.splitLimit(key, ":", 2); + parts, + { + // Process each field in the processor configuration + [field.key]: + // First replace {class}, then substitute parameters + local class_replaced = std.strReplace(field.value, "{class}", class_name); + param_processor.substitute_parameters(class_replaced, parameters) + for field in std.objectKeysValuesAll(processor.value) + } + ] + for processor in std.objectKeysValuesAll(flow_classes[class_name].class) + ], + + // Builds flow-level processors with parameter substitution + // Processes the 'flow' section of flow classes + build_flow_processors: function(flow_classes, class_name, flow_id, parameters) + [ + [ + // Replace both {class} and {id} in the processor key + local key = std.strReplace( + std.strReplace(processor.key, "{class}", class_name), + "{id}", flow_id + ); + local parts = std.splitLimit(key, ":", 2); + parts, + { + // Process each field in the processor configuration + [field.key]: + // Replace {class} and {id}, then substitute parameters + local class_replaced = std.strReplace(field.value, "{class}", class_name); + local id_replaced = std.strReplace(class_replaced, "{id}", flow_id); + param_processor.substitute_parameters(id_replaced, parameters) + for field in std.objectKeysValuesAll(processor.value) + } + ] + for processor in std.objectKeysValuesAll(flow_classes[class_name].flow) + ], + + // Combines class and flow processors into flow objects + build_flow_objects: function(processor_array) + std.map( + function(item) { + [item[0][0]] +: { + [item[0][1]]: item[1] + } + }, + processor_array + ), + + // Merges all flow objects into a single flows_active configuration + merge_flow_objects: function(flow_objects) + std.foldr( + function(a, b) a + b, + flow_objects, + {} + ), +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config/interface-builder.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config/interface-builder.jsonnet new file mode 100644 index 00000000..2143e600 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config/interface-builder.jsonnet @@ -0,0 +1,30 @@ +// Interface Builder Module +// Processes flow class interfaces with parameter substitution +// Handles both string interfaces and nested object interfaces + +local param_processor = import "parameter-processor.jsonnet"; + +{ + // Builds interfaces for a specific flow class and instance + // Processes the 'interfaces' section of flow classes + build_interfaces: function(flow_classes, class_name, flow_id, parameters) + local interface_spec = flow_classes[class_name].interfaces; + { + [interface.key]: + if std.isString(interface.value) then + // Simple string interface - apply all substitutions + local class_replaced = std.strReplace(interface.value, "{class}", class_name); + local id_replaced = std.strReplace(class_replaced, "{id}", flow_id); + param_processor.substitute_parameters(id_replaced, parameters) + else + // Complex object interface - process nested fields + { + [field.key]: + local class_replaced = std.strReplace(field.value, "{class}", class_name); + local id_replaced = std.strReplace(class_replaced, "{id}", flow_id); + param_processor.substitute_parameters(id_replaced, parameters) + for field in std.objectKeysValuesAll(interface.value) + } + for interface in std.objectKeysValuesAll(interface_spec) + }, +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config/interface-descriptions.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config/interface-descriptions.jsonnet new file mode 100644 index 00000000..f9dda8b0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config/interface-descriptions.jsonnet @@ -0,0 +1,89 @@ +// Interface Descriptions Module +// Defines all external interfaces available in TrustGraph flows +// These are the 'endpoints' that external systems can interact with + +{ + // Document loading interfaces - for data ingestion + "document-load": { + "description": "Document loader", + "kind": "send", + "visible": true, + }, + "text-load": { + "description": "Text document loader", + "kind": "send", + "visible": true, + }, + + // Data storage interfaces - for processed data streams + "entity-contexts-load": { + "description": "Entity contexts loader", + "kind": "send", + }, + "triples-store": { + "description": "Triples loader", + "kind": "send", + }, + "graph-embeddings-store": { + "description": "Graph embeddings loader", + "kind": "send", + }, + "document-embeddings-store": { + "description": "Document embeddings loader", + "kind": "send", + }, + "objects-store": { + "description": "Object store", + "kind": "request-response", + }, + + // Query interfaces - for retrieving information + "graph-rag": { + "description": "GraphRAG service", + "kind": "request-response", + }, + "document-rag": { + "description": "ChunkRAG service", + "kind": "request-response", + }, + "triples": { + "description": "Triples query service", + "kind": "request-response", + }, + "graph-embeddings": { + "description": "Graph embeddings service", + "kind": "request-response", + }, + "document-embeddings": { + "description": "Document embeddings service", + "kind": "request-response", + }, + "objects": { + "description": "Object query service", + "kind": "request-response", + }, + + // Processing services - for text and data processing + "prompt": { + "description": "Prompt service", + "kind": "request-response", + }, + "agent": { + "description": "Agent service", + "kind": "request-response", + }, + "text-completion": { + "description": "Text completion service", + "kind": "request-response", + }, + + // Query translation services - for natural language queries + "nlp-query": { + "description": "NLP question to GraphQL service", + "kind": "request-response", + }, + "structured-query": { + "description": "Structured query service", + "kind": "request-response", + }, +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config/parameter-processor.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config/parameter-processor.jsonnet new file mode 100644 index 00000000..f317ae9f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config/parameter-processor.jsonnet @@ -0,0 +1,38 @@ +// Parameter Processing Module +// Handles dynamic parameter replacement in configuration values +// Replaces {parameter_name} placeholders with actual parameter values + +{ + // Applies parameter substitutions to string values + // Only processes strings - leaves other types unchanged + substitute_parameters: function(value, parameters) + if std.isString(value) then + std.foldl( + function(acc, param) + // Only do string replacement if param.value is a string + if std.isString(param.value) then + std.strReplace(acc, "{" + param.key + "}", param.value) + else + acc, // Skip replacement for non-string parameter values + std.objectKeysValuesAll(parameters), + value + ) + else + value, + + // Applies parameter substitutions to all values in an object + // Recursively processes nested objects and arrays + substitute_parameters_in_object: function(obj, parameters) + if std.isObject(obj) then + { + [key]: $.substitute_parameters_in_object(obj[key], parameters) + for key in std.objectFields(obj) + } + else if std.isArray(obj) then + [ + $.substitute_parameters_in_object(item, parameters) + for item in obj + ] + else + $.substitute_parameters(obj, parameters), +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config/tools.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config/tools.jsonnet new file mode 100644 index 00000000..a84cd0bb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/config/tools.jsonnet @@ -0,0 +1,52 @@ +// Tools Configuration Module +// Defines all available tools that can be used by agents and flows +// Each tool specifies its interface, arguments, and behavior + +[ + // Knowledge extraction tool - extracts structured knowledge from text + { + id: "knowledge-extraction", + name: "Knowledge extraction", + description: "Takes a chunk of text and extracts knowledge in definition and relationship formats. The input is a text chunk", + type: "prompt", + template: "agent-kg-extract", + arguments: [ + { + "name": "text", + "type": "string", + "description": "The text chunk", + } + ], + }, + + // Knowledge query tool - queries the knowledge base + { + id: "knowledge-query", + name: "Knowledge query", + description: "This tool queries a knowledge base that holds information about domain-specific information. The question should be a natural language question.", + type: "knowledge-query", + collection: "default", + arguments: [ + { + name: "question", + type: "string", + description: "A simple natural language question.", + } + ] + }, + + // LLM completion tool - general purpose text completion + { + id: "llm-completion", + name: "LLM text completion", + type: "text-completion", + description: "This tool queries an LLM for non-domain-specific information. The question should be a natural language question.", + arguments: [ + { + name: "question", + type: "string", + description: "The question which should be asked of the LLM.", + } + ] + } +] \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/engine/aks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/engine/aks-k8s.jsonnet new file mode 100644 index 00000000..07e050f2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/engine/aks-k8s.jsonnet @@ -0,0 +1,45 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "disk.csi.azure.com", + parameters: { + // Standard disks (spinning magnetic), Locally Redundant Storage + // Cheapest, basically + skuName: "Standard_LRS", + }, + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/engine/docker-compose.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/engine/docker-compose.jsonnet new file mode 100644 index 00000000..954cde15 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/engine/docker-compose.jsonnet @@ -0,0 +1,230 @@ +{ + + // Extract resources using the engine + package:: function(patterns) + std.foldl( + function(state, p) state + p.create(self), + std.objectValues(patterns), + {} + ), + + container:: function(name) + { + + local container = self, + + name:: name, + + with_image:: function(x) self + { image: x }, + + with_user:: function(x) self + { user: x }, + + with_command:: function(x) self + { command: x }, + + with_runtime:: function(x) self + { runtime: x }, + + with_privileged:: function(x) self + { privileged: x }, + + with_ipc:: function(x) self + { ipc: x }, + + with_capability:: function(x) self + + if std.objectHas(container, "capability") then + { cap_add: container.capability + x } + else + { cap_add: [x], }, + + with_environment:: function(x) self + + if std.objectHas(container, "environment") then + { environment: container.environment + x } + else + { environment: x, }, + + with_device:: function(hdev, cdev) self + + if std.objectHas(container, "devices") then + { devices: container.devices + "%s:%s" % [hdev, cdev] } + else + { devices: [ "%s:%s" % [hdev, cdev] ], }, + + with_limits:: function(c, m) self + { + deploy +: { resources +: { + limits: { cpus: c, memory: m } + } }, + }, + + with_reservations:: function(c, m) self + { + deploy +: { resources +: { + reservations: { cpus: c, memory: m } + } }, + }, + + with_volume_mount:: + function(vol, mnt) + self + { + volumes: + if std.objectHas(container, "volumes") then + container.volumes + [ + "%s:%s" % [vol.volid, mnt] + ] + else + [ + "%s:%s" % [vol.volid, mnt] + ] + }, + + with_port:: + function(src, dest, name) + self + { + ports: + if std.objectHas(container, "ports") then + container.ports + [ "%d:%d" % [src, dest] ] + else + [ "%d:%d" % [src, dest] ] + }, + + with_env_var_secrets:: + function(vars) + std.foldl( + function(obj, x) obj.with_environment( + { [x]: "${" + x + "}" } + ), + vars.variables, + self + ), + + restart: "on-failure:100", + + add:: function() { + services +: { + [container.name]: container, + } + } + + }, + + internalService:: function(containers) + { + + local service = self, + + name: containers.name, + + with_port:: function(src, dest, name) + self + { port: [src, dest] }, + + add:: function() { + } + + }, + + service:: function(containers) + { + + local service = self, + + name: containers.name, + + with_port:: function(src, dest, name) + self + { port: [src, dest] }, + + add:: function() { + } + + }, + + volume:: function(name) + { + + local volume = self, + + name: name, + + volid:: name, + + with_size:: function(size) self + { size: size }, + + add:: function() { + volumes +: { + [volume.name]: {} + } + } + + }, + + configVolume:: function(name, dir, parts) + { + + local volume = self, + + name: dir, + + volid:: "./" + dir, + + with_size:: function(size) self + { size: size }, + + add:: function() { + } + + }, + + secretVolume:: function(name, dir, parts) + { + + local volume = self, + + name: dir, + + volid:: dir, + + with_size:: function(size) self + { size: size }, + + add:: function() { + } + + }, + + envSecrets:: function(name) + { + + local volume = self, + + name: name, + + volid:: name, + + variables:: [], + + with_env_var:: + function(name, key) self + { + variables: super.variables + [name], + }, + + add:: function() { + } + + }, + + containers:: function(name, containers) + { + + local cont = self, + + name: name, + containers: containers, + + add:: function() std.foldl( + function(state, c) state + c.add(), + cont.containers, + {} + ), + + }, + + resources:: function(res) + std.foldl( + function(state, c) state + c.add(), + res, + {} + ), + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/engine/eks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/engine/eks-k8s.jsonnet new file mode 100644 index 00000000..3fc3f035 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/engine/eks-k8s.jsonnet @@ -0,0 +1,46 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "ebs.csi.aws.com", + parameters: { + type: "gp3", + encrypted: "true", + iops: "6000", + throughput: "400", + }, + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/engine/gcp-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/engine/gcp-k8s.jsonnet new file mode 100644 index 00000000..71792426 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/engine/gcp-k8s.jsonnet @@ -0,0 +1,44 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "pd.csi.storage.gke.io", + parameters: { + type: "pd-balanced", + "csi.storage.k8s.io/fstype": "ext4", + }, + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/engine/k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/engine/k8s.jsonnet new file mode 100644 index 00000000..686f2029 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/engine/k8s.jsonnet @@ -0,0 +1,358 @@ +{ + + container:: function(name) + { + + local container = self, + + name: name, + limits: {}, + reservations: {}, + ports: [], + volumes: [], + environment: [], + + with_image:: function(x) self + { image: x }, + + with_user:: function(x) self + { user: x }, + + with_command:: function(x) self + { command: x }, + + with_environment:: function(x) self + { + environment: super.environment + [ + { + name: v.key, value: v.value + } + for v in std.objectKeysValues(x) + ], + }, + + with_limits:: function(c, m) self + { limits: { cpu: c, memory: m } }, + + with_reservations:: + function(c, m) self + { reservations: { cpu: c, memory: m } }, + + with_volume_mount:: + function(vol, mnt) + self + { + volumes: super.volumes + [{ + volume: vol, mount: mnt + }] + }, + + with_port:: + function(src, dest, name) self + { + ports: super.ports + [ + { src: src, dest: dest, name : name } + ] + }, + + with_env_var_secrets:: + function(vars) + std.foldl( + function(obj, x) obj + { + environment: super.environment + [{ + name: x, + valueFrom: { + secretKeyRef: { + name: vars.name, + key: vars.keyMap[x], + } + } + }] + }, + vars.variables, + self + ), + + add:: function() [ + + { + apiVersion: "apps/v1", + kind: "Deployment", + metadata: { + name: container.name, + namespace: "trustgraph", + labels: { + app: container.name + } + }, + spec: { + replicas: 1, + selector: { + matchLabels: { + app: container.name, + } + }, + template: { + metadata: { + labels: { + app: container.name, + } + }, + spec: { + containers: [ + { + name: container.name, + image: container.image, + + // FIXME: Make everything run as + // root. Needed to get filesystems + // to be accessible. There's a + // better way of doing this? + securityContext: { + runAsUser: 0, + runAsGroup: 0, + }, + + resources: { + requests: container.reservations, + limits: container.limits + }, + } + ( + if std.length(container.ports) > 0 then + { + ports: [ + { + hostPort: port.src, + containerPort: port.dest, + } + for port in container.ports + ] + } else + {}) + + + (if std.objectHas(container, "command") then + { command: container.command } + else {}) + + + (if std.length(container.environment) > 0 then + { + env: container.environment, + } + else {}) + + + (if std.length(container.volumes) > 0 then + { + volumeMounts: [ + { + mountPath: vol.mount, + name: vol.volume.name, + } + for vol in container.volumes + ] + } + + else + {} + ) + ], + volumes: [ + vol.volume.volRef() + for vol in container.volumes + + ] + } + }, + } + {} + + } + + ] + + }, + + // Just an alias + internalService:: self.service, + + service:: function(containers) + { + + local service = self, + + name: containers.name, + + ports: [], + + with_port:: + function(src, dest, name) + self + { + ports: super.ports + [ + { src: src, dest: dest, name: name } + ] + }, + + add:: function() [ + + { + + apiVersion: "v1", + kind: "Service", + metadata: { + name: service.name, + namespace: "trustgraph", + }, + spec: { + selector: { + app: service.name, + }, + ports: [ + { + port: port.src, + targetPort: port.dest, + name: port.name, + } + for port in service.ports + ], + } + } + ], + + }, + + volume:: function(name) + { + + local volume = self, + + name: name, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + { + apiVersion: "v1", + kind: "PersistentVolumeClaim", + metadata: { + name: volume.name, + namespace: "trustgraph", + }, + spec: { + storageClassName: "tg", + accessModes: [ "ReadWriteOnce" ], + resources: { + requests: { + storage: volume.size, + } + }, + } + } + ], + + volRef:: function() { + name: volume.name, + persistentVolumeClaim: { claimName: volume.name }, + } + + }, + + configVolume:: function(name, dir, parts) + { + + local volume = self, + + name: name, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + { + apiVersion: "v1", + kind: "ConfigMap", + metadata: { + name: volume.name, + namespace: "trustgraph", + }, + data: parts + }, + ], + + + volRef:: function() { + name: volume.name, + configMap: { name: volume.name }, + } + + }, + + secretVolume:: function(name, dir, parts) + { + + local volume = self, + + name: name, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + { + apiVersion: "v1", + kind: "Secret", + metadata: { + name: volume.name, + namespace: "trustgraph", + }, + data: { + [item.key]: std.base64(item.value) + for item in std.objectKeysValues(parts) + } + }, + ], + + volRef:: function() { + name: volume.name, + secret: { secretName: volume.name }, + } + + }, + + envSecrets:: function(name) + { + + local volume = self, + + name: name, + + variables: [], + keyMap: {}, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + ], + + volRef:: function() { + name: volume.name, + secret: { secretName: volume.name }, + }, + + with_env_var:: + function(name, key) self + { + variables: super.variables + [name], + keyMap: super.keyMap + { [name]: key }, + }, + + }, + + containers:: function(name, containers) + { + + local cont = self, + + name: name, + containers: containers, + + add:: function() std.flattenArrays( + [ c.add() for c in cont.containers ] + ), + + }, + + resources:: function(res) + + std.flattenArrays( + [ c.add() for c in res ] + ), + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/engine/minikube-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/engine/minikube-k8s.jsonnet new file mode 100644 index 00000000..858b17ad --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/engine/minikube-k8s.jsonnet @@ -0,0 +1,115 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList, + + volume:: function(name) + { + local volume = self, + name: name, + with_size:: function(size) self + { size: size }, + add:: function() [ + { + apiVersion: "v1", + kind: "PersistentVolume", + metadata: { + name: volume.name, + }, + spec: { + accessModes: [ "ReadWriteOnce" ], + capacity: { + storage: volume.size, + }, + persistentVolumeReclaimPolicy: "Delete", + hostPath: { + path: "/data/pv-" + volume.name, + }, + } + }, + { + apiVersion: "v1", + kind: "PersistentVolumeClaim", + metadata: { + name: volume.name, + namespace: "trustgraph", + }, + spec: { + accessModes: [ "ReadWriteOnce" ], + resources: { + requests: { + storage: volume.size, + } + }, + } + } + ], + + volRef:: function() { + name: volume.name, + persistentVolumeClaim: { claimName: volume.name }, + } + + }, + + service:: function(containers) + { + local service = self, + name: containers.name, + ports: [], + with_port:: + function(src, dest, name) + self + { + ports: super.ports + [ + { src: src, dest: dest, name: name } + ] + }, + add:: function() [ + { + apiVersion: "v1", + kind: "Service", + metadata: { + name: service.name, + namespace: "trustgraph", + }, + spec: { + selector: { + app: service.name, + }, + type: "LoadBalancer", + ports: [ + { + port: port.src, + targetPort: port.dest, + name: port.name, + } + for port in service.ports + ], + } + } + ], + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/engine/noop.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/engine/noop.jsonnet new file mode 100644 index 00000000..1f384648 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/engine/noop.jsonnet @@ -0,0 +1,79 @@ +{ + + // Extract resources usnig the engine + package:: function(patterns) {}, + + container:: function(name) { + + with_image:: function(x) self + {}, + + with_user:: function(x) self + {}, + + with_command:: function(x) self + {}, + + with_runtime:: function(x) self + {}, + + with_privileged:: function(x) self + {}, + + with_ipc:: function(x) self + {}, + + with_capability:: function(x) self + {}, + + with_environment:: function(x) self + {}, + + with_device:: function(hdev, cdev) self + {}, + + with_limits:: function(c, m) self + {}, + + with_reservations:: function(c, m) self + {}, + + with_volume_mount:: self + {}, + + with_port:: function(src, dest, name) self + {}, + + with_env_var_secrets:: function(vars) self + {}, + + add:: function() {}, + }, + + internalService:: function(containers) { + with_port:: function(src, dest, name) self + {}, + add:: function() {}, + }, + + service:: function(containers) { + with_port:: function(src, dest, name) self + {}, + add:: function() {}, + }, + + volume:: function(name) { + with_size:: function(size) self + {}, + add:: function() {}, + }, + + configVolume:: function(name, dir, parts) { + add:: function() {}, + }, + + secretVolume:: function(name, dir, parts) { + add:: function() {}, + }, + + envSecrets:: function(name) { + with_env_var:: function(name, key) self + {}, + add:: function() {}, + }, + + containers:: function(name, containers) { + add:: function() {}, + }, + + resources:: function(res) + std.foldl( + function(state, c) state + c.add(), + res, + {} + ), + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/engine/ovh-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/engine/ovh-k8s.jsonnet new file mode 100644 index 00000000..15d57c83 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/engine/ovh-k8s.jsonnet @@ -0,0 +1,45 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "cinder.csi.openstack.org", + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", + parameters: { + availability: "nova", + fsType: "ext4", + type: "high-speed", + }, +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: [ns, sc] + resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/engine/scw-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/engine/scw-k8s.jsonnet new file mode 100644 index 00000000..eed23968 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/engine/scw-k8s.jsonnet @@ -0,0 +1,40 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "csi.scaleway.com", + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: [ns, sc] + resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/agent-extract.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/agent-extract.jsonnet new file mode 100644 index 00000000..143e736a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/agent-extract.jsonnet @@ -0,0 +1,35 @@ +// Agent-based extraction module +// Uses AI agents for more sophisticated knowledge extraction from text +// Leverages agent tools and reasoning for complex extraction tasks + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; + +{ + // No external interfaces - internal agent extraction service + "interfaces" +: { + }, + + // No configurable parameters for agent extraction + "parameters" +: { + }, + + // Flow-level processors for agent-based extraction + "flow" +: { + // Agent-based knowledge extraction processor + // Uses AI agents with tools to extract structured knowledge + "kg-extract-agent:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output knowledge triples + "entity-contexts": flow("entity-contexts-load:{id}"), // Entity context information + "agent-request": request("agent:{id}"), // Agent service requests + "agent-response": response("agent:{id}"), // Agent service responses + }, + }, + + // No class-level processors needed + "class" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/agent.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/agent.jsonnet new file mode 100644 index 00000000..19afe660 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/agent.jsonnet @@ -0,0 +1,59 @@ +// Agent management module +// Provides AI agent orchestration and tool integration +// Manages agent conversations, tool calls, and response coordination +// Supports MCP tools, GraphRAG, and structured queries + +local helpers = import "helpers.jsonnet"; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +{ + // External interfaces for agent operations + "interfaces" +: { + "agent": request_response("agent:{id}"), // Main agent service interface + "mcp-tool": request_response("mcp-tool:{id}"), // MCP tool execution interface + }, + + // No configurable parameters for agent management + "parameters" +: { + }, + + // Flow-level processors for agent management + "flow" +: { + // Agent manager orchestrates agent conversations and tool usage + "agent-manager:{id}": { + // Agent communication channels + request: request("agent:{id}"), // Incoming agent requests + next: request("agent:{id}"), // Multi-turn conversation support + response: response("agent:{id}"), // Agent responses + + // LLM and prompt services + "text-completion-request": request("text-completion:{id}"), // LLM requests + "text-completion-response": response("text-completion:{id}"), // LLM responses + "prompt-request": request("prompt:{id}"), // Prompt processing + "prompt-response": response("prompt:{id}"), + + // Tool integrations + "mcp-tool-request": request("mcp-tool:{id}"), // MCP tool calls + "mcp-tool-response": response("mcp-tool:{id}"), + "graph-rag-request": request("graph-rag:{id}"), // GraphRAG queries + "graph-rag-response": response("graph-rag:{id}"), + "structured-query-request": request("structured-query:{id}"), // Structured data queries + "structured-query-response": response("structured-query:{id}"), + }, + + // MCP tool executor for agent tool usage + "mcp-tool:{id}": { + request: request("mcp-tool:{id}"), // Tool invocation requests + response: response("mcp-tool:{id}"), // Tool execution results + "text-completion-request": request("text-completion:{id}"), // LLM for tool reasoning + "text-completion-response": response("text-completion:{id}"), + }, + + }, + + // Class-level processors for agent-related services + "class" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/documentrag.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/documentrag.jsonnet new file mode 100644 index 00000000..65910684 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/documentrag.jsonnet @@ -0,0 +1,100 @@ +// Document RAG (Retrieval Augmented Generation) module +// Implements document-based RAG using chunk embeddings +// Provides semantic search and context-aware question answering +// Supports MCP (Model Context Protocol) tool integration + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; +local llm_parameters = import "llm-parameters.jsonnet"; + +{ + // External interfaces for document RAG functionality + "interfaces" +: { + // Document embedding storage and retrieval + "document-embeddings-store": flow("document-embeddings-store:{id}"), // Embedding storage stream + "document-rag": request_response("document-rag:{id}"), // Main document RAG interface + "document-embeddings": request_response("document-embeddings:{id}"), // Document embedding queries + + // Supporting services + "embeddings": request_response("embeddings:{id}"), // General embedding service + "prompt": request_response("prompt:{id}"), // Prompt processing + "mcp-tool": request_response("mcp-tool:{id}"), // MCP tool integration + "text-completion": request_response("text-completion:{id}"), // LLM text completion + }, + + // Parameters that can be configured for this flow + "parameters" +: llm_parameters, + + // Flow-level processors for document embedding and storage + "flow" +: { + "document-embeddings:{id}": { + input: flow("chunk-load:{id}"), + output: flow("document-embeddings-store:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + }, + "de-write:{id}": { + input: flow("document-embeddings-store:{id}"), + }, + "text-completion:{id}": { + request: request("text-completion:{id}"), + response: response("text-completion:{id}"), + model: "{llm-model}", + }, + "text-completion-rag:{id}": { + request: request("text-completion-rag:{id}"), + response: response("text-completion-rag:{id}"), + model: "{llm-rag-model}", + }, + "embeddings:{id}": { + request: request("embeddings:{id}"), + response: response("embeddings:{id}"), + model: "{embeddings-model}", + }, + "document-rag:{id}": { + request: request("document-rag:{id}"), + response: response("document-rag:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + "prompt-request": request("prompt-rag:{id}"), + "prompt-response": response("prompt-rag:{id}"), + "document-embeddings-request": request("document-embeddings:{id}"), + "document-embeddings-response": response("document-embeddings:{id}"), + }, + "de-query:{id}": { + request: request("document-embeddings:{id}"), + response: response("document-embeddings:{id}"), + }, + "prompt:{id}": { + request: request("prompt:{id}"), + response: response("prompt:{id}"), + "text-completion-request": request("text-completion:{id}"), + "text-completion-response": response("text-completion:{id}"), + }, + "prompt-rag:{id}": { + request: request("prompt-rag:{id}"), + response: response("prompt-rag:{id}"), + "text-completion-request": request("text-completion-rag:{id}"), + "text-completion-response": response("text-completion-rag:{id}"), + }, + "mcp-tool:{id}": { + request: request("mcp-tool:{id}"), + response: response("mcp-tool:{id}"), + "text-completion-request": request("text-completion:{id}"), + "text-completion-response": response("text-completion:{id}"), + }, + "metering:{id}": { + input: response("text-completion:{id}"), + }, + "metering-rag:{id}": { + input: response("text-completion-rag:{id}"), + }, + }, + + // Class-level processors for document RAG operations + "class" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/flow-classes.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/flow-classes.jsonnet new file mode 100644 index 00000000..1a1adabe --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/flow-classes.jsonnet @@ -0,0 +1,105 @@ +// TrustGraph Flow Classes Configuration +// Defines different flow combinations for various use cases +// Each flow class combines multiple functional modules to create complete processing pipelines +// +// Available modules: +// - graphrag: Graph-based RAG with knowledge graphs +// - documentrag: Document-based RAG with chunk embeddings +// - structured: Structured data processing and NLP queries +// - agent: AI agent orchestration and tool integration +// - load: Document loading and preprocessing +// - kg-base: Basic knowledge extraction from text +// - agent-extract: Agent-based knowledge extraction +// - kgcore: Knowledge graph core storage + +// Import all the modular flow components +local graphrag_part = import "graphrag.jsonnet"; +local kg_base_part = import "kg-base.jsonnet"; +local onto_base_part = import "onto-base.jsonnet"; +local agent_extract_part = import "agent-extract.jsonnet"; +local structured_part = import "structured.jsonnet"; +local documentrag_part = import "documentrag.jsonnet"; +local agent_part = import "agent.jsonnet"; +local load_part = import "load.jsonnet"; +local kgcore_part = import "kgcore.jsonnet"; + +{ + + // Complete TrustGraph system with all capabilities + // Includes GraphRAG, DocumentRAG, structured data processing, and knowledge cores + "everything": { + description: "GraphRAG, DocumentRAG, structured data + knowledge cores", + tags: [ + "document-rag", "graph-rag", "knowledge-extraction", + "structured-data", "kgcore" + ], + } + + graphrag_part + documentrag_part + agent_part + load_part + + kg_base_part + structured_part, + + // Dual RAG system without knowledge core creation + // Combines both document and graph-based retrieval + "document-rag+graph-rag": { + description: "Supports GraphRAG and document RAG, no core creation", + tags: ["document-rag", "graph-rag", "knowledge-extraction"], + } + + graphrag_part + documentrag_part + agent_part + load_part + kg_base_part, + + // Graph-based RAG only + // Uses knowledge graphs for context-aware question answering + "graph-rag": { + description: "GraphRAG only", + tags: ["graph-rag", "knowledge-extraction"], + } + + graphrag_part + agent_part + load_part + kg_base_part, + + // Graph-based RAG only + // Uses knowledge graphs for context-aware question answering + "onto-rag": { + description: "Ontology RAG only", + tags: ["graph-rag", "knowledge-extraction"], + } + + graphrag_part + agent_part + load_part + onto_base_part, + + // Document-based RAG only + // Uses document embeddings for semantic search and answers + "document-rag": { + description: "DocumentRAG only", + tags: ["document-rag"], + } + + documentrag_part + load_part, + + // Full RAG system with knowledge core creation + // Includes both RAG types plus persistent knowledge storage + "document-rag+graph-rag+kgcore": { + description: "GraphRAG + DocumentRAG + knowledge core creation", + tags: ["document-rag", "graph-rag", "knowledge-extraction"], + } + + graphrag_part + documentrag_part + agent_part + load_part + + kgcore_part + kg_base_part, + + // GraphRAG with advanced agent-based extraction + // Uses AI agents for sophisticated knowledge extraction + "graph-rag+agent-extract": { + description: "GraphRAG + agent extract", + tags: ["graph-rag", "knowledge-extraction", "agent-extract"], + } + + graphrag_part + agent_part + load_part + agent_extract_part, + + // GraphRAG with structured data processing + // Combines knowledge graphs with structured data queries + "graph-rag+structured-data": { + description: "GraphRAG + structured data", + tags: ["graph-rag", "knowledge-extraction", "structured-data"], + } + + graphrag_part + agent_part + load_part + structured_part, + + // Structured data processing only + // Handles structured data extraction and NLP queries + "structured-data": { + description: "Structured data only", + tags: ["knowledge-extraction", "structured-data"], + } + + agent_part + load_part + structured_part, + +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/graphrag.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/graphrag.jsonnet new file mode 100644 index 00000000..94cf5750 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/graphrag.jsonnet @@ -0,0 +1,107 @@ +// GraphRAG flow configuration module +// Implements graph-based retrieval augmented generation (GraphRAG) functionality +// Handles knowledge graph storage, embeddings, and graph-based question answering + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; +local llm_parameters = import "llm-parameters.jsonnet"; + +{ + // External interfaces exposed by the GraphRAG flow + "interfaces" +: { + // Data ingestion interfaces for graph construction + "entity-contexts-load": flow("entity-contexts-load:{id}"), // Entity context data stream + "triples-store": flow("triples-store:{id}"), // RDF triples storage stream + "graph-embeddings-store": flow("graph-embeddings-store:{id}"), // Graph embedding storage + + // Query interfaces for graph-based operations + "graph-rag": request_response("graph-rag:{id}"), // Main GraphRAG query interface + "triples": request_response("triples:{id}"), // Triple store queries + "graph-embeddings": request_response("graph-embeddings:{id}"), // Graph embedding queries + + // Supporting services + "embeddings": request_response("embeddings:{id}"), // General embedding service + "prompt": request_response("prompt:{id}"), // Prompt processing service + "text-completion": request_response("text-completion:{id}"), // LLM text completion + }, + + + // Parameters that can be configured for this flow + "parameters" +: llm_parameters, + + // Flow-level processors - handle data streams for a specific flow instance + "flow" +: { + "graph-embeddings:{id}": { + input: flow("entity-contexts-load:{id}"), + output: flow("graph-embeddings-store:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + }, + "triples-write:{id}": { + input: flow("triples-store:{id}"), + }, + "ge-write:{id}": { + input: flow("graph-embeddings-store:{id}"), + }, + "text-completion:{id}": { + request: request("text-completion:{id}"), + response: response("text-completion:{id}"), + model: "{llm-model}", + }, + "text-completion-rag:{id}": { + request: request("text-completion-rag:{id}"), + response: response("text-completion-rag:{id}"), + model: "{llm-rag-model}", + }, + "embeddings:{id}": { + request: request("embeddings:{id}"), + response: response("embeddings:{id}"), + model: "{embeddings-model}", + }, + "graph-rag:{id}": { + request: request("graph-rag:{id}"), + response: response("graph-rag:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + "prompt-request": request("prompt-rag:{id}"), + "prompt-response": response("prompt-rag:{id}"), + "graph-embeddings-request": request("graph-embeddings:{id}"), + "graph-embeddings-response": response("graph-embeddings:{id}"), + "triples-request": request("triples:{id}"), + "triples-response": response("triples:{id}"), + }, + "triples-query:{id}": { + request: request("triples:{id}"), + response: response("triples:{id}"), + }, + "ge-query:{id}": { + request: request("graph-embeddings:{id}"), + response: response("graph-embeddings:{id}"), + }, + "prompt:{id}": { + request: request("prompt:{id}"), + response: response("prompt:{id}"), + "text-completion-request": request("text-completion:{id}"), + "text-completion-response": response("text-completion:{id}"), + }, + "prompt-rag:{id}": { + request: request("prompt-rag:{id}"), + response: response("prompt-rag:{id}"), + "text-completion-request": request("text-completion-rag:{id}"), + "text-completion-response": response("text-completion-rag:{id}"), + }, + "metering:{id}": { + input: response("text-completion:{id}"), + }, + "metering-rag:{id}": { + input: response("text-completion-rag:{id}"), + }, + }, + + // Class-level processors - shared across all flow instances of this class + "class" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/helpers.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/helpers.jsonnet new file mode 100644 index 00000000..eabb3bf4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/helpers.jsonnet @@ -0,0 +1,29 @@ +// Helper functions for flow configuration +// Provides utility functions for constructing flow, request, and response URIs +// used throughout the TrustGraph flow configuration system + +// Creates a persistent flow URI for data streams +// Persistent flows retain messages until consumed +local flow(x) = "persistent://tg/flow/" + x; + +// Creates a non-persistent request URI for request-response patterns +// Non-persistent means messages are not retained if no consumer is present +local request(x) = "non-persistent://tg/request/" + x; + +// Creates a non-persistent response URI for request-response patterns +local response(x) = "non-persistent://tg/response/" + x; + +// Creates a request-response pair for bidirectional communication +// Returns an object with both request and response URIs +local request_response(x) = { + request: request(x), + response: response(x), +}; + +// Export all helper functions for use in other modules +{ + flow: flow, + request: request, + response: response, + request_response: request_response, +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/kg-base.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/kg-base.jsonnet new file mode 100644 index 00000000..215dd791 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/kg-base.jsonnet @@ -0,0 +1,44 @@ +// Knowledge Graph Base extraction module +// Provides basic knowledge extraction capabilities from text chunks +// Extracts entity definitions and relationships using prompt-based processing + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; + +{ + // No external interfaces - this module provides internal extraction services + "interfaces" +: { + }, + + // No configurable parameters for basic KG extraction + "parameters" +: { + }, + + // Flow-level processors for knowledge extraction + "flow" +: { + // Extracts entity definitions from text chunks + // Identifies and defines key entities mentioned in the text + "kg-extract-definitions:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output definition triples + "entity-contexts": flow("entity-contexts-load:{id}"), // Entity context information + "prompt-request": request("prompt:{id}"), // Definition extraction prompts + "prompt-response": response("prompt:{id}"), + }, + + // Extracts relationships between entities + // Identifies how entities are connected and interact + "kg-extract-relationships:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output relationship triples + "prompt-request": request("prompt:{id}"), // Relationship extraction prompts + "prompt-response": response("prompt:{id}"), + }, + }, + + // No class-level processors needed + "class" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/kgcore.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/kgcore.jsonnet new file mode 100644 index 00000000..4a857e66 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/kgcore.jsonnet @@ -0,0 +1,31 @@ +// Knowledge Graph Core storage module +// Handles persistent storage of knowledge graph data +// Consolidates triples and graph embeddings into permanent storage +// Creates the core knowledge base for long-term use + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; + +{ + // No external interfaces - internal storage service + "interfaces" +: { + }, + + // No configurable parameters for core storage + "parameters" +: { + }, + + // Flow-level processors for knowledge graph storage + "flow" +: { + // Knowledge graph store consolidates extracted knowledge + // Takes processed triples and embeddings and stores them permanently + "kg-store:{id}": { + "triples-input": flow("triples-store:{id}"), // Input RDF triples stream + "graph-embeddings-input": flow("graph-embeddings-store:{id}"), // Input graph embeddings + }, + }, + + // No class-level processors needed + "class" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/llm-parameters.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/llm-parameters.jsonnet new file mode 100644 index 00000000..a99f72ae --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/llm-parameters.jsonnet @@ -0,0 +1,59 @@ +{ + + // LLM model selection for normal LLM + "llm-model": { + "type": "llm-model", + "description": "LLM model", + "order": 1, + "advanced": false, + }, + + // LLM model for RAG operations + "llm-rag-model": { + "type": "llm-model", + "description": "LLM model for RAG", + "order": 2, + "advanced": true, + "controlled-by": "llm-model", + }, + + // LLM model selection for normal LLM + "llm-temperature": { + "type": "llm-temperature", + "description": "LLM temperature", + "order": 3, + "advanced": true, + }, + + // LLM model selection for normal LLM + "llm-rag-temperature": { + "type": "llm-temperature", + "description": "LLM temperature for RAG", + "order": 4, + "advanced": true, + }, + + "embeddings-model": { + "type": "embeddings-model", + "description": "Embeddings model", + "order": 5, + "advanced": true, + }, + + // LLM model selection for normal LLM + "chunk-size": { + "type": "chunk-size", + "description": "Chunk size", + "order": 6, + "advanced": true, + }, + + // LLM model selection for normal LLM + "chunk-overlap": { + "type": "chunk-overlap", + "description": "Chunk overlap", + "order": 7, + "advanced": true, + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/load.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/load.jsonnet new file mode 100644 index 00000000..eacf60dd --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/load.jsonnet @@ -0,0 +1,50 @@ +// Document loading and preprocessing module +// Handles document ingestion, format conversion, and chunking +// Converts PDFs to text and splits documents into processable chunks + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +{ + + // External interfaces for document loading + "interfaces" +: { + "document-load": flow("document-load:{id}"), // Raw document input stream + "text-load": flow("text-document-load:{id}"), // Text document stream + "embeddings": request_response("embeddings:{id}"), // Embedding service for chunks + }, + + // No configurable parameters for document loading + "parameters" +: { + }, + + // Flow-level processors for document preprocessing + "flow" +: { + // PDF decoder converts PDF documents to text + "pdf-decoder:{id}": { + input: flow("document-load:{id}"), // Raw PDF input + output: flow("text-document-load:{id}"), // Extracted text output + }, + + // Chunker splits documents into smaller, processable pieces + "chunker:{id}": { + input: flow("text-document-load:{id}"), // Full text documents + output: flow("chunk-load:{id}"), // Document chunks for processing + "chunk-size": "{chunk-size}", // Chunk size + "chunk-overlap": "{chunk-overlap}", // Overlap between chunks + }, + // Embedding service for converting text chunks to vectors + "embeddings:{id}": { + request: request("embeddings:{id}"), // Embedding requests + response: response("embeddings:{id}"), // Embedding responses + model: "{embeddings-model}", + }, + }, + + // Class-level processors for document loading services + "class" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/onto-base.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/onto-base.jsonnet new file mode 100644 index 00000000..2236f24d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/onto-base.jsonnet @@ -0,0 +1,39 @@ +// Knowledge Graph Base extraction module +// Provides basic knowledge extraction capabilities from text chunks +// Extracts entity definitions and relationships using prompt-based processing + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; + +{ + // No external interfaces - this module provides internal extraction services + "interfaces" +: { + }, + + // No configurable parameters for basic KG extraction + "parameters" +: { + }, + + // Flow-level processors for knowledge extraction + "flow" +: { + // Extracts using ontology definitions + "kg-extract-ontology:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output triples + "entity-contexts": flow("entity-contexts-load:{id}"), // Entity context information + "prompt-request": request("prompt:{id}"), // Definition + // extraction prompts + "prompt-response": response("prompt:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + }, + + }, + + // No class-level processors needed + "class" +: { + } +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/ontorag.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/ontorag.jsonnet new file mode 100644 index 00000000..94cf5750 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/ontorag.jsonnet @@ -0,0 +1,107 @@ +// GraphRAG flow configuration module +// Implements graph-based retrieval augmented generation (GraphRAG) functionality +// Handles knowledge graph storage, embeddings, and graph-based question answering + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; +local llm_parameters = import "llm-parameters.jsonnet"; + +{ + // External interfaces exposed by the GraphRAG flow + "interfaces" +: { + // Data ingestion interfaces for graph construction + "entity-contexts-load": flow("entity-contexts-load:{id}"), // Entity context data stream + "triples-store": flow("triples-store:{id}"), // RDF triples storage stream + "graph-embeddings-store": flow("graph-embeddings-store:{id}"), // Graph embedding storage + + // Query interfaces for graph-based operations + "graph-rag": request_response("graph-rag:{id}"), // Main GraphRAG query interface + "triples": request_response("triples:{id}"), // Triple store queries + "graph-embeddings": request_response("graph-embeddings:{id}"), // Graph embedding queries + + // Supporting services + "embeddings": request_response("embeddings:{id}"), // General embedding service + "prompt": request_response("prompt:{id}"), // Prompt processing service + "text-completion": request_response("text-completion:{id}"), // LLM text completion + }, + + + // Parameters that can be configured for this flow + "parameters" +: llm_parameters, + + // Flow-level processors - handle data streams for a specific flow instance + "flow" +: { + "graph-embeddings:{id}": { + input: flow("entity-contexts-load:{id}"), + output: flow("graph-embeddings-store:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + }, + "triples-write:{id}": { + input: flow("triples-store:{id}"), + }, + "ge-write:{id}": { + input: flow("graph-embeddings-store:{id}"), + }, + "text-completion:{id}": { + request: request("text-completion:{id}"), + response: response("text-completion:{id}"), + model: "{llm-model}", + }, + "text-completion-rag:{id}": { + request: request("text-completion-rag:{id}"), + response: response("text-completion-rag:{id}"), + model: "{llm-rag-model}", + }, + "embeddings:{id}": { + request: request("embeddings:{id}"), + response: response("embeddings:{id}"), + model: "{embeddings-model}", + }, + "graph-rag:{id}": { + request: request("graph-rag:{id}"), + response: response("graph-rag:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + "prompt-request": request("prompt-rag:{id}"), + "prompt-response": response("prompt-rag:{id}"), + "graph-embeddings-request": request("graph-embeddings:{id}"), + "graph-embeddings-response": response("graph-embeddings:{id}"), + "triples-request": request("triples:{id}"), + "triples-response": response("triples:{id}"), + }, + "triples-query:{id}": { + request: request("triples:{id}"), + response: response("triples:{id}"), + }, + "ge-query:{id}": { + request: request("graph-embeddings:{id}"), + response: response("graph-embeddings:{id}"), + }, + "prompt:{id}": { + request: request("prompt:{id}"), + response: response("prompt:{id}"), + "text-completion-request": request("text-completion:{id}"), + "text-completion-response": response("text-completion:{id}"), + }, + "prompt-rag:{id}": { + request: request("prompt-rag:{id}"), + response: response("prompt-rag:{id}"), + "text-completion-request": request("text-completion-rag:{id}"), + "text-completion-response": response("text-completion-rag:{id}"), + }, + "metering:{id}": { + input: response("text-completion:{id}"), + }, + "metering-rag:{id}": { + input: response("text-completion-rag:{id}"), + }, + }, + + // Class-level processors - shared across all flow instances of this class + "class" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/structured.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/structured.jsonnet new file mode 100644 index 00000000..45bd35d9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/flows/structured.jsonnet @@ -0,0 +1,108 @@ +// Structured data processing module +// Handles extraction and querying of structured data objects +// Provides natural language to GraphQL query capabilities +// Supports structured data storage and retrieval + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; +local llm_parameters = import "llm-parameters.jsonnet"; + +{ + // External interfaces for structured data operations + "interfaces" +: { + // Supporting services + "embeddings": request_response("embeddings:{id}"), // Embedding service + "prompt": request_response("prompt:{id}"), // Prompt processing + "text-completion": request_response("text-completion:{id}"), // LLM completion + + // Structured data storage and querying + "objects-store": flow("objects-store:{id}"), // Object storage stream + "objects": request_response("objects:{id}"), // Object query service + + // Query interfaces + "nlp-query": request_response("nlp-query:{id}"), // NLP to GraphQL translation + "structured-query": request_response("structured-query:{id}"), // Structured query execution + "structured-diag": request_response("structured-diag:{id}"), // Query diagnostics + }, + + + // Parameters that can be configured for this flow + "parameters" +: llm_parameters, + + // Flow-level processors for structured data extraction + "flow" +: { + "kg-extract-objects:{id}": { + input: flow("chunk-load:{id}"), + output: flow("objects-store:{id}"), + "entity-contexts": flow("entity-contexts-load:{id}"), + "prompt-request": request("prompt:{id}"), + "prompt-response": response("prompt:{id}"), + }, + "objects-write:{id}": { + input: flow("objects-store:{id}"), + }, + "text-completion:{id}": { + request: request("text-completion:{id}"), + response: response("text-completion:{id}"), + model: "{llm-model}", + }, + "text-completion-rag:{id}": { + request: request("text-completion-rag:{id}"), + response: response("text-completion-rag:{id}"), + model: "{llm-rag-model}", + }, + "objects-query:{id}": { + request: request("objects:{id}"), + response: response("objects:{id}"), + }, + "nlp-query:{id}": { + request: request("nlp-query:{id}"), + response: response("nlp-query:{id}"), + "prompt-request": request("prompt-rag:{id}"), + "prompt-response": response("prompt-rag:{id}"), + }, + "structured-query:{id}": { + request: request("structured-query:{id}"), + response: response("structured-query:{id}"), + "nlp-query-request": request("nlp-query:{id}"), + "nlp-query-response": response("nlp-query:{id}"), + "objects-query-request": request("objects:{id}"), + "objects-query-response": response("objects:{id}"), + }, + "structured-diag:{id}": { + request: request("structured-diag:{id}"), + response: response("structured-diag:{id}"), + "prompt-request": request("prompt:{id}"), + "prompt-response": response("prompt:{id}"), + }, + "embeddings:{id}": { + request: request("embeddings:{id}"), + response: response("embeddings:{id}"), + model: "{embeddings-model}", + }, + "prompt:{id}": { + request: request("prompt:{id}"), + response: response("prompt:{id}"), + "text-completion-request": request("text-completion:{id}"), + "text-completion-response": response("text-completion:{id}"), + }, + "prompt-rag:{id}": { + request: request("prompt-rag:{id}"), + response: response("prompt-rag:{id}"), + "text-completion-request": request("text-completion-rag:{id}"), + "text-completion-response": response("text-completion-rag:{id}"), + }, + "metering:{id}": { + input: response("text-completion:{id}"), + }, + "metering-rag:{id}": { + input: response("text-completion-rag:{id}"), + }, + }, + // Class-level processors for structured data operations + "class" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/mcp/ddg-mcp-server.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/mcp/ddg-mcp-server.jsonnet new file mode 100644 index 00000000..bb9157f4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/mcp/ddg-mcp-server.jsonnet @@ -0,0 +1,48 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "ddg-mcp-server-port":: 9870, + + "ddg-mcp-server" +: { + + create:: function(engine) + + local port = $["ddg-mcp-server-port"]; + + local container = + engine.container("ddg-mcp-server") + .with_image(images["ddg-mcp-server"]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_port(port, port, "mcp"); + + local containerSet = engine.containers( + "ddg-mcp-server", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(port, port, "mcp"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + mcp +:: { + "duckduckgo": { + "remote-name": "search", + local port = $["ddg-mcp-server-port"], + local url = "http://ddg-mcp-server:%s/mcp" % port, + "url": url, + } + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/azure-openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/azure-openai.jsonnet new file mode 100644 index 00000000..0e3ea907 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/azure-openai.jsonnet @@ -0,0 +1,9 @@ +// Azure OpenAI LLM Model Definitions +// Model input is just text + +{ + "type": "string", + "description": "LLM model to use", + "default": "gpt-4o", + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/azure.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/azure.jsonnet new file mode 100644 index 00000000..6ffa5fdf --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/azure.jsonnet @@ -0,0 +1,9 @@ +// Azure LLM Model Definitions +// Model input is just text + +{ + "type": "string", + "description": "LLM model to use", + "default": "phi4:14b", + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/bedrock.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/bedrock.jsonnet new file mode 100644 index 00000000..df930e59 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/bedrock.jsonnet @@ -0,0 +1,63 @@ +// AWS Bedrock LLM Model Definitions +// Defines available models and their configurations for AWS Bedrock + +{ + "type": "string", + "description": "LLM model to use", + "default": "global.anthropic.claude-sonnet-4-5-20250929-v1:0", + "enum": [ + { + id: "global.anthropic.claude-sonnet-4-5-20250929-v1:0", + description: "Claude Sonnet 4.5 (smartest for complex agents and coding)" + }, + { + id: "global.anthropic.claude-opus-4-5-20251101-v1:0", + description: "Claude Opus 4.5 (maximum intelligence)" + }, + { + id: "global.anthropic.claude-haiku-4-5-20251001-v1:0", + description: "Claude Haiku 4.5 (fastest with near-frontier intelligence)" + }, + { + id: "global.anthropic.claude-opus-4-1-20250805-v1:0", + description: "Claude Opus 4.1 (specialized reasoning)" + }, + { + id: "global.anthropic.claude-sonnet-4-20250514-v1:0", + description: "Claude Sonnet 4.0" + }, + { + id: "global.anthropic.claude-opus-4-20250514-v1:0", + description: "Claude Opus 4.0" + }, + { + id: "anthropic.claude-3-5-haiku-20241022-v1:0", + description: "Claude 3.5 Haiku" + }, + { + id: "anthropic.claude-3-haiku-20240307-v1:0", + description: "Claude 3 Haiku" + }, + { + id: "meta.llama3-1-405b-instruct-v1:0", + description: "Llama 3.1 405B Instruct" + }, + { + id: "meta.llama3-1-70b-instruct-v1:0", + description: "Llama 3.1 70B Instruct" + }, + { + id: "meta.llama3-1-8b-instruct-v1:0", + description: "Llama 3.1 8B Instruct" + }, + { + id: "mistral.mistral-large-2407-v1:0", + description: "Mistral Large" + }, + { + id: "mistral.mixtral-8x7b-instruct-v0:1", + description: "Mixtral 8x7B Instruct" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/chunking-param-types.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/chunking-param-types.jsonnet new file mode 100644 index 00000000..f82fa3f7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/chunking-param-types.jsonnet @@ -0,0 +1,25 @@ +// Chunk parameter type definitions + +{ + "chunk-size": { + "type": "integer", + "description": "Chunk size", + "placeholder": 2000, + "helper": "An integer, usually 2000 .. 8000", + "default": 2000, + "min": 0, + "max": 32768, + "required": true + }, + "chunk-overlap": { + "type": "integer", + "description": "Chunk overlap", + "placeholder": 50, + "helper": "An integer, usually 50 .. 100", + "default": 50, + "min": 0, + "max": 8000, + "required": true + }, +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/claude.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/claude.jsonnet new file mode 100644 index 00000000..40fd434b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/claude.jsonnet @@ -0,0 +1,35 @@ +// Claude LLM Model Definitions +// Defines available models and their configurations for Anthropic's Claude + +{ + "type": "string", + "description": "LLM model to use", + "default": "claude-sonnet-4-5-20250929", + "enum": [ + { + id: "claude-sonnet-4-5-20250929", + description: "Claude Sonnet 4.5 (complex agents + coding)" + }, + { + id: "claude-opus-4-5-20251101", + description: "Claude Opus 4.5 (maximum intelligence)" + }, + { + id: "claude-haiku-4-5-20251001", + description: "Claude Haiku 4.5 (fast)" + }, + { + id: "claude-opus-4-1-20250805", + description: "Claude Opus 4.1 (specialized reasoning)" + }, + { + id: "claude-sonnet-4-20250514", + description: "Claude Sonnet 4.0" + }, + { + id: "claude-3-5-haiku-20241022", + description: "Claude 3.5 Haiku" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/cohere.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/cohere.jsonnet new file mode 100644 index 00000000..5a5865a3 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/cohere.jsonnet @@ -0,0 +1,35 @@ +// Cohere LLM Model Definitions +// Defines available models and their configurations for Cohere + +{ + "type": "string", + "description": "LLM model to use", + "default": "command-r-plus-08-2024", + "enum": [ + { + id: "command-r-plus-08-2024", + description: "Command R+ (August 2024)" + }, + { + id: "command-r-08-2024", + description: "Command R (August 2024)" + }, + { + id: "command-r-plus", + description: "Command R+ (legacy)" + }, + { + id: "command-r", + description: "Command R (legacy)" + }, + { + id: "command", + description: "Command" + }, + { + id: "command-light", + description: "Command Light" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/embeddings-fastembed.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/embeddings-fastembed.jsonnet new file mode 100644 index 00000000..477e25dc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/embeddings-fastembed.jsonnet @@ -0,0 +1,127 @@ +// Embeddings model definitions for fastembed +// Defines available models and their configurations for Fastembed + +{ + "type": "string", + "description": "Embeddings model to use", + "default": "sentence-transformers/all-MiniLM-L6-v2", + "enum": [ + { + "id": "sentence-transformers/all-MiniLM-L6-v2", + "description": "all-MiniLM-L6-v2" + }, + { + "id": "BAAI/bge-small-en-v1.5", + "description": "bge-small-en-v1.5" + }, + { + "id": "BAAI/bge-small-zh-v1.5", + "description": "bge-small-zh-v1.5" + }, + { + "id": "snowflake/snowflake-arctic-embed-xs", + "description": "snowflake-arctic-embed-xs" + }, + { + "id": "jinaai/jina-embeddings-v2-small-en", + "description": "jina-embeddings-v2-small-en" + }, + { + "id": "nomic-ai/nomic-embed-text-v1.5-Q", + "description": "nomic-embed-text-v1.5-Q" + }, + { + "id": "snowflake/snowflake-arctic-embed-s", + "description": "snowflake-arctic-embed-s" + }, + { + "id": "BAAI/bge-small-en", + "description": "bge-small-en" + }, + { + "id": "BAAI/bge-base-en-v1.5", + "description": "bge-base-en-v1.5" + }, + { + "id": "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2", + "description": "paraphrase-multilingual-MiniLM-L12-v2" + }, + { + "id": "Qdrant/clip-ViT-B-32-text", + "description": "clip-ViT-B-32-text" + }, + { + "id": "jinaai/jina-embeddings-v2-base-de", + "description": "jina-embeddings-v2-base-de" + }, + { + "id": "BAAI/bge-base-en", + "description": "bge-base-en" + }, + { + "id": "snowflake/snowflake-arctic-embed-m", + "description": "snowflake-arctic-embed-m" + }, + { + "id": "thenlper/gte-base", + "description": "gte-base" + }, + { + "id": "jinaai/jina-embeddings-v2-base-en", + "description": "jina-embeddings-v2-base-en" + }, + { + "id": "nomic-ai/nomic-embed-text-v1", + "description": "nomic-embed-text-v1" + }, + { + "id": "nomic-ai/nomic-embed-text-v1.5", + "description": "nomic-embed-text-v1.5" + }, + { + "id": "snowflake/snowflake-arctic-embed-m-long", + "description": "snowflake-arctic-embed-m-long" + }, + { + "id": "jinaai/jina-clip-v1", + "description": "jina-clip-v1" + }, + { + "id": "mixedbread-ai/mxbai-embed-large-v1", + "description": "mxbai-embed-large-v1" + }, + { + "id": "jinaai/jina-embeddings-v2-base-es", + "description": "jina-embeddings-v2-base-es" + }, + { + "id": "jinaai/jina-embeddings-v2-base-code", + "description": "jina-embeddings-v2-base-code" + }, + { + "id": "jinaai/jina-embeddings-v2-base-zh", + "description": "jina-embeddings-v2-base-zh" + }, + { + "id": "sentence-transformers/paraphrase-multilingual-mpnet-base-v2", + "description": "paraphrase-multilingual-mpnet-base-v2" + }, + { + "id": "snowflake/snowflake-arctic-embed-l", + "description": "snowflake-arctic-embed-l" + }, + { + "id": "BAAI/bge-large-en-v1.5", + "description": "bge-large-en-v1.5" + }, + { + "id": "thenlper/gte-large", + "description": "gte-large" + }, + { + "id": "intfloat/multilingual-e5-large", + "description": "multilingual-e5-large" + } + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/embeddings-huggingface.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/embeddings-huggingface.jsonnet new file mode 100644 index 00000000..32254a2b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/embeddings-huggingface.jsonnet @@ -0,0 +1,31 @@ +// Embeddings model definitions for fastembed +// Defines available models and their configurations for Fastembed + +{ + "type": "string", + "description": "Embeddings model to use", + "default": "sentence-transformers/all-MiniLM-L6-v2", + "enum": [ + { + "id": "all-MiniLM-L6-v2", + "description": "all-MiniLM-L6-v2" + }, + { + "id": "all-mpnet-base-v2", + "description": "all-mpnet-base-v2" + }, + { + "id": "all-distilroberta-v1", + "description": "all-distilroberta-v1" + }, + { + "id": "stsb-bert-large", + "description": "stsb-bert-large" + }, + { + "id": "sentence-camembert-large", + "description": "sentence-camembert-large" + } + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/embeddings-ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/embeddings-ollama.jsonnet new file mode 100644 index 00000000..c3f29fbb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/embeddings-ollama.jsonnet @@ -0,0 +1,23 @@ +// Embeddings model definitions for Ollama +// Defines available models and their configurations for Ollama + +{ + "type": "string", + "description": "Embeddings model to use", + "default": "all-minilm:latest", + "enum": [ + { + "id": "all-minilm:latest", + "description": "all-MiniLM-L6-v2" + }, + { + "id": "nomic-ai/nomic-embed-text-v1.5-Q", + "description": "nomic-embed-text-v1.5-Q" + }, + { + "id": "mixedbread-ai/mxbai-embed-large-v1", + "description": "mxbai-embed-large-v1" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/googleaistudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/googleaistudio.jsonnet new file mode 100644 index 00000000..a8891932 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/googleaistudio.jsonnet @@ -0,0 +1,67 @@ +// Google AI Studio LLM Model Definitions +// Defines available models and their configurations for Google AI Studio + +{ + "type": "string", + "description": "LLM model to use", + "default": "gemini-2.5-flash-lite", + "enum": [ + // Gemini 2.5 models (latest generation) + { + id: "gemini-2.5-pro", + description: "Gemini 2.5 Pro" + }, + { + id: "gemini-2.5-flash", + description: "Gemini 2.5 Flash" + }, + { + id: "gemini-2.5-flash-lite", + description: "Gemini 2.5 Flash Lite" + }, + + // Gemini 2.0 models + { + id: "gemini-2.0-flash-exp", + description: "Gemini 2.0 Flash (experimental)" + }, + + + // Claude models on VertexAI + { + id: "claude-3-5-sonnet@20241022", + description: "Claude 3.5 Sonnet (via VertexAI)" + }, + { + id: "claude-3-5-haiku@20241022", + description: "Claude 3.5 Haiku (via VertexAI)" + }, + { + id: "claude-3-opus@20240229", + description: "Claude 3 Opus (via VertexAI)" + }, + { + id: "claude-3-sonnet@20240229", + description: "Claude 3 Sonnet (via VertexAI)" + }, + { + id: "claude-3-haiku@20240307", + description: "Claude 3 Haiku (via VertexAI)" + }, + + // Llama models on VertexAI + { + id: "llama3-405b-instruct-maas", + description: "Llama 3 405B Instruct (via VertexAI)" + }, + { + id: "llama3-70b-instruct-maas", + description: "Llama 3 70B Instruct (via VertexAI)" + }, + { + id: "llama3-8b-instruct-maas", + description: "Llama 3 8B Instruct (via VertexAI)" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/llamafile.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/llamafile.jsonnet new file mode 100644 index 00000000..f6480aa9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/llamafile.jsonnet @@ -0,0 +1,9 @@ +// LlamaFile LLM Model Definitions +// Model input is just text + +{ + "type": "string", + "description": "LLM model to use", + "default": "phi4:14b", + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/lmstudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/lmstudio.jsonnet new file mode 100644 index 00000000..1c88da3c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/lmstudio.jsonnet @@ -0,0 +1,150 @@ +// LMStudio LLM Model Definitions +// Defines available models and their configurations for LMStudio + +{ + "type": "string", + "description": "LLM model to use", + "default": "llama3.1:70b", + "enum": [ + // Gemma3 models + { + id: "gemma3:1b", + description: "Gemma3 1B" + }, + { + id: "gemma3:4b", + description: "Gemma3 4B" + }, + { + id: "gemma3:12b", + description: "Gemma3 12B" + }, + { + id: "gemma3:27b", + description: "Gemma3 27B" + }, + + // Phi4 models + { + id: "phi4:mini:3.8b", + description: "Phi4 Mini 3.8B" + }, + { + id: "phi4:14b", + description: "Phi4 14B" + }, + + // DeepSeek-R1 models + { + id: "deepseek-r1:671b", + description: "DeepSeek-R1 671B" + }, + { + id: "deepseek-r1:70b", + description: "DeepSeek-R1 70B" + }, + { + id: "deepseek-r1:32b", + description: "DeepSeek-R1 32B" + }, + { + id: "deepseek-r1:14b", + description: "DeepSeek-R1 14B" + }, + { + id: "deepseek-r1:8b", + description: "DeepSeek-R1 8B" + }, + { + id: "deepseek-r1:7b", + description: "DeepSeek-R1 7B" + }, + { + id: "deepseek-r1:1.5b", + description: "DeepSeek-R1 1.5B" + }, + + // Llama3.1 models + { + id: "llama3.1:405b", + description: "Llama 3.1 405B" + }, + { + id: "llama3.1:70b", + description: "Llama 3.1 70B" + }, + { + id: "llama3.1:8b", + description: "Llama 3.1 8B" + }, + + // Gemma2 models + { + id: "gemma2:2b", + description: "Gemma2 2B" + }, + { + id: "gemma2:9b", + description: "Gemma2 9B" + }, + { + id: "gemma2:27b", + description: "Gemma2 27B" + }, + + // Qwen2.5 models + { + id: "qwen2.5:0.5b", + description: "Qwen2.5 0.5B" + }, + { + id: "qwen2.5:1.5b", + description: "Qwen2.5 1.5B" + }, + { + id: "qwen2.5:3b", + description: "Qwen2.5 3B" + }, + { + id: "qwen2.5:7b", + description: "Qwen2.5 7B" + }, + { + id: "qwen2.5:14b", + description: "Qwen2.5 14B" + }, + { + id: "qwen2.5:32b", + description: "Qwen2.5 32B" + }, + { + id: "qwen2.5:72b", + description: "Qwen2.5 72B" + }, + + // Mistral models + { + id: "mistral:7b", + description: "Mistral 7B" + }, + { + id: "mistral-nemo:12b", + description: "Mistral Nemo 12B" + }, + { + id: "mistral-large:123b", + description: "Mistral Large 123B" + }, + + // Command-R models + { + id: "command-r:35b", + description: "Command-R 35B" + }, + { + id: "command-r-plus:104b", + description: "Command-R Plus 104B" + }, + ], + "required": true +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/mistral.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/mistral.jsonnet new file mode 100644 index 00000000..23b7bf79 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/mistral.jsonnet @@ -0,0 +1,39 @@ +// Mistral LLM Model Definitions +// Defines available models and their configurations for Mistral AI + +{ + "type": "string", + "description": "LLM model to use", + "default": "mistral-large-latest", + "enum": [ + { + id: "mistral-large-latest", + description: "Mistral Large (latest)" + }, + { + id: "mistral-medium-latest", + description: "Mistral Medium (latest)" + }, + { + id: "mistral-small-latest", + description: "Mistral Small (latest)" + }, + { + id: "open-mistral-nemo", + description: "Open Mistral Nemo" + }, + { + id: "open-mistral-7b", + description: "Open Mistral 7B" + }, + { + id: "open-mixtral-8x7b", + description: "Open Mixtral 8x7B" + }, + { + id: "open-mixtral-8x22b", + description: "Open Mixtral 8x22B" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/ollama.jsonnet new file mode 100644 index 00000000..4ef98a6a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/ollama.jsonnet @@ -0,0 +1,150 @@ +// Ollama LLM Model Definitions +// Defines available models and their configurations for Ollama + +{ + "type": "string", + "description": "LLM model to use", + "default": "gemma3:12b", + "enum": [ + // Gemma3 models + { + id: "gemma3:1b", + description: "Gemma3 1B" + }, + { + id: "gemma3:4b", + description: "Gemma3 4B" + }, + { + id: "gemma3:12b", + description: "Gemma3 12B" + }, + { + id: "gemma3:27b", + description: "Gemma3 27B" + }, + + // Phi4 models + { + id: "phi4:mini:3.8b", + description: "Phi4 Mini 3.8B" + }, + { + id: "phi4:14b", + description: "Phi4 14B" + }, + + // DeepSeek-R1 models + { + id: "deepseek-r1:671b", + description: "DeepSeek-R1 671B" + }, + { + id: "deepseek-r1:70b", + description: "DeepSeek-R1 70B" + }, + { + id: "deepseek-r1:32b", + description: "DeepSeek-R1 32B" + }, + { + id: "deepseek-r1:14b", + description: "DeepSeek-R1 14B" + }, + { + id: "deepseek-r1:8b", + description: "DeepSeek-R1 8B" + }, + { + id: "deepseek-r1:7b", + description: "DeepSeek-R1 7B" + }, + { + id: "deepseek-r1:1.5b", + description: "DeepSeek-R1 1.5B" + }, + + // Llama3.1 models + { + id: "llama3.1:405b", + description: "Llama 3.1 405B" + }, + { + id: "llama3.1:70b", + description: "Llama 3.1 70B" + }, + { + id: "llama3.1:8b", + description: "Llama 3.1 8B" + }, + + // Gemma2 models + { + id: "gemma2:2b", + description: "Gemma2 2B" + }, + { + id: "gemma2:9b", + description: "Gemma2 9B" + }, + { + id: "gemma2:27b", + description: "Gemma2 27B" + }, + + // Qwen2.5 models + { + id: "qwen2.5:0.5b", + description: "Qwen2.5 0.5B" + }, + { + id: "qwen2.5:1.5b", + description: "Qwen2.5 1.5B" + }, + { + id: "qwen2.5:3b", + description: "Qwen2.5 3B" + }, + { + id: "qwen2.5:7b", + description: "Qwen2.5 7B" + }, + { + id: "qwen2.5:14b", + description: "Qwen2.5 14B" + }, + { + id: "qwen2.5:32b", + description: "Qwen2.5 32B" + }, + { + id: "qwen2.5:72b", + description: "Qwen2.5 72B" + }, + + // Mistral models + { + id: "mistral:7b", + description: "Mistral 7B" + }, + { + id: "mistral-nemo:12b", + description: "Mistral Nemo 12B" + }, + { + id: "mistral-large:123b", + description: "Mistral Large 123B" + }, + + // Command-R models + { + id: "command-r:35b", + description: "Command-R 35B" + }, + { + id: "command-r-plus:104b", + description: "Command-R Plus 104B" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/openai.jsonnet new file mode 100644 index 00000000..75b2670f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/openai.jsonnet @@ -0,0 +1,31 @@ +// OpenAI LLM Model Definitions +// Defines available models and their configurations for OpenAI's platform + +{ + "type": "string", + "description": "LLM model to use", + "default": "gpt-4o", + "enum": [ + { + id: "gpt-4o", + description: "GPT-4o (latest)" + }, + { + id: "gpt-4o-mini", + description: "GPT-4o Mini" + }, + { + id: "gpt-4-turbo", + description: "GPT-4 Turbo" + }, + { + id: "gpt-4", + description: "GPT-4" + }, + { + id: "gpt-3.5-turbo", + description: "GPT-3.5 Turbo" + }, + ], + "required": true +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/temperature-param-types.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/temperature-param-types.jsonnet new file mode 100644 index 00000000..f5523e88 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/temperature-param-types.jsonnet @@ -0,0 +1,15 @@ +// LLM temperature definitions + +{ + "llm-temperature": { + "type": "float", + "description": "LLM temperature", + "placeholder": 0.3, + "helper": "A floating point number between 0 and 1", + "default": 0.3, + "min": 0.0, + "max": 10.0, + "required": true + } +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/vertexai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/vertexai.jsonnet new file mode 100644 index 00000000..6d90f8d3 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/vertexai.jsonnet @@ -0,0 +1,68 @@ +// VertexAI LLM Model Definitions +// Defines available models and their configurations for Google's VertexAI platform + +{ + "type": "string", + "description": "LLM model to use", + "default": "gemini-2.5-flash-lite", + "enum": [ + // Gemini 2.5 models (latest generation) + { + id: "gemini-2.5-pro", + description: "Gemini 2.5 Pro" + }, + { + id: "gemini-2.5-flash", + description: "Gemini 2.5 Flash" + }, + { + id: "gemini-2.5-flash-lite", + description: "Gemini 2.5 Flash Lite" + }, + + // Gemini 2.0 models + { + id: "gemini-2.0-flash-exp", + description: "Gemini 2.0 Flash (experimental)" + }, + + + // Claude models on VertexAI + { + id: "claude-3-5-sonnet@20241022", + description: "Claude 3.5 Sonnet (via VertexAI)" + }, + { + id: "claude-3-5-haiku@20241022", + description: "Claude 3.5 Haiku (via VertexAI)" + }, + { + id: "claude-3-opus@20240229", + description: "Claude 3 Opus (via VertexAI)" + }, + { + id: "claude-3-sonnet@20240229", + description: "Claude 3 Sonnet (via VertexAI)" + }, + { + id: "claude-3-haiku@20240307", + description: "Claude 3 Haiku (via VertexAI)" + }, + + // Llama models on VertexAI + { + id: "llama3-405b-instruct-maas", + description: "Llama 3 405B Instruct (via VertexAI)" + }, + { + id: "llama3-70b-instruct-maas", + description: "Llama 3 70B Instruct (via VertexAI)" + }, + { + id: "llama3-8b-instruct-maas", + description: "Llama 3 8B Instruct (via VertexAI)" + }, + ], + "required": true +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/vllm.jsonnet new file mode 100644 index 00000000..e372a2ba --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/parameters/vllm.jsonnet @@ -0,0 +1,125 @@ +// vLLM Model Definitions +// Defines available models and their configurations for vLLM +// vLLM works with any HuggingFace model, these are common optimized choices + +{ + "type": "string", + "description": "LLM model to use", + "default": "meta-llama/Llama-3.1-8B-Instruct", + "enum": [ + // Llama 3.1 models + { + id: "meta-llama/Llama-3.1-8B-Instruct", + description: "Llama 3.1 8B Instruct" + }, + { + id: "meta-llama/Llama-3.1-70B-Instruct", + description: "Llama 3.1 70B Instruct" + }, + { + id: "meta-llama/Llama-3.1-405B-Instruct", + description: "Llama 3.1 405B Instruct" + }, + + // Llama 3.2 models + { + id: "meta-llama/Llama-3.2-1B-Instruct", + description: "Llama 3.2 1B Instruct" + }, + { + id: "meta-llama/Llama-3.2-3B-Instruct", + description: "Llama 3.2 3B Instruct" + }, + + // Qwen2.5 models + { + id: "Qwen/Qwen2.5-0.5B-Instruct", + description: "Qwen2.5 0.5B Instruct" + }, + { + id: "Qwen/Qwen2.5-1.5B-Instruct", + description: "Qwen2.5 1.5B Instruct" + }, + { + id: "Qwen/Qwen2.5-3B-Instruct", + description: "Qwen2.5 3B Instruct" + }, + { + id: "Qwen/Qwen2.5-7B-Instruct", + description: "Qwen2.5 7B Instruct" + }, + { + id: "Qwen/Qwen2.5-14B-Instruct", + description: "Qwen2.5 14B Instruct" + }, + { + id: "Qwen/Qwen2.5-32B-Instruct", + description: "Qwen2.5 32B Instruct" + }, + { + id: "Qwen/Qwen2.5-72B-Instruct", + description: "Qwen2.5 72B Instruct" + }, + + // Mistral models + { + id: "mistralai/Mistral-7B-Instruct-v0.3", + description: "Mistral 7B Instruct v0.3" + }, + { + id: "mistralai/Mixtral-8x7B-Instruct-v0.1", + description: "Mixtral 8x7B Instruct" + }, + { + id: "mistralai/Mixtral-8x22B-Instruct-v0.1", + description: "Mixtral 8x22B Instruct" + }, + + // Phi models + { + id: "microsoft/Phi-3.5-mini-instruct", + description: "Phi 3.5 Mini Instruct" + }, + { + id: "microsoft/Phi-4", + description: "Phi 4" + }, + + // Gemma models + { + id: "google/gemma-2-2b-it", + description: "Gemma 2 2B Instruct" + }, + { + id: "google/gemma-2-9b-it", + description: "Gemma 2 9B Instruct" + }, + { + id: "google/gemma-2-27b-it", + description: "Gemma 2 27B Instruct" + }, + + // DeepSeek models + { + id: "deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B", + description: "DeepSeek-R1 Distill Qwen 1.5B" + }, + { + id: "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B", + description: "DeepSeek-R1 Distill Qwen 7B" + }, + { + id: "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B", + description: "DeepSeek-R1 Distill Qwen 14B" + }, + { + id: "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B", + description: "DeepSeek-R1 Distill Qwen 32B" + }, + { + id: "deepseek-ai/DeepSeek-R1-Distill-Llama-70B", + description: "DeepSeek-R1 Distill Llama 70B" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/agent-prompt.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/agent-prompt.txt new file mode 100644 index 00000000..9155d20f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/agent-prompt.txt @@ -0,0 +1,104 @@ +# ReAct Agent System Prompt + +You are an AI assistant that uses the ReAct (Reasoning + Acting) framework to solve problems through systematic reasoning and tool use. + +## Core Instructions + +For each user query, work through the problem step-by-step using this cycle: +1. **Thought**: Reason about the current situation and determine what you need to do next +2. **Action**: Take ONE specific action using an available tool +3. Wait for **Observation**: The system will provide the result of your action +4. Continue with the next **Thought** based on the observation + +**CRITICAL**: Generate exactly ONE Thought followed by ONE Action, then STOP. Do not generate multiple Thought/Action pairs in a single response. Do not generate Observations yourself - the system will provide them. + +## Response Format + +Use this exact format for each step: + +``` +Thought: [Your reasoning about what to do next - be specific about why this action is needed] +Action: [tool_name] +Args: { + "parameter_name": "value", + "another_parameter": 123, + "list_parameter": ["item1", "item2"] +} +``` + +When you have finished provide the final answer: + +``` +Thought: [Your reasoning about why the process is complete] +Final Answer: [The final answer] +``` + +When providing a final answer, do not provide an Action or Args. + +## Action Format Rules + +1. **Tool Name**: Write "Action: " followed by the exact tool name on its own line +2. **Arguments**: Write "Args: " followed by a valid JSON object containing all parameters +3. **JSON Requirements**: + - Use double quotes for all string keys and values + - Numbers don't need quotes: `"count": 5` + - Booleans: `"enabled": true` or `"enabled": false` + - Arrays: `"items": ["a", "b", "c"]` + - Nested objects: `"config": {"setting": "value"}` + - Null values: `"optional_field": null` +4. **Required Parameters**: Include all required parameters for the tool +5. **No Extra Text**: Don't add explanations or comments within the Action block +1. **Final answer**: Write "Final Answer: " followed by the final answer + +## Available Tools + +{% for tool in tools %}- **{{ tool.name }}**: {{ tool.description }} +{% for arg in tool.arguments %} - Required: `"{{ arg.name }}"` ({{ arg.type }}): {{ arg.description }} +{% endfor %} +{% endfor %} + +## Behavior Rules + +1. **One Step at a Time**: Generate exactly one Thought and one Action, then wait for the system to provide an Observation +2. **Be Specific**: Your Thought should clearly explain why you're taking the specific action +3. **Use Context**: Build on previous Observations to inform your next steps +4. **Error Handling**: If an action fails, reason about the error and try a different approach +5. **Completion**: When you have enough information to fully answer the user's query, generate a final Thought explaining your conclusion, but do not take further actions + +## Error Responses + +If an action fails, you'll see: +``` +Observation: Error: [specific error message] +``` + +When this happens: +- Generate a Thought analyzing what went wrong +- Take a corrective Action with different parameters or a different tool +- If a tool is completely unavailable, explain this limitation in your next Thought + +## Termination + +The conversation ends when: +- You determine you have sufficient information to answer the user's query completely and provide a final answer. +- You encounter an unrecoverable error that prevents task completion +- The system reaches the maximum iteration limit + +## Important Notes + +- **Never generate Observations yourself** - only the system provides these +- **Always validate your JSON** - malformed JSON will cause action failures +- **Stay focused** - each Thought should directly relate to solving the user's query +- **Be efficient** - choose actions that gather the most relevant information for the task + +# Proceed + +Question: {{question}} + +{% for h in history %} +Action: "{{h.action}}" +Args: { +{% for k, v in h.arguments.items() %} "{{k}}": "{{v}}" +{% endfor %}} +Observation: "{{h.observation}}" +{% endfor %} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/cohere.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/cohere.jsonnet new file mode 100644 index 00000000..9541e4c2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/cohere.jsonnet @@ -0,0 +1,42 @@ +// For Cohere. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/default-prompts.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/default-prompts.jsonnet new file mode 100644 index 00000000..35dda682 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/default-prompts.jsonnet @@ -0,0 +1,234 @@ + +// Prompt templates. For tidy JSONNET use, don't change these templates +// here, but use over-rides in the prompt directory + +{ + + "system-template":: "You are a helpful assistant.", + + "templates":: { + + "question":: { + "prompt": "{{question}}", + }, + + "extract-definitions":: { + "prompt": "\nStudy the following text and derive definitions for any discovered entities.\nDo not provide definitions for entities whose definitions are incomplete\nor unknown.\nOutput relationships in JSON format as an array of objects with fields:\n- entity: the name of the entity\n- definition: English text which defines the entity\n\n\n\n{{text}}\n\n\n\nYou will respond only with raw JSON format data. Do not provide\nexplanations. Do not use special characters in the abstract text. The\nabstract will be written as plain text. Do not add markdown formatting\nor headers or prefixes. Do not include null or unknown definitions.\n", + "response-type": "json", + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "entity": { + "type": "string" + }, + "definition": { + "type": "string" + } + }, + "required": [ + "entity", + "definition" + ] + } + } + }, + + "extract-relationships":: { + "prompt": "\nStudy the following text and derive entity relationships. For each\nrelationship, derive the subject, predicate and object of the relationship.\nOutput relationships in JSON format as an array of objects with fields:\n- subject: the subject of the relationship\n- predicate: the predicate\n- object: the object of the relationship\n- object-entity: false if the object is a simple data type: name, value or date. true if it is an entity.\n\n\n\n{{text}}\n\n\n\nYou will respond only with raw JSON format data. Do not provide\nexplanations. Do not use special characters in the abstract text. The\nabstract must be written as plain text. Do not add markdown formatting\nor headers or prefixes.\n", + "response-type": "json", + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "subject": { + "type": "string" + }, + "predicate": { + "type": "string" + }, + "object": { + "type": "string" + }, + "object-entity": { + "type": "boolean" + }, + }, + "required": [ + "subject", + "predicate", + "object", + "object-entity" + ] + } + } + }, + + "extract-topics":: { + "prompt": "You are a helpful assistant that performs information extraction tasks for a provided text.\nRead the provided text. You will identify topics and their definitions in JSON.\n\nReading Instructions:\n- Ignore document formatting in the provided text.\n- Study the provided text carefully.\n\nHere is the text:\n{{text}}\n\nResponse Instructions: \n- Do not respond with special characters.\n- Return only topics that are concepts and unique to the provided text.\n- Respond only with well-formed JSON.\n- The JSON response shall be an array of objects with keys \"topic\" and \"definition\". \n- The JSON response shall use the following structure:\n\n```json\n[{\"topic\": string, \"definition\": string}]\n```\n\n- Do not write any additional text or explanations.", + "response-type": "json", + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "topic": { + "type": "string" + }, + "definition": { + "type": "string" + } + }, + "required": [ + "topic", + "definition" + ] + } + } + }, + + "extract-rows":: { + "prompt": "\nStudy the following text and derive objects which match the schema provided.\n\nYou must output an array of JSON objects for each object you discover\nwhich matches the schema. For each object, output a JSON object whose fields\ncarry the name field specified in the schema.\n\n\n\n{{schema}}\n\n\n\n{{text}}\n\n\n\nYou will respond only with raw JSON format data. Do not provide\nexplanations. Do not add markdown formatting or headers or prefixes.\n", + "response-type": "json", + }, + + "kg-prompt":: { + "prompt": "Study the following set of knowledge statements. The statements are written in Cypher format that has been extracted from a knowledge graph. Use only the provided set of knowledge statements in your response. Do not speculate if the answer is not found in the provided set of knowledge statements.\n\nHere's the knowledge statements:\n{% for edge in knowledge %}({{edge.s}})-[{{edge.p}}]->({{edge.o}})\n{%endfor%}\n\nUse only the provided knowledge statements to respond to the following:\n{{query}}\n", + "response-type": "text", + }, + + "document-prompt":: { + "prompt": "Study the following context. Use only the information provided in the context in your response. Do not speculate if the answer is not found in the provided set of knowledge statements.\n\nHere is the context:\n{{documents}}\n\nUse only the provided knowledge statements to respond to the following:\n{{query}}\n", + "response-type": "text", + }, + + "agent-react":: { + "prompt": importstr "agent-prompt.txt", + "response-type": "text" + }, + + "agent-kg-extract":: { + "prompt": "Analyze the following text and extract both entity definitions and relationships. Return the results as JSON with 'definitions' and 'relationships' arrays.\n\nFor definitions, extract entities and their explanations or descriptions.\nFor relationships, extract subject-predicate-object triples where subjects and objects are entities, and predicates are relationship types.\n\nText: {{text}}\n\nReturn JSON only, no other text. Use this exact format:\n{\n \"definitions\": [\n {\n \"entity\": \"entity_name\",\n \"definition\": \"definition_text\"\n }\n ],\n \"relationships\": [\n {\n \"subject\": \"subject_entity\",\n \"predicate\": \"relationship_type\",\n \"object\": \"object_entity_or_literal\",\n \"object-entity\": true\n }\n ]\n}\n", + "response-type": "json", + "schema": { + "type": "object", + "properties": { + "definitions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "entity": { + "type": "string" + }, + "definition": { + "type": "string" + } + }, + "required": [ + "entity", + "definition" + ] + } + }, + "relationships": { + "type": "array", + "items": { + "type": "object", + "properties": { + "subject": { + "type": "string" + }, + "predicate": { + "type": "string" + }, + "object": { + "type": "string" + }, + "object-entity": { + "type": "boolean" + } + }, + "required": [ + "subject", + "predicate", + "object" + ] + } + } + }, + "required": [ + "definitions", + "relationships" + ] + } + }, + + "schema-selection":: { + "prompt": importstr "schema-selection.txt", + "response-type": "json", + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "description": "An array of schema names that are relevant to answering the given question" + } + }, + + "graphql-generation":: { + "prompt": importstr "graphql-generation.txt", + "response-type": "json", + "schema": { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "The GraphQL query string generated to answer the question" + }, + "variables": { + "type": "object", + "description": "Object containing any GraphQL variables needed for the query", + "additionalProperties": true + }, + "confidence": { + "type": "number", + "minimum": 0.0, + "maximum": 1.0, + "description": "Float between 0.0-1.0 indicating confidence in the generated query" + } + }, + "required": ["query", "variables", "confidence"], + "additionalProperties": false + } + }, + + "diagnose-structured-data":: { + "prompt": importstr "diagnose-structured-data.txt", + "response-type": "json", + }, + + "diagnose-xml":: { + "prompt": importstr "diagnose-xml.txt", + "response-type": "json", + }, + "diagnose-json":: { + "prompt": importstr "diagnose-json.txt", + "response-type": "json", + }, + "diagnose-csv":: { + "prompt": importstr "diagnose-csv.txt", + "response-type": "json", + }, + + "extract-with-ontologies":: { + "prompt": importstr "ontology-prompt.txt", + "response-type": "json", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/diagnose-csv.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/diagnose-csv.txt new file mode 100644 index 00000000..ad9725ba --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/diagnose-csv.txt @@ -0,0 +1,370 @@ +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for data import pipelines, with particular expertise in CSV processing and delimiter-separated value formats. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured CSV data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## CSV Processing Expertise + +When working with CSV data, you must: + +1. **Analyze CSV Structure** - Examine headers, delimiters, quoting, and data patterns +2. **Identify Column Mappings** - Map source column names to target fields +3. **Handle Complex CSV Patterns** - Support various CSV formats including: + - Standard comma-separated values: `name,age,city` + - Alternative delimiters: tab-separated (TSV), pipe-separated, semicolon-separated + - Quoted fields with embedded delimiters: `"Last, First",25,"New York, NY"` + - Headers with spaces or special characters: `"Customer Name","Order Date","Total Amount"` + - Files with or without headers + - Multi-line fields with embedded newlines + +## CSV Format Configuration Guidelines + +For CSV format configurations, use these patterns: + +**Basic CSV Configuration:** +```json +{ + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + "delimiter": ",", + "quote_char": "\"", + "has_header": true, + "skip_rows": 0 + } + } +} +``` + +**Advanced CSV Options:** +```json +{ + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + "delimiter": "\t", // Tab-separated + "quote_char": "\"", + "escape_char": "\\", + "has_header": true, + "skip_rows": 2, // Skip metadata rows + "null_values": ["", "NULL", "N/A"], + "trim_whitespace": true, + "skip_blank_lines": true + } + } +} +``` + +**CRITICAL: Source Field Names in Mappings** + +When processing CSV files, the parser uses column headers (if present) or generates column indices as field names. Your source field names in mappings must match exactly: + +**CORRECT Example with Headers:** +CSV file: +```csv +Customer Name,Order Date,Total Amount,Status +John Smith,2024-01-15,1000.50,Active +Jane Doe,2024-01-16,750.25,Pending +``` + +Becomes parsed data: +```json +{ + "Customer Name": "John Smith", + "Order Date": "2024-01-15", + "Total Amount": "1000.50", + "Status": "Active" +} +``` + +Your mappings should use: +```json +{ + "source_field": "Customer Name", // ✅ Correct - matches header exactly + "source_field": "Order Date", // ✅ Correct - matches header exactly + "source_field": "Total Amount", // ✅ Correct - matches header exactly + "source_field": "Status" // ✅ Correct - matches header exactly +} +``` + +**CORRECT Example without Headers:** +CSV file without headers uses column indices: +```csv +John Smith,2024-01-15,1000.50,Active +Jane Doe,2024-01-16,750.25,Pending +``` + +Becomes parsed data: +```json +{ + "0": "John Smith", + "1": "2024-01-15", + "2": "1000.50", + "3": "Active" +} +``` + +Your mappings should use: +```json +{ + "source_field": "0", // ✅ Correct - first column + "source_field": "1", // ✅ Correct - second column + "source_field": "2", // ✅ Correct - third column + "source_field": "3" // ✅ Correct - fourth column +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **Source Data Format** + - Delimiter character (comma, tab, pipe, semicolon, etc.) + - Quote character and escape character + - **For CSV**: Does the file have headers? Sample structure + - Text encoding (UTF-8, Windows-1252, etc.) + - Any rows to skip (metadata, blank lines) + - How null/empty values are represented + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source column → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + - Date/time format conversions + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or invalid data + - Duplicate handling strategy + - Row-level validation rules + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## CSV Structure Analysis + +When presented with CSV data, analyze: + +1. **Delimiter Detection**: What character separates the fields? +2. **Header Presence**: Does the first row contain column names? +3. **Quote Pattern**: Are fields quoted? What quote character is used? +4. **Data Types**: What types are present in each column? +5. **Null Representation**: How are empty/null values represented? +6. **Special Characters**: Are there embedded commas, quotes, or newlines? +7. **Encoding Issues**: Are there any character encoding problems? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + // CSV-specific parsing options + // delimiter, quote_char, has_header, skip_rows, etc. + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` +- `split`, `strip_quotes` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` +- `parse_number`, `parse_currency` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` +- `clean_whitespace`, `normalize_encoding` + +**Date/Time Operations:** +- `parse_date`, `format_date`, `date_component` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` +- `numeric_range`, `date_range` + +## CSV-Specific Best Practices + +1. **Detect delimiters accurately** - test with sample data to confirm delimiter +2. **Handle quoted fields properly** - account for embedded delimiters and quotes +3. **Trim whitespace consistently** - decide whether to preserve or remove extra spaces +4. **Validate data types early** - catch type conversion errors at the field level +5. **Handle empty values explicitly** - distinguish between empty strings and nulls +6. **Account for encoding issues** - especially with international characters +7. **Validate row structure** - ensure consistent column counts across rows + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes +9. **Handle CSV-specific edge cases** like malformed quotes, inconsistent delimiters +10. **Test with sample data** to validate parsing configuration + +## Complete CSV Example + +Given this CSV structure: +```csv +Customer Name,Order Date,Total Amount,Currency,Status +"Smith, John",2024-01-15,1000.50,USD,Active +"Doe, Jane",2024-01-16,750.25,EUR,Pending +"Johnson, Bob",2024-01-17,,USD,Cancelled +``` + +The parser will: +1. Use `delimiter: ","` and `quote_char: "\""` to parse fields correctly +2. Use `has_header: true` to treat first row as column names +3. Create this parsed data structure for each record: + ```json + { + "Customer Name": "Smith, John", + "Order Date": "2024-01-15", + "Total Amount": "1000.50", + "Currency": "USD", + "Status": "Active" + } + ``` + +Generate this COMPLETE configuration: +```json +{ + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + "delimiter": ",", + "quote_char": "\"", + "has_header": true, + "trim_whitespace": true, + "null_values": ["", "NULL"] + } + }, + "mappings": [ + { + "source_field": "Customer Name", // ✅ Matches header exactly + "target_field": "customer_name", + "transforms": [{"type": "trim"}] + }, + { + "source_field": "Order Date", // ✅ Matches header exactly + "target_field": "order_date", + "transforms": [{"type": "to_date", "format": "YYYY-MM-DD"}], + "validation": [{"type": "required"}] + }, + { + "source_field": "Total Amount", // ✅ Matches header exactly + "target_field": "amount", + "transforms": [ + {"type": "to_float"}, + {"type": "default", "value": 0.0} + ] + }, + { + "source_field": "Currency", // ✅ Matches header exactly + "target_field": "currency_code", + "transforms": [{"type": "upper"}], + "validation": [{"type": "in_list", "values": ["USD", "EUR", "GBP"]}] + }, + { + "source_field": "Status", // ✅ Matches header exactly + "target_field": "order_status", + "transforms": [{"type": "lower"}] + } + ] +} +``` + +**KEY RULE: source_field names must match the column headers exactly, or use column indices (0, 1, 2, etc.) for files without headers.** + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the CSV structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to delimiter detection, header identification, quoting patterns, and data type inference: + +{{sample}} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/diagnose-json.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/diagnose-json.txt new file mode 100644 index 00000000..2fe6341d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/diagnose-json.txt @@ -0,0 +1,327 @@ +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for data import pipelines, with particular expertise in JSON processing and JSONPath expressions. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured JSON data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## JSON Processing Expertise + +When working with JSON data, you must: + +1. **Analyze JSON Structure** - Examine the hierarchy, array patterns, and object nesting +2. **Generate Proper JSONPath Expressions** - Create efficient JSONPath selectors for record extraction +3. **Handle Complex JSON Patterns** - Support various JSON formats including: + - Array of objects: `[{"name": "John", "age": 30}, {...}]` + - Nested object arrays: `{"data": {"records": [{"id": 1}, {...}]}}` + - Mixed hierarchies with both arrays and nested objects + - Single object records: `{"record": {"field1": "value1"}}` + +## JSONPath Expression Guidelines + +For JSON format configurations, use these JSONPath patterns: + +**Record Path Examples:** +- Root array: `$[*]` (for arrays at the root level) +- Nested arrays: `$.data.records[*]` or `$.response.items[*]` +- Single object: `$.record` (when there's one record per file) +- Deep nesting: `$.data.results.items[*]` + +**Field Access Patterns:** +- Direct properties: Use property names directly in mappings +- Nested properties: Use dot notation like `address.street` or `contact.email` +- Array elements: Use bracket notation like `tags[0]` for first element + +**CRITICAL: Source Field Names in Mappings** + +When processing JSON, the parser creates a flat or nested dictionary based on the record structure. Your source field names in mappings must match the actual property names in the parsed records: + +**CORRECT Example:** +```json +{ + "Country or Area": "Albania", + "Trade (USD)": "1000.50", + "metadata": { + "source": "UN", + "year": 2024 + } +} +``` + +Your mappings should use: +```json +{ + "source_field": "Country or Area", // ✅ Correct - matches property name + "source_field": "Trade (USD)", // ✅ Correct - matches property name + "source_field": "metadata.source", // ✅ Correct - nested property access + "source_field": "metadata.year" // ✅ Correct - nested property access +} +``` + +**JSON Format Configuration Template:** +```json +{ + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + "record_path": "$[*]", // JSONPath to extract records + "flatten_nested": true, // Whether to flatten nested objects + "array_handling": "expand" // How to handle arrays: expand, first, concat + } + } +} +``` + +**Alternative JSON Options:** +```json +{ + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + "record_path": "$.data.items[*]", // For nested array structures + "flatten_nested": false, // Keep nested structure + "null_value_handling": "skip" // skip, empty_string, or preserve + } + } +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **Source Data Format** + - JSON structure type (array of objects, nested objects, single records) + - **For JSON**: Sample structure, nesting patterns, array locations + - Sample data or field descriptions + - Any format-specific details (encoding, special null handling, etc.) + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source field → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + - Nested object flattening requirements + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or null values + - Duplicate handling strategy + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## JSON Structure Analysis + +When presented with JSON data, analyze: + +1. **Root Structure**: Is it an array, object, or nested structure? +2. **Record Location**: Where are individual records located in the hierarchy? +3. **Field Pattern**: How are field names and values structured? + - Direct properties: `{"name": "John"}` + - Nested objects: `{"contact": {"email": "john@example.com"}}` + - Arrays: `{"tags": ["red", "blue"]}` + - Mixed types: `{"data": [{"id": 1, "details": {"name": "John"}}]}` +4. **Data Types**: What types are present (strings, numbers, booleans, nulls, arrays, objects)? +5. **Hierarchy Depth**: How deeply nested are the records and fields? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + // JSON-specific parsing options + // record_path (JSONPath), flatten_nested, array_handling, etc. + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` +- `flatten_object`, `extract_array_element`, `join_array` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` + +## JSON-Specific Best Practices + +1. **Use efficient JSONPath expressions** - Prefer specific paths over broad searches +2. **Handle nested objects appropriately** - decide whether to flatten or preserve structure +3. **Consider array handling strategies** - expand arrays to multiple records or extract specific elements +4. **Account for null vs undefined** values in field mappings +5. **Handle mixed data types** within the same field across records +6. **Use appropriate flattening** for deeply nested structures + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes +9. **Handle JSON-specific edge cases** like empty arrays, null objects, mixed types + +## Complete JSON Example + +Given this JSON structure: +```json +{ + "data": { + "records": [ + { + "Country": "USA", + "Year": 2024, + "Amount": 1000.50, + "metadata": { + "source": "World Bank", + "confidence": 0.95 + } + } + ] + } +} +``` + +The parser will: +1. Use `record_path: "$.data.records[*]"` to extract record objects from the array +2. Create this parsed data structure for each record: + ```json + { + "Country": "USA", + "Year": 2024, + "Amount": 1000.50, + "metadata.source": "World Bank", // If flattened + "metadata.confidence": 0.95 // If flattened + } + ``` + +Generate this COMPLETE configuration: +```json +{ + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + "record_path": "$.data.records[*]", + "flatten_nested": true, + "array_handling": "expand" + } + }, + "mappings": [ + { + "source_field": "Country", // ✅ Matches property name + "target_field": "country_name" + }, + { + "source_field": "Year", // ✅ Matches property name + "target_field": "year", + "transforms": [{"type": "to_int"}] + }, + { + "source_field": "Amount", // ✅ Matches property name + "target_field": "amount", + "transforms": [{"type": "to_float"}] + }, + { + "source_field": "metadata.source", // ✅ Flattened nested field + "target_field": "data_source" + } + ] +} +``` + +**KEY RULE: source_field names must match the actual property names in the parsed JSON records, using dot notation for nested properties when flattened.** + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the JSON structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to JSON hierarchy, object patterns, array structures, and generate appropriate JSONPath expressions: + +{{sample}} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/diagnose-structured-data.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/diagnose-structured-data.txt new file mode 100644 index 00000000..84c4b8be --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/diagnose-structured-data.txt @@ -0,0 +1,309 @@ + +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for data import pipelines, with particular expertise in XML processing and XPath expressions. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## XML Processing Expertise + +When working with XML data, you must: + +1. **Analyze XML Structure** - Examine the hierarchy, namespaces, and element patterns +2. **Generate Proper XPath Expressions** - Create efficient XPath selectors for record extraction +3. **Handle Complex XML Patterns** - Support various XML formats including: + - Standard element structures: `John` + - Attribute-based fields: `USA` + - Mixed content and nested hierarchies + - Namespaced XML documents + +## XPath Expression Guidelines + +For XML format configurations, use these XPath patterns: + +**Record Path Examples:** +- Simple records: `//record` or `//customer` +- Nested records: `//data/records/record` or `//customers/customer` +- Absolute paths: `/ROOT/data/record` (will be converted to relative paths automatically) +- With namespaces: `//ns:record` or `//soap:Body/data/record` + +**Field Attribute Patterns:** +- When fields use name attributes: set `field_attribute: "name"` for `value` +- For other attribute patterns: set appropriate attribute name + +**CRITICAL: Source Field Names in Mappings** + +When using `field_attribute`, the XML parser extracts field names from the attribute values and creates a flat dictionary. Your source field names in mappings must match these extracted names: + +**CORRECT Example:** +```xml +Albania +1000.50 +``` + +Becomes parsed data: +```json +{ + "Country or Area": "Albania", + "Trade (USD)": "1000.50" +} +``` + +So your mappings should use: +```json +{ + "source_field": "Country or Area", // ✅ Correct - matches parsed field name + "source_field": "Trade (USD)" // ✅ Correct - matches parsed field name +} +``` + +**INCORRECT Example:** +```json +{ + "source_field": "Field[@name='Country or Area']", // ❌ Wrong - XPath not needed here + "source_field": "field[@name='Trade (USD)']" // ❌ Wrong - XPath not needed here +} +``` + +**XML Format Configuration Template:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//data/record", // XPath to find record elements + "field_attribute": "name" // For value pattern + } + } +} +``` + +**Alternative XML Options:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//customer", // Direct element-based records + // No field_attribute needed for standard XML + } + } +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **Source Data Format** + - File type (CSV, JSON, XML, Excel, fixed-width, etc.) + - **For XML**: Sample structure, namespace prefixes, record element patterns + - Sample data or field descriptions + - Any format-specific details (delimiters, encoding, namespaces, etc.) + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source field → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or invalid data + - Duplicate handling strategy + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## XML Structure Analysis + +When presented with XML data, analyze: + +1. **Document Root**: What is the root element? +2. **Record Container**: Where are individual records located? +3. **Field Pattern**: How are field names and values structured? + - Direct child elements: `John` + - Attribute-based: `John` + - Mixed patterns +4. **Namespaces**: Are there any namespace prefixes? +5. **Hierarchy Depth**: How deeply nested are the records? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "[csv|json|xml|fixed-width|excel]", + "encoding": "utf-8", + "options": { + // Format-specific parsing options + // For XML: record_path (XPath), field_attribute (if applicable) + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` + +## XML-Specific Best Practices + +1. **Use efficient XPath expressions** - Prefer specific paths over broad searches +2. **Handle namespace prefixes** when present +3. **Identify field attribute patterns** correctly +4. **Test XPath expressions** mentally against the provided structure +5. **Consider XML element vs attribute data** in field mappings +6. **Account for mixed content** and nested structures + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes + +## Complete XML Example + +Given this XML structure: +```xml + + + + USA + 2024 + 1000.50 + + + +``` + +The parser will: +1. Use `record_path: "/ROOT/data/record"` to find record elements +2. Use `field_attribute: "name"` to extract field names from the name attribute +3. Create this parsed data structure: `{"Country": "USA", "Year": "2024", "Amount": "1000.50"}` + +Generate this COMPLETE configuration: +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "/ROOT/data/record", + "field_attribute": "name" + } + }, + "mappings": [ + { + "source_field": "Country", // ✅ Matches parsed field name + "target_field": "country_name" + }, + { + "source_field": "Year", // ✅ Matches parsed field name + "target_field": "year", + "transforms": [{"type": "to_int"}] + }, + { + "source_field": "Amount", // ✅ Matches parsed field name + "target_field": "amount", + "transforms": [{"type": "to_float"}] + } + ] +} +``` + +**KEY RULE: source_field names must match the extracted field names, NOT the XML element structure.** + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the XML structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to XML hierarchy, element patterns, and generate appropriate XPath expressions: + +{{sample}} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/diagnose-xml.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/diagnose-xml.txt new file mode 100644 index 00000000..a3d8efa4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/diagnose-xml.txt @@ -0,0 +1,453 @@ +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for XML data import pipelines, with particular expertise in XML processing and XPath expressions. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured XML data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## XML Processing Expertise + +When working with XML data, you must: + +1. **Analyze XML Structure** - Examine the hierarchy, namespaces, and element patterns +2. **Generate Proper XPath Expressions** - Create efficient XPath selectors for record extraction +3. **Handle Complex XML Patterns** - Support various XML formats including: + - Standard element structures: `John` + - Attribute-based fields: `USA` + - Mixed content and nested hierarchies + - Namespaced XML documents + - CDATA sections and text nodes + +## XPath Expression Guidelines + +For XML format configurations, use these XPath patterns: + +**Record Path Examples:** +- Simple records: `//record` or `//customer` +- Nested records: `//data/records/record` or `//customers/customer` +- Absolute paths: `/ROOT/data/record` +- With namespaces: `//ns:record` or `//soap:Body/data/record` +- Attribute-filtered: `//record[@type='customer']` + +**Field Attribute Patterns:** +- When fields use name attributes: set `field_attribute: "name"` for `value` +- For other attribute patterns: set appropriate attribute name like `field_attribute: "id"` or `field_attribute: "type"` + +## CRITICAL: Source Field Names in Mappings + +The behavior depends on whether you use `field_attribute`: + +### With field_attribute (Attribute-Based Fields) + +When using `field_attribute`, the XML parser extracts field names from the attribute values and creates a flat dictionary: + +**Example:** +```xml + + Albania + 1000.50 + Active + +``` + +Parser configuration: +```json +{ + "record_path": "//record", + "field_attribute": "name" +} +``` + +Becomes parsed data: +```json +{ + "Country or Area": "Albania", + "Trade (USD)": "1000.50", + "Status": "Active" +} +``` + +Your mappings should use: +```json +{ + "source_field": "Country or Area", // ✅ Correct - matches parsed field name + "source_field": "Trade (USD)", // ✅ Correct - matches parsed field name + "source_field": "Status" // ✅ Correct - matches parsed field name +} +``` + +### Without field_attribute (Element-Based Fields) + +When NOT using `field_attribute`, use direct element names or XPath expressions: + +**Example:** +```xml + + Albania + 1000.50 + Active + + UN + 2024 + + +``` + +Parser configuration: +```json +{ + "record_path": "//record" + // No field_attribute specified +} +``` + +Your mappings should use: +```json +{ + "source_field": "country", // ✅ Direct element name + "source_field": "trade_amount", // ✅ Direct element name + "source_field": "metadata/source", // ✅ Nested element path + "source_field": "metadata/year" // ✅ Nested element path +} +``` + +## XML Format Configuration Templates + +**For Attribute-Based Fields:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//data/record", // XPath to find record elements + "field_attribute": "name", // Extract field names from 'name' attribute + "namespace_prefixes": { // Optional: define namespaces + "ns": "http://example.com/namespace" + } + } + } +} +``` + +**For Element-Based Fields:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//customer", // XPath to find record elements + "preserve_namespaces": true, // Optional: keep namespace info + "ignore_attributes": false // Optional: include element attributes + } + } +} +``` + +**For Complex XML with Namespaces:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//soap:Body//data:record", + "namespace_prefixes": { + "soap": "http://schemas.xmlsoap.org/soap/envelope/", + "data": "http://example.com/data" + }, + "field_attribute": "name" + } + } +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **XML Structure Details** + - Sample XML structure or schema + - Root element and record location + - Field organization (elements vs attributes) + - Namespace declarations and prefixes + - Text encoding (UTF-8, ISO-8859-1, etc.) + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source field → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or invalid data + - Duplicate handling strategy + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## XML Structure Analysis + +When presented with XML data, analyze: + +1. **Document Root**: What is the root element? +2. **Namespaces**: Are there namespace declarations? What prefixes are used? +3. **Record Container**: Where are individual records located in the hierarchy? +4. **Field Pattern**: How are field names and values structured? + - Direct child elements: `John` + - Attribute-based: `John` + - Mixed patterns: `John` +5. **Data Location**: Are values in element text, attributes, or both? +6. **Hierarchy Depth**: How deeply nested are the records? +7. **Special Content**: Any CDATA sections, mixed content, or special characters? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + // XML-specific parsing options + // record_path (XPath), field_attribute (if applicable), namespace_prefixes + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` +- `strip_cdata`, `decode_html_entities` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` +- `parse_xml_date`, `parse_iso_date` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` +- `extract_attribute`, `get_text_content` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` +- `valid_xml_name`, `namespace_check` + +## XML-Specific Best Practices + +1. **Use efficient XPath expressions** - Prefer specific paths over broad searches like `//` when possible +2. **Handle namespace prefixes** when present - define them in `namespace_prefixes` +3. **Identify field attribute patterns** correctly - determine if using `field_attribute` +4. **Test XPath expressions** mentally against the provided structure +5. **Consider XML element vs attribute data** in field mappings +6. **Account for mixed content** and nested structures +7. **Handle CDATA sections** and special characters properly +8. **Preserve or normalize whitespace** as appropriate +9. **Consider XML schema validation** if XSD is available + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes +9. **Handle XML-specific edge cases** like empty elements, mixed content, processing instructions + +## Complete XML Examples + +### Example 1: Attribute-Based Fields + +Given this XML structure: +```xml + + + + USA + 2024 + 1000.50 + + + +``` + +The parser will: +1. Use `record_path: "//data/record"` to find record elements +2. Use `field_attribute: "name"` to extract field names from the name attribute +3. Create this parsed data structure: `{"Country": "USA", "Year": "2024", "Amount": "1000.50"}` + +Generate this configuration: +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//data/record", + "field_attribute": "name" + } + }, + "mappings": [ + { + "source_field": "Country", // ✅ Matches parsed field name + "target_field": "country_name" + }, + { + "source_field": "Year", // ✅ Matches parsed field name + "target_field": "year", + "transforms": [{"type": "to_int"}] + }, + { + "source_field": "Amount", // ✅ Matches parsed field name + "target_field": "amount", + "transforms": [{"type": "to_float"}] + } + ] +} +``` + +### Example 2: Element-Based Fields + +Given this XML structure: +```xml + + + John Smith + john@example.com +
+ 123 Main St + New York +
+ + + 456 + 100.50 + + +
+
+``` + +Generate this configuration: +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//customer" + } + }, + "mappings": [ + { + "source_field": "name", // ✅ Direct element name + "target_field": "customer_name" + }, + { + "source_field": "email", // ✅ Direct element name + "target_field": "email_address" + }, + { + "source_field": "address/street", // ✅ Nested element path + "target_field": "street_address" + }, + { + "source_field": "address/city", // ✅ Nested element path + "target_field": "city" + }, + { + "source_field": "@id", // ✅ Attribute reference + "target_field": "customer_id", + "transforms": [{"type": "to_int"}] + } + ] +} +``` + +**KEY RULES:** +- With `field_attribute`: source_field names must match the extracted attribute values +- Without `field_attribute`: use direct element names, nested paths, or XPath expressions +- Use `@attribute` notation for XML element attributes + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the XML structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to XML hierarchy, element patterns, namespace usage, and generate appropriate XPath expressions: + +{{sample}} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/gemini.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/gemini.jsonnet new file mode 100644 index 00000000..b9a1e0c0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/gemini.jsonnet @@ -0,0 +1,42 @@ +// For VertexAI Gemini. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/graphql-generation.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/graphql-generation.txt new file mode 100644 index 00000000..58af61dc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/graphql-generation.txt @@ -0,0 +1,59 @@ +You are a GraphQL query generation expert. Given a natural language question and provided database schemas, generate a valid GraphQL query embedded in valid JSON. + +## Question: +{{ question }} + +## Provided Schemas: +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %}- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif %}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif %}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ field.enum_values|join(', ') }}]{% endif %} +{% endfor %} +{% endfor %} + +## GraphQL Query Rules: +1. Use the schema names as GraphQL query fields (e.g., `customers`, `orders`) +2. Apply filters using the `where` parameter with nested filter objects +3. Available filter operators per field type: + - String fields: `eq`, `contains`, `startsWith`, `endsWith`, `in`, `not`, `not_in` + - Integer/Float fields: `eq`, `gt`, `gte`, `lt`, `lte`, `in`, `not`, `not_in` +4. Use `order_by` for sorting (field name as string) +5. Use `direction` for sort direction: `ASC` or `DESC` +6. Use `limit` to restrict number of results +7. Select specific fields in the query body + +## Task Instructions: +1. Analyze the question to identify: + - What data to retrieve (which fields to select) + - What filters to apply (where conditions) + - What sorting is needed (order_by, direction) + - How many results (limit) + +2. Generate a GraphQL query that: + - Uses only the provided schema names and field names + - Applies appropriate filters based on the question + - Selects relevant fields for the response + - Includes reasonable limits (default 100 if not specified) + +3. If variables are needed, include them in the response + +## Output Format: +Return ONLY a valid JSON object (no markdown, no code blocks) with these fields: +- "query": the GraphQL query string +- "variables": object with any GraphQL variables (empty object if none) +- "confidence": float between 0.0-1.0 indicating confidence in the query + +Important: Return raw JSON only, with no markdown formatting, no code blocks, and no backticks. + +## Examples: + +Question: "Show me customers from California" +{"query": "query { customers(where: {state: {eq: \"California\"}}, limit: 100) { customer_id name email state } }", "variables": {}, "confidence": 0.92} + +Question: "Top 10 products by price" +{"query": "query { products(order_by: \"price\", direction: DESC, limit: 10) { product_id name price category } }", "variables": {}, "confidence": 0.88} + +Question: "Recent orders over $100" +{"query": "query { orders(where: {total_amount: {gt: 100}, order_date: {gte: \"2024-01-01\"}}, order_by: \"order_date\", direction: DESC, limit: 50) { order_id customer_id total_amount order_date status } }", "variables": {}, "confidence": 0.96} + +Now generate the GraphQL query for the question above. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/mixtral.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/mixtral.jsonnet new file mode 100644 index 00000000..cd56e7ef --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/mixtral.jsonnet @@ -0,0 +1,42 @@ +// For Mixtral. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/ontology-prompt.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/ontology-prompt.txt new file mode 100644 index 00000000..6be255b7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/ontology-prompt.txt @@ -0,0 +1,54 @@ +You are a knowledge extraction expert. Extract structured triples from text using ONLY the provided ontology elements. + +## Ontology Classes: + +{% for class_id, class_def in classes.items() %} +- **{{class_id}}**{% if class_def.subclass_of %} (subclass of {{class_def.subclass_of}}){% endif %}{% if class_def.comment %}: {{class_def.comment}}{% endif %} +{% endfor %} + +## Object Properties (connect entities): + +{% for prop_id, prop_def in object_properties.items() %} +- **{{prop_id}}**{% if prop_def.domain and prop_def.range %} ({{prop_def.domain}} → {{prop_def.range}}){% endif %}{% if prop_def.comment %}: {{prop_def.comment}}{% endif %} +{% endfor %} + +## Datatype Properties (entity attributes): + +{% for prop_id, prop_def in datatype_properties.items() %} +- **{{prop_id}}**{% if prop_def.domain and prop_def.range %} ({{prop_def.domain}} → {{prop_def.range}}){% endif %}{% if prop_def.comment %}: {{prop_def.comment}}{% endif %} +{% endfor %} + +## Text to Analyze: + +{{text}} + +## Extraction Rules: + +1. Only use classes defined above for entity types +2. Only use properties defined above for relationships and attributes +3. Respect domain and range constraints where specified +4. For class instances, use `rdf:type` as the predicate +5. Include `rdfs:label` for new entities to provide human-readable names +6. Extract all relevant triples that can be inferred from the text +7. Use entity URIs or meaningful identifiers as subjects/objects + +## Output Format: + +Return ONLY a valid JSON array (no markdown, no code blocks) containing objects with these fields: +- "subject": the subject entity (URI or identifier) +- "predicate": the property (from ontology or rdf:type/rdfs:label) +- "object": the object entity or literal value + +Important: Return raw JSON only, with no markdown formatting, no code blocks, and no backticks. + +## Example Output: + +[ + {"subject": "recipe:cornish-pasty", "predicate": "rdf:type", "object": "Recipe"}, + {"subject": "recipe:cornish-pasty", "predicate": "rdfs:label", "object": "Cornish Pasty"}, + {"subject": "recipe:cornish-pasty", "predicate": "has_ingredient", "object": "ingredient:flour"}, + {"subject": "ingredient:flour", "predicate": "rdf:type", "object": "Ingredient"}, + {"subject": "ingredient:flour", "predicate": "rdfs:label", "object": "plain flour"} +] + +Now extract triples from the text above. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/openai.jsonnet new file mode 100644 index 00000000..5d232337 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/openai.jsonnet @@ -0,0 +1,42 @@ +// For OpenAI LLMs. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/schema-selection.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/schema-selection.txt new file mode 100644 index 00000000..39b180e5 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/schema-selection.txt @@ -0,0 +1,25 @@ +You are a database schema selection expert. Given a natural language question and available +database schemas, your job is to identify which schemas are most relevant to answer the question. + +## Available Schemas: +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}): {{ field.description }} +{% endfor %} + +{% endfor %} + +## Question: +{{ question }} + +## Instructions: +1. Analyze the question to understand what data is being requested +2. Examine each schema to understand what data it contains +3. Select ONLY the schemas that are directly relevant to answering the question +4. Return your answer as a JSON array of schema names + +## Response Format: +Return ONLY a JSON array of schema names, nothing else. +Example: ["customers", "orders", "products"] diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/slm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/slm.jsonnet new file mode 100644 index 00000000..48eb96d0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/prompts/slm.jsonnet @@ -0,0 +1,44 @@ +// For SLM. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + + + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/stores/cassandra.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/stores/cassandra.jsonnet new file mode 100644 index 00000000..2a9d6d7a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/stores/cassandra.jsonnet @@ -0,0 +1,40 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + "cassandra" +: { + + create:: function(engine) + + local vol = engine.volume("cassandra").with_size("20G"); + + local container = + engine.container("cassandra") + .with_image(images.cassandra) + .with_environment({ + JVM_OPTS: "-Xms300M -Xmx300M -Dcassandra.skip_wait_for_gossip_to_settle=0", + }) + .with_limits("1.0", "1000M") + .with_reservations("0.5", "1000M") + .with_port(9042, 9042, "cassandra") + .with_volume_mount(vol, "/var/lib/cassandra"); + + local containerSet = engine.containers( + "cassandra", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9042, 9042, "api"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/stores/falkordb.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/stores/falkordb.jsonnet new file mode 100644 index 00000000..78509a43 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/stores/falkordb.jsonnet @@ -0,0 +1,39 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + "falkordb" +: { + + create:: function(engine) + + local vol = engine.volume("falkordb").with_size("20G"); + + local container = + engine.container("falkordb") + .with_image(images.falkordb) + .with_limits("1.0", "768M") + .with_reservations("0.5", "768M") + .with_port(6379, 6379, "api") + .with_port(3010, 3000, "ui") + .with_volume_mount(vol, "/data"); + + local containerSet = engine.containers( + "falkordb", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(6379, 6379, "api") + .with_port(3010, 3010, "ui"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/stores/memgraph.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/stores/memgraph.jsonnet new file mode 100644 index 00000000..70ad127a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/stores/memgraph.jsonnet @@ -0,0 +1,71 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + "memgraph" +: { + + create:: function(engine) + + local vol = engine.volume("memgraph").with_size("20G"); + + local container = + engine.container("memgraph") + .with_image(images.memgraph_mage) + .with_environment({ + MEMGRAPH: "--storage-properties-on-edges=true --storage-enable-edges-metadata=true" + }) + .with_limits("1.0", "1000M") + .with_reservations("0.5", "1000M") + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2") + .with_volume_mount(vol, "/var/lib/memgraph"); + + local containerSet = engine.containers( + "memgraph", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + + "memgraph-lab" +: { + + create:: function(engine) + + local container = + engine.container("lab") + .with_image(images.memgraph_lab) + .with_environment({ + QUICK_CONNECT_MG_HOST: "memgraph", + QUICK_CONNECT_MG_PORT: "7687", + }) + .with_limits("1.0", "512M") + .with_reservations("0.5", "512M") + .with_port(3010, 3000, "http"); + + local containerSet = engine.containers( + "lab", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(3010, 3010, "http"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/stores/milvus.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/stores/milvus.jsonnet new file mode 100644 index 00000000..1c3e3734 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/stores/milvus.jsonnet @@ -0,0 +1,90 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local minio = import "stores/minio.jsonnet"; + +minio { + + etcd +: { + + create:: function(engine) + + local vol = engine.volume("etcd").with_size("20G"); + + local container = + engine.container("etcd") + .with_image(images.etcd) + .with_command([ + "etcd", + "-advertise-client-urls=http://127.0.0.1:2379", + "-listen-client-urls", + "http://0.0.0.0:2379", + "--data-dir", + "/etcd", + ]) + .with_environment({ + ETCD_AUTO_COMPACTION_MODE: "revision", + ETCD_AUTO_COMPACTION_RETENTION: "1000", + ETCD_QUOTA_BACKEND_BYTES: "4294967296", + ETCD_SNAPSHOT_COUNT: "50000" + }) + .with_limits("1.0", "128M") + .with_reservations("0.25", "128M") + .with_port(2379, 2379, "api") + .with_volume_mount(vol, "/etcd"); + + local containerSet = engine.containers( + "etcd", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(2379, 2379, "api"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + + milvus +: { + + create:: function(engine) + + local vol = engine.volume("milvus").with_size("20G"); + + local container = + engine.container("milvus") + .with_image(images.milvus) + .with_command([ + "milvus", "run", "standalone" + ]) + .with_environment({ + ETCD_ENDPOINTS: "etcd:2379", + MINIO_ADDRESS: "minio:9000", + }) + .with_limits("1.0", "256M") + .with_reservations("0.5", "256M") + .with_port(9091, 9091, "api") + .with_port(19530, 19530, "api2") + .with_volume_mount(vol, "/var/lib/milvus"); + + local containerSet = engine.containers( + "milvus", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9091, 9091, "api") + .with_port(19530, 19530, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/stores/minio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/stores/minio.jsonnet new file mode 100644 index 00000000..8a6aa149 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/stores/minio.jsonnet @@ -0,0 +1,49 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + minio +: { + + create:: function(engine) + + local vol = engine.volume("minio-data").with_size("20G"); + + local container = + engine.container("minio") + .with_image(images.minio) + .with_command([ + "minio", + "server", + "/minio_data", + "--console-address", + ":9001", + ]) + .with_environment({ + MINIO_ROOT_USER: "minioadmin", + MINIO_ROOT_PASSWORD: "minioadmin", + }) + .with_limits("0.5", "128M") + .with_reservations("0.25", "128M") + .with_port(9000, 9000, "api") + .with_port(9001, 9001, "console") + .with_volume_mount(vol, "/minio_data"); + + local containerSet = engine.containers( + "minio", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9000, 9000, "api") + .with_port(9001, 9001, "console"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/stores/neo4j.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/stores/neo4j.jsonnet new file mode 100644 index 00000000..3a8bb783 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/stores/neo4j.jsonnet @@ -0,0 +1,47 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + "neo4j" +: { + + create:: function(engine) + + local vol = engine.volume("neo4j").with_size("20G"); + + local container = + engine.container("neo4j") + .with_image(images.neo4j) + .with_environment({ + NEO4J_AUTH: "neo4j/password", + NEO4J_server_memory_pagecache_size: "512m", + NEO4J_server_memory_heap_max__size: "512m", + // NEO4J_server_bolt_listen__address: "0.0.0.0:7687", + // NEO4J_server_default__listen__address: "0.0.0.0", + // NEO4J_server_http_listen__address: "0.0.0.0:7474", + }) + .with_limits("1.0", "1536M") + .with_reservations("0.5", "1536M") + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2") + .with_volume_mount(vol, "/data"); + + local containerSet = engine.containers( + "neo4j", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/stores/pinecone.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/stores/pinecone.jsonnet new file mode 100644 index 00000000..2bef70fb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/stores/pinecone.jsonnet @@ -0,0 +1,45 @@ +local images = import "values/images.jsonnet"; + +{ + + "pinecone" +: { + + create:: function(engine) + + local container = + engine.container("pinecone") + .with_image(images.pinecone) + .with_environment({ + + PORT: "5080", + + INDEX_TYPE: "serverless", + + // Dimension is fixed, 384 is right for miniLM + // sentence embedding + DIMENSION: 384, + + METRIC: "cosine" + + }) + .with_limits("1.0", "256M") + .with_reservations("0.5", "256M") + .with_port(5080, 5080, "api"); + + local containerSet = engine.containers( + "pinecone", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(5080, 5080, "api"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/stores/qdrant.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/stores/qdrant.jsonnet new file mode 100644 index 00000000..9e807632 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/stores/qdrant.jsonnet @@ -0,0 +1,39 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + "qdrant" +: { + + create:: function(engine) + + local vol = engine.volume("qdrant").with_size("20G"); + + local container = + engine.container("qdrant") + .with_image(images.qdrant) + .with_limits("1.0", "1024M") + .with_reservations("0.5", "1024M") + .with_port(6333, 6333, "api") + .with_port(6334, 6334, "api2") + .with_volume_mount(vol, "/qdrant/storage"); + + local containerSet = engine.containers( + "qdrant", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(6333, 6333, "api") + .with_port(6334, 6334, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/trustgraph-config.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/trustgraph-config.jsonnet new file mode 100644 index 00000000..1cfd3002 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/trustgraph-config.jsonnet @@ -0,0 +1,80 @@ +// TrustGraph Main Configuration +// Clean, modular composition of TrustGraph configuration +// Uses specialized modules for different aspects of config building + +// Import dependencies +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local default_prompts = import "prompts/default-prompts.jsonnet"; +local token_costs = import "values/token-costs.jsonnet"; +local flow_classes = import "flows/flow-classes.jsonnet"; +local config_composer = import "config/config-composer.jsonnet"; +local interface_descriptions = import "config/interface-descriptions.jsonnet"; +local tools = import "config/tools.jsonnet"; +local temperature_params = import "parameters/temperature-param-types.jsonnet"; +local chunking_params = import "parameters/chunking-param-types.jsonnet"; + +// Main configuration object +local configuration = { + + // Prompt templates + prompts:: default_prompts, + + // Tool definitions + tools:: tools, + + // MCP configuration + mcp:: {}, + + // Flow classes reference + "flow-classes":: flow_classes, + + // LLM model parameters + "llm-models" +:: {}, + + // Embeddings model parameters + "embeddings-models" +:: {}, + + // Default model and flow parameters + flow_init_parameters:: { + "llm-model": $["llm-models"].default, + "llm-rag-model": $["llm-models"].default, + "llm-temperature": "%0.3f" % $["parameter-types"]["llm-temperature"].default, + "llm-rag-temperature": "%0.3f" % $["parameter-types"]["llm-temperature"].default, + "chunk-size": std.toString($["parameter-types"]["chunk-size"].default), + "chunk-overlap": std.toString($["parameter-types"]["chunk-overlap"].default), + "embeddings-model": $["embeddings-models"].default, + }, + + // Interface descriptions for external endpoints + "interface-descriptions":: interface_descriptions, + + // Parameter type definitions + "parameter-types":: { + "llm-model": $["llm-models"], + "embeddings-model": $["embeddings-models"], + } + chunking_params + temperature_params, + + // Token costs + "token-costs":: token_costs, + + // Build the complete configuration using the composer + configuration:: config_composer.build({ + flow_classes: $["flow-classes"], + default_flow_class: "everything", + default_flow_id: "default", + flow_init_parameters: $["flow_init_parameters"], + prompts: $["prompts"], + tools: $["tools"], + mcp: $["mcp"], + interface_descriptions: $["interface-descriptions"], + parameter_types: $["parameter-types"], + token_costs: $["token-costs"], + }), + +} + default_prompts; + +// Export the final configuration +configuration \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/util/decode-config.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/util/decode-config.jsonnet new file mode 100644 index 00000000..503b5b6b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/util/decode-config.jsonnet @@ -0,0 +1,31 @@ + +local components = import "components.jsonnet"; + +local apply = function(p, components) + + local base = { + + with:: function(k, v) self + { + [k]:: v + }, + + with_params:: function(pars) + self + std.foldl( + function(obj, par) obj.with(par.key, par.value), + std.objectKeysValues(pars), + self + ), + + }; + + local component = base + components[p.name]; + + component.with_params(p.parameters); + +local decode = function(config) + local add = function(state, c) state + apply(c, components); + local patterns = std.foldl(add, config, {}); + patterns; + +decode + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/values/images.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/values/images.jsonnet new file mode 100644 index 00000000..5cf21ee1 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/values/images.jsonnet @@ -0,0 +1,31 @@ +local version = import "version.jsonnet"; +{ + cassandra: "docker.io/cassandra:4.1.10", + neo4j: "docker.io/neo4j:2025.08.0-community-bullseye", + pulsar: "docker.io/apachepulsar/pulsar:4.1.0", + pulsar_manager: "docker.io/apachepulsar/pulsar-manager:v0.4.0", + etcd: "quay.io/coreos/etcd:v3.6.4", + minio: "docker.io/minio/minio:RELEASE.2025-09-07T16-13-09Z", + milvus: "docker.io/milvusdb/milvus:v2.5.17", + prometheus: "docker.io/prom/prometheus:v3.5.0", + grafana: "docker.io/grafana/grafana:12.1.1", + trustgraph_base: "docker.io/trustgraph/trustgraph-base:" + version, + trustgraph_flow: "docker.io/trustgraph/trustgraph-flow:" + version, + trustgraph_ocr: "docker.io/trustgraph/trustgraph-ocr:" + version, + trustgraph_bedrock: "docker.io/trustgraph/trustgraph-bedrock:" + version, + trustgraph_vertexai: "docker.io/trustgraph/trustgraph-vertexai:" + version, + trustgraph_hf: "docker.io/trustgraph/trustgraph-hf:" + version, + trustgraph_mcp: "docker.io/trustgraph/trustgraph-mcp:" + version, + qdrant: "docker.io/qdrant/qdrant:v1.15.4", + memgraph_mage: "docker.io/memgraph/memgraph-mage:3.5", + memgraph_lab: "docker.io/memgraph/lab:3.5.0", + falkordb: "docker.io/falkordb/falkordb:v4.12.5", + "workbench-ui": "docker.io/trustgraph/workbench-ui:1.4.2", + "ddg-mcp-server": "docker.io/trustgraph/ddg-mcp-server:0.1.0", + "tgi-service-intel-xpu": "ghcr.io/huggingface/text-generation-inference:3.3.1-intel-xpu", + "tgi-service-cpu": "ghcr.io/huggingface/text-generation-inference:3.3.1-intel-cpu", + "tgi-service-gaudi": "ghcr.io/huggingface/text-generation-inference:sha-f140440-gaudi", + "vllm-service-intel-xpu": "docker.io/intel/vllm:0.8.0-xpu", + "vllm-service-gaudi": "docker.io/trustgraph/vllm-hpu:027f5645", + "vllm-service-nvidia": "docker.io/vllm/vllm-openai:latest", +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/values/token-costs.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/values/token-costs.jsonnet new file mode 100644 index 00000000..84c70373 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/values/token-costs.jsonnet @@ -0,0 +1,102 @@ +{ + "mistral.mistral-large-2407-v1:0": { + "model_name": "mistral.mistral-large-2407-v1:0", + "input_price": 0.000004, + "output_price": 0.000012 + }, + "meta.llama3-1-405b-instruct-v1:0": { + "model_name": "meta.llama3-1-405b-instruct-v1:0", + "input_price": 0.00000532, + "output_price": 0.000016 + }, + "mistral.mixtral-8x7b-instruct-v0:1": { + "model_name": "mistral.mixtral-8x7b-instruct-v0:1", + "input_price": 0.00000045, + "output_price": 0.0000007 + }, + "meta.llama3-1-70b-instruct-v1:0": { + "model_name": "meta.llama3-1-70b-instruct-v1:0", + "input_price": 0.00000099, + "output_price": 0.00000099 + }, + "meta.llama3-1-8b-instruct-v1:0": { + "model_name": "meta.llama3-1-8b-instruct-v1:0", + "input_price": 0.00000022, + "output_price": 0.00000022 + }, + "anthropic.claude-3-haiku-20240307-v1:0": { + "model_name": "anthropic.claude-3-haiku-20240307-v1:0", + "input_price": 0.00000025, + "output_price": 0.00000125 + }, + "anthropic.claude-3-5-sonnet-20240620-v1:0": { + "model_name": "anthropic.claude-3-5-sonnet-20240620-v1:0", + "input_price": 0.000003, + "output_price": 0.000015 + }, + "cohere.command-r-plus-v1:0": { + "model_name": "cohere.command-r-plus-v1:0", + "input_price": 0.0000030, + "output_price": 0.0000150 + }, + "ollama": { + "model_name": "ollama", + "input_price": 0, + "output_price": 0 + }, + "claude-3-haiku-20240307": { + "model_name": "claude-3-haiku-20240307", + "input_price": 0.00000025, + "output_price": 0.00000125 + }, + "claude-3-5-sonnet-20240620": { + "model_name": "claude-3-5-sonnet-20240620", + "input_price": 0.000003, + "output_price": 0.000015 + }, + "claude-3-opus-20240229": { + "model_name": "claude-3-opus-20240229", + "input_price": 0.000015, + "output_price": 0.000075 + }, + "claude-3-sonnet-20240229": { + "model_name": "claude-3-sonnet-20240229", + "input_price": 0.000003, + "output_price": 0.000015 + }, + "command-r-08-202": { + "model_name": "command-r-08-202", + "input_price": 0.0000025, + "output_price": 0.000010 + }, + "c4ai-aya-23-8b": { + "model_name": "c4ai-aya-23-8b", + "input_price": 0, + "output_price": 0 + }, + "llama.cpp": { + "model_name": "llama.cpp", + "input_price": 0, + "output_price": 0 + }, + "gpt-4o": { + "model_name": "gpt-4o", + "input_price": 0.000005, + "output_price": 0.000015 + }, + "gpt-4o-2024-08-06": { + "model_name": "gpt-4o-2024-08-06", + "input_price": 0.0000025, + "output_price": 0.000010 + }, + "gpt-4o-2024-05-13": { + "model_name": "gpt-4o-2024-05-13", + "input_price": 0.000005, + "output_price": 0.000015 + }, + "gpt-4o-mini": { + "model_name": "gpt-4o-mini", + "input_price": 0.00000015, + "output_price": 0.0000006 + } +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/values/url.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/values/url.jsonnet new file mode 100644 index 00000000..1bacb067 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/values/url.jsonnet @@ -0,0 +1,6 @@ +{ + pulsar: "pulsar://pulsar:6650", + pulsar_admin: "http://pulsar:8080", + milvus: "http://milvus:19530", + qdrant: "http://qdrant:6333", +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/zip-readme.md b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/zip-readme.md new file mode 100644 index 00000000..0b117792 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.6/zip-readme.md @@ -0,0 +1,28 @@ + +Note! this is a subset of possible configurations, to generate your own +launch config use the config util... + +- Production: https://config-ui.demo.trustgraph.ai +- Early release: https://dev.config-ui.demo.trustgraph.ai + +The config util auto-generates deployment instructions for your +configuration, so that's the recommended way to deploy. + +---------------------------------------------------------------------------- + +These are launch configurations for TrustGraph. See https://trustgraph.ai for +the quickstart using docker compose. + +Hint for Linux: There are files here which get mounted as volumes inside +Docker Compose containers. This may trigger SELinux rules on your system, to +permit access insider the containers, use a command like this... + +chcon -Rt svirt_sandbox_file_t grafana/ prometheus/ + +The file vertexai/private.json is a placeholder for real GCP credentials if +you are using the VertexAI LLM. If you're using that in Docker Compose, +replace with your real credentials, and don't forget to permit access if you +are using Linux: + +chcon -Rt svirt_sandbox_file_t vertexai/ + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/README.md b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/README.md new file mode 100644 index 00000000..23039e9a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/README.md @@ -0,0 +1,125 @@ + +# TrustGraph template generation + +There are two utilities here: + +- `generate`: Generates a single Docker Compose launch configuration + based on configuration you provide. +- `generate-all`: Generates the release bundle for releases. You won't + need to use this unless you are managing releases. + +## `generate-all` + +Previously, this generates a full set of all vector DB / triple store / LLM +combinations, and put them in a single ZIP file. But this got out of +hand, so at the time of writing, this generates a single configuraton +using Qdrant vector DB, Ollama LLM support and Cassandra for a triple store. + +The combinations are contained withing the code, it takes two arguments: +- output ZIP file (is over-written) +- TrustGraph version number + +``` +templates/generate-all output.zip 0.18.11 +``` + +## `generate` + +This utility takes a configuration file describing the components to bundle, +and outputs a Docker Compose YAML file. + +### Input configuration + +The input configuration is a JSON file, an array of components to pull into +the configuration. For each component, there is a name and a (possibly empty) +object describing addtional parameters for that component. + +Example: + +``` +[ + { + "name": "cassandra", + "parameters": {} + }, + { + "name": "pulsar", + "parameters": {} + }, + { + "name": "qdrant", + "parameters": {} + }, + { + "name": "embeddings-hf", + "parameters": {} + }, + { + "name": "graph-rag", + "parameters": {} + }, + { + "name": "grafana", + "parameters": {} + }, + { + "name": "trustgraph", + "parameters": {} + }, + { + "name": "googleaistudio", + "parameters": { + "googleaistudio-temperature": 0.3, + "googleaistudio-max-output-tokens": 2048, + "googleaistudio-model": "gemini-1.5-pro-002" + } + }, + { + "name": "prompt-template", + "parameters": {} + }, + { + "name": "override-recursive-chunker", + "parameters": { + "chunk-size": 1000, + "chunk-overlap": 50 + } + }, + { + "name": "workbench-ui", + "parameters": {} + }, + { + "name": "agent-manager-react", + "parameters": {} + } +] +``` + +If you want to make your own configuration you could try changing the +configuration above: +- Components which are essential: pulsar, trustgraph, graph-rag, grafana, + agent-manager-react +- You need a triple store, one of: cassandra, memgraph, falkordb, neo4j +- You need a vector store, one of: qdrant, pinecone +- You need an LLM, one of: azure, azure-openai, bedrock, claude, cohere, + llamafile, ollama, openai, vertexai. +- You need an embeddings implementation, one of: embeddings-hf, + embeddings-ollama +- Optionally add the Workbench tool: workbench-ui + +Components have over-ridable parameters, look in the component definition +in `templates/components/` to see what you can override. + +### Invocation + +Two parameters: +- The output ZIP file +- The version number + +The configuration file described above is provided on standard input + +``` +templates/generate out.zip 0.18.9 < config.json +``` + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/base/base.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/base/base.jsonnet new file mode 100644 index 00000000..9f82efb2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/base/base.jsonnet @@ -0,0 +1,3 @@ +{ + restart: "on-failure:100", +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components.jsonnet new file mode 100644 index 00000000..f1c0d176 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components.jsonnet @@ -0,0 +1,99 @@ +{ + + // Essentials + "trustgraph-base": import "components/trustgraph.jsonnet", + "rev-gateway": import "components/rev-gateway.jsonnet", + "pulsar": import "components/pulsar.jsonnet", + + // LLMs + "azure": import "components/azure.jsonnet", + "azure-openai": import "components/azure-openai.jsonnet", + "bedrock": import "components/bedrock.jsonnet", + "claude": import "components/claude.jsonnet", + "cohere": import "components/cohere.jsonnet", + "googleaistudio": import "components/googleaistudio.jsonnet", + "llamafile": import "components/llamafile.jsonnet", + "lmstudio": import "components/lmstudio.jsonnet", + "mistral": import "components/mistral.jsonnet", + "ollama": import "components/ollama.jsonnet", + "openai": import "components/openai.jsonnet", + "vertexai": import "components/vertexai.jsonnet", + "tgi": import "components/tgi.jsonnet", + "vllm": import "components/vllm.jsonnet", + + // LLMs for RAG. RAG components have been collapsed into the core + // component, so gone away. + "azure-rag": {}, + "azure-openai-rag": {}, + "bedrock-rag": {}, + "claude-rag": {}, + "cohere-rag": {}, + "googleaistudio-rag": {}, + "llamafile-rag": {}, + "lmstudio-rag": {}, + "mistral-rag": {}, + "ollama-rag": {}, + "openai-rag": {}, + "vertexai-rag": {}, + "tgi-rag": import "components/tgi-rag.jsonnet", + "vllm-rag": {}, + + "tgi-service-cpu": import "components/tgi-service-cpu.jsonnet", + "tgi-service-intel-gpu": import "components/tgi-service-intel-gpu.jsonnet", + "tgi-service-gaudi": import "components/tgi-service-gaudi.jsonnet", + + "vllm-service-intel-gpu": import "components/vllm-service-intel-gpu.jsonnet", + "vllm-service-gaudi": import "components/vllm-service-gaudi.jsonnet", + "vllm-service-nvidia": import "components/vllm-service-nvidia.jsonnet", + + // Embeddings + "embeddings-ollama": import "components/embeddings-ollama.jsonnet", + "embeddings-hf": import "components/embeddings-hf.jsonnet", + "embeddings-fastembed": import "components/embeddings-fastembed.jsonnet", + + // OCR options + "ocr": import "components/ocr.jsonnet", + "mistral-ocr": import "components/mistral-ocr.jsonnet", + + // Vector stores + "vector-store-milvus": import "components/vector-store-milvus.jsonnet", + "vector-store-qdrant": import "components/vector-store-qdrant.jsonnet", + "vector-store-pinecone": import "components/vector-store-pinecone.jsonnet", + + // Triples stores + "triple-store-cassandra": import "components/triple-store-cassandra.jsonnet", + "triple-store-neo4j": import "components/triple-store-neo4j.jsonnet", + "triple-store-falkordb": import "components/triple-store-falkordb.jsonnet", + "triple-store-memgraph": import "components/triple-store-memgraph.jsonnet", + + // Object stores + "row-store-cassandra": import "components/row-store-cassandra.jsonnet", + + // Observability support + "grafana": import "components/grafana.jsonnet", + "loki": import "components/loki.jsonnet", + + // Pulsar manager is a UI for Pulsar. Uses a LOT of memory + "pulsar-manager": import "components/pulsar-manager.jsonnet", + + "override-recursive-chunker": import "components/chunker-recursive.jsonnet", + + // The prompt manager + "prompt-overrides": import "components/prompt-overrides.jsonnet", + + // Archaic - part of core system, just making sure these don't + // cause a failure + "workbench-ui": {}, + "prompt-template": {}, + "agent-manager-react": {}, + "graph-rag": {}, + "document-rag": {}, + "librarian": {}, + + // Extra MCP services + "ddg-mcp-server": import "mcp/ddg-mcp-server.jsonnet", + + // Does nothing. But, can be a hack to overwrite parameters + "null": {}, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/agent-manager-react.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/agent-manager-react.jsonnet new file mode 100644 index 00000000..d99ae88c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/agent-manager-react.jsonnet @@ -0,0 +1,40 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "agent-manager" +: { + + create:: function(engine) + + local container = + engine.container("agent-manager") + .with_image(images.trustgraph_flow) + .with_command([ + "agent-manager-react", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "agent-manager", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/azure-openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/azure-openai.jsonnet new file mode 100644 index 00000000..4d5a70e8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/azure-openai.jsonnet @@ -0,0 +1,113 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/azure-openai.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["azure-openai-" + key]:: value, + }, + +// Strategy is to specify the model with the AZURE_MODEL environment +// variable. This isn't something that can just be specified dynamically, +// it has to match what was provisioned in Azure. + + "azure-openai-max-output-tokens":: 4192, + "azure-openai-temperature":: 0.0, + "azure-openai-models":: models, + + "llm-models" +:: $["azure-openai-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-openai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_MODEL", "azure-model") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure-openai", + "-p", + url.pulsar, + "-x", + std.toString($["azure-openai-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-openai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_MODEL", "azure-model") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure-openai", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["azure-openai-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/azure.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/azure.jsonnet new file mode 100644 index 00000000..f4db7500 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/azure.jsonnet @@ -0,0 +1,106 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/azure.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["azure-" + key]:: value, + }, + + "azure-max-output-tokens":: 4096, + "azure-temperature":: 0.0, + "azure-models":: models, + + "llm-models" +:: $["azure-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-ai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure", + "-p", + url.pulsar, + "-x", + std.toString($["azure-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-ai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["azure-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/bedrock.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/bedrock.jsonnet new file mode 100644 index 00000000..19bf1459 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/bedrock.jsonnet @@ -0,0 +1,106 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local chunker = import "chunker-recursive.jsonnet"; +local models = import "parameters/bedrock.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["bedrock-" + key]:: value, + }, + + "bedrock-max-output-tokens":: 4096, + "bedrock-temperature":: 0.0, + "bedrock-models":: models, + + "llm-models" +:: $["bedrock-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("bedrock-credentials") + .with_env_var("AWS_ACCESS_KEY_ID", "aws-id-key") + .with_env_var("AWS_SECRET_ACCESS_KEY", "aws-secret") + .with_env_var("AWS_DEFAULT_REGION", "aws-region"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_bedrock) + .with_command([ + "text-completion-bedrock", + "-p", + url.pulsar, + "-x", + std.toString($["bedrock-max-output-tokens"]), + "-t", + "%0.3f" % $["bedrock-temperature"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("bedrock-credentials") + .with_env_var("AWS_ACCESS_KEY_ID", "aws-id-key") + .with_env_var("AWS_SECRET_ACCESS_KEY", "aws-secret") + .with_env_var("AWS_DEFAULT_REGION", "aws-region"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_bedrock) + .with_command([ + "text-completion-bedrock", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["bedrock-max-output-tokens"]), + "-t", + "%0.3f" % $["bedrock-temperature"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + chunker + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/chunker-recursive.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/chunker-recursive.jsonnet new file mode 100644 index 00000000..a9f31d73 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/chunker-recursive.jsonnet @@ -0,0 +1,48 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; + +{ + + "chunk-size":: 2000, + "chunk-overlap":: 100, + + "chunker" +: { + + create:: function(engine) + + local container = + engine.container("chunker") + .with_image(images.trustgraph_flow) + .with_command([ + "chunker-recursive", + "-p", + url.pulsar, + "--chunk-size", + std.toString($["chunk-size"]), + "--chunk-overlap", + std.toString($["chunk-overlap"]), + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "chunker", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/claude.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/claude.jsonnet new file mode 100644 index 00000000..f2dcc7e1 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/claude.jsonnet @@ -0,0 +1,105 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/claude.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["claude-" + key]:: value, + }, + + "claude-max-output-tokens":: 4096, + "claude-temperature":: 0.0, + "claude-models":: models, + + "llm-models" +:: $["claude-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("claude-credentials") + .with_env_var("CLAUDE_KEY", "claude-key"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-claude", + "-p", + url.pulsar, + "-x", + std.toString($["claude-max-output-tokens"]), + "-t", + "%0.3f" % $["claude-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("claude-credentials") + .with_env_var("CLAUDE_KEY", "claude-key"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-claude", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["claude-max-output-tokens"]), + "-t", + "%0.3f" % $["claude-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/cohere.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/cohere.jsonnet new file mode 100644 index 00000000..644b7b4a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/cohere.jsonnet @@ -0,0 +1,98 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/cohere.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["cohere-" + key]:: value, + }, + + "cohere-temperature":: 0.0, + "cohere-models":: models, + + "llm-models" +:: $["cohere-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("cohere-credentials") + .with_env_var("COHERE_KEY", "cohere-key"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-cohere", + "-p", + url.pulsar, + "-t", + "%0.3f" % $["cohere-temperature"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("cohere-credentials") + .with_env_var("COHERE_KEY", "cohere-key"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-cohere", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-t", + "%0.3f" % $["cohere-temperature"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/configuration.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/configuration.jsonnet new file mode 100644 index 00000000..b2499ff6 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/configuration.jsonnet @@ -0,0 +1,50 @@ + +// This puts the default configuration together. References many things, +// flow classes, a default flow, token costs, prompts, agent tools + +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "init-trustgraph" +: { + + create:: function(engine) + + local cfgVol = engine.configVolume( + "trustgraph-cfg", "trustgraph", + { + "config.json": importstr "trustgraph/config.json", + } + ); + + local container = + engine.container("init-trustgraph") + .with_image(images.trustgraph_flow) + .with_command( + [ + "tg-init-trustgraph", + "-p", + url.pulsar_admin, + "--config-file", + "/trustgraph/config.json", + ] + ) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M") + .with_volume_mount(cfgVol, "/trustgraph/"); + + local containerSet = engine.containers( + "init-trustgraph", [ container ] + ); + + engine.resources([ + cfgVol, + containerSet, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/document-rag.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/document-rag.jsonnet new file mode 100644 index 00000000..239598b7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/document-rag.jsonnet @@ -0,0 +1,77 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; + +{ + + "document-rag-doc-limit":: 20, + + "document-rag" +: { + + create:: function(engine) + + local container = + engine.container("document-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "document-rag", + "-p", + url.pulsar, + "--doc-limit", + std.toString($["document-rag-doc-limit"]), + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "document-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "document-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("document-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "document-embeddings", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits("1.0", "512M") + .with_reservations("0.5", "512M"); + + local containerSet = engine.containers( + "document-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/embeddings-fastembed.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/embeddings-fastembed.jsonnet new file mode 100644 index 00000000..1e4d6cb9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/embeddings-fastembed.jsonnet @@ -0,0 +1,48 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/embeddings-fastembed.jsonnet"; + +{ + + "fastembed-models":: models, + + "embeddings-models" +:: $["fastembed-models"], + + embeddings +: { + + create:: function(engine) + + local container = + engine.container("embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "embeddings-fastembed", + "-p", + url.pulsar, + "--concurrency", + std.toString($["embeddings-concurrency"]), + "--log-level", + $["log-level"], + ]) + .with_limits("1.0", "400M") + .with_reservations("0.5", "400M"); + + local containerSet = engine.containers( + "embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/embeddings-hf.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/embeddings-hf.jsonnet new file mode 100644 index 00000000..29008129 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/embeddings-hf.jsonnet @@ -0,0 +1,46 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/embeddings-huggingface.jsonnet"; + +{ + + "huggingface-embeddings-models":: models, + + "embeddings-models" +:: $["huggingface-embeddings-models"], + + embeddings +: { + + create:: function(engine) + + local container = + engine.container("embeddings") + .with_image(images.trustgraph_hf) + .with_command([ + "embeddings-hf", + "-p", + url.pulsar, + "--concurrency", + std.toString($["embeddings-concurrency"]), + ]) + .with_limits("1.0", "400M") + .with_reservations("0.5", "400M"); + + local containerSet = engine.containers( + "embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/embeddings-ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/embeddings-ollama.jsonnet new file mode 100644 index 00000000..8422b646 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/embeddings-ollama.jsonnet @@ -0,0 +1,51 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local models = import "parameters/embeddings-ollama.jsonnet"; + +{ + + "ollama-url":: "${OLLAMA_HOST}", + + "ollama-models":: models, + + "embeddings-models" +:: $["ollama-models"], + + embeddings +: { + + create:: function(engine) + + local container = + engine.container("embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "embeddings-ollama", + "-p", + url.pulsar, + "--concurrency", + std.toString($["embeddings-concurrency"]), + "-r", + $["ollama-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/googleaistudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/googleaistudio.jsonnet new file mode 100644 index 00000000..9b6ff6c5 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/googleaistudio.jsonnet @@ -0,0 +1,105 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/googleaistudio.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["googleaistudio-" + key]:: value, + }, + + "googleaistudio-max-output-tokens":: 4096, + "googleaistudio-temperature":: 0.0, + "googleaistudio-models":: models, + + "llm-models" +:: $["googleaistudio-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("googleaistudio-credentials") + .with_env_var("GOOGLE_AI_STUDIO_KEY", "googleaistudio-key"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-googleaistudio", + "-p", + url.pulsar, + "-x", + std.toString($["googleaistudio-max-output-tokens"]), + "-t", + "%0.3f" % $["googleaistudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("googleaistudio-credentials") + .with_env_var("GOOGLE_AI_STUDIO_KEY", "googleaistudio-key"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-googleaistudio", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["googleaistudio-max-output-tokens"]), + "-t", + "%0.3f" % $["googleaistudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/grafana.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/grafana.jsonnet new file mode 100644 index 00000000..decdf4f3 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/grafana.jsonnet @@ -0,0 +1,125 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local loki = import "loki.jsonnet"; + +{ + + "prometheus" +: { + + create:: function(engine) + + local vol = engine.volume("prometheus-data").with_size("20G"); + + local cfgVol = engine.configVolume( + "prometheus-cfg", "prometheus", + { + "prometheus.yml": importstr "prometheus/prometheus.yml", + } + ); + + local container = + engine.container("prometheus") + .with_image(images.prometheus) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M") + .with_port(9090, 9090, "http") + .with_volume_mount(cfgVol, "/etc/prometheus/") + .with_volume_mount(vol, "/prometheus"); + + local containerSet = engine.containers( + "prometheus", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9090, 9090, "http"); + + engine.resources([ + cfgVol, + vol, + containerSet, + service, + ]) + + }, + + "grafana" +: { + + create:: function(engine) + + local vol = engine.volume("grafana-storage").with_size("20G"); + + local provDashVol = engine.configVolume( + "prov-dash", "grafana/provisioning/", + { + "dashboard.yml": + importstr "grafana/provisioning/dashboard.yml", + } + + ); + + local provDataVol = engine.configVolume( + "prov-data", "grafana/provisioning/", + { + "datasource.yml": + importstr "grafana/provisioning/datasource.yml", + } + + ); + + local dashVol = engine.configVolume( + "dashboards", "grafana/dashboards/", + { + "overview-dashboard.json": + importstr "grafana/dashboards/overview-dashboard.json", + "log-dashboard.json": + importstr "grafana/dashboards/log-dashboard.json", + } + + ); + + local container = + engine.container("grafana") + .with_image(images.grafana) + .with_environment({ + // GF_AUTH_ANONYMOUS_ORG_ROLE: "Admin", + // GF_AUTH_ANONYMOUS_ENABLED: "true", + // GF_ORG_ROLE: "Admin", + GF_ORG_NAME: "trustgraph.ai", + // GF_SERVER_ROOT_URL: "https://example.com", + }) + .with_limits("1.0", "256M") + .with_reservations("0.5", "256M") + .with_port(3000, 3000, "cassandra") + .with_volume_mount(vol, "/var/lib/grafana") + .with_volume_mount( + provDashVol, "/etc/grafana/provisioning/dashboards/" + ) + .with_volume_mount( + provDataVol, "/etc/grafana/provisioning/datasources/" + ) + .with_volume_mount( + dashVol, "/var/lib/grafana/dashboards/" + ); + + local containerSet = engine.containers( + "grafana", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(3000, 3000, "http"); + + engine.resources([ + vol, + provDashVol, + provDataVol, + dashVol, + containerSet, + service, + ]) + + }, + +} + loki + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/graph-rag.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/graph-rag.jsonnet new file mode 100644 index 00000000..64ab0130 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/graph-rag.jsonnet @@ -0,0 +1,223 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "graph-rag-entity-limit":: 50, + "graph-rag-triple-limit":: 30, + "graph-rag-max-subgraph-size":: 400, + "graph-rag-max-path-length":: 2, + + "kg-extract-definitions" +: { + + create:: function(engine) + + local container = + engine.container("kg-extract-definitions") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-definitions", + "-p", + url.pulsar, + "--concurrency", + std.toString($["kg-extraction-concurrency"]), + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "kg-extract-definitions", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-relationships" +: { + + create:: function(engine) + + local container = + engine.container("kg-extract-relationships") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-relationships", + "-p", + url.pulsar, + "--concurrency", + std.toString($["kg-extraction-concurrency"]), + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "kg-extract-relationships", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-agent" +: { + + create:: function(engine) + + local container = + engine.container("kg-extract-agent") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-agent", + "-p", + url.pulsar, + "--concurrency", + std.toString($["kg-extraction-concurrency"]), + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "kg-extract-agent", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-ontology" +: { + + create:: function(engine) + + local container = + engine.container("kg-extract-ontology") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-ontology", + "-p", + url.pulsar, + "--concurrency", + std.toString($["kg-extraction-concurrency"]), + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "300M") + .with_reservations("0.1", "300M"); + + local containerSet = engine.containers( + "kg-extract-ontology", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "graph-rag" +: { + + create:: function(engine) + + local container = + engine.container("graph-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "graph-rag", + "-p", + url.pulsar, +// "--concurrency", +// std.toString($["graph-rag-concurrency"]), + "--entity-limit", + std.toString($["graph-rag-entity-limit"]), + "--triple-limit", + std.toString($["graph-rag-triple-limit"]), + "--max-subgraph-size", + std.toString($["graph-rag-max-subgraph-size"]), + "--max-path-length", + std.toString($["graph-rag-max-path-length"]), + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "graph-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "graph-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "graph-embeddings", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits("1.0", "512M") + .with_reservations("0.5", "512M"); + + local containerSet = engine.containers( + "graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/librarian.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/librarian.jsonnet new file mode 100644 index 00000000..cfcbcff0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/librarian.jsonnet @@ -0,0 +1,43 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local minio = import "stores/minio.jsonnet"; +local cassandra = import "stores/cassandra.jsonnet"; + +{ + + "librarian" +: { + + create:: function(engine) + + local container = + engine.container("librarian") + .with_image(images.trustgraph_flow) + .with_command([ + "librarian", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M"); + + local containerSet = engine.containers( + "librarian", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +// Minio and Cassandra are used by the Librarian +} + minio + cassandra + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/llamafile.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/llamafile.jsonnet new file mode 100644 index 00000000..bb6c55fd --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/llamafile.jsonnet @@ -0,0 +1,95 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/slm.jsonnet"; +local models = import "parameters/llamafile.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["llamafile-" + key]:: value, + }, + + "llamafile-models":: models, + + "llm-models" +:: $["llamafile-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("llamafile-credentials") + .with_env_var("LLAMAFILE_URL", "llamafile-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-llamafile", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("llamafile-credentials") + .with_env_var("LLAMAFILE_URL", "llamafile-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-llamafile", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/lmstudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/lmstudio.jsonnet new file mode 100644 index 00000000..a578b694 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/lmstudio.jsonnet @@ -0,0 +1,105 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/lmstudio.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["lmstudio-" + key]:: value, + }, + + "lmstudio-max-output-tokens":: 4096, + "lmstudio-temperature":: 0.0, + "lmstudio-models":: models, + + "llm-models" +:: $["lmstudio-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("lmstudio-credentials") + .with_env_var("LMSTUDIO_URL", "lmstudio-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-lmstudio", + "-p", + url.pulsar, + "-x", + std.toString($["lmstudio-max-output-tokens"]), + "-t", + "%0.3f" % $["lmstudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("lmstudio-credentials") + .with_env_var("LMSTUDIO_URL", "lmstudio-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-lmstudio", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["lmstudio-max-output-tokens"]), + "-t", + "%0.3f" % $["lmstudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/loki.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/loki.jsonnet new file mode 100644 index 00000000..35bbff3b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/loki.jsonnet @@ -0,0 +1,46 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + "loki" +: { + + create:: function(engine) + + local vol = engine.volume("loki-data").with_size("20G"); + + local cfgVol = engine.configVolume( + "loki-cfg", "loki", + { + "local-config.yaml": importstr "loki/local-config.yaml", + } + ); + + local container = + engine.container("loki") + .with_image(images.loki) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M") + .with_port(3100, 3100, "http") + .with_volume_mount(cfgVol, "/etc/loki/") + .with_volume_mount(vol, "/loki"); + + local containerSet = engine.containers( + "loki", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(3100, 3100, "http"); + + engine.resources([ + cfgVol, + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/mcp-server.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/mcp-server.jsonnet new file mode 100644 index 00000000..7769889e --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/mcp-server.jsonnet @@ -0,0 +1,48 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "mcp-server-port":: 8000, + + "mcp-server" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("mcp-server-secret") + .with_env_var("MCP_SERVER_SECRET", "mcp-server-secret"); + + local port = $["mcp-server-port"]; + + local container = + engine.container("mcp-server") + .with_image(images.trustgraph_mcp) + .with_command([ + "mcp-server", + "--port", + std.toString(port), + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_port(port, port, "mcp"); + + local containerSet = engine.containers( + "mcp-server", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(port, port, "mcp"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/mistral-ocr.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/mistral-ocr.jsonnet new file mode 100644 index 00000000..f70e0cd6 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/mistral-ocr.jsonnet @@ -0,0 +1,49 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["mistral-" + key]:: value, + }, + + "pdf-decoder" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("mistral-credentials") + .with_env_var("MISTRAL_TOKEN", "mistral-token"); + + local container = + engine.container("mistral-ocr") + .with_image(images.trustgraph_flow) + .with_command([ + "pdf-ocr-mistral", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "mistral-ocr", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/mistral.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/mistral.jsonnet new file mode 100644 index 00000000..6d01fbc9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/mistral.jsonnet @@ -0,0 +1,105 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/mistral.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["mistral-" + key]:: value, + }, + + "mistral-max-output-tokens":: 4096, + "mistral-temperature":: 0.0, + "mistral-models":: models, + + "llm-models" +:: $["mistral-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("mistral-credentials") + .with_env_var("MISTRAL_TOKEN", "mistral-token"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-mistral", + "-p", + url.pulsar, + "-x", + std.toString($["mistral-max-output-tokens"]), + "-t", + "%0.3f" % $["mistral-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("mistral-credentials") + .with_env_var("MISTRAL_TOKEN", "mistral-token"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-mistral", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["mistral-max-output-tokens"]), + "-t", + "%0.3f" % $["mistral-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/null.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/null.jsonnet new file mode 100644 index 00000000..2c63c085 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/null.jsonnet @@ -0,0 +1,2 @@ +{ +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/object-store-cassandra.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/object-store-cassandra.jsonnet new file mode 100644 index 00000000..8362b5c4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/object-store-cassandra.jsonnet @@ -0,0 +1,78 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local cassandra = import "stores/cassandra.jsonnet"; + +cassandra + { + + "store-objects" +: { + + create:: function(engine) + + local container = + engine.container("store-objects") + .with_image(images.trustgraph_flow) + .with_command([ + "objects-write-cassandra", + "-p", + url.pulsar, + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-objects", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-objects" +: { + + create:: function(engine) + + local container = + engine.container("query-objects") + .with_image(images.trustgraph_flow) + .with_command([ + "objects-query-cassandra", + "-p", + url.pulsar, + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "512M") + .with_reservations("0.1", "512M"); + + local containerSet = engine.containers( + "query-objects", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/ocr.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/ocr.jsonnet new file mode 100644 index 00000000..4f53aa5a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/ocr.jsonnet @@ -0,0 +1,37 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "pdf-decoder" +: { + + create:: function(engine) + + local container = + engine.container("pdf-ocr") + .with_image(images.trustgraph_ocr) + .with_command([ + "pdf-ocr", + "-p", + url.pulsar, + ]) + .with_limits("1.0", "512M") + .with_reservations("0.1", "512M"); + + local containerSet = engine.containers( + "pdf-ocr", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/ollama.jsonnet new file mode 100644 index 00000000..bccfd0f1 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/ollama.jsonnet @@ -0,0 +1,99 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/ollama.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["ollama-" + key]:: value, + }, + + "ollama-models":: models, + + "llm-models" +:: $["ollama-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("ollama-credentials") + .with_env_var("OLLAMA_HOST", "ollama-host"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-ollama", + "-p", + url.pulsar, + "--concurrency", + std.toString($["text-completion-concurrency"]), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("ollama-credentials") + .with_env_var("OLLAMA_HOST", "ollama-host"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-ollama", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "--concurrency", + std.toString($["text-completion-rag-concurrency"]), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/openai.jsonnet new file mode 100644 index 00000000..c8754280 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/openai.jsonnet @@ -0,0 +1,107 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/openai.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["openai-" + key]:: value, + }, + + "openai-max-output-tokens":: 4096, + "openai-temperature":: 0.0, + "openai-models":: models, + + "llm-models" +:: $["openai-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("openai-credentials") + .with_env_var("OPENAI_TOKEN", "openai-token") + .with_env_var("OPENAI_BASE_URL", "openai-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-openai", + "-p", + url.pulsar, + "-x", + std.toString($["openai-max-output-tokens"]), + "-t", + "%0.3f" % $["openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("openai-credentials") + .with_env_var("OPENAI_TOKEN", "openai-token") + .with_env_var("OPENAI_BASE_URL", "openai-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-openai", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["openai-max-output-tokens"]), + "-t", + "%0.3f" % $["openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/prompt-overrides.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/prompt-overrides.jsonnet new file mode 100644 index 00000000..852ec09d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/prompt-overrides.jsonnet @@ -0,0 +1,24 @@ +local default_prompts = import "prompts/default-prompts.jsonnet"; + +{ + + with:: function(key, value) + if (key == "system-template") then + self + { + prompts +:: { + "system-template": value, + } + } + else + self + { + prompts +:: { + templates +:: { + [key] +:: { + prompt: value + } + } + } + }, + +} + default_prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/prompt-template.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/prompt-template.jsonnet new file mode 100644 index 00000000..5d1af35b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/prompt-template.jsonnet @@ -0,0 +1,78 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "prompt" +: { + + create:: function(engine) + + local container = + engine.container("prompt") + .with_image(images.trustgraph_flow) + .with_command([ + "prompt-template", + "-p", + url.pulsar, + "--concurrency", + std.toString($["prompt-concurrency"]), + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "prompt", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "prompt-rag" +: { + + create:: function(engine) + + local container = + engine.container("prompt-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "prompt-template", + "-p", + url.pulsar, + "--id", + "prompt-rag", + "--concurrency", + std.toString($["prompt-rag-concurrency"]), + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "prompt-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/pulsar-manager.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/pulsar-manager.jsonnet new file mode 100644 index 00000000..9a0b59b2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/pulsar-manager.jsonnet @@ -0,0 +1,41 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + "pulsar" +: { + + create:: function(engine) + +// FIXME: Should persist something? +// local volume = engine.volume(...) + + local container = + engine.container("pulsar") + .with_image(images.pulsar_manager) + .with_environment({ + SPRING_CONFIGURATION_FILE: "/pulsar-manager/pulsar-manager/application.properties", + }) + .with_limits("0.5", "1.4G") + .with_reservations("0.1", "1.4G") + .with_port(9527, 9527, "api") + .with_port(7750, 7750, "api2"); + + local containerSet = engine.containers( + "pulsar", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9527, 9527, "api") + .with_port(7750, 7750, "api2); + + engine.resources([ + containerSet, + service, + ]) + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/pulsar.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/pulsar.jsonnet new file mode 100644 index 00000000..0cfd1854 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/pulsar.jsonnet @@ -0,0 +1,171 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +// This is a Pulsar configuration. Non-standalone mode so we deploy +// individual components: bookkeeper, broker and zookeeper. +// +// This also deploys the TrustGraph 'admin' container which initialises +// TrustGraph-specific namespaces etc. + +{ + + "pulsar" +: { + + create:: function(engine) + + // Zookeeper volume + local zkVolume = engine.volume("zookeeper").with_size("1G"); + + // Zookeeper container + local zkContainer = + engine.container("zookeeper") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "bin/apply-config-from-env.py conf/zookeeper.conf && bin/generate-zookeeper-config.sh conf/zookeeper.conf && exec bin/pulsar zookeeper" + ]) + .with_limits("1", "400M") + .with_reservations("0.05", "400M") + .with_user("0:1000") + .with_volume_mount(zkVolume, "/pulsar/data/zookeeper") + .with_environment({ + "metadataStoreUrl": "zk:zookeeper:2181", + "PULSAR_MEM": "-Xms256m -Xmx256m -XX:MaxDirectMemorySize=256m", + }) + .with_port(2181, 2181, "zookeeper") + .with_port(2888, 2888, "zookeeper2") + .with_port(3888, 3888, "zookeeper3"); + + // Pulsar cluster init container + local initContainer = + engine.container("pulsar-init") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "sleep 10 && bin/pulsar initialize-cluster-metadata --cluster cluster-a --zookeeper zookeeper:2181 --configuration-store zookeeper:2181 --web-service-url http://pulsar:8080 --broker-service-url pulsar://pulsar:6650", + ]) + .with_limits("1", "512M") + .with_reservations("0.05", "512M") + .with_environment({ + "PULSAR_MEM": "-Xms256m -Xmx256m -XX:MaxDirectMemorySize=256m", + }); + + + // Bookkeeper volume + local bookieVolume = engine.volume("bookie").with_size("20G"); + + // Bookkeeper container + local bookieContainer = + engine.container("bookie") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "bin/apply-config-from-env.py conf/bookkeeper.conf && exec bin/pulsar bookie" + // false ^ causes this to be a 'failure' exit. + ]) + .with_limits("1", "1024M") + .with_reservations("0.1", "1024M") + .with_user("0:1000") + .with_volume_mount(bookieVolume, "/pulsar/data/bookkeeper") + .with_environment({ + "clusterName": "cluster-a", + "zkServers": "zookeeper:2181", + "bookieId": "bookie", + "metadataStoreUri": "metadata-store:zk:zookeeper:2181", + "advertisedAddress": "bookie", + "BOOKIE_MEM": "-Xms512m -Xmx512m -XX:MaxDirectMemorySize=256m", + }) + .with_port(3181, 3181, "bookie"); + + // Pulsar broker, stateless (uses ZK and Bookkeeper for state) + local brokerContainer = + engine.container("pulsar") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "bin/apply-config-from-env.py conf/broker.conf && exec bin/pulsar broker" + ]) + .with_limits("1", "800M") + .with_reservations("0.1", "800M") + .with_environment({ + "metadataStoreUrl": "zk:zookeeper:2181", + "zookeeperServers": "zookeeper:2181", + "clusterName": "cluster-a", + "managedLedgerDefaultEnsembleSize": "1", + "managedLedgerDefaultWriteQuorum": "1", + "managedLedgerDefaultAckQuorum": "1", + "advertisedAddress": "pulsar", + "advertisedListeners": "external:pulsar://pulsar:6650,localhost:pulsar://localhost:6650", + "PULSAR_MEM": "-Xms512m -Xmx512m -XX:MaxDirectMemorySize=256m", + }) + .with_port(6650, 6650, "pulsar") + .with_port(8080, 8080, "admin"); + + // Container sets + local zkContainerSet = engine.containers( + "zookeeper", + [ + zkContainer, + ] + ); + + local initContainerSet = engine.containers( + "init-pulsar", + [ + initContainer, + ] + ); + + local bookieContainerSet = engine.containers( + "bookie", + [ + bookieContainer, + ] + ); + + local brokerContainerSet = engine.containers( + "pulsar", + [ + brokerContainer, + ] + ); + + // Zookeeper service + local zkService = + engine.service(zkContainerSet) + .with_port(2181, 2181, "zookeeper") + .with_port(2888, 2888, "zookeeper2") + .with_port(3888, 3888, "zookeeper3"); + + // Bookkeeper service + local bookieService = + engine.service(bookieContainerSet) + .with_port(3181, 3181, "bookie"); + + // Pulsar broker service + local brokerService = + engine.service(brokerContainerSet) + .with_port(6650, 6650, "pulsar") + .with_port(8080, 8080, "admin"); + + engine.resources([ + zkVolume, + bookieVolume, + zkContainerSet, + initContainerSet, + bookieContainerSet, + brokerContainerSet, + zkService, + bookieService, + brokerService, + ]) + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/rev-gateway.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/rev-gateway.jsonnet new file mode 100644 index 00000000..b549f402 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/rev-gateway.jsonnet @@ -0,0 +1,54 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + // Invalid, but at least means the rev-gateway won't connect to anything + // it shouldn't. + "rev-gateway-token":: "INVALID_TOKEN", + "rev-gateway-uri":: "wss://127.0.0.1/api/v1/relay?token=" + $["rev-gateway-token"], + + "rev-gateway" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("rev-gateway-secret") + .with_env_var("REV_GATEWAY_SECRET", "rev-gateway-secret"); + + local container = + engine.container("api-gateway") + .with_image(images.trustgraph_flow) + .with_command([ + "rev-gateway", + "-p", + url.pulsar, + "--websocket-uri", + std.toString($["rev-gateway-uri"]), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_port(8000, 8000, "metrics") + .with_port(port, port, "api"); + + local containerSet = engine.containers( + "api-gateway", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics") + .with_port(port, port, "api"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/structured-data.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/structured-data.jsonnet new file mode 100644 index 00000000..33d0ef06 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/structured-data.jsonnet @@ -0,0 +1,137 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "nlp-query" +: { + + create:: function(engine) + + local container = + engine.container("nlp-query") + .with_image(images.trustgraph_flow) + .with_command([ + "nlp-query", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "nlp-query", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "structured-query" +: { + + create:: function(engine) + + local container = + engine.container("structured-query") + .with_image(images.trustgraph_flow) + .with_command([ + "structured-query", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "structured-query", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "structured-diag" +: { + + create:: function(engine) + + local container = + engine.container("structured-diag") + .with_image(images.trustgraph_flow) + .with_command([ + "structured-diag", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "96M") + .with_reservations("0.1", "96M"); + + local containerSet = engine.containers( + "structured-diag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-objects" +: { + + create:: function(engine) + + local container = + engine.container("kg-extract-objects") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-objects", + "-p", + url.pulsar, + "--concurrency", + std.toString($["kg-extraction-concurrency"]), + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "kg-extract-objects", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/tgi-rag.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/tgi-rag.jsonnet new file mode 100644 index 00000000..0a8a7af9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/tgi-rag.jsonnet @@ -0,0 +1,62 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-rag-" + key]:: value, + }, + + "tgi-rag-max-output-tokens":: 1024, + "tgi-rag-temperature":: 0.0, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("tgi-credentials") + .with_env_var("TGI_BASE_URL", "tgi-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-tgi", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "--concurrency", + std.toString($["text-completion-rag-concurrency"]), + "-x", + std.toString($["tgi-rag-max-output-tokens"]), + "-t", + "%0.3f" % $["tgi-rag-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/tgi-service-cpu.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/tgi-service-cpu.jsonnet new file mode 100644 index 00000000..0a02f210 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/tgi-service-cpu.jsonnet @@ -0,0 +1,65 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-service-" + key]:: value, + }, + + "tgi-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "tgi-service-cpus":: "8.0", + "tgi-service-memory":: "16G", + + "tgi-service" +: { + + create:: function(engine) + + local vol = engine.volume("tgi-storage").with_size("20G"); + + local container = + engine.container("tgi-service") + .with_image(images["tgi-service-cpu"]) + .with_command([ + "--model-id", + $["tgi-service-model"], + "--cuda-graphs", + "0", + "--port", + "8899" + ]) + .with_privileged(true) + .with_device("/dev/dri", "/dev/dri") + .with_environment({ + HF_TOKEN: $["hf-token"], + }) + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_reservations( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_port(8899, 8899, "tgi") + .with_volume_mount(vol, "/data"); + + local containerSet = engine.containers( + "tgi-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(8899, 8899, "tgi"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/tgi-service-gaudi.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/tgi-service-gaudi.jsonnet new file mode 100644 index 00000000..9c52ef3d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/tgi-service-gaudi.jsonnet @@ -0,0 +1,92 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-service-" + key]:: value, + }, + + "tgi-service-model":: "meta-llama/Llama-3.3-70B-Instruct", + "tgi-service-cpus":: "64.0", + "tgi-service-memory":: "64G", + + "tgi-service" +: { + + create:: function(engine) + + local vol = engine.volume("tgi-storage").with_size("50G"); + + local container = + engine.container("tgi-service") + .with_image(images["tgi-service-gaudi"]) + .with_command([ + "--model-id", + $["tgi-service-model"], + "--sharded", + "true", + "--num-shard", + "8", + "--max-input-tokens", + "4096", + "--max-total-tokens", + "4096", + "--max-batch-size", + "128", +// "--max-batch-prefill-tokens", +// "16384", + "--max-waiting-tokens", + "7", +// "--waiting-served-ratio", +// "1.2", + "--max-concurrent-requests", + "512", + "--cuda-graphs", + "0", + "--port", + "8899" + ]) + .with_runtime("habana") + .with_environment({ + HABANA_VISIBLE_DEVICES: "all", + OMPI_MCA_btl_vader_single_copy_mechanism: "none", + HF_TOKEN: $["hf-token"], + ENABLE_HPU_GRAPH: 'true', + LIMIT_HPU_GRAPH: 'true', + USE_FLASH_ATTENTION: 'true', + FLASH_ATTENTION_RECOMPUTE: 'true', +// PT_HPU_ENABLE_LAZY_COLLECTIVES: 'true', +// PREFILL_BATCH_BUCKET_SIZE: "1", +// BATCH_BUCKET_SIZE: "1", + + }) + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_reservations( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_port(8899, 8899, "tgi") + .with_volume_mount(vol, "/data"); + + local containerSet = engine.containers( + "tgi-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(8899, 8899, "tgi"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/tgi-service-intel-gpu.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/tgi-service-intel-gpu.jsonnet new file mode 100644 index 00000000..ef223288 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/tgi-service-intel-gpu.jsonnet @@ -0,0 +1,65 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-service-" + key]:: value, + }, + + "tgi-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "tgi-service-cpus":: "8.0", + "tgi-service-memory":: "16G", + + "tgi-service" +: { + + create:: function(engine) + + local vol = engine.volume("tgi-storage").with_size("20G"); + + local container = + engine.container("tgi-service") + .with_image(images["tgi-service-intel-xpu"]) + .with_command([ + "--model-id", + $["tgi-service-model"], + "--cuda-graphs", + "0", + "--port", + "8899" + ]) + .with_environment({ + HF_TOKEN: $["hf-token"], + }) + .with_privileged(true) + .with_device("/dev/dri", "/dev/dri") + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_reservations( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_port(8899, 8899, "tgi") + .with_volume_mount(vol, "/data"); + + local containerSet = engine.containers( + "tgi-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(8899, 8899, "tgi"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/tgi.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/tgi.jsonnet new file mode 100644 index 00000000..f62266a2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/tgi.jsonnet @@ -0,0 +1,60 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-" + key]:: value, + }, + + "tgi-max-output-tokens":: 1024, + "tgi-temperature":: 0.0, + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("tgi-credentials") + .with_env_var("TGI_BASE_URL", "tgi-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-tgi", + "-p", + url.pulsar, + "--concurrency", + std.toString($["text-completion-concurrency"]), + "-x", + std.toString($["tgi-max-output-tokens"]), + "-t", + "%0.3f" % $["tgi-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/triple-store-cassandra.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/triple-store-cassandra.jsonnet new file mode 100644 index 00000000..a7106cb5 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/triple-store-cassandra.jsonnet @@ -0,0 +1,78 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local cassandra = import "stores/cassandra.jsonnet"; + +cassandra + { + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-cassandra", + "-p", + url.pulsar, + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-cassandra", + "-p", + url.pulsar, + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "512M") + .with_reservations("0.1", "512M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/triple-store-falkordb.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/triple-store-falkordb.jsonnet new file mode 100644 index 00000000..f9177a18 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/triple-store-falkordb.jsonnet @@ -0,0 +1,80 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local falkordb = import "stores/falkordb.jsonnet"; + +falkordb + { + + "falkordb-url":: "falkor://falkordb:6379", + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-falkordb", + "-p", + url.pulsar, + "-g", + $["falkordb-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-falkordb", + "-p", + url.pulsar, + "-g", + $["falkordb-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/triple-store-memgraph.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/triple-store-memgraph.jsonnet new file mode 100644 index 00000000..bc86a5a7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/triple-store-memgraph.jsonnet @@ -0,0 +1,85 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local memgraph = import "stores/memgraph.jsonnet"; + +memgraph + { + + "memgraph-url":: "bolt://memgraph:7687", + "memgraph-database":: "memgraph", + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-memgraph", + "-p", + url.pulsar, + "-g", + $["memgraph-url"], + "--database", + $["memgraph-database"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-memgraph", + "-p", + url.pulsar, + "-g", + $["memgraph-url"], + "--database", + $["memgraph-database"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/triple-store-neo4j.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/triple-store-neo4j.jsonnet new file mode 100644 index 00000000..f4a93d9c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/triple-store-neo4j.jsonnet @@ -0,0 +1,80 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local neo4j = import "stores/neo4j.jsonnet"; + +neo4j + { + + "neo4j-url":: "bolt://neo4j:7687", + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-neo4j", + "-p", + url.pulsar, + "-g", + $["neo4j-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-neo4j", + "-p", + url.pulsar, + "-g", + $["neo4j-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/trustgraph.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/trustgraph.jsonnet new file mode 100644 index 00000000..1eac5dd8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/trustgraph.jsonnet @@ -0,0 +1,349 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +local config_initialiser = import "configuration.jsonnet"; +local config = import "trustgraph-config.jsonnet"; +local librarian = import "librarian.jsonnet"; +local mcp_server = import "mcp-server.jsonnet"; +local workbench = import "workbench-ui.jsonnet"; +local graphrag = import "graph-rag.jsonnet"; +local documentrag = import "document-rag.jsonnet"; +local prompt_template = import "prompt-template.jsonnet"; +local agent_manager = import "agent-manager-react.jsonnet"; +local structured_data = import "structured-data.jsonnet"; +local ddg = import "mcp/ddg-mcp-server.jsonnet"; + +{ + + "log-level":: "INFO", + + "api-gateway-port":: 8088, + "api-gateway-timeout":: 600, + + "chunk-size":: 250, + "chunk-overlap":: 15, + + "prompt-concurrency":: 1, + "prompt-rag-concurrency":: 1, + + "text-completion-concurrency":: 1, + "text-completion-rag-concurrency":: 1, + + "kg-extraction-concurrency":: 1, + "graph-rag-concurrency":: 1, + + "embeddings-concurrency":: 1, + + "api-gateway" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("gateway-secret") + .with_env_var("GATEWAY_SECRET", "gateway-secret"); + + local port = $["api-gateway-port"]; + + local container = + engine.container("api-gateway") + .with_image(images.trustgraph_flow) + .with_command([ + "api-gateway", + "-p", + url.pulsar, + "--timeout", + std.toString($["api-gateway-timeout"]), + "--port", + std.toString(port), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_port(port, port, "api"); + + local containerSet = engine.containers( + "api-gateway", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics") + .with_port(port, port, "api"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "chunker" +: { + + create:: function(engine) + + local container = + engine.container("chunker") + .with_image(images.trustgraph_flow) + .with_command([ + "chunker-token", + "-p", + url.pulsar, + "--chunk-size", + std.toString($["chunk-size"]), + "--chunk-overlap", + std.toString($["chunk-overlap"]), + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "chunker", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "config-svc" +: { + + create:: function(engine) + + local container = + engine.container("config-svc") + .with_image(images.trustgraph_flow) + .with_command([ + "config-svc", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "config-svc", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "pdf-decoder" +: { + + create:: function(engine) + + local container = + engine.container("pdf-decoder") + .with_image(images.trustgraph_flow) + .with_command([ + "pdf-decoder", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "512M") + .with_reservations("0.1", "512M"); + + local containerSet = engine.containers( + "pdf-decoder", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "mcp-tool" +: { + + create:: function(engine) + + local container = + engine.container("mcp-tool") + .with_image(images.trustgraph_flow) + .with_command([ + "mcp-tool", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "mcp-tool", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "metering" +: { + + create:: function(engine) + + local container = + engine.container("metering") + .with_image(images.trustgraph_flow) + .with_command([ + "metering", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "metering", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "metering-rag" +: { + + create:: function(engine) + + local container = + engine.container("metering-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "metering", + "-p", + url.pulsar, + "--id", + "metering-rag", + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "metering-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-store" +: { + + create:: function(engine) + + local container = + engine.container("kg-store") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-store", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "kg-store", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-manager" +: { + + create:: function(engine) + + local container = + engine.container("kg-manager") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-manager", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "kg-manager", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + librarian + mcp_server + workbench + graphrag + + documentrag + prompt_template + agent_manager + structured_data + + config_initialiser + config + + ddg + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/vector-store-milvus.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/vector-store-milvus.jsonnet new file mode 100644 index 00000000..32d6fab4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/vector-store-milvus.jsonnet @@ -0,0 +1,147 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local milvus = import "stores/milvus.jsonnet"; + +milvus + { + + "store-graph-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("store-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-write-milvus", + "-p", + url.pulsar, + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-graph-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("query-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-query-milvus", + "-p", + url.pulsar, + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "store-doc-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("store-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-write-milvus", + "-p", + url.pulsar, + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-doc-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("query-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-query-milvus", + "-p", + url.pulsar, + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/vector-store-pinecone.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/vector-store-pinecone.jsonnet new file mode 100644 index 00000000..0f8d6a33 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/vector-store-pinecone.jsonnet @@ -0,0 +1,161 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; + +{ + + "pinecone-cloud":: "aws", + "pinecone-region":: "us-east-1", + + "store-graph-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("store-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-write-pinecone", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "query-graph-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("query-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-query-pinecone", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "store-doc-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("store-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-write-pinecone", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "query-doc-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("query-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-query-pinecone", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/vector-store-qdrant.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/vector-store-qdrant.jsonnet new file mode 100644 index 00000000..0582cd2f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/vector-store-qdrant.jsonnet @@ -0,0 +1,147 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local qdrant = import "stores/qdrant.jsonnet"; + +qdrant + { + + "store-graph-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("store-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-write-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-graph-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("query-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-query-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "store-doc-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("store-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-write-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-doc-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("query-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-query-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/vertexai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/vertexai.jsonnet new file mode 100644 index 00000000..05f608ce --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/vertexai.jsonnet @@ -0,0 +1,121 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/vertexai.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vertexai-" + key]:: value, + }, + + "vertexai-private-key":: "/vertexai/private.json", + "vertexai-region":: "us-central1", + "vertexai-max-output-tokens":: 4096, + "vertexai-temperature":: 0.0, + "vertexai-models":: models, + + "llm-models" +:: $["vertexai-models"], + + "text-completion" +: { + + create:: function(engine) + + local cfgVol = engine.secretVolume( + "vertexai-creds", + "./vertexai", + { + "private.json": importstr "vertexai/private.json", + } + ); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_vertexai) + .with_command([ + "text-completion-vertexai", + "-p", + url.pulsar, + "-k", + $["vertexai-private-key"], + "-r", + $["vertexai-region"], + "-x", + std.toString($["vertexai-max-output-tokens"]), + "-t", + "%0.3f" % $["vertexai-temperature"], + ]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_volume_mount(cfgVol, "/vertexai"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + cfgVol, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local cfgVol = engine.secretVolume( + "vertexai-creds", + "./vertexai", + { + "private.json": importstr "vertexai/private.json", + } + ); + + local container = + engine.container("text-completion-rag") + .with_image(images.trustgraph_vertexai) + .with_command([ + "text-completion-vertexai", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-k", + $["vertexai-private-key"], + "-r", + $["vertexai-region"], + "-x", + std.toString($["vertexai-max-output-tokens"]), + "-t", + "%0.3f" % $["vertexai-temperature"], + ]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_volume_mount(cfgVol, "/vertexai"); + + local containerSet = engine.containers( + "text-completion-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + cfgVol, + containerSet, + service, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/vllm-service-gaudi.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/vllm-service-gaudi.jsonnet new file mode 100644 index 00000000..25c1d921 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/vllm-service-gaudi.jsonnet @@ -0,0 +1,66 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-service-" + key]:: value, + }, + + "vllm-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "vllm-service-cpus":: "64.0", + "vllm-service-memory":: "64G", + + "vllm-service" +: { + + create:: function(engine) + + local vol = engine.volume("vllm-storage").with_size("50G"); + + local container = + engine.container("vllm-service") + .with_image(images["vllm-service-gaudi"]) + .with_command([ + "--model", + $["vllm-service-model"], + "--tensor-parallel-size=8", + "--port", + "8899", + ]) + .with_runtime("habana") + .with_environment({ + VLLM_SKIP_WARMUP: "true", + HUGGING_FACE_HUB_TOKEN: $["hf-token"], + HABANA_VISIBLE_DEVICES: "all", + VLLM_CACHE_ROOT: "/data", + }) + .with_privileged(true) + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_reservations( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_port(8899, 8899, "vllm") + .with_volume_mount(vol, "/data"); + + local containerSet = engine.containers( + "vllm-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(8899, 8899, "vllm"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/vllm-service-intel-gpu.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/vllm-service-intel-gpu.jsonnet new file mode 100644 index 00000000..cdd98abc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/vllm-service-intel-gpu.jsonnet @@ -0,0 +1,77 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-service-" + key]:: value, + }, + + "vllm-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "vllm-service-cpus":: "8.0", + "vllm-service-memory":: "16G", + + "vllm-service" +: { + + create:: function(engine) + + local vol = engine.volume("vllm-storage").with_size("20G"); + + local container = + engine.container("vllm-service") + .with_image(images["vllm-service-intel-xpu"]) + .with_command([ + "python", + "-m", + "vllm.entrypoints.openai.api_server", + "--model", + $["vllm-service-model"], + "--dtype=float16", + "--device=xpu", + "--enforce-eager", + "--port", + "8899", + "--block-size", + "64", + "--gpu-memory-util", + "0.85", + "--trust-remote-code", + "--disable-sliding-window", + ]) + .with_environment({ + HF_TOKEN: $["hf-token"], + VLLM_USE_V1: "1", + W_LONG_MAX_MODEL_LEN: "1", + VLLM_WORKER_MULTIPROC_METHOD: "spawn", + }) + .with_privileged(true) + .with_device("/dev/dri", "/dev/dri") + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_reservations( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_port(8899, 8899, "vllm") + .with_volume_mount(vol, "/data"); + + local containerSet = engine.containers( + "vllm-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(8899, 8899, "vllm"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/vllm-service-nvidia.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/vllm-service-nvidia.jsonnet new file mode 100644 index 00000000..42f23530 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/vllm-service-nvidia.jsonnet @@ -0,0 +1,64 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-service-" + key]:: value, + }, + + "vllm-service-model":: "mistralai/Mistral-7B-Instruct-v0.3", + "vllm-service-cpus":: "0.5", + "vllm-service-memory":: "1G", + + "vllm-service" +: { + + create:: function(engine) + + local vol = engine.volume("vllm-storage").with_size("50G"); + + local container = + engine.container("vllm-service") + .with_image(images["vllm-service-nvidia"]) + .with_command([ + "--model", + $["vllm-service-model"], + "--port", + "8899", + ]) + .with_runtime("nvidia") + .with_environment({ + VLLM_SKIP_WARMUP: "true", + HUGGING_FACE_HUB_TOKEN: $["hf-token"], + VLLM_CACHE_ROOT: "/data", + }) + .with_privileged(true) + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_reservations( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_port(8899, 8899, "vllm") + .with_volume_mount(vol, "/data"); + + local containerSet = engine.containers( + "vllm-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(8899, 8899, "vllm"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/vllm.jsonnet new file mode 100644 index 00000000..63dfe84b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/vllm.jsonnet @@ -0,0 +1,110 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/vllm.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-" + key]:: value, + }, + + "vllm-models":: models, + + "llm-models" +:: $["vllm-models"], + + "vllm-max-output-tokens":: 1024, + "vllm-temperature":: 0.0, + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("vllm-credentials") + .with_env_var("VLLM_BASE_URL", "vllm-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-vllm", + "-p", + url.pulsar, + "--concurrency", + std.toString($["text-completion-concurrency"]), + "-x", + std.toString($["vllm-max-output-tokens"]), + "-t", + "%0.3f" % $["vllm-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("vllm-credentials") + .with_env_var("VLLM_BASE_URL", "vllm-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-vllm", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "--concurrency", + std.toString($["text-completion-rag-concurrency"]), + "-x", + std.toString($["vllm-max-output-tokens"]), + "-t", + "%0.3f" % $["vllm-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/workbench-ui.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/workbench-ui.jsonnet new file mode 100644 index 00000000..f2048e47 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/components/workbench-ui.jsonnet @@ -0,0 +1,32 @@ +local images = import "values/images.jsonnet"; + +{ + + "workbench-ui" +: { + + create:: function(engine) + + local container = + engine.container("workbench-ui") + .with_image(images["workbench-ui"]) + .with_limits("0.1", "256M") + .with_reservations("0.1", "256M") + .with_port(8888, 8888, "ui"); + + local containerSet = engine.containers( + "workbench-ui", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8888, 8888, "ui"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-aks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-aks-k8s.jsonnet new file mode 100644 index 00000000..c603a0d8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-aks-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "engine/aks-k8s.jsonnet"; +local decode = import "util/decode-config.jsonnet"; +local components = import "components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-docker-compose.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-docker-compose.jsonnet new file mode 100644 index 00000000..442d2cb7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-docker-compose.jsonnet @@ -0,0 +1,20 @@ + +local engine = import "engine/docker-compose.jsonnet"; +local decode = import "util/decode-config.jsonnet"; +local components = import "components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resources = std.foldl( + function(state, p) state + p.create(engine), + std.objectValues(patterns), + {} +); + +resources + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-eks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-eks-k8s.jsonnet new file mode 100644 index 00000000..af8f8ed8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-eks-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "engine/eks-k8s.jsonnet"; +local decode = import "util/decode-config.jsonnet"; +local components = import "components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-gcp-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-gcp-k8s.jsonnet new file mode 100644 index 00000000..3d089a24 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-gcp-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "engine/gcp-k8s.jsonnet"; +local decode = import "util/decode-config.jsonnet"; +local components = import "components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-minikube-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-minikube-k8s.jsonnet new file mode 100644 index 00000000..073358cb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-minikube-k8s.jsonnet @@ -0,0 +1,26 @@ + +local engine = import "engine/minikube-k8s.jsonnet"; +local decode = import "util/decode-config.jsonnet"; +local components = import "components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +// Extract resources using the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-noop.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-noop.jsonnet new file mode 100644 index 00000000..cae548f7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-noop.jsonnet @@ -0,0 +1,20 @@ + +local engine = import "engine/noop.jsonnet"; +local decode = import "util/decode-config.jsonnet"; +local components = import "components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resources = std.foldl( + function(state, p) state + p.create(engine), + std.objectValues(patterns), + {} +); + +resources + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-ovh-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-ovh-k8s.jsonnet new file mode 100644 index 00000000..9c18f449 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-ovh-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "engine/ovh-k8s.jsonnet"; +local decode = import "util/decode-config.jsonnet"; +local components = import "components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-podman-compose.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-podman-compose.jsonnet new file mode 100644 index 00000000..442d2cb7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-podman-compose.jsonnet @@ -0,0 +1,20 @@ + +local engine = import "engine/docker-compose.jsonnet"; +local decode = import "util/decode-config.jsonnet"; +local components = import "components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resources = std.foldl( + function(state, p) state + p.create(engine), + std.objectValues(patterns), + {} +); + +resources + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-scw-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-scw-k8s.jsonnet new file mode 100644 index 00000000..98b79254 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-scw-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "engine/scw-k8s.jsonnet"; +local decode = import "util/decode-config.jsonnet"; +local components = import "components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-tg-configuration.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-tg-configuration.jsonnet new file mode 100644 index 00000000..4d4c6711 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config-to-tg-configuration.jsonnet @@ -0,0 +1,15 @@ + +local engine = import "engine/noop.jsonnet"; +local decode = import "util/decode-config.jsonnet"; +local components = import "components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract configuration directly from patterns +patterns.configuration.configuration + + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config/config-composer.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config/config-composer.jsonnet new file mode 100644 index 00000000..52967736 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config/config-composer.jsonnet @@ -0,0 +1,97 @@ +// Configuration Composer Module +// Orchestrates the complete configuration building process +// Combines all components into the final TrustGraph configuration + +local flow_builder = import "flow-builder.jsonnet"; +local interface_builder = import "interface-builder.jsonnet"; + +{ + // Main function to build the complete configuration + build: function(config_spec) + // Extract configuration parameters + local flow_classes = config_spec.flow_classes; + local default_flow_class = config_spec.default_flow_class; + local default_flow_id = config_spec.default_flow_id; + local flow_init_parameters = config_spec.flow_init_parameters; + + // Build all processors for the default flow + local class_processors = flow_builder.build_class_processors( + flow_classes, + default_flow_class, + flow_init_parameters + ); + + local flow_processors = flow_builder.build_flow_processors( + flow_classes, + default_flow_class, + default_flow_id, + flow_init_parameters + ); + + // Combine processors into flow objects + local processor_array = class_processors + flow_processors; + local flow_objects = flow_builder.build_flow_objects(processor_array); + local flows_active = flow_builder.merge_flow_objects(flow_objects); + + // Build interfaces for the default flow + local default_flow_interfaces = interface_builder.build_interfaces( + flow_classes, + default_flow_class, + default_flow_id, + flow_init_parameters + ); + + // Return object with nested configuration (for backwards compatibility) + { + // Create function (for backwards compatibility) + create: function(engine) {}, + + // The actual configuration object + configuration: { + // Prompts configuration + prompt: { + "system": config_spec.prompts["system-template"], + "template-index": std.objectFieldsAll(config_spec.prompts.templates), + } + { + ["template." + template.key]: template.value + for template in std.objectKeysValuesAll(config_spec.prompts.templates) + }, + + // Tools configuration + tool: { + [tool.id]: tool + for tool in config_spec.tools + }, + + // MCP configuration + mcp: config_spec.mcp, + + // Flow classes reference + "flow-classes": flow_classes, + + // Interface descriptions + "interface-descriptions": config_spec.interface_descriptions, + + // Flow instances + "flows": { + [default_flow_id]: { + "description": "Default processing flow", + "class-name": default_flow_class, + "interfaces": default_flow_interfaces, + "parameters": flow_init_parameters, + }, + }, + + // Active flow processors + "flows-active": flows_active, + + // Token costs and parameter types + "token-costs": config_spec.token_costs, + "parameter-types": config_spec.parameter_types, + + // Collections configuration + "collection": config_spec.collection, + + }, + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config/flow-builder.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config/flow-builder.jsonnet new file mode 100644 index 00000000..eff7c893 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config/flow-builder.jsonnet @@ -0,0 +1,72 @@ +// Flow Builder Module +// Processes flow classes and builds complete flow configurations +// Handles {class}, {id}, and parameter substitutions + +local param_processor = import "parameter-processor.jsonnet"; + +{ + // Builds class-level processors with parameter substitution + // Processes the 'class' section of flow classes + build_class_processors: function(flow_classes, class_name, parameters) + [ + [ + // Replace {class} in the processor key + local key = std.strReplace(processor.key, "{class}", class_name); + local parts = std.splitLimit(key, ":", 2); + parts, + { + // Process each field in the processor configuration + [field.key]: + // First replace {class}, then substitute parameters + local class_replaced = std.strReplace(field.value, "{class}", class_name); + param_processor.substitute_parameters(class_replaced, parameters) + for field in std.objectKeysValuesAll(processor.value) + } + ] + for processor in std.objectKeysValuesAll(flow_classes[class_name].class) + ], + + // Builds flow-level processors with parameter substitution + // Processes the 'flow' section of flow classes + build_flow_processors: function(flow_classes, class_name, flow_id, parameters) + [ + [ + // Replace both {class} and {id} in the processor key + local key = std.strReplace( + std.strReplace(processor.key, "{class}", class_name), + "{id}", flow_id + ); + local parts = std.splitLimit(key, ":", 2); + parts, + { + // Process each field in the processor configuration + [field.key]: + // Replace {class} and {id}, then substitute parameters + local class_replaced = std.strReplace(field.value, "{class}", class_name); + local id_replaced = std.strReplace(class_replaced, "{id}", flow_id); + param_processor.substitute_parameters(id_replaced, parameters) + for field in std.objectKeysValuesAll(processor.value) + } + ] + for processor in std.objectKeysValuesAll(flow_classes[class_name].flow) + ], + + // Combines class and flow processors into flow objects + build_flow_objects: function(processor_array) + std.map( + function(item) { + [item[0][0]] +: { + [item[0][1]]: item[1] + } + }, + processor_array + ), + + // Merges all flow objects into a single flows_active configuration + merge_flow_objects: function(flow_objects) + std.foldr( + function(a, b) a + b, + flow_objects, + {} + ), +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config/interface-builder.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config/interface-builder.jsonnet new file mode 100644 index 00000000..2143e600 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config/interface-builder.jsonnet @@ -0,0 +1,30 @@ +// Interface Builder Module +// Processes flow class interfaces with parameter substitution +// Handles both string interfaces and nested object interfaces + +local param_processor = import "parameter-processor.jsonnet"; + +{ + // Builds interfaces for a specific flow class and instance + // Processes the 'interfaces' section of flow classes + build_interfaces: function(flow_classes, class_name, flow_id, parameters) + local interface_spec = flow_classes[class_name].interfaces; + { + [interface.key]: + if std.isString(interface.value) then + // Simple string interface - apply all substitutions + local class_replaced = std.strReplace(interface.value, "{class}", class_name); + local id_replaced = std.strReplace(class_replaced, "{id}", flow_id); + param_processor.substitute_parameters(id_replaced, parameters) + else + // Complex object interface - process nested fields + { + [field.key]: + local class_replaced = std.strReplace(field.value, "{class}", class_name); + local id_replaced = std.strReplace(class_replaced, "{id}", flow_id); + param_processor.substitute_parameters(id_replaced, parameters) + for field in std.objectKeysValuesAll(interface.value) + } + for interface in std.objectKeysValuesAll(interface_spec) + }, +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config/interface-descriptions.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config/interface-descriptions.jsonnet new file mode 100644 index 00000000..f9dda8b0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config/interface-descriptions.jsonnet @@ -0,0 +1,89 @@ +// Interface Descriptions Module +// Defines all external interfaces available in TrustGraph flows +// These are the 'endpoints' that external systems can interact with + +{ + // Document loading interfaces - for data ingestion + "document-load": { + "description": "Document loader", + "kind": "send", + "visible": true, + }, + "text-load": { + "description": "Text document loader", + "kind": "send", + "visible": true, + }, + + // Data storage interfaces - for processed data streams + "entity-contexts-load": { + "description": "Entity contexts loader", + "kind": "send", + }, + "triples-store": { + "description": "Triples loader", + "kind": "send", + }, + "graph-embeddings-store": { + "description": "Graph embeddings loader", + "kind": "send", + }, + "document-embeddings-store": { + "description": "Document embeddings loader", + "kind": "send", + }, + "objects-store": { + "description": "Object store", + "kind": "request-response", + }, + + // Query interfaces - for retrieving information + "graph-rag": { + "description": "GraphRAG service", + "kind": "request-response", + }, + "document-rag": { + "description": "ChunkRAG service", + "kind": "request-response", + }, + "triples": { + "description": "Triples query service", + "kind": "request-response", + }, + "graph-embeddings": { + "description": "Graph embeddings service", + "kind": "request-response", + }, + "document-embeddings": { + "description": "Document embeddings service", + "kind": "request-response", + }, + "objects": { + "description": "Object query service", + "kind": "request-response", + }, + + // Processing services - for text and data processing + "prompt": { + "description": "Prompt service", + "kind": "request-response", + }, + "agent": { + "description": "Agent service", + "kind": "request-response", + }, + "text-completion": { + "description": "Text completion service", + "kind": "request-response", + }, + + // Query translation services - for natural language queries + "nlp-query": { + "description": "NLP question to GraphQL service", + "kind": "request-response", + }, + "structured-query": { + "description": "Structured query service", + "kind": "request-response", + }, +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config/parameter-processor.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config/parameter-processor.jsonnet new file mode 100644 index 00000000..f317ae9f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config/parameter-processor.jsonnet @@ -0,0 +1,38 @@ +// Parameter Processing Module +// Handles dynamic parameter replacement in configuration values +// Replaces {parameter_name} placeholders with actual parameter values + +{ + // Applies parameter substitutions to string values + // Only processes strings - leaves other types unchanged + substitute_parameters: function(value, parameters) + if std.isString(value) then + std.foldl( + function(acc, param) + // Only do string replacement if param.value is a string + if std.isString(param.value) then + std.strReplace(acc, "{" + param.key + "}", param.value) + else + acc, // Skip replacement for non-string parameter values + std.objectKeysValuesAll(parameters), + value + ) + else + value, + + // Applies parameter substitutions to all values in an object + // Recursively processes nested objects and arrays + substitute_parameters_in_object: function(obj, parameters) + if std.isObject(obj) then + { + [key]: $.substitute_parameters_in_object(obj[key], parameters) + for key in std.objectFields(obj) + } + else if std.isArray(obj) then + [ + $.substitute_parameters_in_object(item, parameters) + for item in obj + ] + else + $.substitute_parameters(obj, parameters), +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config/tools.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config/tools.jsonnet new file mode 100644 index 00000000..a84cd0bb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/config/tools.jsonnet @@ -0,0 +1,52 @@ +// Tools Configuration Module +// Defines all available tools that can be used by agents and flows +// Each tool specifies its interface, arguments, and behavior + +[ + // Knowledge extraction tool - extracts structured knowledge from text + { + id: "knowledge-extraction", + name: "Knowledge extraction", + description: "Takes a chunk of text and extracts knowledge in definition and relationship formats. The input is a text chunk", + type: "prompt", + template: "agent-kg-extract", + arguments: [ + { + "name": "text", + "type": "string", + "description": "The text chunk", + } + ], + }, + + // Knowledge query tool - queries the knowledge base + { + id: "knowledge-query", + name: "Knowledge query", + description: "This tool queries a knowledge base that holds information about domain-specific information. The question should be a natural language question.", + type: "knowledge-query", + collection: "default", + arguments: [ + { + name: "question", + type: "string", + description: "A simple natural language question.", + } + ] + }, + + // LLM completion tool - general purpose text completion + { + id: "llm-completion", + name: "LLM text completion", + type: "text-completion", + description: "This tool queries an LLM for non-domain-specific information. The question should be a natural language question.", + arguments: [ + { + name: "question", + type: "string", + description: "The question which should be asked of the LLM.", + } + ] + } +] \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/engine/aks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/engine/aks-k8s.jsonnet new file mode 100644 index 00000000..07e050f2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/engine/aks-k8s.jsonnet @@ -0,0 +1,45 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "disk.csi.azure.com", + parameters: { + // Standard disks (spinning magnetic), Locally Redundant Storage + // Cheapest, basically + skuName: "Standard_LRS", + }, + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/engine/docker-compose.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/engine/docker-compose.jsonnet new file mode 100644 index 00000000..954cde15 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/engine/docker-compose.jsonnet @@ -0,0 +1,230 @@ +{ + + // Extract resources using the engine + package:: function(patterns) + std.foldl( + function(state, p) state + p.create(self), + std.objectValues(patterns), + {} + ), + + container:: function(name) + { + + local container = self, + + name:: name, + + with_image:: function(x) self + { image: x }, + + with_user:: function(x) self + { user: x }, + + with_command:: function(x) self + { command: x }, + + with_runtime:: function(x) self + { runtime: x }, + + with_privileged:: function(x) self + { privileged: x }, + + with_ipc:: function(x) self + { ipc: x }, + + with_capability:: function(x) self + + if std.objectHas(container, "capability") then + { cap_add: container.capability + x } + else + { cap_add: [x], }, + + with_environment:: function(x) self + + if std.objectHas(container, "environment") then + { environment: container.environment + x } + else + { environment: x, }, + + with_device:: function(hdev, cdev) self + + if std.objectHas(container, "devices") then + { devices: container.devices + "%s:%s" % [hdev, cdev] } + else + { devices: [ "%s:%s" % [hdev, cdev] ], }, + + with_limits:: function(c, m) self + { + deploy +: { resources +: { + limits: { cpus: c, memory: m } + } }, + }, + + with_reservations:: function(c, m) self + { + deploy +: { resources +: { + reservations: { cpus: c, memory: m } + } }, + }, + + with_volume_mount:: + function(vol, mnt) + self + { + volumes: + if std.objectHas(container, "volumes") then + container.volumes + [ + "%s:%s" % [vol.volid, mnt] + ] + else + [ + "%s:%s" % [vol.volid, mnt] + ] + }, + + with_port:: + function(src, dest, name) + self + { + ports: + if std.objectHas(container, "ports") then + container.ports + [ "%d:%d" % [src, dest] ] + else + [ "%d:%d" % [src, dest] ] + }, + + with_env_var_secrets:: + function(vars) + std.foldl( + function(obj, x) obj.with_environment( + { [x]: "${" + x + "}" } + ), + vars.variables, + self + ), + + restart: "on-failure:100", + + add:: function() { + services +: { + [container.name]: container, + } + } + + }, + + internalService:: function(containers) + { + + local service = self, + + name: containers.name, + + with_port:: function(src, dest, name) + self + { port: [src, dest] }, + + add:: function() { + } + + }, + + service:: function(containers) + { + + local service = self, + + name: containers.name, + + with_port:: function(src, dest, name) + self + { port: [src, dest] }, + + add:: function() { + } + + }, + + volume:: function(name) + { + + local volume = self, + + name: name, + + volid:: name, + + with_size:: function(size) self + { size: size }, + + add:: function() { + volumes +: { + [volume.name]: {} + } + } + + }, + + configVolume:: function(name, dir, parts) + { + + local volume = self, + + name: dir, + + volid:: "./" + dir, + + with_size:: function(size) self + { size: size }, + + add:: function() { + } + + }, + + secretVolume:: function(name, dir, parts) + { + + local volume = self, + + name: dir, + + volid:: dir, + + with_size:: function(size) self + { size: size }, + + add:: function() { + } + + }, + + envSecrets:: function(name) + { + + local volume = self, + + name: name, + + volid:: name, + + variables:: [], + + with_env_var:: + function(name, key) self + { + variables: super.variables + [name], + }, + + add:: function() { + } + + }, + + containers:: function(name, containers) + { + + local cont = self, + + name: name, + containers: containers, + + add:: function() std.foldl( + function(state, c) state + c.add(), + cont.containers, + {} + ), + + }, + + resources:: function(res) + std.foldl( + function(state, c) state + c.add(), + res, + {} + ), + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/engine/eks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/engine/eks-k8s.jsonnet new file mode 100644 index 00000000..3fc3f035 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/engine/eks-k8s.jsonnet @@ -0,0 +1,46 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "ebs.csi.aws.com", + parameters: { + type: "gp3", + encrypted: "true", + iops: "6000", + throughput: "400", + }, + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/engine/gcp-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/engine/gcp-k8s.jsonnet new file mode 100644 index 00000000..71792426 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/engine/gcp-k8s.jsonnet @@ -0,0 +1,44 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "pd.csi.storage.gke.io", + parameters: { + type: "pd-balanced", + "csi.storage.k8s.io/fstype": "ext4", + }, + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/engine/k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/engine/k8s.jsonnet new file mode 100644 index 00000000..686f2029 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/engine/k8s.jsonnet @@ -0,0 +1,358 @@ +{ + + container:: function(name) + { + + local container = self, + + name: name, + limits: {}, + reservations: {}, + ports: [], + volumes: [], + environment: [], + + with_image:: function(x) self + { image: x }, + + with_user:: function(x) self + { user: x }, + + with_command:: function(x) self + { command: x }, + + with_environment:: function(x) self + { + environment: super.environment + [ + { + name: v.key, value: v.value + } + for v in std.objectKeysValues(x) + ], + }, + + with_limits:: function(c, m) self + { limits: { cpu: c, memory: m } }, + + with_reservations:: + function(c, m) self + { reservations: { cpu: c, memory: m } }, + + with_volume_mount:: + function(vol, mnt) + self + { + volumes: super.volumes + [{ + volume: vol, mount: mnt + }] + }, + + with_port:: + function(src, dest, name) self + { + ports: super.ports + [ + { src: src, dest: dest, name : name } + ] + }, + + with_env_var_secrets:: + function(vars) + std.foldl( + function(obj, x) obj + { + environment: super.environment + [{ + name: x, + valueFrom: { + secretKeyRef: { + name: vars.name, + key: vars.keyMap[x], + } + } + }] + }, + vars.variables, + self + ), + + add:: function() [ + + { + apiVersion: "apps/v1", + kind: "Deployment", + metadata: { + name: container.name, + namespace: "trustgraph", + labels: { + app: container.name + } + }, + spec: { + replicas: 1, + selector: { + matchLabels: { + app: container.name, + } + }, + template: { + metadata: { + labels: { + app: container.name, + } + }, + spec: { + containers: [ + { + name: container.name, + image: container.image, + + // FIXME: Make everything run as + // root. Needed to get filesystems + // to be accessible. There's a + // better way of doing this? + securityContext: { + runAsUser: 0, + runAsGroup: 0, + }, + + resources: { + requests: container.reservations, + limits: container.limits + }, + } + ( + if std.length(container.ports) > 0 then + { + ports: [ + { + hostPort: port.src, + containerPort: port.dest, + } + for port in container.ports + ] + } else + {}) + + + (if std.objectHas(container, "command") then + { command: container.command } + else {}) + + + (if std.length(container.environment) > 0 then + { + env: container.environment, + } + else {}) + + + (if std.length(container.volumes) > 0 then + { + volumeMounts: [ + { + mountPath: vol.mount, + name: vol.volume.name, + } + for vol in container.volumes + ] + } + + else + {} + ) + ], + volumes: [ + vol.volume.volRef() + for vol in container.volumes + + ] + } + }, + } + {} + + } + + ] + + }, + + // Just an alias + internalService:: self.service, + + service:: function(containers) + { + + local service = self, + + name: containers.name, + + ports: [], + + with_port:: + function(src, dest, name) + self + { + ports: super.ports + [ + { src: src, dest: dest, name: name } + ] + }, + + add:: function() [ + + { + + apiVersion: "v1", + kind: "Service", + metadata: { + name: service.name, + namespace: "trustgraph", + }, + spec: { + selector: { + app: service.name, + }, + ports: [ + { + port: port.src, + targetPort: port.dest, + name: port.name, + } + for port in service.ports + ], + } + } + ], + + }, + + volume:: function(name) + { + + local volume = self, + + name: name, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + { + apiVersion: "v1", + kind: "PersistentVolumeClaim", + metadata: { + name: volume.name, + namespace: "trustgraph", + }, + spec: { + storageClassName: "tg", + accessModes: [ "ReadWriteOnce" ], + resources: { + requests: { + storage: volume.size, + } + }, + } + } + ], + + volRef:: function() { + name: volume.name, + persistentVolumeClaim: { claimName: volume.name }, + } + + }, + + configVolume:: function(name, dir, parts) + { + + local volume = self, + + name: name, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + { + apiVersion: "v1", + kind: "ConfigMap", + metadata: { + name: volume.name, + namespace: "trustgraph", + }, + data: parts + }, + ], + + + volRef:: function() { + name: volume.name, + configMap: { name: volume.name }, + } + + }, + + secretVolume:: function(name, dir, parts) + { + + local volume = self, + + name: name, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + { + apiVersion: "v1", + kind: "Secret", + metadata: { + name: volume.name, + namespace: "trustgraph", + }, + data: { + [item.key]: std.base64(item.value) + for item in std.objectKeysValues(parts) + } + }, + ], + + volRef:: function() { + name: volume.name, + secret: { secretName: volume.name }, + } + + }, + + envSecrets:: function(name) + { + + local volume = self, + + name: name, + + variables: [], + keyMap: {}, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + ], + + volRef:: function() { + name: volume.name, + secret: { secretName: volume.name }, + }, + + with_env_var:: + function(name, key) self + { + variables: super.variables + [name], + keyMap: super.keyMap + { [name]: key }, + }, + + }, + + containers:: function(name, containers) + { + + local cont = self, + + name: name, + containers: containers, + + add:: function() std.flattenArrays( + [ c.add() for c in cont.containers ] + ), + + }, + + resources:: function(res) + + std.flattenArrays( + [ c.add() for c in res ] + ), + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/engine/minikube-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/engine/minikube-k8s.jsonnet new file mode 100644 index 00000000..858b17ad --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/engine/minikube-k8s.jsonnet @@ -0,0 +1,115 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList, + + volume:: function(name) + { + local volume = self, + name: name, + with_size:: function(size) self + { size: size }, + add:: function() [ + { + apiVersion: "v1", + kind: "PersistentVolume", + metadata: { + name: volume.name, + }, + spec: { + accessModes: [ "ReadWriteOnce" ], + capacity: { + storage: volume.size, + }, + persistentVolumeReclaimPolicy: "Delete", + hostPath: { + path: "/data/pv-" + volume.name, + }, + } + }, + { + apiVersion: "v1", + kind: "PersistentVolumeClaim", + metadata: { + name: volume.name, + namespace: "trustgraph", + }, + spec: { + accessModes: [ "ReadWriteOnce" ], + resources: { + requests: { + storage: volume.size, + } + }, + } + } + ], + + volRef:: function() { + name: volume.name, + persistentVolumeClaim: { claimName: volume.name }, + } + + }, + + service:: function(containers) + { + local service = self, + name: containers.name, + ports: [], + with_port:: + function(src, dest, name) + self + { + ports: super.ports + [ + { src: src, dest: dest, name: name } + ] + }, + add:: function() [ + { + apiVersion: "v1", + kind: "Service", + metadata: { + name: service.name, + namespace: "trustgraph", + }, + spec: { + selector: { + app: service.name, + }, + type: "LoadBalancer", + ports: [ + { + port: port.src, + targetPort: port.dest, + name: port.name, + } + for port in service.ports + ], + } + } + ], + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/engine/noop.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/engine/noop.jsonnet new file mode 100644 index 00000000..1f384648 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/engine/noop.jsonnet @@ -0,0 +1,79 @@ +{ + + // Extract resources usnig the engine + package:: function(patterns) {}, + + container:: function(name) { + + with_image:: function(x) self + {}, + + with_user:: function(x) self + {}, + + with_command:: function(x) self + {}, + + with_runtime:: function(x) self + {}, + + with_privileged:: function(x) self + {}, + + with_ipc:: function(x) self + {}, + + with_capability:: function(x) self + {}, + + with_environment:: function(x) self + {}, + + with_device:: function(hdev, cdev) self + {}, + + with_limits:: function(c, m) self + {}, + + with_reservations:: function(c, m) self + {}, + + with_volume_mount:: self + {}, + + with_port:: function(src, dest, name) self + {}, + + with_env_var_secrets:: function(vars) self + {}, + + add:: function() {}, + }, + + internalService:: function(containers) { + with_port:: function(src, dest, name) self + {}, + add:: function() {}, + }, + + service:: function(containers) { + with_port:: function(src, dest, name) self + {}, + add:: function() {}, + }, + + volume:: function(name) { + with_size:: function(size) self + {}, + add:: function() {}, + }, + + configVolume:: function(name, dir, parts) { + add:: function() {}, + }, + + secretVolume:: function(name, dir, parts) { + add:: function() {}, + }, + + envSecrets:: function(name) { + with_env_var:: function(name, key) self + {}, + add:: function() {}, + }, + + containers:: function(name, containers) { + add:: function() {}, + }, + + resources:: function(res) + std.foldl( + function(state, c) state + c.add(), + res, + {} + ), + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/engine/ovh-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/engine/ovh-k8s.jsonnet new file mode 100644 index 00000000..15d57c83 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/engine/ovh-k8s.jsonnet @@ -0,0 +1,45 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "cinder.csi.openstack.org", + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", + parameters: { + availability: "nova", + fsType: "ext4", + type: "high-speed", + }, +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: [ns, sc] + resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/engine/scw-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/engine/scw-k8s.jsonnet new file mode 100644 index 00000000..eed23968 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/engine/scw-k8s.jsonnet @@ -0,0 +1,40 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "csi.scaleway.com", + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: [ns, sc] + resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/agent-extract.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/agent-extract.jsonnet new file mode 100644 index 00000000..143e736a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/agent-extract.jsonnet @@ -0,0 +1,35 @@ +// Agent-based extraction module +// Uses AI agents for more sophisticated knowledge extraction from text +// Leverages agent tools and reasoning for complex extraction tasks + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; + +{ + // No external interfaces - internal agent extraction service + "interfaces" +: { + }, + + // No configurable parameters for agent extraction + "parameters" +: { + }, + + // Flow-level processors for agent-based extraction + "flow" +: { + // Agent-based knowledge extraction processor + // Uses AI agents with tools to extract structured knowledge + "kg-extract-agent:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output knowledge triples + "entity-contexts": flow("entity-contexts-load:{id}"), // Entity context information + "agent-request": request("agent:{id}"), // Agent service requests + "agent-response": response("agent:{id}"), // Agent service responses + }, + }, + + // No class-level processors needed + "class" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/agent.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/agent.jsonnet new file mode 100644 index 00000000..19afe660 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/agent.jsonnet @@ -0,0 +1,59 @@ +// Agent management module +// Provides AI agent orchestration and tool integration +// Manages agent conversations, tool calls, and response coordination +// Supports MCP tools, GraphRAG, and structured queries + +local helpers = import "helpers.jsonnet"; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +{ + // External interfaces for agent operations + "interfaces" +: { + "agent": request_response("agent:{id}"), // Main agent service interface + "mcp-tool": request_response("mcp-tool:{id}"), // MCP tool execution interface + }, + + // No configurable parameters for agent management + "parameters" +: { + }, + + // Flow-level processors for agent management + "flow" +: { + // Agent manager orchestrates agent conversations and tool usage + "agent-manager:{id}": { + // Agent communication channels + request: request("agent:{id}"), // Incoming agent requests + next: request("agent:{id}"), // Multi-turn conversation support + response: response("agent:{id}"), // Agent responses + + // LLM and prompt services + "text-completion-request": request("text-completion:{id}"), // LLM requests + "text-completion-response": response("text-completion:{id}"), // LLM responses + "prompt-request": request("prompt:{id}"), // Prompt processing + "prompt-response": response("prompt:{id}"), + + // Tool integrations + "mcp-tool-request": request("mcp-tool:{id}"), // MCP tool calls + "mcp-tool-response": response("mcp-tool:{id}"), + "graph-rag-request": request("graph-rag:{id}"), // GraphRAG queries + "graph-rag-response": response("graph-rag:{id}"), + "structured-query-request": request("structured-query:{id}"), // Structured data queries + "structured-query-response": response("structured-query:{id}"), + }, + + // MCP tool executor for agent tool usage + "mcp-tool:{id}": { + request: request("mcp-tool:{id}"), // Tool invocation requests + response: response("mcp-tool:{id}"), // Tool execution results + "text-completion-request": request("text-completion:{id}"), // LLM for tool reasoning + "text-completion-response": response("text-completion:{id}"), + }, + + }, + + // Class-level processors for agent-related services + "class" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/documentrag.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/documentrag.jsonnet new file mode 100644 index 00000000..65910684 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/documentrag.jsonnet @@ -0,0 +1,100 @@ +// Document RAG (Retrieval Augmented Generation) module +// Implements document-based RAG using chunk embeddings +// Provides semantic search and context-aware question answering +// Supports MCP (Model Context Protocol) tool integration + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; +local llm_parameters = import "llm-parameters.jsonnet"; + +{ + // External interfaces for document RAG functionality + "interfaces" +: { + // Document embedding storage and retrieval + "document-embeddings-store": flow("document-embeddings-store:{id}"), // Embedding storage stream + "document-rag": request_response("document-rag:{id}"), // Main document RAG interface + "document-embeddings": request_response("document-embeddings:{id}"), // Document embedding queries + + // Supporting services + "embeddings": request_response("embeddings:{id}"), // General embedding service + "prompt": request_response("prompt:{id}"), // Prompt processing + "mcp-tool": request_response("mcp-tool:{id}"), // MCP tool integration + "text-completion": request_response("text-completion:{id}"), // LLM text completion + }, + + // Parameters that can be configured for this flow + "parameters" +: llm_parameters, + + // Flow-level processors for document embedding and storage + "flow" +: { + "document-embeddings:{id}": { + input: flow("chunk-load:{id}"), + output: flow("document-embeddings-store:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + }, + "de-write:{id}": { + input: flow("document-embeddings-store:{id}"), + }, + "text-completion:{id}": { + request: request("text-completion:{id}"), + response: response("text-completion:{id}"), + model: "{llm-model}", + }, + "text-completion-rag:{id}": { + request: request("text-completion-rag:{id}"), + response: response("text-completion-rag:{id}"), + model: "{llm-rag-model}", + }, + "embeddings:{id}": { + request: request("embeddings:{id}"), + response: response("embeddings:{id}"), + model: "{embeddings-model}", + }, + "document-rag:{id}": { + request: request("document-rag:{id}"), + response: response("document-rag:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + "prompt-request": request("prompt-rag:{id}"), + "prompt-response": response("prompt-rag:{id}"), + "document-embeddings-request": request("document-embeddings:{id}"), + "document-embeddings-response": response("document-embeddings:{id}"), + }, + "de-query:{id}": { + request: request("document-embeddings:{id}"), + response: response("document-embeddings:{id}"), + }, + "prompt:{id}": { + request: request("prompt:{id}"), + response: response("prompt:{id}"), + "text-completion-request": request("text-completion:{id}"), + "text-completion-response": response("text-completion:{id}"), + }, + "prompt-rag:{id}": { + request: request("prompt-rag:{id}"), + response: response("prompt-rag:{id}"), + "text-completion-request": request("text-completion-rag:{id}"), + "text-completion-response": response("text-completion-rag:{id}"), + }, + "mcp-tool:{id}": { + request: request("mcp-tool:{id}"), + response: response("mcp-tool:{id}"), + "text-completion-request": request("text-completion:{id}"), + "text-completion-response": response("text-completion:{id}"), + }, + "metering:{id}": { + input: response("text-completion:{id}"), + }, + "metering-rag:{id}": { + input: response("text-completion-rag:{id}"), + }, + }, + + // Class-level processors for document RAG operations + "class" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/flow-classes.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/flow-classes.jsonnet new file mode 100644 index 00000000..1a1adabe --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/flow-classes.jsonnet @@ -0,0 +1,105 @@ +// TrustGraph Flow Classes Configuration +// Defines different flow combinations for various use cases +// Each flow class combines multiple functional modules to create complete processing pipelines +// +// Available modules: +// - graphrag: Graph-based RAG with knowledge graphs +// - documentrag: Document-based RAG with chunk embeddings +// - structured: Structured data processing and NLP queries +// - agent: AI agent orchestration and tool integration +// - load: Document loading and preprocessing +// - kg-base: Basic knowledge extraction from text +// - agent-extract: Agent-based knowledge extraction +// - kgcore: Knowledge graph core storage + +// Import all the modular flow components +local graphrag_part = import "graphrag.jsonnet"; +local kg_base_part = import "kg-base.jsonnet"; +local onto_base_part = import "onto-base.jsonnet"; +local agent_extract_part = import "agent-extract.jsonnet"; +local structured_part = import "structured.jsonnet"; +local documentrag_part = import "documentrag.jsonnet"; +local agent_part = import "agent.jsonnet"; +local load_part = import "load.jsonnet"; +local kgcore_part = import "kgcore.jsonnet"; + +{ + + // Complete TrustGraph system with all capabilities + // Includes GraphRAG, DocumentRAG, structured data processing, and knowledge cores + "everything": { + description: "GraphRAG, DocumentRAG, structured data + knowledge cores", + tags: [ + "document-rag", "graph-rag", "knowledge-extraction", + "structured-data", "kgcore" + ], + } + + graphrag_part + documentrag_part + agent_part + load_part + + kg_base_part + structured_part, + + // Dual RAG system without knowledge core creation + // Combines both document and graph-based retrieval + "document-rag+graph-rag": { + description: "Supports GraphRAG and document RAG, no core creation", + tags: ["document-rag", "graph-rag", "knowledge-extraction"], + } + + graphrag_part + documentrag_part + agent_part + load_part + kg_base_part, + + // Graph-based RAG only + // Uses knowledge graphs for context-aware question answering + "graph-rag": { + description: "GraphRAG only", + tags: ["graph-rag", "knowledge-extraction"], + } + + graphrag_part + agent_part + load_part + kg_base_part, + + // Graph-based RAG only + // Uses knowledge graphs for context-aware question answering + "onto-rag": { + description: "Ontology RAG only", + tags: ["graph-rag", "knowledge-extraction"], + } + + graphrag_part + agent_part + load_part + onto_base_part, + + // Document-based RAG only + // Uses document embeddings for semantic search and answers + "document-rag": { + description: "DocumentRAG only", + tags: ["document-rag"], + } + + documentrag_part + load_part, + + // Full RAG system with knowledge core creation + // Includes both RAG types plus persistent knowledge storage + "document-rag+graph-rag+kgcore": { + description: "GraphRAG + DocumentRAG + knowledge core creation", + tags: ["document-rag", "graph-rag", "knowledge-extraction"], + } + + graphrag_part + documentrag_part + agent_part + load_part + + kgcore_part + kg_base_part, + + // GraphRAG with advanced agent-based extraction + // Uses AI agents for sophisticated knowledge extraction + "graph-rag+agent-extract": { + description: "GraphRAG + agent extract", + tags: ["graph-rag", "knowledge-extraction", "agent-extract"], + } + + graphrag_part + agent_part + load_part + agent_extract_part, + + // GraphRAG with structured data processing + // Combines knowledge graphs with structured data queries + "graph-rag+structured-data": { + description: "GraphRAG + structured data", + tags: ["graph-rag", "knowledge-extraction", "structured-data"], + } + + graphrag_part + agent_part + load_part + structured_part, + + // Structured data processing only + // Handles structured data extraction and NLP queries + "structured-data": { + description: "Structured data only", + tags: ["knowledge-extraction", "structured-data"], + } + + agent_part + load_part + structured_part, + +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/graphrag.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/graphrag.jsonnet new file mode 100644 index 00000000..94cf5750 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/graphrag.jsonnet @@ -0,0 +1,107 @@ +// GraphRAG flow configuration module +// Implements graph-based retrieval augmented generation (GraphRAG) functionality +// Handles knowledge graph storage, embeddings, and graph-based question answering + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; +local llm_parameters = import "llm-parameters.jsonnet"; + +{ + // External interfaces exposed by the GraphRAG flow + "interfaces" +: { + // Data ingestion interfaces for graph construction + "entity-contexts-load": flow("entity-contexts-load:{id}"), // Entity context data stream + "triples-store": flow("triples-store:{id}"), // RDF triples storage stream + "graph-embeddings-store": flow("graph-embeddings-store:{id}"), // Graph embedding storage + + // Query interfaces for graph-based operations + "graph-rag": request_response("graph-rag:{id}"), // Main GraphRAG query interface + "triples": request_response("triples:{id}"), // Triple store queries + "graph-embeddings": request_response("graph-embeddings:{id}"), // Graph embedding queries + + // Supporting services + "embeddings": request_response("embeddings:{id}"), // General embedding service + "prompt": request_response("prompt:{id}"), // Prompt processing service + "text-completion": request_response("text-completion:{id}"), // LLM text completion + }, + + + // Parameters that can be configured for this flow + "parameters" +: llm_parameters, + + // Flow-level processors - handle data streams for a specific flow instance + "flow" +: { + "graph-embeddings:{id}": { + input: flow("entity-contexts-load:{id}"), + output: flow("graph-embeddings-store:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + }, + "triples-write:{id}": { + input: flow("triples-store:{id}"), + }, + "ge-write:{id}": { + input: flow("graph-embeddings-store:{id}"), + }, + "text-completion:{id}": { + request: request("text-completion:{id}"), + response: response("text-completion:{id}"), + model: "{llm-model}", + }, + "text-completion-rag:{id}": { + request: request("text-completion-rag:{id}"), + response: response("text-completion-rag:{id}"), + model: "{llm-rag-model}", + }, + "embeddings:{id}": { + request: request("embeddings:{id}"), + response: response("embeddings:{id}"), + model: "{embeddings-model}", + }, + "graph-rag:{id}": { + request: request("graph-rag:{id}"), + response: response("graph-rag:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + "prompt-request": request("prompt-rag:{id}"), + "prompt-response": response("prompt-rag:{id}"), + "graph-embeddings-request": request("graph-embeddings:{id}"), + "graph-embeddings-response": response("graph-embeddings:{id}"), + "triples-request": request("triples:{id}"), + "triples-response": response("triples:{id}"), + }, + "triples-query:{id}": { + request: request("triples:{id}"), + response: response("triples:{id}"), + }, + "ge-query:{id}": { + request: request("graph-embeddings:{id}"), + response: response("graph-embeddings:{id}"), + }, + "prompt:{id}": { + request: request("prompt:{id}"), + response: response("prompt:{id}"), + "text-completion-request": request("text-completion:{id}"), + "text-completion-response": response("text-completion:{id}"), + }, + "prompt-rag:{id}": { + request: request("prompt-rag:{id}"), + response: response("prompt-rag:{id}"), + "text-completion-request": request("text-completion-rag:{id}"), + "text-completion-response": response("text-completion-rag:{id}"), + }, + "metering:{id}": { + input: response("text-completion:{id}"), + }, + "metering-rag:{id}": { + input: response("text-completion-rag:{id}"), + }, + }, + + // Class-level processors - shared across all flow instances of this class + "class" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/helpers.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/helpers.jsonnet new file mode 100644 index 00000000..eabb3bf4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/helpers.jsonnet @@ -0,0 +1,29 @@ +// Helper functions for flow configuration +// Provides utility functions for constructing flow, request, and response URIs +// used throughout the TrustGraph flow configuration system + +// Creates a persistent flow URI for data streams +// Persistent flows retain messages until consumed +local flow(x) = "persistent://tg/flow/" + x; + +// Creates a non-persistent request URI for request-response patterns +// Non-persistent means messages are not retained if no consumer is present +local request(x) = "non-persistent://tg/request/" + x; + +// Creates a non-persistent response URI for request-response patterns +local response(x) = "non-persistent://tg/response/" + x; + +// Creates a request-response pair for bidirectional communication +// Returns an object with both request and response URIs +local request_response(x) = { + request: request(x), + response: response(x), +}; + +// Export all helper functions for use in other modules +{ + flow: flow, + request: request, + response: response, + request_response: request_response, +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/kg-base.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/kg-base.jsonnet new file mode 100644 index 00000000..215dd791 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/kg-base.jsonnet @@ -0,0 +1,44 @@ +// Knowledge Graph Base extraction module +// Provides basic knowledge extraction capabilities from text chunks +// Extracts entity definitions and relationships using prompt-based processing + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; + +{ + // No external interfaces - this module provides internal extraction services + "interfaces" +: { + }, + + // No configurable parameters for basic KG extraction + "parameters" +: { + }, + + // Flow-level processors for knowledge extraction + "flow" +: { + // Extracts entity definitions from text chunks + // Identifies and defines key entities mentioned in the text + "kg-extract-definitions:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output definition triples + "entity-contexts": flow("entity-contexts-load:{id}"), // Entity context information + "prompt-request": request("prompt:{id}"), // Definition extraction prompts + "prompt-response": response("prompt:{id}"), + }, + + // Extracts relationships between entities + // Identifies how entities are connected and interact + "kg-extract-relationships:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output relationship triples + "prompt-request": request("prompt:{id}"), // Relationship extraction prompts + "prompt-response": response("prompt:{id}"), + }, + }, + + // No class-level processors needed + "class" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/kgcore.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/kgcore.jsonnet new file mode 100644 index 00000000..4a857e66 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/kgcore.jsonnet @@ -0,0 +1,31 @@ +// Knowledge Graph Core storage module +// Handles persistent storage of knowledge graph data +// Consolidates triples and graph embeddings into permanent storage +// Creates the core knowledge base for long-term use + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; + +{ + // No external interfaces - internal storage service + "interfaces" +: { + }, + + // No configurable parameters for core storage + "parameters" +: { + }, + + // Flow-level processors for knowledge graph storage + "flow" +: { + // Knowledge graph store consolidates extracted knowledge + // Takes processed triples and embeddings and stores them permanently + "kg-store:{id}": { + "triples-input": flow("triples-store:{id}"), // Input RDF triples stream + "graph-embeddings-input": flow("graph-embeddings-store:{id}"), // Input graph embeddings + }, + }, + + // No class-level processors needed + "class" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/llm-parameters.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/llm-parameters.jsonnet new file mode 100644 index 00000000..a99f72ae --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/llm-parameters.jsonnet @@ -0,0 +1,59 @@ +{ + + // LLM model selection for normal LLM + "llm-model": { + "type": "llm-model", + "description": "LLM model", + "order": 1, + "advanced": false, + }, + + // LLM model for RAG operations + "llm-rag-model": { + "type": "llm-model", + "description": "LLM model for RAG", + "order": 2, + "advanced": true, + "controlled-by": "llm-model", + }, + + // LLM model selection for normal LLM + "llm-temperature": { + "type": "llm-temperature", + "description": "LLM temperature", + "order": 3, + "advanced": true, + }, + + // LLM model selection for normal LLM + "llm-rag-temperature": { + "type": "llm-temperature", + "description": "LLM temperature for RAG", + "order": 4, + "advanced": true, + }, + + "embeddings-model": { + "type": "embeddings-model", + "description": "Embeddings model", + "order": 5, + "advanced": true, + }, + + // LLM model selection for normal LLM + "chunk-size": { + "type": "chunk-size", + "description": "Chunk size", + "order": 6, + "advanced": true, + }, + + // LLM model selection for normal LLM + "chunk-overlap": { + "type": "chunk-overlap", + "description": "Chunk overlap", + "order": 7, + "advanced": true, + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/load.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/load.jsonnet new file mode 100644 index 00000000..eacf60dd --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/load.jsonnet @@ -0,0 +1,50 @@ +// Document loading and preprocessing module +// Handles document ingestion, format conversion, and chunking +// Converts PDFs to text and splits documents into processable chunks + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +{ + + // External interfaces for document loading + "interfaces" +: { + "document-load": flow("document-load:{id}"), // Raw document input stream + "text-load": flow("text-document-load:{id}"), // Text document stream + "embeddings": request_response("embeddings:{id}"), // Embedding service for chunks + }, + + // No configurable parameters for document loading + "parameters" +: { + }, + + // Flow-level processors for document preprocessing + "flow" +: { + // PDF decoder converts PDF documents to text + "pdf-decoder:{id}": { + input: flow("document-load:{id}"), // Raw PDF input + output: flow("text-document-load:{id}"), // Extracted text output + }, + + // Chunker splits documents into smaller, processable pieces + "chunker:{id}": { + input: flow("text-document-load:{id}"), // Full text documents + output: flow("chunk-load:{id}"), // Document chunks for processing + "chunk-size": "{chunk-size}", // Chunk size + "chunk-overlap": "{chunk-overlap}", // Overlap between chunks + }, + // Embedding service for converting text chunks to vectors + "embeddings:{id}": { + request: request("embeddings:{id}"), // Embedding requests + response: response("embeddings:{id}"), // Embedding responses + model: "{embeddings-model}", + }, + }, + + // Class-level processors for document loading services + "class" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/onto-base.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/onto-base.jsonnet new file mode 100644 index 00000000..2236f24d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/onto-base.jsonnet @@ -0,0 +1,39 @@ +// Knowledge Graph Base extraction module +// Provides basic knowledge extraction capabilities from text chunks +// Extracts entity definitions and relationships using prompt-based processing + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; + +{ + // No external interfaces - this module provides internal extraction services + "interfaces" +: { + }, + + // No configurable parameters for basic KG extraction + "parameters" +: { + }, + + // Flow-level processors for knowledge extraction + "flow" +: { + // Extracts using ontology definitions + "kg-extract-ontology:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output triples + "entity-contexts": flow("entity-contexts-load:{id}"), // Entity context information + "prompt-request": request("prompt:{id}"), // Definition + // extraction prompts + "prompt-response": response("prompt:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + }, + + }, + + // No class-level processors needed + "class" +: { + } +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/ontorag.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/ontorag.jsonnet new file mode 100644 index 00000000..94cf5750 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/ontorag.jsonnet @@ -0,0 +1,107 @@ +// GraphRAG flow configuration module +// Implements graph-based retrieval augmented generation (GraphRAG) functionality +// Handles knowledge graph storage, embeddings, and graph-based question answering + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; +local llm_parameters = import "llm-parameters.jsonnet"; + +{ + // External interfaces exposed by the GraphRAG flow + "interfaces" +: { + // Data ingestion interfaces for graph construction + "entity-contexts-load": flow("entity-contexts-load:{id}"), // Entity context data stream + "triples-store": flow("triples-store:{id}"), // RDF triples storage stream + "graph-embeddings-store": flow("graph-embeddings-store:{id}"), // Graph embedding storage + + // Query interfaces for graph-based operations + "graph-rag": request_response("graph-rag:{id}"), // Main GraphRAG query interface + "triples": request_response("triples:{id}"), // Triple store queries + "graph-embeddings": request_response("graph-embeddings:{id}"), // Graph embedding queries + + // Supporting services + "embeddings": request_response("embeddings:{id}"), // General embedding service + "prompt": request_response("prompt:{id}"), // Prompt processing service + "text-completion": request_response("text-completion:{id}"), // LLM text completion + }, + + + // Parameters that can be configured for this flow + "parameters" +: llm_parameters, + + // Flow-level processors - handle data streams for a specific flow instance + "flow" +: { + "graph-embeddings:{id}": { + input: flow("entity-contexts-load:{id}"), + output: flow("graph-embeddings-store:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + }, + "triples-write:{id}": { + input: flow("triples-store:{id}"), + }, + "ge-write:{id}": { + input: flow("graph-embeddings-store:{id}"), + }, + "text-completion:{id}": { + request: request("text-completion:{id}"), + response: response("text-completion:{id}"), + model: "{llm-model}", + }, + "text-completion-rag:{id}": { + request: request("text-completion-rag:{id}"), + response: response("text-completion-rag:{id}"), + model: "{llm-rag-model}", + }, + "embeddings:{id}": { + request: request("embeddings:{id}"), + response: response("embeddings:{id}"), + model: "{embeddings-model}", + }, + "graph-rag:{id}": { + request: request("graph-rag:{id}"), + response: response("graph-rag:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + "prompt-request": request("prompt-rag:{id}"), + "prompt-response": response("prompt-rag:{id}"), + "graph-embeddings-request": request("graph-embeddings:{id}"), + "graph-embeddings-response": response("graph-embeddings:{id}"), + "triples-request": request("triples:{id}"), + "triples-response": response("triples:{id}"), + }, + "triples-query:{id}": { + request: request("triples:{id}"), + response: response("triples:{id}"), + }, + "ge-query:{id}": { + request: request("graph-embeddings:{id}"), + response: response("graph-embeddings:{id}"), + }, + "prompt:{id}": { + request: request("prompt:{id}"), + response: response("prompt:{id}"), + "text-completion-request": request("text-completion:{id}"), + "text-completion-response": response("text-completion:{id}"), + }, + "prompt-rag:{id}": { + request: request("prompt-rag:{id}"), + response: response("prompt-rag:{id}"), + "text-completion-request": request("text-completion-rag:{id}"), + "text-completion-response": response("text-completion-rag:{id}"), + }, + "metering:{id}": { + input: response("text-completion:{id}"), + }, + "metering-rag:{id}": { + input: response("text-completion-rag:{id}"), + }, + }, + + // Class-level processors - shared across all flow instances of this class + "class" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/structured.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/structured.jsonnet new file mode 100644 index 00000000..45bd35d9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/flows/structured.jsonnet @@ -0,0 +1,108 @@ +// Structured data processing module +// Handles extraction and querying of structured data objects +// Provides natural language to GraphQL query capabilities +// Supports structured data storage and retrieval + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; +local llm_parameters = import "llm-parameters.jsonnet"; + +{ + // External interfaces for structured data operations + "interfaces" +: { + // Supporting services + "embeddings": request_response("embeddings:{id}"), // Embedding service + "prompt": request_response("prompt:{id}"), // Prompt processing + "text-completion": request_response("text-completion:{id}"), // LLM completion + + // Structured data storage and querying + "objects-store": flow("objects-store:{id}"), // Object storage stream + "objects": request_response("objects:{id}"), // Object query service + + // Query interfaces + "nlp-query": request_response("nlp-query:{id}"), // NLP to GraphQL translation + "structured-query": request_response("structured-query:{id}"), // Structured query execution + "structured-diag": request_response("structured-diag:{id}"), // Query diagnostics + }, + + + // Parameters that can be configured for this flow + "parameters" +: llm_parameters, + + // Flow-level processors for structured data extraction + "flow" +: { + "kg-extract-objects:{id}": { + input: flow("chunk-load:{id}"), + output: flow("objects-store:{id}"), + "entity-contexts": flow("entity-contexts-load:{id}"), + "prompt-request": request("prompt:{id}"), + "prompt-response": response("prompt:{id}"), + }, + "objects-write:{id}": { + input: flow("objects-store:{id}"), + }, + "text-completion:{id}": { + request: request("text-completion:{id}"), + response: response("text-completion:{id}"), + model: "{llm-model}", + }, + "text-completion-rag:{id}": { + request: request("text-completion-rag:{id}"), + response: response("text-completion-rag:{id}"), + model: "{llm-rag-model}", + }, + "objects-query:{id}": { + request: request("objects:{id}"), + response: response("objects:{id}"), + }, + "nlp-query:{id}": { + request: request("nlp-query:{id}"), + response: response("nlp-query:{id}"), + "prompt-request": request("prompt-rag:{id}"), + "prompt-response": response("prompt-rag:{id}"), + }, + "structured-query:{id}": { + request: request("structured-query:{id}"), + response: response("structured-query:{id}"), + "nlp-query-request": request("nlp-query:{id}"), + "nlp-query-response": response("nlp-query:{id}"), + "objects-query-request": request("objects:{id}"), + "objects-query-response": response("objects:{id}"), + }, + "structured-diag:{id}": { + request: request("structured-diag:{id}"), + response: response("structured-diag:{id}"), + "prompt-request": request("prompt:{id}"), + "prompt-response": response("prompt:{id}"), + }, + "embeddings:{id}": { + request: request("embeddings:{id}"), + response: response("embeddings:{id}"), + model: "{embeddings-model}", + }, + "prompt:{id}": { + request: request("prompt:{id}"), + response: response("prompt:{id}"), + "text-completion-request": request("text-completion:{id}"), + "text-completion-response": response("text-completion:{id}"), + }, + "prompt-rag:{id}": { + request: request("prompt-rag:{id}"), + response: response("prompt-rag:{id}"), + "text-completion-request": request("text-completion-rag:{id}"), + "text-completion-response": response("text-completion-rag:{id}"), + }, + "metering:{id}": { + input: response("text-completion:{id}"), + }, + "metering-rag:{id}": { + input: response("text-completion-rag:{id}"), + }, + }, + // Class-level processors for structured data operations + "class" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/mcp/ddg-mcp-server.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/mcp/ddg-mcp-server.jsonnet new file mode 100644 index 00000000..bb9157f4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/mcp/ddg-mcp-server.jsonnet @@ -0,0 +1,48 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "ddg-mcp-server-port":: 9870, + + "ddg-mcp-server" +: { + + create:: function(engine) + + local port = $["ddg-mcp-server-port"]; + + local container = + engine.container("ddg-mcp-server") + .with_image(images["ddg-mcp-server"]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_port(port, port, "mcp"); + + local containerSet = engine.containers( + "ddg-mcp-server", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(port, port, "mcp"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + mcp +:: { + "duckduckgo": { + "remote-name": "search", + local port = $["ddg-mcp-server-port"], + local url = "http://ddg-mcp-server:%s/mcp" % port, + "url": url, + } + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/azure-openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/azure-openai.jsonnet new file mode 100644 index 00000000..0e3ea907 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/azure-openai.jsonnet @@ -0,0 +1,9 @@ +// Azure OpenAI LLM Model Definitions +// Model input is just text + +{ + "type": "string", + "description": "LLM model to use", + "default": "gpt-4o", + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/azure.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/azure.jsonnet new file mode 100644 index 00000000..6ffa5fdf --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/azure.jsonnet @@ -0,0 +1,9 @@ +// Azure LLM Model Definitions +// Model input is just text + +{ + "type": "string", + "description": "LLM model to use", + "default": "phi4:14b", + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/bedrock.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/bedrock.jsonnet new file mode 100644 index 00000000..df930e59 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/bedrock.jsonnet @@ -0,0 +1,63 @@ +// AWS Bedrock LLM Model Definitions +// Defines available models and their configurations for AWS Bedrock + +{ + "type": "string", + "description": "LLM model to use", + "default": "global.anthropic.claude-sonnet-4-5-20250929-v1:0", + "enum": [ + { + id: "global.anthropic.claude-sonnet-4-5-20250929-v1:0", + description: "Claude Sonnet 4.5 (smartest for complex agents and coding)" + }, + { + id: "global.anthropic.claude-opus-4-5-20251101-v1:0", + description: "Claude Opus 4.5 (maximum intelligence)" + }, + { + id: "global.anthropic.claude-haiku-4-5-20251001-v1:0", + description: "Claude Haiku 4.5 (fastest with near-frontier intelligence)" + }, + { + id: "global.anthropic.claude-opus-4-1-20250805-v1:0", + description: "Claude Opus 4.1 (specialized reasoning)" + }, + { + id: "global.anthropic.claude-sonnet-4-20250514-v1:0", + description: "Claude Sonnet 4.0" + }, + { + id: "global.anthropic.claude-opus-4-20250514-v1:0", + description: "Claude Opus 4.0" + }, + { + id: "anthropic.claude-3-5-haiku-20241022-v1:0", + description: "Claude 3.5 Haiku" + }, + { + id: "anthropic.claude-3-haiku-20240307-v1:0", + description: "Claude 3 Haiku" + }, + { + id: "meta.llama3-1-405b-instruct-v1:0", + description: "Llama 3.1 405B Instruct" + }, + { + id: "meta.llama3-1-70b-instruct-v1:0", + description: "Llama 3.1 70B Instruct" + }, + { + id: "meta.llama3-1-8b-instruct-v1:0", + description: "Llama 3.1 8B Instruct" + }, + { + id: "mistral.mistral-large-2407-v1:0", + description: "Mistral Large" + }, + { + id: "mistral.mixtral-8x7b-instruct-v0:1", + description: "Mixtral 8x7B Instruct" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/chunking-param-types.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/chunking-param-types.jsonnet new file mode 100644 index 00000000..f82fa3f7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/chunking-param-types.jsonnet @@ -0,0 +1,25 @@ +// Chunk parameter type definitions + +{ + "chunk-size": { + "type": "integer", + "description": "Chunk size", + "placeholder": 2000, + "helper": "An integer, usually 2000 .. 8000", + "default": 2000, + "min": 0, + "max": 32768, + "required": true + }, + "chunk-overlap": { + "type": "integer", + "description": "Chunk overlap", + "placeholder": 50, + "helper": "An integer, usually 50 .. 100", + "default": 50, + "min": 0, + "max": 8000, + "required": true + }, +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/claude.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/claude.jsonnet new file mode 100644 index 00000000..40fd434b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/claude.jsonnet @@ -0,0 +1,35 @@ +// Claude LLM Model Definitions +// Defines available models and their configurations for Anthropic's Claude + +{ + "type": "string", + "description": "LLM model to use", + "default": "claude-sonnet-4-5-20250929", + "enum": [ + { + id: "claude-sonnet-4-5-20250929", + description: "Claude Sonnet 4.5 (complex agents + coding)" + }, + { + id: "claude-opus-4-5-20251101", + description: "Claude Opus 4.5 (maximum intelligence)" + }, + { + id: "claude-haiku-4-5-20251001", + description: "Claude Haiku 4.5 (fast)" + }, + { + id: "claude-opus-4-1-20250805", + description: "Claude Opus 4.1 (specialized reasoning)" + }, + { + id: "claude-sonnet-4-20250514", + description: "Claude Sonnet 4.0" + }, + { + id: "claude-3-5-haiku-20241022", + description: "Claude 3.5 Haiku" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/cohere.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/cohere.jsonnet new file mode 100644 index 00000000..5a5865a3 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/cohere.jsonnet @@ -0,0 +1,35 @@ +// Cohere LLM Model Definitions +// Defines available models and their configurations for Cohere + +{ + "type": "string", + "description": "LLM model to use", + "default": "command-r-plus-08-2024", + "enum": [ + { + id: "command-r-plus-08-2024", + description: "Command R+ (August 2024)" + }, + { + id: "command-r-08-2024", + description: "Command R (August 2024)" + }, + { + id: "command-r-plus", + description: "Command R+ (legacy)" + }, + { + id: "command-r", + description: "Command R (legacy)" + }, + { + id: "command", + description: "Command" + }, + { + id: "command-light", + description: "Command Light" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/embeddings-fastembed.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/embeddings-fastembed.jsonnet new file mode 100644 index 00000000..477e25dc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/embeddings-fastembed.jsonnet @@ -0,0 +1,127 @@ +// Embeddings model definitions for fastembed +// Defines available models and their configurations for Fastembed + +{ + "type": "string", + "description": "Embeddings model to use", + "default": "sentence-transformers/all-MiniLM-L6-v2", + "enum": [ + { + "id": "sentence-transformers/all-MiniLM-L6-v2", + "description": "all-MiniLM-L6-v2" + }, + { + "id": "BAAI/bge-small-en-v1.5", + "description": "bge-small-en-v1.5" + }, + { + "id": "BAAI/bge-small-zh-v1.5", + "description": "bge-small-zh-v1.5" + }, + { + "id": "snowflake/snowflake-arctic-embed-xs", + "description": "snowflake-arctic-embed-xs" + }, + { + "id": "jinaai/jina-embeddings-v2-small-en", + "description": "jina-embeddings-v2-small-en" + }, + { + "id": "nomic-ai/nomic-embed-text-v1.5-Q", + "description": "nomic-embed-text-v1.5-Q" + }, + { + "id": "snowflake/snowflake-arctic-embed-s", + "description": "snowflake-arctic-embed-s" + }, + { + "id": "BAAI/bge-small-en", + "description": "bge-small-en" + }, + { + "id": "BAAI/bge-base-en-v1.5", + "description": "bge-base-en-v1.5" + }, + { + "id": "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2", + "description": "paraphrase-multilingual-MiniLM-L12-v2" + }, + { + "id": "Qdrant/clip-ViT-B-32-text", + "description": "clip-ViT-B-32-text" + }, + { + "id": "jinaai/jina-embeddings-v2-base-de", + "description": "jina-embeddings-v2-base-de" + }, + { + "id": "BAAI/bge-base-en", + "description": "bge-base-en" + }, + { + "id": "snowflake/snowflake-arctic-embed-m", + "description": "snowflake-arctic-embed-m" + }, + { + "id": "thenlper/gte-base", + "description": "gte-base" + }, + { + "id": "jinaai/jina-embeddings-v2-base-en", + "description": "jina-embeddings-v2-base-en" + }, + { + "id": "nomic-ai/nomic-embed-text-v1", + "description": "nomic-embed-text-v1" + }, + { + "id": "nomic-ai/nomic-embed-text-v1.5", + "description": "nomic-embed-text-v1.5" + }, + { + "id": "snowflake/snowflake-arctic-embed-m-long", + "description": "snowflake-arctic-embed-m-long" + }, + { + "id": "jinaai/jina-clip-v1", + "description": "jina-clip-v1" + }, + { + "id": "mixedbread-ai/mxbai-embed-large-v1", + "description": "mxbai-embed-large-v1" + }, + { + "id": "jinaai/jina-embeddings-v2-base-es", + "description": "jina-embeddings-v2-base-es" + }, + { + "id": "jinaai/jina-embeddings-v2-base-code", + "description": "jina-embeddings-v2-base-code" + }, + { + "id": "jinaai/jina-embeddings-v2-base-zh", + "description": "jina-embeddings-v2-base-zh" + }, + { + "id": "sentence-transformers/paraphrase-multilingual-mpnet-base-v2", + "description": "paraphrase-multilingual-mpnet-base-v2" + }, + { + "id": "snowflake/snowflake-arctic-embed-l", + "description": "snowflake-arctic-embed-l" + }, + { + "id": "BAAI/bge-large-en-v1.5", + "description": "bge-large-en-v1.5" + }, + { + "id": "thenlper/gte-large", + "description": "gte-large" + }, + { + "id": "intfloat/multilingual-e5-large", + "description": "multilingual-e5-large" + } + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/embeddings-huggingface.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/embeddings-huggingface.jsonnet new file mode 100644 index 00000000..32254a2b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/embeddings-huggingface.jsonnet @@ -0,0 +1,31 @@ +// Embeddings model definitions for fastembed +// Defines available models and their configurations for Fastembed + +{ + "type": "string", + "description": "Embeddings model to use", + "default": "sentence-transformers/all-MiniLM-L6-v2", + "enum": [ + { + "id": "all-MiniLM-L6-v2", + "description": "all-MiniLM-L6-v2" + }, + { + "id": "all-mpnet-base-v2", + "description": "all-mpnet-base-v2" + }, + { + "id": "all-distilroberta-v1", + "description": "all-distilroberta-v1" + }, + { + "id": "stsb-bert-large", + "description": "stsb-bert-large" + }, + { + "id": "sentence-camembert-large", + "description": "sentence-camembert-large" + } + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/embeddings-ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/embeddings-ollama.jsonnet new file mode 100644 index 00000000..c3f29fbb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/embeddings-ollama.jsonnet @@ -0,0 +1,23 @@ +// Embeddings model definitions for Ollama +// Defines available models and their configurations for Ollama + +{ + "type": "string", + "description": "Embeddings model to use", + "default": "all-minilm:latest", + "enum": [ + { + "id": "all-minilm:latest", + "description": "all-MiniLM-L6-v2" + }, + { + "id": "nomic-ai/nomic-embed-text-v1.5-Q", + "description": "nomic-embed-text-v1.5-Q" + }, + { + "id": "mixedbread-ai/mxbai-embed-large-v1", + "description": "mxbai-embed-large-v1" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/googleaistudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/googleaistudio.jsonnet new file mode 100644 index 00000000..a8891932 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/googleaistudio.jsonnet @@ -0,0 +1,67 @@ +// Google AI Studio LLM Model Definitions +// Defines available models and their configurations for Google AI Studio + +{ + "type": "string", + "description": "LLM model to use", + "default": "gemini-2.5-flash-lite", + "enum": [ + // Gemini 2.5 models (latest generation) + { + id: "gemini-2.5-pro", + description: "Gemini 2.5 Pro" + }, + { + id: "gemini-2.5-flash", + description: "Gemini 2.5 Flash" + }, + { + id: "gemini-2.5-flash-lite", + description: "Gemini 2.5 Flash Lite" + }, + + // Gemini 2.0 models + { + id: "gemini-2.0-flash-exp", + description: "Gemini 2.0 Flash (experimental)" + }, + + + // Claude models on VertexAI + { + id: "claude-3-5-sonnet@20241022", + description: "Claude 3.5 Sonnet (via VertexAI)" + }, + { + id: "claude-3-5-haiku@20241022", + description: "Claude 3.5 Haiku (via VertexAI)" + }, + { + id: "claude-3-opus@20240229", + description: "Claude 3 Opus (via VertexAI)" + }, + { + id: "claude-3-sonnet@20240229", + description: "Claude 3 Sonnet (via VertexAI)" + }, + { + id: "claude-3-haiku@20240307", + description: "Claude 3 Haiku (via VertexAI)" + }, + + // Llama models on VertexAI + { + id: "llama3-405b-instruct-maas", + description: "Llama 3 405B Instruct (via VertexAI)" + }, + { + id: "llama3-70b-instruct-maas", + description: "Llama 3 70B Instruct (via VertexAI)" + }, + { + id: "llama3-8b-instruct-maas", + description: "Llama 3 8B Instruct (via VertexAI)" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/llamafile.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/llamafile.jsonnet new file mode 100644 index 00000000..f6480aa9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/llamafile.jsonnet @@ -0,0 +1,9 @@ +// LlamaFile LLM Model Definitions +// Model input is just text + +{ + "type": "string", + "description": "LLM model to use", + "default": "phi4:14b", + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/lmstudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/lmstudio.jsonnet new file mode 100644 index 00000000..1c88da3c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/lmstudio.jsonnet @@ -0,0 +1,150 @@ +// LMStudio LLM Model Definitions +// Defines available models and their configurations for LMStudio + +{ + "type": "string", + "description": "LLM model to use", + "default": "llama3.1:70b", + "enum": [ + // Gemma3 models + { + id: "gemma3:1b", + description: "Gemma3 1B" + }, + { + id: "gemma3:4b", + description: "Gemma3 4B" + }, + { + id: "gemma3:12b", + description: "Gemma3 12B" + }, + { + id: "gemma3:27b", + description: "Gemma3 27B" + }, + + // Phi4 models + { + id: "phi4:mini:3.8b", + description: "Phi4 Mini 3.8B" + }, + { + id: "phi4:14b", + description: "Phi4 14B" + }, + + // DeepSeek-R1 models + { + id: "deepseek-r1:671b", + description: "DeepSeek-R1 671B" + }, + { + id: "deepseek-r1:70b", + description: "DeepSeek-R1 70B" + }, + { + id: "deepseek-r1:32b", + description: "DeepSeek-R1 32B" + }, + { + id: "deepseek-r1:14b", + description: "DeepSeek-R1 14B" + }, + { + id: "deepseek-r1:8b", + description: "DeepSeek-R1 8B" + }, + { + id: "deepseek-r1:7b", + description: "DeepSeek-R1 7B" + }, + { + id: "deepseek-r1:1.5b", + description: "DeepSeek-R1 1.5B" + }, + + // Llama3.1 models + { + id: "llama3.1:405b", + description: "Llama 3.1 405B" + }, + { + id: "llama3.1:70b", + description: "Llama 3.1 70B" + }, + { + id: "llama3.1:8b", + description: "Llama 3.1 8B" + }, + + // Gemma2 models + { + id: "gemma2:2b", + description: "Gemma2 2B" + }, + { + id: "gemma2:9b", + description: "Gemma2 9B" + }, + { + id: "gemma2:27b", + description: "Gemma2 27B" + }, + + // Qwen2.5 models + { + id: "qwen2.5:0.5b", + description: "Qwen2.5 0.5B" + }, + { + id: "qwen2.5:1.5b", + description: "Qwen2.5 1.5B" + }, + { + id: "qwen2.5:3b", + description: "Qwen2.5 3B" + }, + { + id: "qwen2.5:7b", + description: "Qwen2.5 7B" + }, + { + id: "qwen2.5:14b", + description: "Qwen2.5 14B" + }, + { + id: "qwen2.5:32b", + description: "Qwen2.5 32B" + }, + { + id: "qwen2.5:72b", + description: "Qwen2.5 72B" + }, + + // Mistral models + { + id: "mistral:7b", + description: "Mistral 7B" + }, + { + id: "mistral-nemo:12b", + description: "Mistral Nemo 12B" + }, + { + id: "mistral-large:123b", + description: "Mistral Large 123B" + }, + + // Command-R models + { + id: "command-r:35b", + description: "Command-R 35B" + }, + { + id: "command-r-plus:104b", + description: "Command-R Plus 104B" + }, + ], + "required": true +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/mistral.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/mistral.jsonnet new file mode 100644 index 00000000..07f75b81 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/mistral.jsonnet @@ -0,0 +1,61 @@ +// Mistral LLM Model Definitions +// Defines available models and their configurations for Mistral AI + +{ + "type": "string", + "description": "LLM model to use", + "default": "mistral-medium-2508", + "enum": [ + // Featured models + { + id: "mistral-medium-2508", + description: "Mistral Medium 3.1" + }, + { + id: "mistral-large-2512", + description: "Mistral Large 3" + }, + { + id: "mistral-small-2506", + description: "Mistral Small 3.2" + }, + { + id: "ministral-14b-2512", + description: "Ministral 3 14B" + }, + { + id: "ministral-8b-2512", + description: "Ministral 3 8B" + }, + { + id: "ministral-3b-2512", + description: "Ministral 3 3B" + }, + { + id: "magistral-medium-2509", + description: "Magistral Medium 1.2 (reasoning)" + }, + { + id: "magistral-small-2509", + description: "Magistral Small 1.2 (reasoning)" + }, + { + id: "devstral-2512", + description: "Devstral 2 (code)" + }, + // Other models + { + id: "codestral-2508", + description: "Codestral (code)" + }, + { + id: "pixtral-large-2411", + description: "Pixtral Large (vision)" + }, + { + id: "open-mistral-nemo", + description: "Open Mistral Nemo" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/ollama.jsonnet new file mode 100644 index 00000000..4ef98a6a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/ollama.jsonnet @@ -0,0 +1,150 @@ +// Ollama LLM Model Definitions +// Defines available models and their configurations for Ollama + +{ + "type": "string", + "description": "LLM model to use", + "default": "gemma3:12b", + "enum": [ + // Gemma3 models + { + id: "gemma3:1b", + description: "Gemma3 1B" + }, + { + id: "gemma3:4b", + description: "Gemma3 4B" + }, + { + id: "gemma3:12b", + description: "Gemma3 12B" + }, + { + id: "gemma3:27b", + description: "Gemma3 27B" + }, + + // Phi4 models + { + id: "phi4:mini:3.8b", + description: "Phi4 Mini 3.8B" + }, + { + id: "phi4:14b", + description: "Phi4 14B" + }, + + // DeepSeek-R1 models + { + id: "deepseek-r1:671b", + description: "DeepSeek-R1 671B" + }, + { + id: "deepseek-r1:70b", + description: "DeepSeek-R1 70B" + }, + { + id: "deepseek-r1:32b", + description: "DeepSeek-R1 32B" + }, + { + id: "deepseek-r1:14b", + description: "DeepSeek-R1 14B" + }, + { + id: "deepseek-r1:8b", + description: "DeepSeek-R1 8B" + }, + { + id: "deepseek-r1:7b", + description: "DeepSeek-R1 7B" + }, + { + id: "deepseek-r1:1.5b", + description: "DeepSeek-R1 1.5B" + }, + + // Llama3.1 models + { + id: "llama3.1:405b", + description: "Llama 3.1 405B" + }, + { + id: "llama3.1:70b", + description: "Llama 3.1 70B" + }, + { + id: "llama3.1:8b", + description: "Llama 3.1 8B" + }, + + // Gemma2 models + { + id: "gemma2:2b", + description: "Gemma2 2B" + }, + { + id: "gemma2:9b", + description: "Gemma2 9B" + }, + { + id: "gemma2:27b", + description: "Gemma2 27B" + }, + + // Qwen2.5 models + { + id: "qwen2.5:0.5b", + description: "Qwen2.5 0.5B" + }, + { + id: "qwen2.5:1.5b", + description: "Qwen2.5 1.5B" + }, + { + id: "qwen2.5:3b", + description: "Qwen2.5 3B" + }, + { + id: "qwen2.5:7b", + description: "Qwen2.5 7B" + }, + { + id: "qwen2.5:14b", + description: "Qwen2.5 14B" + }, + { + id: "qwen2.5:32b", + description: "Qwen2.5 32B" + }, + { + id: "qwen2.5:72b", + description: "Qwen2.5 72B" + }, + + // Mistral models + { + id: "mistral:7b", + description: "Mistral 7B" + }, + { + id: "mistral-nemo:12b", + description: "Mistral Nemo 12B" + }, + { + id: "mistral-large:123b", + description: "Mistral Large 123B" + }, + + // Command-R models + { + id: "command-r:35b", + description: "Command-R 35B" + }, + { + id: "command-r-plus:104b", + description: "Command-R Plus 104B" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/openai.jsonnet new file mode 100644 index 00000000..75b2670f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/openai.jsonnet @@ -0,0 +1,31 @@ +// OpenAI LLM Model Definitions +// Defines available models and their configurations for OpenAI's platform + +{ + "type": "string", + "description": "LLM model to use", + "default": "gpt-4o", + "enum": [ + { + id: "gpt-4o", + description: "GPT-4o (latest)" + }, + { + id: "gpt-4o-mini", + description: "GPT-4o Mini" + }, + { + id: "gpt-4-turbo", + description: "GPT-4 Turbo" + }, + { + id: "gpt-4", + description: "GPT-4" + }, + { + id: "gpt-3.5-turbo", + description: "GPT-3.5 Turbo" + }, + ], + "required": true +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/temperature-param-types.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/temperature-param-types.jsonnet new file mode 100644 index 00000000..f5523e88 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/temperature-param-types.jsonnet @@ -0,0 +1,15 @@ +// LLM temperature definitions + +{ + "llm-temperature": { + "type": "float", + "description": "LLM temperature", + "placeholder": 0.3, + "helper": "A floating point number between 0 and 1", + "default": 0.3, + "min": 0.0, + "max": 10.0, + "required": true + } +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/vertexai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/vertexai.jsonnet new file mode 100644 index 00000000..6d90f8d3 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/vertexai.jsonnet @@ -0,0 +1,68 @@ +// VertexAI LLM Model Definitions +// Defines available models and their configurations for Google's VertexAI platform + +{ + "type": "string", + "description": "LLM model to use", + "default": "gemini-2.5-flash-lite", + "enum": [ + // Gemini 2.5 models (latest generation) + { + id: "gemini-2.5-pro", + description: "Gemini 2.5 Pro" + }, + { + id: "gemini-2.5-flash", + description: "Gemini 2.5 Flash" + }, + { + id: "gemini-2.5-flash-lite", + description: "Gemini 2.5 Flash Lite" + }, + + // Gemini 2.0 models + { + id: "gemini-2.0-flash-exp", + description: "Gemini 2.0 Flash (experimental)" + }, + + + // Claude models on VertexAI + { + id: "claude-3-5-sonnet@20241022", + description: "Claude 3.5 Sonnet (via VertexAI)" + }, + { + id: "claude-3-5-haiku@20241022", + description: "Claude 3.5 Haiku (via VertexAI)" + }, + { + id: "claude-3-opus@20240229", + description: "Claude 3 Opus (via VertexAI)" + }, + { + id: "claude-3-sonnet@20240229", + description: "Claude 3 Sonnet (via VertexAI)" + }, + { + id: "claude-3-haiku@20240307", + description: "Claude 3 Haiku (via VertexAI)" + }, + + // Llama models on VertexAI + { + id: "llama3-405b-instruct-maas", + description: "Llama 3 405B Instruct (via VertexAI)" + }, + { + id: "llama3-70b-instruct-maas", + description: "Llama 3 70B Instruct (via VertexAI)" + }, + { + id: "llama3-8b-instruct-maas", + description: "Llama 3 8B Instruct (via VertexAI)" + }, + ], + "required": true +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/vllm.jsonnet new file mode 100644 index 00000000..e372a2ba --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/parameters/vllm.jsonnet @@ -0,0 +1,125 @@ +// vLLM Model Definitions +// Defines available models and their configurations for vLLM +// vLLM works with any HuggingFace model, these are common optimized choices + +{ + "type": "string", + "description": "LLM model to use", + "default": "meta-llama/Llama-3.1-8B-Instruct", + "enum": [ + // Llama 3.1 models + { + id: "meta-llama/Llama-3.1-8B-Instruct", + description: "Llama 3.1 8B Instruct" + }, + { + id: "meta-llama/Llama-3.1-70B-Instruct", + description: "Llama 3.1 70B Instruct" + }, + { + id: "meta-llama/Llama-3.1-405B-Instruct", + description: "Llama 3.1 405B Instruct" + }, + + // Llama 3.2 models + { + id: "meta-llama/Llama-3.2-1B-Instruct", + description: "Llama 3.2 1B Instruct" + }, + { + id: "meta-llama/Llama-3.2-3B-Instruct", + description: "Llama 3.2 3B Instruct" + }, + + // Qwen2.5 models + { + id: "Qwen/Qwen2.5-0.5B-Instruct", + description: "Qwen2.5 0.5B Instruct" + }, + { + id: "Qwen/Qwen2.5-1.5B-Instruct", + description: "Qwen2.5 1.5B Instruct" + }, + { + id: "Qwen/Qwen2.5-3B-Instruct", + description: "Qwen2.5 3B Instruct" + }, + { + id: "Qwen/Qwen2.5-7B-Instruct", + description: "Qwen2.5 7B Instruct" + }, + { + id: "Qwen/Qwen2.5-14B-Instruct", + description: "Qwen2.5 14B Instruct" + }, + { + id: "Qwen/Qwen2.5-32B-Instruct", + description: "Qwen2.5 32B Instruct" + }, + { + id: "Qwen/Qwen2.5-72B-Instruct", + description: "Qwen2.5 72B Instruct" + }, + + // Mistral models + { + id: "mistralai/Mistral-7B-Instruct-v0.3", + description: "Mistral 7B Instruct v0.3" + }, + { + id: "mistralai/Mixtral-8x7B-Instruct-v0.1", + description: "Mixtral 8x7B Instruct" + }, + { + id: "mistralai/Mixtral-8x22B-Instruct-v0.1", + description: "Mixtral 8x22B Instruct" + }, + + // Phi models + { + id: "microsoft/Phi-3.5-mini-instruct", + description: "Phi 3.5 Mini Instruct" + }, + { + id: "microsoft/Phi-4", + description: "Phi 4" + }, + + // Gemma models + { + id: "google/gemma-2-2b-it", + description: "Gemma 2 2B Instruct" + }, + { + id: "google/gemma-2-9b-it", + description: "Gemma 2 9B Instruct" + }, + { + id: "google/gemma-2-27b-it", + description: "Gemma 2 27B Instruct" + }, + + // DeepSeek models + { + id: "deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B", + description: "DeepSeek-R1 Distill Qwen 1.5B" + }, + { + id: "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B", + description: "DeepSeek-R1 Distill Qwen 7B" + }, + { + id: "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B", + description: "DeepSeek-R1 Distill Qwen 14B" + }, + { + id: "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B", + description: "DeepSeek-R1 Distill Qwen 32B" + }, + { + id: "deepseek-ai/DeepSeek-R1-Distill-Llama-70B", + description: "DeepSeek-R1 Distill Llama 70B" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/agent-prompt.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/agent-prompt.txt new file mode 100644 index 00000000..9155d20f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/agent-prompt.txt @@ -0,0 +1,104 @@ +# ReAct Agent System Prompt + +You are an AI assistant that uses the ReAct (Reasoning + Acting) framework to solve problems through systematic reasoning and tool use. + +## Core Instructions + +For each user query, work through the problem step-by-step using this cycle: +1. **Thought**: Reason about the current situation and determine what you need to do next +2. **Action**: Take ONE specific action using an available tool +3. Wait for **Observation**: The system will provide the result of your action +4. Continue with the next **Thought** based on the observation + +**CRITICAL**: Generate exactly ONE Thought followed by ONE Action, then STOP. Do not generate multiple Thought/Action pairs in a single response. Do not generate Observations yourself - the system will provide them. + +## Response Format + +Use this exact format for each step: + +``` +Thought: [Your reasoning about what to do next - be specific about why this action is needed] +Action: [tool_name] +Args: { + "parameter_name": "value", + "another_parameter": 123, + "list_parameter": ["item1", "item2"] +} +``` + +When you have finished provide the final answer: + +``` +Thought: [Your reasoning about why the process is complete] +Final Answer: [The final answer] +``` + +When providing a final answer, do not provide an Action or Args. + +## Action Format Rules + +1. **Tool Name**: Write "Action: " followed by the exact tool name on its own line +2. **Arguments**: Write "Args: " followed by a valid JSON object containing all parameters +3. **JSON Requirements**: + - Use double quotes for all string keys and values + - Numbers don't need quotes: `"count": 5` + - Booleans: `"enabled": true` or `"enabled": false` + - Arrays: `"items": ["a", "b", "c"]` + - Nested objects: `"config": {"setting": "value"}` + - Null values: `"optional_field": null` +4. **Required Parameters**: Include all required parameters for the tool +5. **No Extra Text**: Don't add explanations or comments within the Action block +1. **Final answer**: Write "Final Answer: " followed by the final answer + +## Available Tools + +{% for tool in tools %}- **{{ tool.name }}**: {{ tool.description }} +{% for arg in tool.arguments %} - Required: `"{{ arg.name }}"` ({{ arg.type }}): {{ arg.description }} +{% endfor %} +{% endfor %} + +## Behavior Rules + +1. **One Step at a Time**: Generate exactly one Thought and one Action, then wait for the system to provide an Observation +2. **Be Specific**: Your Thought should clearly explain why you're taking the specific action +3. **Use Context**: Build on previous Observations to inform your next steps +4. **Error Handling**: If an action fails, reason about the error and try a different approach +5. **Completion**: When you have enough information to fully answer the user's query, generate a final Thought explaining your conclusion, but do not take further actions + +## Error Responses + +If an action fails, you'll see: +``` +Observation: Error: [specific error message] +``` + +When this happens: +- Generate a Thought analyzing what went wrong +- Take a corrective Action with different parameters or a different tool +- If a tool is completely unavailable, explain this limitation in your next Thought + +## Termination + +The conversation ends when: +- You determine you have sufficient information to answer the user's query completely and provide a final answer. +- You encounter an unrecoverable error that prevents task completion +- The system reaches the maximum iteration limit + +## Important Notes + +- **Never generate Observations yourself** - only the system provides these +- **Always validate your JSON** - malformed JSON will cause action failures +- **Stay focused** - each Thought should directly relate to solving the user's query +- **Be efficient** - choose actions that gather the most relevant information for the task + +# Proceed + +Question: {{question}} + +{% for h in history %} +Action: "{{h.action}}" +Args: { +{% for k, v in h.arguments.items() %} "{{k}}": "{{v}}" +{% endfor %}} +Observation: "{{h.observation}}" +{% endfor %} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/cohere.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/cohere.jsonnet new file mode 100644 index 00000000..9541e4c2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/cohere.jsonnet @@ -0,0 +1,42 @@ +// For Cohere. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/default-prompts.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/default-prompts.jsonnet new file mode 100644 index 00000000..35dda682 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/default-prompts.jsonnet @@ -0,0 +1,234 @@ + +// Prompt templates. For tidy JSONNET use, don't change these templates +// here, but use over-rides in the prompt directory + +{ + + "system-template":: "You are a helpful assistant.", + + "templates":: { + + "question":: { + "prompt": "{{question}}", + }, + + "extract-definitions":: { + "prompt": "\nStudy the following text and derive definitions for any discovered entities.\nDo not provide definitions for entities whose definitions are incomplete\nor unknown.\nOutput relationships in JSON format as an array of objects with fields:\n- entity: the name of the entity\n- definition: English text which defines the entity\n\n\n\n{{text}}\n\n\n\nYou will respond only with raw JSON format data. Do not provide\nexplanations. Do not use special characters in the abstract text. The\nabstract will be written as plain text. Do not add markdown formatting\nor headers or prefixes. Do not include null or unknown definitions.\n", + "response-type": "json", + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "entity": { + "type": "string" + }, + "definition": { + "type": "string" + } + }, + "required": [ + "entity", + "definition" + ] + } + } + }, + + "extract-relationships":: { + "prompt": "\nStudy the following text and derive entity relationships. For each\nrelationship, derive the subject, predicate and object of the relationship.\nOutput relationships in JSON format as an array of objects with fields:\n- subject: the subject of the relationship\n- predicate: the predicate\n- object: the object of the relationship\n- object-entity: false if the object is a simple data type: name, value or date. true if it is an entity.\n\n\n\n{{text}}\n\n\n\nYou will respond only with raw JSON format data. Do not provide\nexplanations. Do not use special characters in the abstract text. The\nabstract must be written as plain text. Do not add markdown formatting\nor headers or prefixes.\n", + "response-type": "json", + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "subject": { + "type": "string" + }, + "predicate": { + "type": "string" + }, + "object": { + "type": "string" + }, + "object-entity": { + "type": "boolean" + }, + }, + "required": [ + "subject", + "predicate", + "object", + "object-entity" + ] + } + } + }, + + "extract-topics":: { + "prompt": "You are a helpful assistant that performs information extraction tasks for a provided text.\nRead the provided text. You will identify topics and their definitions in JSON.\n\nReading Instructions:\n- Ignore document formatting in the provided text.\n- Study the provided text carefully.\n\nHere is the text:\n{{text}}\n\nResponse Instructions: \n- Do not respond with special characters.\n- Return only topics that are concepts and unique to the provided text.\n- Respond only with well-formed JSON.\n- The JSON response shall be an array of objects with keys \"topic\" and \"definition\". \n- The JSON response shall use the following structure:\n\n```json\n[{\"topic\": string, \"definition\": string}]\n```\n\n- Do not write any additional text or explanations.", + "response-type": "json", + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "topic": { + "type": "string" + }, + "definition": { + "type": "string" + } + }, + "required": [ + "topic", + "definition" + ] + } + } + }, + + "extract-rows":: { + "prompt": "\nStudy the following text and derive objects which match the schema provided.\n\nYou must output an array of JSON objects for each object you discover\nwhich matches the schema. For each object, output a JSON object whose fields\ncarry the name field specified in the schema.\n\n\n\n{{schema}}\n\n\n\n{{text}}\n\n\n\nYou will respond only with raw JSON format data. Do not provide\nexplanations. Do not add markdown formatting or headers or prefixes.\n", + "response-type": "json", + }, + + "kg-prompt":: { + "prompt": "Study the following set of knowledge statements. The statements are written in Cypher format that has been extracted from a knowledge graph. Use only the provided set of knowledge statements in your response. Do not speculate if the answer is not found in the provided set of knowledge statements.\n\nHere's the knowledge statements:\n{% for edge in knowledge %}({{edge.s}})-[{{edge.p}}]->({{edge.o}})\n{%endfor%}\n\nUse only the provided knowledge statements to respond to the following:\n{{query}}\n", + "response-type": "text", + }, + + "document-prompt":: { + "prompt": "Study the following context. Use only the information provided in the context in your response. Do not speculate if the answer is not found in the provided set of knowledge statements.\n\nHere is the context:\n{{documents}}\n\nUse only the provided knowledge statements to respond to the following:\n{{query}}\n", + "response-type": "text", + }, + + "agent-react":: { + "prompt": importstr "agent-prompt.txt", + "response-type": "text" + }, + + "agent-kg-extract":: { + "prompt": "Analyze the following text and extract both entity definitions and relationships. Return the results as JSON with 'definitions' and 'relationships' arrays.\n\nFor definitions, extract entities and their explanations or descriptions.\nFor relationships, extract subject-predicate-object triples where subjects and objects are entities, and predicates are relationship types.\n\nText: {{text}}\n\nReturn JSON only, no other text. Use this exact format:\n{\n \"definitions\": [\n {\n \"entity\": \"entity_name\",\n \"definition\": \"definition_text\"\n }\n ],\n \"relationships\": [\n {\n \"subject\": \"subject_entity\",\n \"predicate\": \"relationship_type\",\n \"object\": \"object_entity_or_literal\",\n \"object-entity\": true\n }\n ]\n}\n", + "response-type": "json", + "schema": { + "type": "object", + "properties": { + "definitions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "entity": { + "type": "string" + }, + "definition": { + "type": "string" + } + }, + "required": [ + "entity", + "definition" + ] + } + }, + "relationships": { + "type": "array", + "items": { + "type": "object", + "properties": { + "subject": { + "type": "string" + }, + "predicate": { + "type": "string" + }, + "object": { + "type": "string" + }, + "object-entity": { + "type": "boolean" + } + }, + "required": [ + "subject", + "predicate", + "object" + ] + } + } + }, + "required": [ + "definitions", + "relationships" + ] + } + }, + + "schema-selection":: { + "prompt": importstr "schema-selection.txt", + "response-type": "json", + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "description": "An array of schema names that are relevant to answering the given question" + } + }, + + "graphql-generation":: { + "prompt": importstr "graphql-generation.txt", + "response-type": "json", + "schema": { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "The GraphQL query string generated to answer the question" + }, + "variables": { + "type": "object", + "description": "Object containing any GraphQL variables needed for the query", + "additionalProperties": true + }, + "confidence": { + "type": "number", + "minimum": 0.0, + "maximum": 1.0, + "description": "Float between 0.0-1.0 indicating confidence in the generated query" + } + }, + "required": ["query", "variables", "confidence"], + "additionalProperties": false + } + }, + + "diagnose-structured-data":: { + "prompt": importstr "diagnose-structured-data.txt", + "response-type": "json", + }, + + "diagnose-xml":: { + "prompt": importstr "diagnose-xml.txt", + "response-type": "json", + }, + "diagnose-json":: { + "prompt": importstr "diagnose-json.txt", + "response-type": "json", + }, + "diagnose-csv":: { + "prompt": importstr "diagnose-csv.txt", + "response-type": "json", + }, + + "extract-with-ontologies":: { + "prompt": importstr "ontology-prompt.txt", + "response-type": "json", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/diagnose-csv.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/diagnose-csv.txt new file mode 100644 index 00000000..ad9725ba --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/diagnose-csv.txt @@ -0,0 +1,370 @@ +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for data import pipelines, with particular expertise in CSV processing and delimiter-separated value formats. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured CSV data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## CSV Processing Expertise + +When working with CSV data, you must: + +1. **Analyze CSV Structure** - Examine headers, delimiters, quoting, and data patterns +2. **Identify Column Mappings** - Map source column names to target fields +3. **Handle Complex CSV Patterns** - Support various CSV formats including: + - Standard comma-separated values: `name,age,city` + - Alternative delimiters: tab-separated (TSV), pipe-separated, semicolon-separated + - Quoted fields with embedded delimiters: `"Last, First",25,"New York, NY"` + - Headers with spaces or special characters: `"Customer Name","Order Date","Total Amount"` + - Files with or without headers + - Multi-line fields with embedded newlines + +## CSV Format Configuration Guidelines + +For CSV format configurations, use these patterns: + +**Basic CSV Configuration:** +```json +{ + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + "delimiter": ",", + "quote_char": "\"", + "has_header": true, + "skip_rows": 0 + } + } +} +``` + +**Advanced CSV Options:** +```json +{ + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + "delimiter": "\t", // Tab-separated + "quote_char": "\"", + "escape_char": "\\", + "has_header": true, + "skip_rows": 2, // Skip metadata rows + "null_values": ["", "NULL", "N/A"], + "trim_whitespace": true, + "skip_blank_lines": true + } + } +} +``` + +**CRITICAL: Source Field Names in Mappings** + +When processing CSV files, the parser uses column headers (if present) or generates column indices as field names. Your source field names in mappings must match exactly: + +**CORRECT Example with Headers:** +CSV file: +```csv +Customer Name,Order Date,Total Amount,Status +John Smith,2024-01-15,1000.50,Active +Jane Doe,2024-01-16,750.25,Pending +``` + +Becomes parsed data: +```json +{ + "Customer Name": "John Smith", + "Order Date": "2024-01-15", + "Total Amount": "1000.50", + "Status": "Active" +} +``` + +Your mappings should use: +```json +{ + "source_field": "Customer Name", // ✅ Correct - matches header exactly + "source_field": "Order Date", // ✅ Correct - matches header exactly + "source_field": "Total Amount", // ✅ Correct - matches header exactly + "source_field": "Status" // ✅ Correct - matches header exactly +} +``` + +**CORRECT Example without Headers:** +CSV file without headers uses column indices: +```csv +John Smith,2024-01-15,1000.50,Active +Jane Doe,2024-01-16,750.25,Pending +``` + +Becomes parsed data: +```json +{ + "0": "John Smith", + "1": "2024-01-15", + "2": "1000.50", + "3": "Active" +} +``` + +Your mappings should use: +```json +{ + "source_field": "0", // ✅ Correct - first column + "source_field": "1", // ✅ Correct - second column + "source_field": "2", // ✅ Correct - third column + "source_field": "3" // ✅ Correct - fourth column +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **Source Data Format** + - Delimiter character (comma, tab, pipe, semicolon, etc.) + - Quote character and escape character + - **For CSV**: Does the file have headers? Sample structure + - Text encoding (UTF-8, Windows-1252, etc.) + - Any rows to skip (metadata, blank lines) + - How null/empty values are represented + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source column → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + - Date/time format conversions + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or invalid data + - Duplicate handling strategy + - Row-level validation rules + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## CSV Structure Analysis + +When presented with CSV data, analyze: + +1. **Delimiter Detection**: What character separates the fields? +2. **Header Presence**: Does the first row contain column names? +3. **Quote Pattern**: Are fields quoted? What quote character is used? +4. **Data Types**: What types are present in each column? +5. **Null Representation**: How are empty/null values represented? +6. **Special Characters**: Are there embedded commas, quotes, or newlines? +7. **Encoding Issues**: Are there any character encoding problems? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + // CSV-specific parsing options + // delimiter, quote_char, has_header, skip_rows, etc. + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` +- `split`, `strip_quotes` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` +- `parse_number`, `parse_currency` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` +- `clean_whitespace`, `normalize_encoding` + +**Date/Time Operations:** +- `parse_date`, `format_date`, `date_component` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` +- `numeric_range`, `date_range` + +## CSV-Specific Best Practices + +1. **Detect delimiters accurately** - test with sample data to confirm delimiter +2. **Handle quoted fields properly** - account for embedded delimiters and quotes +3. **Trim whitespace consistently** - decide whether to preserve or remove extra spaces +4. **Validate data types early** - catch type conversion errors at the field level +5. **Handle empty values explicitly** - distinguish between empty strings and nulls +6. **Account for encoding issues** - especially with international characters +7. **Validate row structure** - ensure consistent column counts across rows + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes +9. **Handle CSV-specific edge cases** like malformed quotes, inconsistent delimiters +10. **Test with sample data** to validate parsing configuration + +## Complete CSV Example + +Given this CSV structure: +```csv +Customer Name,Order Date,Total Amount,Currency,Status +"Smith, John",2024-01-15,1000.50,USD,Active +"Doe, Jane",2024-01-16,750.25,EUR,Pending +"Johnson, Bob",2024-01-17,,USD,Cancelled +``` + +The parser will: +1. Use `delimiter: ","` and `quote_char: "\""` to parse fields correctly +2. Use `has_header: true` to treat first row as column names +3. Create this parsed data structure for each record: + ```json + { + "Customer Name": "Smith, John", + "Order Date": "2024-01-15", + "Total Amount": "1000.50", + "Currency": "USD", + "Status": "Active" + } + ``` + +Generate this COMPLETE configuration: +```json +{ + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + "delimiter": ",", + "quote_char": "\"", + "has_header": true, + "trim_whitespace": true, + "null_values": ["", "NULL"] + } + }, + "mappings": [ + { + "source_field": "Customer Name", // ✅ Matches header exactly + "target_field": "customer_name", + "transforms": [{"type": "trim"}] + }, + { + "source_field": "Order Date", // ✅ Matches header exactly + "target_field": "order_date", + "transforms": [{"type": "to_date", "format": "YYYY-MM-DD"}], + "validation": [{"type": "required"}] + }, + { + "source_field": "Total Amount", // ✅ Matches header exactly + "target_field": "amount", + "transforms": [ + {"type": "to_float"}, + {"type": "default", "value": 0.0} + ] + }, + { + "source_field": "Currency", // ✅ Matches header exactly + "target_field": "currency_code", + "transforms": [{"type": "upper"}], + "validation": [{"type": "in_list", "values": ["USD", "EUR", "GBP"]}] + }, + { + "source_field": "Status", // ✅ Matches header exactly + "target_field": "order_status", + "transforms": [{"type": "lower"}] + } + ] +} +``` + +**KEY RULE: source_field names must match the column headers exactly, or use column indices (0, 1, 2, etc.) for files without headers.** + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the CSV structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to delimiter detection, header identification, quoting patterns, and data type inference: + +{{sample}} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/diagnose-json.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/diagnose-json.txt new file mode 100644 index 00000000..2fe6341d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/diagnose-json.txt @@ -0,0 +1,327 @@ +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for data import pipelines, with particular expertise in JSON processing and JSONPath expressions. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured JSON data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## JSON Processing Expertise + +When working with JSON data, you must: + +1. **Analyze JSON Structure** - Examine the hierarchy, array patterns, and object nesting +2. **Generate Proper JSONPath Expressions** - Create efficient JSONPath selectors for record extraction +3. **Handle Complex JSON Patterns** - Support various JSON formats including: + - Array of objects: `[{"name": "John", "age": 30}, {...}]` + - Nested object arrays: `{"data": {"records": [{"id": 1}, {...}]}}` + - Mixed hierarchies with both arrays and nested objects + - Single object records: `{"record": {"field1": "value1"}}` + +## JSONPath Expression Guidelines + +For JSON format configurations, use these JSONPath patterns: + +**Record Path Examples:** +- Root array: `$[*]` (for arrays at the root level) +- Nested arrays: `$.data.records[*]` or `$.response.items[*]` +- Single object: `$.record` (when there's one record per file) +- Deep nesting: `$.data.results.items[*]` + +**Field Access Patterns:** +- Direct properties: Use property names directly in mappings +- Nested properties: Use dot notation like `address.street` or `contact.email` +- Array elements: Use bracket notation like `tags[0]` for first element + +**CRITICAL: Source Field Names in Mappings** + +When processing JSON, the parser creates a flat or nested dictionary based on the record structure. Your source field names in mappings must match the actual property names in the parsed records: + +**CORRECT Example:** +```json +{ + "Country or Area": "Albania", + "Trade (USD)": "1000.50", + "metadata": { + "source": "UN", + "year": 2024 + } +} +``` + +Your mappings should use: +```json +{ + "source_field": "Country or Area", // ✅ Correct - matches property name + "source_field": "Trade (USD)", // ✅ Correct - matches property name + "source_field": "metadata.source", // ✅ Correct - nested property access + "source_field": "metadata.year" // ✅ Correct - nested property access +} +``` + +**JSON Format Configuration Template:** +```json +{ + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + "record_path": "$[*]", // JSONPath to extract records + "flatten_nested": true, // Whether to flatten nested objects + "array_handling": "expand" // How to handle arrays: expand, first, concat + } + } +} +``` + +**Alternative JSON Options:** +```json +{ + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + "record_path": "$.data.items[*]", // For nested array structures + "flatten_nested": false, // Keep nested structure + "null_value_handling": "skip" // skip, empty_string, or preserve + } + } +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **Source Data Format** + - JSON structure type (array of objects, nested objects, single records) + - **For JSON**: Sample structure, nesting patterns, array locations + - Sample data or field descriptions + - Any format-specific details (encoding, special null handling, etc.) + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source field → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + - Nested object flattening requirements + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or null values + - Duplicate handling strategy + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## JSON Structure Analysis + +When presented with JSON data, analyze: + +1. **Root Structure**: Is it an array, object, or nested structure? +2. **Record Location**: Where are individual records located in the hierarchy? +3. **Field Pattern**: How are field names and values structured? + - Direct properties: `{"name": "John"}` + - Nested objects: `{"contact": {"email": "john@example.com"}}` + - Arrays: `{"tags": ["red", "blue"]}` + - Mixed types: `{"data": [{"id": 1, "details": {"name": "John"}}]}` +4. **Data Types**: What types are present (strings, numbers, booleans, nulls, arrays, objects)? +5. **Hierarchy Depth**: How deeply nested are the records and fields? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + // JSON-specific parsing options + // record_path (JSONPath), flatten_nested, array_handling, etc. + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` +- `flatten_object`, `extract_array_element`, `join_array` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` + +## JSON-Specific Best Practices + +1. **Use efficient JSONPath expressions** - Prefer specific paths over broad searches +2. **Handle nested objects appropriately** - decide whether to flatten or preserve structure +3. **Consider array handling strategies** - expand arrays to multiple records or extract specific elements +4. **Account for null vs undefined** values in field mappings +5. **Handle mixed data types** within the same field across records +6. **Use appropriate flattening** for deeply nested structures + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes +9. **Handle JSON-specific edge cases** like empty arrays, null objects, mixed types + +## Complete JSON Example + +Given this JSON structure: +```json +{ + "data": { + "records": [ + { + "Country": "USA", + "Year": 2024, + "Amount": 1000.50, + "metadata": { + "source": "World Bank", + "confidence": 0.95 + } + } + ] + } +} +``` + +The parser will: +1. Use `record_path: "$.data.records[*]"` to extract record objects from the array +2. Create this parsed data structure for each record: + ```json + { + "Country": "USA", + "Year": 2024, + "Amount": 1000.50, + "metadata.source": "World Bank", // If flattened + "metadata.confidence": 0.95 // If flattened + } + ``` + +Generate this COMPLETE configuration: +```json +{ + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + "record_path": "$.data.records[*]", + "flatten_nested": true, + "array_handling": "expand" + } + }, + "mappings": [ + { + "source_field": "Country", // ✅ Matches property name + "target_field": "country_name" + }, + { + "source_field": "Year", // ✅ Matches property name + "target_field": "year", + "transforms": [{"type": "to_int"}] + }, + { + "source_field": "Amount", // ✅ Matches property name + "target_field": "amount", + "transforms": [{"type": "to_float"}] + }, + { + "source_field": "metadata.source", // ✅ Flattened nested field + "target_field": "data_source" + } + ] +} +``` + +**KEY RULE: source_field names must match the actual property names in the parsed JSON records, using dot notation for nested properties when flattened.** + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the JSON structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to JSON hierarchy, object patterns, array structures, and generate appropriate JSONPath expressions: + +{{sample}} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/diagnose-structured-data.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/diagnose-structured-data.txt new file mode 100644 index 00000000..84c4b8be --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/diagnose-structured-data.txt @@ -0,0 +1,309 @@ + +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for data import pipelines, with particular expertise in XML processing and XPath expressions. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## XML Processing Expertise + +When working with XML data, you must: + +1. **Analyze XML Structure** - Examine the hierarchy, namespaces, and element patterns +2. **Generate Proper XPath Expressions** - Create efficient XPath selectors for record extraction +3. **Handle Complex XML Patterns** - Support various XML formats including: + - Standard element structures: `John` + - Attribute-based fields: `USA` + - Mixed content and nested hierarchies + - Namespaced XML documents + +## XPath Expression Guidelines + +For XML format configurations, use these XPath patterns: + +**Record Path Examples:** +- Simple records: `//record` or `//customer` +- Nested records: `//data/records/record` or `//customers/customer` +- Absolute paths: `/ROOT/data/record` (will be converted to relative paths automatically) +- With namespaces: `//ns:record` or `//soap:Body/data/record` + +**Field Attribute Patterns:** +- When fields use name attributes: set `field_attribute: "name"` for `value` +- For other attribute patterns: set appropriate attribute name + +**CRITICAL: Source Field Names in Mappings** + +When using `field_attribute`, the XML parser extracts field names from the attribute values and creates a flat dictionary. Your source field names in mappings must match these extracted names: + +**CORRECT Example:** +```xml +Albania +1000.50 +``` + +Becomes parsed data: +```json +{ + "Country or Area": "Albania", + "Trade (USD)": "1000.50" +} +``` + +So your mappings should use: +```json +{ + "source_field": "Country or Area", // ✅ Correct - matches parsed field name + "source_field": "Trade (USD)" // ✅ Correct - matches parsed field name +} +``` + +**INCORRECT Example:** +```json +{ + "source_field": "Field[@name='Country or Area']", // ❌ Wrong - XPath not needed here + "source_field": "field[@name='Trade (USD)']" // ❌ Wrong - XPath not needed here +} +``` + +**XML Format Configuration Template:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//data/record", // XPath to find record elements + "field_attribute": "name" // For value pattern + } + } +} +``` + +**Alternative XML Options:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//customer", // Direct element-based records + // No field_attribute needed for standard XML + } + } +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **Source Data Format** + - File type (CSV, JSON, XML, Excel, fixed-width, etc.) + - **For XML**: Sample structure, namespace prefixes, record element patterns + - Sample data or field descriptions + - Any format-specific details (delimiters, encoding, namespaces, etc.) + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source field → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or invalid data + - Duplicate handling strategy + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## XML Structure Analysis + +When presented with XML data, analyze: + +1. **Document Root**: What is the root element? +2. **Record Container**: Where are individual records located? +3. **Field Pattern**: How are field names and values structured? + - Direct child elements: `John` + - Attribute-based: `John` + - Mixed patterns +4. **Namespaces**: Are there any namespace prefixes? +5. **Hierarchy Depth**: How deeply nested are the records? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "[csv|json|xml|fixed-width|excel]", + "encoding": "utf-8", + "options": { + // Format-specific parsing options + // For XML: record_path (XPath), field_attribute (if applicable) + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` + +## XML-Specific Best Practices + +1. **Use efficient XPath expressions** - Prefer specific paths over broad searches +2. **Handle namespace prefixes** when present +3. **Identify field attribute patterns** correctly +4. **Test XPath expressions** mentally against the provided structure +5. **Consider XML element vs attribute data** in field mappings +6. **Account for mixed content** and nested structures + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes + +## Complete XML Example + +Given this XML structure: +```xml + + + + USA + 2024 + 1000.50 + + + +``` + +The parser will: +1. Use `record_path: "/ROOT/data/record"` to find record elements +2. Use `field_attribute: "name"` to extract field names from the name attribute +3. Create this parsed data structure: `{"Country": "USA", "Year": "2024", "Amount": "1000.50"}` + +Generate this COMPLETE configuration: +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "/ROOT/data/record", + "field_attribute": "name" + } + }, + "mappings": [ + { + "source_field": "Country", // ✅ Matches parsed field name + "target_field": "country_name" + }, + { + "source_field": "Year", // ✅ Matches parsed field name + "target_field": "year", + "transforms": [{"type": "to_int"}] + }, + { + "source_field": "Amount", // ✅ Matches parsed field name + "target_field": "amount", + "transforms": [{"type": "to_float"}] + } + ] +} +``` + +**KEY RULE: source_field names must match the extracted field names, NOT the XML element structure.** + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the XML structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to XML hierarchy, element patterns, and generate appropriate XPath expressions: + +{{sample}} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/diagnose-xml.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/diagnose-xml.txt new file mode 100644 index 00000000..a3d8efa4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/diagnose-xml.txt @@ -0,0 +1,453 @@ +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for XML data import pipelines, with particular expertise in XML processing and XPath expressions. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured XML data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## XML Processing Expertise + +When working with XML data, you must: + +1. **Analyze XML Structure** - Examine the hierarchy, namespaces, and element patterns +2. **Generate Proper XPath Expressions** - Create efficient XPath selectors for record extraction +3. **Handle Complex XML Patterns** - Support various XML formats including: + - Standard element structures: `John` + - Attribute-based fields: `USA` + - Mixed content and nested hierarchies + - Namespaced XML documents + - CDATA sections and text nodes + +## XPath Expression Guidelines + +For XML format configurations, use these XPath patterns: + +**Record Path Examples:** +- Simple records: `//record` or `//customer` +- Nested records: `//data/records/record` or `//customers/customer` +- Absolute paths: `/ROOT/data/record` +- With namespaces: `//ns:record` or `//soap:Body/data/record` +- Attribute-filtered: `//record[@type='customer']` + +**Field Attribute Patterns:** +- When fields use name attributes: set `field_attribute: "name"` for `value` +- For other attribute patterns: set appropriate attribute name like `field_attribute: "id"` or `field_attribute: "type"` + +## CRITICAL: Source Field Names in Mappings + +The behavior depends on whether you use `field_attribute`: + +### With field_attribute (Attribute-Based Fields) + +When using `field_attribute`, the XML parser extracts field names from the attribute values and creates a flat dictionary: + +**Example:** +```xml + + Albania + 1000.50 + Active + +``` + +Parser configuration: +```json +{ + "record_path": "//record", + "field_attribute": "name" +} +``` + +Becomes parsed data: +```json +{ + "Country or Area": "Albania", + "Trade (USD)": "1000.50", + "Status": "Active" +} +``` + +Your mappings should use: +```json +{ + "source_field": "Country or Area", // ✅ Correct - matches parsed field name + "source_field": "Trade (USD)", // ✅ Correct - matches parsed field name + "source_field": "Status" // ✅ Correct - matches parsed field name +} +``` + +### Without field_attribute (Element-Based Fields) + +When NOT using `field_attribute`, use direct element names or XPath expressions: + +**Example:** +```xml + + Albania + 1000.50 + Active + + UN + 2024 + + +``` + +Parser configuration: +```json +{ + "record_path": "//record" + // No field_attribute specified +} +``` + +Your mappings should use: +```json +{ + "source_field": "country", // ✅ Direct element name + "source_field": "trade_amount", // ✅ Direct element name + "source_field": "metadata/source", // ✅ Nested element path + "source_field": "metadata/year" // ✅ Nested element path +} +``` + +## XML Format Configuration Templates + +**For Attribute-Based Fields:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//data/record", // XPath to find record elements + "field_attribute": "name", // Extract field names from 'name' attribute + "namespace_prefixes": { // Optional: define namespaces + "ns": "http://example.com/namespace" + } + } + } +} +``` + +**For Element-Based Fields:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//customer", // XPath to find record elements + "preserve_namespaces": true, // Optional: keep namespace info + "ignore_attributes": false // Optional: include element attributes + } + } +} +``` + +**For Complex XML with Namespaces:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//soap:Body//data:record", + "namespace_prefixes": { + "soap": "http://schemas.xmlsoap.org/soap/envelope/", + "data": "http://example.com/data" + }, + "field_attribute": "name" + } + } +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **XML Structure Details** + - Sample XML structure or schema + - Root element and record location + - Field organization (elements vs attributes) + - Namespace declarations and prefixes + - Text encoding (UTF-8, ISO-8859-1, etc.) + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source field → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or invalid data + - Duplicate handling strategy + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## XML Structure Analysis + +When presented with XML data, analyze: + +1. **Document Root**: What is the root element? +2. **Namespaces**: Are there namespace declarations? What prefixes are used? +3. **Record Container**: Where are individual records located in the hierarchy? +4. **Field Pattern**: How are field names and values structured? + - Direct child elements: `John` + - Attribute-based: `John` + - Mixed patterns: `John` +5. **Data Location**: Are values in element text, attributes, or both? +6. **Hierarchy Depth**: How deeply nested are the records? +7. **Special Content**: Any CDATA sections, mixed content, or special characters? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + // XML-specific parsing options + // record_path (XPath), field_attribute (if applicable), namespace_prefixes + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` +- `strip_cdata`, `decode_html_entities` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` +- `parse_xml_date`, `parse_iso_date` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` +- `extract_attribute`, `get_text_content` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` +- `valid_xml_name`, `namespace_check` + +## XML-Specific Best Practices + +1. **Use efficient XPath expressions** - Prefer specific paths over broad searches like `//` when possible +2. **Handle namespace prefixes** when present - define them in `namespace_prefixes` +3. **Identify field attribute patterns** correctly - determine if using `field_attribute` +4. **Test XPath expressions** mentally against the provided structure +5. **Consider XML element vs attribute data** in field mappings +6. **Account for mixed content** and nested structures +7. **Handle CDATA sections** and special characters properly +8. **Preserve or normalize whitespace** as appropriate +9. **Consider XML schema validation** if XSD is available + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes +9. **Handle XML-specific edge cases** like empty elements, mixed content, processing instructions + +## Complete XML Examples + +### Example 1: Attribute-Based Fields + +Given this XML structure: +```xml + + + + USA + 2024 + 1000.50 + + + +``` + +The parser will: +1. Use `record_path: "//data/record"` to find record elements +2. Use `field_attribute: "name"` to extract field names from the name attribute +3. Create this parsed data structure: `{"Country": "USA", "Year": "2024", "Amount": "1000.50"}` + +Generate this configuration: +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//data/record", + "field_attribute": "name" + } + }, + "mappings": [ + { + "source_field": "Country", // ✅ Matches parsed field name + "target_field": "country_name" + }, + { + "source_field": "Year", // ✅ Matches parsed field name + "target_field": "year", + "transforms": [{"type": "to_int"}] + }, + { + "source_field": "Amount", // ✅ Matches parsed field name + "target_field": "amount", + "transforms": [{"type": "to_float"}] + } + ] +} +``` + +### Example 2: Element-Based Fields + +Given this XML structure: +```xml + + + John Smith + john@example.com +
+ 123 Main St + New York +
+ + + 456 + 100.50 + + +
+
+``` + +Generate this configuration: +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//customer" + } + }, + "mappings": [ + { + "source_field": "name", // ✅ Direct element name + "target_field": "customer_name" + }, + { + "source_field": "email", // ✅ Direct element name + "target_field": "email_address" + }, + { + "source_field": "address/street", // ✅ Nested element path + "target_field": "street_address" + }, + { + "source_field": "address/city", // ✅ Nested element path + "target_field": "city" + }, + { + "source_field": "@id", // ✅ Attribute reference + "target_field": "customer_id", + "transforms": [{"type": "to_int"}] + } + ] +} +``` + +**KEY RULES:** +- With `field_attribute`: source_field names must match the extracted attribute values +- Without `field_attribute`: use direct element names, nested paths, or XPath expressions +- Use `@attribute` notation for XML element attributes + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the XML structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to XML hierarchy, element patterns, namespace usage, and generate appropriate XPath expressions: + +{{sample}} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/gemini.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/gemini.jsonnet new file mode 100644 index 00000000..b9a1e0c0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/gemini.jsonnet @@ -0,0 +1,42 @@ +// For VertexAI Gemini. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/graphql-generation.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/graphql-generation.txt new file mode 100644 index 00000000..58af61dc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/graphql-generation.txt @@ -0,0 +1,59 @@ +You are a GraphQL query generation expert. Given a natural language question and provided database schemas, generate a valid GraphQL query embedded in valid JSON. + +## Question: +{{ question }} + +## Provided Schemas: +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %}- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif %}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif %}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ field.enum_values|join(', ') }}]{% endif %} +{% endfor %} +{% endfor %} + +## GraphQL Query Rules: +1. Use the schema names as GraphQL query fields (e.g., `customers`, `orders`) +2. Apply filters using the `where` parameter with nested filter objects +3. Available filter operators per field type: + - String fields: `eq`, `contains`, `startsWith`, `endsWith`, `in`, `not`, `not_in` + - Integer/Float fields: `eq`, `gt`, `gte`, `lt`, `lte`, `in`, `not`, `not_in` +4. Use `order_by` for sorting (field name as string) +5. Use `direction` for sort direction: `ASC` or `DESC` +6. Use `limit` to restrict number of results +7. Select specific fields in the query body + +## Task Instructions: +1. Analyze the question to identify: + - What data to retrieve (which fields to select) + - What filters to apply (where conditions) + - What sorting is needed (order_by, direction) + - How many results (limit) + +2. Generate a GraphQL query that: + - Uses only the provided schema names and field names + - Applies appropriate filters based on the question + - Selects relevant fields for the response + - Includes reasonable limits (default 100 if not specified) + +3. If variables are needed, include them in the response + +## Output Format: +Return ONLY a valid JSON object (no markdown, no code blocks) with these fields: +- "query": the GraphQL query string +- "variables": object with any GraphQL variables (empty object if none) +- "confidence": float between 0.0-1.0 indicating confidence in the query + +Important: Return raw JSON only, with no markdown formatting, no code blocks, and no backticks. + +## Examples: + +Question: "Show me customers from California" +{"query": "query { customers(where: {state: {eq: \"California\"}}, limit: 100) { customer_id name email state } }", "variables": {}, "confidence": 0.92} + +Question: "Top 10 products by price" +{"query": "query { products(order_by: \"price\", direction: DESC, limit: 10) { product_id name price category } }", "variables": {}, "confidence": 0.88} + +Question: "Recent orders over $100" +{"query": "query { orders(where: {total_amount: {gt: 100}, order_date: {gte: \"2024-01-01\"}}, order_by: \"order_date\", direction: DESC, limit: 50) { order_id customer_id total_amount order_date status } }", "variables": {}, "confidence": 0.96} + +Now generate the GraphQL query for the question above. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/mixtral.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/mixtral.jsonnet new file mode 100644 index 00000000..cd56e7ef --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/mixtral.jsonnet @@ -0,0 +1,42 @@ +// For Mixtral. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/ontology-prompt.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/ontology-prompt.txt new file mode 100644 index 00000000..5a0fcce0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/ontology-prompt.txt @@ -0,0 +1,126 @@ +You are a knowledge extraction expert. Your task is to find entities, relationships, and attributes in text based on a provided schema. + +## Entity Types + +These are the types of entities you should look for: + +{% for class_id, class_def in classes.items() %} +- **{{class_id}}**{% if class_def.subclass_of %} (subclass of {{class_def.subclass_of}}){% endif %}{% if class_def.comment %}: {{class_def.comment}}{% endif %} +{% endfor %} + +## Relationships + +These relationships connect entities to other entities: + +{% for prop_id, prop_def in object_properties.items() %} +- **{{prop_id}}**{% if prop_def.domain and prop_def.range %} ({{prop_def.domain}} → {{prop_def.range}}){% endif %}{% if prop_def.comment %}: {{prop_def.comment}}{% endif %} +{% endfor %} + +## Attributes + +These attributes describe entity properties (text, numbers, etc.): + +{% for prop_id, prop_def in datatype_properties.items() %} +- **{{prop_id}}**{% if prop_def.domain and prop_def.range %} ({{prop_def.domain}} → {{prop_def.range}}){% endif %}{% if prop_def.comment %}: {{prop_def.comment}}{% endif %} +{% endfor %} + +## Text to Analyze + +{{text}} + +## Your Task + +Extract the following from the text above: + +1. **Entities**: Things mentioned in the text and their types +2. **Relationships**: How entities relate to each other +3. **Attributes**: Properties of entities (like quantities, descriptions, etc.) + +## Output Format + +Return ONLY valid JSON (no markdown, no code blocks, no backticks) with this structure: + +``` +{ + "entities": [ + { + "entity": "entity name as it appears in text", + "type": "EntityType" + } + ], + "relationships": [ + { + "subject": "subject entity name", + "subject-type": "SubjectType", + "relation": "relationship_name", + "object": "object entity name", + "object-type": "ObjectType" + } + ], + "attributes": [ + { + "entity": "entity name", + "entity-type": "EntityType", + "attribute": "attribute_name", + "value": "literal value" + } + ] +} +``` + +## Important Rules + +1. **Entity names**: Use the exact text as it appears (e.g., "Cornish pasty", "beef") +2. **Types**: Use the EXACT type identifiers from the schema above (e.g., "fo/Recipe", "fo/Food") +3. **Relationships**: Use the EXACT relationship names from the schema (e.g., "fo/has_ingredient") +4. **Attributes**: Use the EXACT attribute names from the schema (e.g., "fo/serves") +5. **Type disambiguation**: If the same entity name appears with different types, list it separately for each type +6. **All arrays can be empty** if nothing is found + +## Example + +Input text: "Cornish pasty is a savory pastry filled with beef and potatoes. This recipe serves 4 people." + +Expected output: +{ + "entities": [ + { + "entity": "Cornish pasty", + "type": "fo/Recipe" + }, + { + "entity": "beef", + "type": "fo/Food" + }, + { + "entity": "potatoes", + "type": "fo/Food" + } + ], + "relationships": [ + { + "subject": "Cornish pasty", + "subject-type": "fo/Recipe", + "relation": "fo/has_ingredient", + "object": "beef", + "object-type": "fo/Food" + }, + { + "subject": "Cornish pasty", + "subject-type": "fo/Recipe", + "relation": "fo/has_ingredient", + "object": "potatoes", + "object-type": "fo/Food" + } + ], + "attributes": [ + { + "entity": "Cornish pasty", + "entity-type": "fo/Recipe", + "attribute": "fo/serves", + "value": "4 people" + } + ] +} + +Now extract entities, relationships, and attributes from the text above. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/openai.jsonnet new file mode 100644 index 00000000..5d232337 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/openai.jsonnet @@ -0,0 +1,42 @@ +// For OpenAI LLMs. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/schema-selection.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/schema-selection.txt new file mode 100644 index 00000000..39b180e5 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/schema-selection.txt @@ -0,0 +1,25 @@ +You are a database schema selection expert. Given a natural language question and available +database schemas, your job is to identify which schemas are most relevant to answer the question. + +## Available Schemas: +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}): {{ field.description }} +{% endfor %} + +{% endfor %} + +## Question: +{{ question }} + +## Instructions: +1. Analyze the question to understand what data is being requested +2. Examine each schema to understand what data it contains +3. Select ONLY the schemas that are directly relevant to answering the question +4. Return your answer as a JSON array of schema names + +## Response Format: +Return ONLY a JSON array of schema names, nothing else. +Example: ["customers", "orders", "products"] diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/slm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/slm.jsonnet new file mode 100644 index 00000000..48eb96d0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/prompts/slm.jsonnet @@ -0,0 +1,44 @@ +// For SLM. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + + + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/stores/cassandra.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/stores/cassandra.jsonnet new file mode 100644 index 00000000..2a9d6d7a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/stores/cassandra.jsonnet @@ -0,0 +1,40 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + "cassandra" +: { + + create:: function(engine) + + local vol = engine.volume("cassandra").with_size("20G"); + + local container = + engine.container("cassandra") + .with_image(images.cassandra) + .with_environment({ + JVM_OPTS: "-Xms300M -Xmx300M -Dcassandra.skip_wait_for_gossip_to_settle=0", + }) + .with_limits("1.0", "1000M") + .with_reservations("0.5", "1000M") + .with_port(9042, 9042, "cassandra") + .with_volume_mount(vol, "/var/lib/cassandra"); + + local containerSet = engine.containers( + "cassandra", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9042, 9042, "api"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/stores/falkordb.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/stores/falkordb.jsonnet new file mode 100644 index 00000000..78509a43 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/stores/falkordb.jsonnet @@ -0,0 +1,39 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + "falkordb" +: { + + create:: function(engine) + + local vol = engine.volume("falkordb").with_size("20G"); + + local container = + engine.container("falkordb") + .with_image(images.falkordb) + .with_limits("1.0", "768M") + .with_reservations("0.5", "768M") + .with_port(6379, 6379, "api") + .with_port(3010, 3000, "ui") + .with_volume_mount(vol, "/data"); + + local containerSet = engine.containers( + "falkordb", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(6379, 6379, "api") + .with_port(3010, 3010, "ui"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/stores/memgraph.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/stores/memgraph.jsonnet new file mode 100644 index 00000000..70ad127a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/stores/memgraph.jsonnet @@ -0,0 +1,71 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + "memgraph" +: { + + create:: function(engine) + + local vol = engine.volume("memgraph").with_size("20G"); + + local container = + engine.container("memgraph") + .with_image(images.memgraph_mage) + .with_environment({ + MEMGRAPH: "--storage-properties-on-edges=true --storage-enable-edges-metadata=true" + }) + .with_limits("1.0", "1000M") + .with_reservations("0.5", "1000M") + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2") + .with_volume_mount(vol, "/var/lib/memgraph"); + + local containerSet = engine.containers( + "memgraph", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + + "memgraph-lab" +: { + + create:: function(engine) + + local container = + engine.container("lab") + .with_image(images.memgraph_lab) + .with_environment({ + QUICK_CONNECT_MG_HOST: "memgraph", + QUICK_CONNECT_MG_PORT: "7687", + }) + .with_limits("1.0", "512M") + .with_reservations("0.5", "512M") + .with_port(3010, 3000, "http"); + + local containerSet = engine.containers( + "lab", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(3010, 3010, "http"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/stores/milvus.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/stores/milvus.jsonnet new file mode 100644 index 00000000..1c3e3734 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/stores/milvus.jsonnet @@ -0,0 +1,90 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local minio = import "stores/minio.jsonnet"; + +minio { + + etcd +: { + + create:: function(engine) + + local vol = engine.volume("etcd").with_size("20G"); + + local container = + engine.container("etcd") + .with_image(images.etcd) + .with_command([ + "etcd", + "-advertise-client-urls=http://127.0.0.1:2379", + "-listen-client-urls", + "http://0.0.0.0:2379", + "--data-dir", + "/etcd", + ]) + .with_environment({ + ETCD_AUTO_COMPACTION_MODE: "revision", + ETCD_AUTO_COMPACTION_RETENTION: "1000", + ETCD_QUOTA_BACKEND_BYTES: "4294967296", + ETCD_SNAPSHOT_COUNT: "50000" + }) + .with_limits("1.0", "128M") + .with_reservations("0.25", "128M") + .with_port(2379, 2379, "api") + .with_volume_mount(vol, "/etcd"); + + local containerSet = engine.containers( + "etcd", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(2379, 2379, "api"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + + milvus +: { + + create:: function(engine) + + local vol = engine.volume("milvus").with_size("20G"); + + local container = + engine.container("milvus") + .with_image(images.milvus) + .with_command([ + "milvus", "run", "standalone" + ]) + .with_environment({ + ETCD_ENDPOINTS: "etcd:2379", + MINIO_ADDRESS: "minio:9000", + }) + .with_limits("1.0", "256M") + .with_reservations("0.5", "256M") + .with_port(9091, 9091, "api") + .with_port(19530, 19530, "api2") + .with_volume_mount(vol, "/var/lib/milvus"); + + local containerSet = engine.containers( + "milvus", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9091, 9091, "api") + .with_port(19530, 19530, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/stores/minio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/stores/minio.jsonnet new file mode 100644 index 00000000..8a6aa149 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/stores/minio.jsonnet @@ -0,0 +1,49 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + minio +: { + + create:: function(engine) + + local vol = engine.volume("minio-data").with_size("20G"); + + local container = + engine.container("minio") + .with_image(images.minio) + .with_command([ + "minio", + "server", + "/minio_data", + "--console-address", + ":9001", + ]) + .with_environment({ + MINIO_ROOT_USER: "minioadmin", + MINIO_ROOT_PASSWORD: "minioadmin", + }) + .with_limits("0.5", "128M") + .with_reservations("0.25", "128M") + .with_port(9000, 9000, "api") + .with_port(9001, 9001, "console") + .with_volume_mount(vol, "/minio_data"); + + local containerSet = engine.containers( + "minio", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9000, 9000, "api") + .with_port(9001, 9001, "console"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/stores/neo4j.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/stores/neo4j.jsonnet new file mode 100644 index 00000000..3a8bb783 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/stores/neo4j.jsonnet @@ -0,0 +1,47 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + "neo4j" +: { + + create:: function(engine) + + local vol = engine.volume("neo4j").with_size("20G"); + + local container = + engine.container("neo4j") + .with_image(images.neo4j) + .with_environment({ + NEO4J_AUTH: "neo4j/password", + NEO4J_server_memory_pagecache_size: "512m", + NEO4J_server_memory_heap_max__size: "512m", + // NEO4J_server_bolt_listen__address: "0.0.0.0:7687", + // NEO4J_server_default__listen__address: "0.0.0.0", + // NEO4J_server_http_listen__address: "0.0.0.0:7474", + }) + .with_limits("1.0", "1536M") + .with_reservations("0.5", "1536M") + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2") + .with_volume_mount(vol, "/data"); + + local containerSet = engine.containers( + "neo4j", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/stores/pinecone.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/stores/pinecone.jsonnet new file mode 100644 index 00000000..2bef70fb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/stores/pinecone.jsonnet @@ -0,0 +1,45 @@ +local images = import "values/images.jsonnet"; + +{ + + "pinecone" +: { + + create:: function(engine) + + local container = + engine.container("pinecone") + .with_image(images.pinecone) + .with_environment({ + + PORT: "5080", + + INDEX_TYPE: "serverless", + + // Dimension is fixed, 384 is right for miniLM + // sentence embedding + DIMENSION: 384, + + METRIC: "cosine" + + }) + .with_limits("1.0", "256M") + .with_reservations("0.5", "256M") + .with_port(5080, 5080, "api"); + + local containerSet = engine.containers( + "pinecone", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(5080, 5080, "api"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/stores/qdrant.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/stores/qdrant.jsonnet new file mode 100644 index 00000000..9e807632 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/stores/qdrant.jsonnet @@ -0,0 +1,39 @@ +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; + +{ + + "qdrant" +: { + + create:: function(engine) + + local vol = engine.volume("qdrant").with_size("20G"); + + local container = + engine.container("qdrant") + .with_image(images.qdrant) + .with_limits("1.0", "1024M") + .with_reservations("0.5", "1024M") + .with_port(6333, 6333, "api") + .with_port(6334, 6334, "api2") + .with_volume_mount(vol, "/qdrant/storage"); + + local containerSet = engine.containers( + "qdrant", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(6333, 6333, "api") + .with_port(6334, 6334, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/trustgraph-config.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/trustgraph-config.jsonnet new file mode 100644 index 00000000..cda29336 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/trustgraph-config.jsonnet @@ -0,0 +1,91 @@ +// TrustGraph Main Configuration +// Clean, modular composition of TrustGraph configuration +// Uses specialized modules for different aspects of config building + +// Import dependencies +local base = import "base/base.jsonnet"; +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local default_prompts = import "prompts/default-prompts.jsonnet"; +local token_costs = import "values/token-costs.jsonnet"; +local flow_classes = import "flows/flow-classes.jsonnet"; +local config_composer = import "config/config-composer.jsonnet"; +local interface_descriptions = import "config/interface-descriptions.jsonnet"; +local tools = import "config/tools.jsonnet"; +local temperature_params = import "parameters/temperature-param-types.jsonnet"; +local chunking_params = import "parameters/chunking-param-types.jsonnet"; + +// Main configuration object +local configuration = { + + // Prompt templates + prompts:: default_prompts, + + // Tool definitions + tools:: tools, + + // MCP configuration + mcp:: {}, + + // Flow classes reference + "flow-classes":: flow_classes, + + // LLM model parameters + "llm-models" +:: {}, + + // Embeddings model parameters + "embeddings-models" +:: {}, + + collections +:: { + "trustgraph:default": { + "user": "default-user", + "collection": "default", + "name": "Default Collection", + "description": "Default collection", + "tags": ["default"], + }, + }, + + // Default model and flow parameters + flow_init_parameters:: { + "llm-model": $["llm-models"].default, + "llm-rag-model": $["llm-models"].default, + "llm-temperature": "%0.3f" % $["parameter-types"]["llm-temperature"].default, + "llm-rag-temperature": "%0.3f" % $["parameter-types"]["llm-temperature"].default, + "chunk-size": std.toString($["parameter-types"]["chunk-size"].default), + "chunk-overlap": std.toString($["parameter-types"]["chunk-overlap"].default), + "embeddings-model": $["embeddings-models"].default, + }, + + // Interface descriptions for external endpoints + "interface-descriptions":: interface_descriptions, + + // Parameter type definitions + "parameter-types":: { + "llm-model": $["llm-models"], + "embeddings-model": $["embeddings-models"], + } + chunking_params + temperature_params, + + // Token costs + "token-costs":: token_costs, + + // Build the complete configuration using the composer + configuration:: config_composer.build({ + flow_classes: $["flow-classes"], + default_flow_class: "everything", + default_flow_id: "default", + flow_init_parameters: $["flow_init_parameters"], + prompts: $["prompts"], + tools: $["tools"], + mcp: $["mcp"], + interface_descriptions: $["interface-descriptions"], + parameter_types: $["parameter-types"], + token_costs: $["token-costs"], + collection: $["collections"], + }), + +} + default_prompts; + +// Export the final configuration +configuration diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/util/decode-config.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/util/decode-config.jsonnet new file mode 100644 index 00000000..503b5b6b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/util/decode-config.jsonnet @@ -0,0 +1,31 @@ + +local components = import "components.jsonnet"; + +local apply = function(p, components) + + local base = { + + with:: function(k, v) self + { + [k]:: v + }, + + with_params:: function(pars) + self + std.foldl( + function(obj, par) obj.with(par.key, par.value), + std.objectKeysValues(pars), + self + ), + + }; + + local component = base + components[p.name]; + + component.with_params(p.parameters); + +local decode = function(config) + local add = function(state, c) state + apply(c, components); + local patterns = std.foldl(add, config, {}); + patterns; + +decode + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/values/images.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/values/images.jsonnet new file mode 100644 index 00000000..e259d58e --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/values/images.jsonnet @@ -0,0 +1,32 @@ +local version = import "version.jsonnet"; +{ + cassandra: "docker.io/cassandra:4.1.10", + neo4j: "docker.io/neo4j:2025.08.0-community-bullseye", + pulsar: "docker.io/apachepulsar/pulsar:4.1.0", + pulsar_manager: "docker.io/apachepulsar/pulsar-manager:v0.4.0", + etcd: "quay.io/coreos/etcd:v3.6.4", + minio: "docker.io/minio/minio:RELEASE.2025-09-07T16-13-09Z", + milvus: "docker.io/milvusdb/milvus:v2.5.17", + prometheus: "docker.io/prom/prometheus:v3.8.0", + grafana: "docker.io/grafana/grafana:12.3.0", + loki: "docker.io/grafana/loki:3.6.2", + trustgraph_base: "docker.io/trustgraph/trustgraph-base:" + version, + trustgraph_flow: "docker.io/trustgraph/trustgraph-flow:" + version, + trustgraph_ocr: "docker.io/trustgraph/trustgraph-ocr:" + version, + trustgraph_bedrock: "docker.io/trustgraph/trustgraph-bedrock:" + version, + trustgraph_vertexai: "docker.io/trustgraph/trustgraph-vertexai:" + version, + trustgraph_hf: "docker.io/trustgraph/trustgraph-hf:" + version, + trustgraph_mcp: "docker.io/trustgraph/trustgraph-mcp:" + version, + qdrant: "docker.io/qdrant/qdrant:v1.15.4", + memgraph_mage: "docker.io/memgraph/memgraph-mage:3.5", + memgraph_lab: "docker.io/memgraph/lab:3.5.0", + falkordb: "docker.io/falkordb/falkordb:v4.12.5", + "workbench-ui": "docker.io/trustgraph/workbench-ui:1.4.2", + "ddg-mcp-server": "docker.io/trustgraph/ddg-mcp-server:0.1.0", + "tgi-service-intel-xpu": "ghcr.io/huggingface/text-generation-inference:3.3.1-intel-xpu", + "tgi-service-cpu": "ghcr.io/huggingface/text-generation-inference:3.3.1-intel-cpu", + "tgi-service-gaudi": "ghcr.io/huggingface/text-generation-inference:sha-f140440-gaudi", + "vllm-service-intel-xpu": "docker.io/intel/vllm:0.8.0-xpu", + "vllm-service-gaudi": "docker.io/trustgraph/vllm-hpu:027f5645", + "vllm-service-nvidia": "docker.io/vllm/vllm-openai:latest", +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/values/token-costs.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/values/token-costs.jsonnet new file mode 100644 index 00000000..84c70373 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/values/token-costs.jsonnet @@ -0,0 +1,102 @@ +{ + "mistral.mistral-large-2407-v1:0": { + "model_name": "mistral.mistral-large-2407-v1:0", + "input_price": 0.000004, + "output_price": 0.000012 + }, + "meta.llama3-1-405b-instruct-v1:0": { + "model_name": "meta.llama3-1-405b-instruct-v1:0", + "input_price": 0.00000532, + "output_price": 0.000016 + }, + "mistral.mixtral-8x7b-instruct-v0:1": { + "model_name": "mistral.mixtral-8x7b-instruct-v0:1", + "input_price": 0.00000045, + "output_price": 0.0000007 + }, + "meta.llama3-1-70b-instruct-v1:0": { + "model_name": "meta.llama3-1-70b-instruct-v1:0", + "input_price": 0.00000099, + "output_price": 0.00000099 + }, + "meta.llama3-1-8b-instruct-v1:0": { + "model_name": "meta.llama3-1-8b-instruct-v1:0", + "input_price": 0.00000022, + "output_price": 0.00000022 + }, + "anthropic.claude-3-haiku-20240307-v1:0": { + "model_name": "anthropic.claude-3-haiku-20240307-v1:0", + "input_price": 0.00000025, + "output_price": 0.00000125 + }, + "anthropic.claude-3-5-sonnet-20240620-v1:0": { + "model_name": "anthropic.claude-3-5-sonnet-20240620-v1:0", + "input_price": 0.000003, + "output_price": 0.000015 + }, + "cohere.command-r-plus-v1:0": { + "model_name": "cohere.command-r-plus-v1:0", + "input_price": 0.0000030, + "output_price": 0.0000150 + }, + "ollama": { + "model_name": "ollama", + "input_price": 0, + "output_price": 0 + }, + "claude-3-haiku-20240307": { + "model_name": "claude-3-haiku-20240307", + "input_price": 0.00000025, + "output_price": 0.00000125 + }, + "claude-3-5-sonnet-20240620": { + "model_name": "claude-3-5-sonnet-20240620", + "input_price": 0.000003, + "output_price": 0.000015 + }, + "claude-3-opus-20240229": { + "model_name": "claude-3-opus-20240229", + "input_price": 0.000015, + "output_price": 0.000075 + }, + "claude-3-sonnet-20240229": { + "model_name": "claude-3-sonnet-20240229", + "input_price": 0.000003, + "output_price": 0.000015 + }, + "command-r-08-202": { + "model_name": "command-r-08-202", + "input_price": 0.0000025, + "output_price": 0.000010 + }, + "c4ai-aya-23-8b": { + "model_name": "c4ai-aya-23-8b", + "input_price": 0, + "output_price": 0 + }, + "llama.cpp": { + "model_name": "llama.cpp", + "input_price": 0, + "output_price": 0 + }, + "gpt-4o": { + "model_name": "gpt-4o", + "input_price": 0.000005, + "output_price": 0.000015 + }, + "gpt-4o-2024-08-06": { + "model_name": "gpt-4o-2024-08-06", + "input_price": 0.0000025, + "output_price": 0.000010 + }, + "gpt-4o-2024-05-13": { + "model_name": "gpt-4o-2024-05-13", + "input_price": 0.000005, + "output_price": 0.000015 + }, + "gpt-4o-mini": { + "model_name": "gpt-4o-mini", + "input_price": 0.00000015, + "output_price": 0.0000006 + } +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/values/url.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/values/url.jsonnet new file mode 100644 index 00000000..1bacb067 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/values/url.jsonnet @@ -0,0 +1,6 @@ +{ + pulsar: "pulsar://pulsar:6650", + pulsar_admin: "http://pulsar:8080", + milvus: "http://milvus:19530", + qdrant: "http://qdrant:6333", +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/zip-readme.md b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/zip-readme.md new file mode 100644 index 00000000..0b117792 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.7/zip-readme.md @@ -0,0 +1,28 @@ + +Note! this is a subset of possible configurations, to generate your own +launch config use the config util... + +- Production: https://config-ui.demo.trustgraph.ai +- Early release: https://dev.config-ui.demo.trustgraph.ai + +The config util auto-generates deployment instructions for your +configuration, so that's the recommended way to deploy. + +---------------------------------------------------------------------------- + +These are launch configurations for TrustGraph. See https://trustgraph.ai for +the quickstart using docker compose. + +Hint for Linux: There are files here which get mounted as volumes inside +Docker Compose containers. This may trigger SELinux rules on your system, to +permit access insider the containers, use a command like this... + +chcon -Rt svirt_sandbox_file_t grafana/ prometheus/ + +The file vertexai/private.json is a placeholder for real GCP credentials if +you are using the VertexAI LLM. If you're using that in Docker Compose, +replace with your real credentials, and don't forget to permit access if you +are using Linux: + +chcon -Rt svirt_sandbox_file_t vertexai/ + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/README.md b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/README.md new file mode 100644 index 00000000..23039e9a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/README.md @@ -0,0 +1,125 @@ + +# TrustGraph template generation + +There are two utilities here: + +- `generate`: Generates a single Docker Compose launch configuration + based on configuration you provide. +- `generate-all`: Generates the release bundle for releases. You won't + need to use this unless you are managing releases. + +## `generate-all` + +Previously, this generates a full set of all vector DB / triple store / LLM +combinations, and put them in a single ZIP file. But this got out of +hand, so at the time of writing, this generates a single configuraton +using Qdrant vector DB, Ollama LLM support and Cassandra for a triple store. + +The combinations are contained withing the code, it takes two arguments: +- output ZIP file (is over-written) +- TrustGraph version number + +``` +templates/generate-all output.zip 0.18.11 +``` + +## `generate` + +This utility takes a configuration file describing the components to bundle, +and outputs a Docker Compose YAML file. + +### Input configuration + +The input configuration is a JSON file, an array of components to pull into +the configuration. For each component, there is a name and a (possibly empty) +object describing addtional parameters for that component. + +Example: + +``` +[ + { + "name": "cassandra", + "parameters": {} + }, + { + "name": "pulsar", + "parameters": {} + }, + { + "name": "qdrant", + "parameters": {} + }, + { + "name": "embeddings-hf", + "parameters": {} + }, + { + "name": "graph-rag", + "parameters": {} + }, + { + "name": "grafana", + "parameters": {} + }, + { + "name": "trustgraph", + "parameters": {} + }, + { + "name": "googleaistudio", + "parameters": { + "googleaistudio-temperature": 0.3, + "googleaistudio-max-output-tokens": 2048, + "googleaistudio-model": "gemini-1.5-pro-002" + } + }, + { + "name": "prompt-template", + "parameters": {} + }, + { + "name": "override-recursive-chunker", + "parameters": { + "chunk-size": 1000, + "chunk-overlap": 50 + } + }, + { + "name": "workbench-ui", + "parameters": {} + }, + { + "name": "agent-manager-react", + "parameters": {} + } +] +``` + +If you want to make your own configuration you could try changing the +configuration above: +- Components which are essential: pulsar, trustgraph, graph-rag, grafana, + agent-manager-react +- You need a triple store, one of: cassandra, memgraph, falkordb, neo4j +- You need a vector store, one of: qdrant, pinecone +- You need an LLM, one of: azure, azure-openai, bedrock, claude, cohere, + llamafile, ollama, openai, vertexai. +- You need an embeddings implementation, one of: embeddings-hf, + embeddings-ollama +- Optionally add the Workbench tool: workbench-ui + +Components have over-ridable parameters, look in the component definition +in `templates/components/` to see what you can override. + +### Invocation + +Two parameters: +- The output ZIP file +- The version number + +The configuration file described above is provided on standard input + +``` +templates/generate out.zip 0.18.9 < config.json +``` + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/cassandra.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/cassandra.jsonnet new file mode 100644 index 00000000..60f262d3 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/cassandra.jsonnet @@ -0,0 +1,50 @@ +local images = import "values/images.jsonnet"; + +{ + + "cassandra" +: { + + // Memory settings (can be overridden by memory-profile) + "memory-limit":: "1000M", + "memory-reservation":: "1000M", + "heap":: "300M", + + create:: function(engine) + + // Capture memory settings into locals + local memLimit = self["memory-limit"]; + local memReserv = self["memory-reservation"]; + local heap = self["heap"]; + + local vol = engine.volume("cassandra").with_size("20G"); + + local container = + engine.container("cassandra") + .with_image(images.cassandra) + .with_environment({ + JVM_OPTS: "-Xms%s -Xmx%s -Dcassandra.skip_wait_for_gossip_to_settle=0" % [ + heap, heap, + ], + }) + .with_limits("1.0", memLimit) + .with_reservations("0.5", memReserv) + .with_port(9042, 9042, "cassandra") + .with_volume_mount(vol, "/var/lib/cassandra"); + + local containerSet = engine.containers( + "cassandra", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9042, 9042, "api"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/ceph.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/ceph.jsonnet new file mode 100644 index 00000000..42406a95 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/ceph.jsonnet @@ -0,0 +1,446 @@ + +// +// This majorly does not work +// + +// This configuration fails primarily because it tries to treat Ceph +// like a stateless web app. You are currently pointing mon host to a +// generic service name (ceph-mon), but you aren't telling the Monitor +// process to assume that service identity. + +// To make this work with the "Service Name" approach across any +// engine, you need to fix the binding logic and the messenger protocol. + +// 1. The ceph.conf Fix + +// Need to enable Messenger v2 (modern) and tell the cluster to use +// the service names for its initial quorum. + +//Change this section: +// Ini, TOML + +// [global] +// fsid = %s +// mon initial members = mon0 +// mon host = ceph-mon:6789 +// ... + +// To this: +// Ini, TOML + +// [global] +// fsid = %s +// # Use the actual service names as members +// mon initial members = ceph-mon +// # Explicitly use the Service name (VIP) +// mon host = ceph-mon +// # Force modern protocol +// ms_bind_msgr2 = true +// ms_bind_msgr1 = true + +// 2. The MON Environment & Command Fix + +// This is the most critical part. Your current config sets MON_IP: +// "0.0.0.0". This causes the MON to bind to the Pod IP, which breaks +// when the Pod restarts. You must force it to bind to the Service IP. + +// Update mon_env: +// Code snippet + +// local mon_env = cluster_env + { +// CEPH_DAEMON: "MON", +// MON_NAME: "mon0", +// # Remove MON_IP: "0.0.0.0" +// # Add these: +// MON_ADDR: "ceph-mon", // This says to resolve the service name +// }; + +// Update mon_container command: You are currently wiping the MON data +// on every start (rm -rf /var/lib/ceph/mon/*). Stop doing that. If you +// wipe the data, you lose the cluster state and the OSDs will refuse to +// talk to the "new" MON. + +// Code snippet + +// .with_command([ +// "bash", "-c", +// # 1. Resolve the Service IP at runtime +// "export MON_IP=$(getent hosts ceph-mon | awk '{ print $1 }'); " + +// inject_mon_config + +// # 2. Start the daemon telling it its PUBLIC address is the Service VIP +// "exec /opt/ceph-container/bin/entrypoint.sh" +// ]) + +// 3. Why your current config "Majorly does not work" + +// The "Wipe" Logic: By running rm -rf /var/lib/ceph/mon/* in the +// MON container, you are creating a "New Cluster" every time the +// container starts. Since the OSDs store the fsid and cluster +// secrets, they will reject the "new" MON. + +// DNS Race Condition: Your OSD/MGR/RGW containers wait for +// ceph-mon DNS, which is good. However, if ceph-mon resolves to a +// Round Robin IP (multiple pods) rather than a stable ClusterIP, the +// connection will be flaky. + +// Messenger Protocol: Without ms_bind_msgr2, Ceph defaults to the +// old v1 protocol which is much more sensitive to NAT/Container IP +// mismatches. + +// SUMMARY + +// Component: ceph.conf +// Change: Add ms_bind_msgr2 = true +// Why: Supports modern container networking better. + +// Component: MON Start +// Change: Remove rm -rf +// Why: Ceph MONs must keep their database to maintain the cluster. + +// Component: MON Address +// Change: Use getent hosts ceph-mon +// Why: Forces the MON to advertise the Service VIP instead of its own Pod IP. + +// Component: MON Keyring +// Change: Ensure /etc/ceph/ceph.mon.keyring exists +// Why: MONs need their specific key to start. + +local images = import "values/images.jsonnet"; + +{ + with:: function(key, value) + self + { + ["ceph-" + key]:: value, + }, + + // Ceph credentials and cluster settings + "ceph-access-key":: "object-user", + "ceph-secret-key":: "object-password", + "ceph-cluster-id":: "ceph", + "ceph-fsid":: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", + + // Pool redundancy settings + // size: 2 = two replicas for fault tolerance + // min_size: 1 = allow degraded I/O if one OSD is down (prevents cluster freeze) + "ceph-pool-size":: "2", + "ceph-pool-min-size":: "1", + + ceph +: { + create:: function(engine) + // Pre-Shared Cryptographic Material - Config-as-Code Approach + // These keys are generated once and distributed to all daemons + // This ensures cryptographic consistency across the shared-nothing architecture + local admin_key = "AQBpxSBlAAAAABAAU99V6D8vS7Uu9y1S8W0iBg=="; + local mon_key = "AQBpxSBlAAAAABAAn7pL/pG9oT+X6vO7V1S6bg=="; + + // Ceph configuration file - rendered from Jsonnet variables + local ceph_conf = ||| + [global] + fsid = %s + mon initial members = mon0 + mon host = ceph-mon:6789 + public network = 0.0.0.0/0 + cluster network = 0.0.0.0/0 + osd pool default size = %s + osd pool default min size = %s + osd crush chooseleaf type = 0 + auth cluster required = cephx + auth service required = cephx + auth client required = cephx + ||| % [$["ceph-fsid"], $["ceph-pool-size"], $["ceph-pool-min-size"]]; + + // Admin keyring - distributed to all daemons + local admin_keyring = ||| + [client.admin] + key = %s + caps mds = "allow *" + caps mgr = "allow *" + caps mon = "allow *" + caps osd = "allow *" + ||| % [admin_key]; + + // Monitor keyring - used by MON for cluster operations + local mon_keyring = ||| + [mon.] + key = %s + caps mon = "allow *" + ||| % [mon_key]; + + // Config injection command - writes files before entrypoint + local inject_config = "printf '%s' > /etc/ceph/ceph.conf; printf '%s' > /etc/ceph/ceph.client.admin.keyring; " % [ceph_conf, admin_keyring]; + local inject_mon_config = inject_config + ("printf '%s' > /etc/ceph/ceph.mon.keyring; " % [mon_keyring]); + + // Data volumes - sized appropriately for production workloads + local vol_mon = engine.volume("ceph-mon").with_size("20G"); + local vol_mgr = engine.volume("ceph-mgr").with_size("20G"); + local vol_osd = engine.volume("ceph-osd").with_size("100G"); + local vol_rgw = engine.volume("ceph-rgw").with_size("20G"); + + // Isolated config volumes per daemon (ReadWriteOnce compatible) + // Each daemon gets its own non-shared config volume to support + // multi-node scheduling in K8s and other orchestrators + local vol_mon_config = engine.volume("ceph-mon-config").with_size("500M"); + local vol_mgr_config = engine.volume("ceph-mgr-config").with_size("500M"); + local vol_osd_config = engine.volume("ceph-osd-config").with_size("500M"); + local vol_rgw_config = engine.volume("ceph-rgw-config").with_size("500M"); + local vol_init_config = engine.volume("ceph-init-config").with_size("500M"); + + // Simplified cluster environment - Config-as-Code model + // No fetch logic needed - config is injected before entrypoint runs + local cluster_env = { + CLUSTER: $["ceph-cluster-id"], + FSID: $["ceph-fsid"], + KV_TYPE: "none", // No external coordination + }; + + // MON-specific environment + // Config-as-Code: MON uses injected config files, not fetch logic + // + // CRITICAL: MON_DATA_AVAIL="0" forces fresh cluster bootstrap + // The ceph/daemon entrypoint script (variables_stack.sh) uses this as a gate: + // - MON_DATA_AVAIL="0" -> run mkfs, create new cluster with our FSID + // - MON_DATA_AVAIL="1" -> attempt to join existing cluster (infinite probe loop) + // + // Network configuration for monmap generation + local mon_env = cluster_env + { + CEPH_DAEMON: "MON", + MON_NAME: "mon0", + MON_PORT: "6789", + MON_DATA_AVAIL: "0", + MON_IP: "0.0.0.0", + NETWORK_AUTO_DETECT: "4", + CEPH_PUBLIC_NETWORK: "0.0.0.0/0", + }; + + // Simplified daemon environments - Config-as-Code model + // All daemons receive config via injection, not fetch from MON + // This eliminates "static mode" errors and networking complexity + + // MGR-specific environment + local mgr_env = cluster_env + { + CEPH_DAEMON: "MGR", + MGR_NAME: "mgr0", + }; + + // OSD-specific environment + local osd_env = cluster_env + { + CEPH_DAEMON: "OSD", + OSD_TYPE: "directory", + }; + + // RGW-specific environment + local rgw_env = cluster_env + { + CEPH_DAEMON: "RGW", + RGW_NAME: "rgw0", + RGW_FRONTEND_PORT: "7480", + }; + + // MON (Monitor) container - cluster state and quorum + // Config-as-Code: Injects pre-shared keys before entrypoint + // CRITICAL: Wipes /var/lib/ceph/mon/* on every start to force fresh bootstrap + // This ensures MON always uses our FSID and doesn't inherit stale cluster state + local mon_container = + engine.container("ceph-mon") + .with_image(images.ceph) + .with_environment(mon_env) + .with_command([ + "bash", "-c", + "rm -rf /var/lib/ceph/mon/*; " + + inject_mon_config + + "exec /opt/ceph-container/bin/entrypoint.sh" + ]) + .with_limits("1.0", "1536M") + .with_reservations("0.5", "1024M") + .with_port(6789, 6789, "mon") + .with_port(3300, 3300, "mon-msgr2") + .with_volume_mount(vol_mon, "/var/lib/ceph/mon") + .with_volume_mount(vol_mon_config, "/etc/ceph"); + + // MGR (Manager) container - cluster management and dashboard + // Config-as-Code: Uses injected config files with pre-shared keys + // DNS wait ensures MON is available before MGR connects + local mgr_container = + engine.container("ceph-mgr") + .with_image(images.ceph) + .with_environment(mgr_env) + .with_command([ + "bash", "-c", + "until getent hosts ceph-mon; do echo 'Waiting for MON DNS...'; sleep 2; done; " + + inject_config + + "exec /opt/ceph-container/bin/entrypoint.sh" + ]) + .with_limits("1.0", "1536M") + .with_reservations("0.5", "1024M") + .with_port(7000, 7000, "mgr") + .with_port(8443, 8443, "dashboard") + .with_port(9283, 9283, "prometheus") + .with_volume_mount(vol_mgr, "/var/lib/ceph/mgr") + .with_volume_mount(vol_mgr_config, "/etc/ceph"); + + // OSD (Object Storage Daemon) - actual data storage + // Config-as-Code: Uses injected config files with pre-shared keys + // Increased resources to prevent OOM during recovery operations + // DNS wait ensures MON is available before OSD connects + local osd_container = + engine.container("ceph-osd") + .with_image(images.ceph) + .with_environment(osd_env) + .with_command([ + "bash", "-c", + "until getent hosts ceph-mon; do echo 'Waiting for MON DNS...'; sleep 2; done; " + + inject_config + + "exec /opt/ceph-container/bin/entrypoint.sh" + ]) + .with_limits("2.0", "4096M") + .with_reservations("1.0", "2048M") + .with_port(6800, 6800, "osd") + .with_volume_mount(vol_osd, "/var/lib/ceph/osd") + .with_volume_mount(vol_osd_config, "/etc/ceph"); + + // RGW (RADOS Gateway) - S3 API endpoint + // Config-as-Code: Uses injected config files with pre-shared keys + // DNS wait ensures MON is available before RGW connects + local rgw_container = + engine.container("ceph-rgw") + .with_image(images.ceph) + .with_environment(rgw_env) + .with_command([ + "bash", "-c", + "until getent hosts ceph-mon; do echo 'Waiting for MON DNS...'; sleep 2; done; " + + inject_config + + "exec /opt/ceph-container/bin/entrypoint.sh" + ]) + .with_limits("1.0", "1536M") + .with_reservations("0.5", "1024M") + .with_port(7480, 7480, "s3") + .with_volume_mount(vol_rgw, "/var/lib/ceph/radosgw") + .with_volume_mount(vol_rgw_config, "/etc/ceph"); + + // Init container - one-time S3 user provisioning + // IMPORTANT: This container exits with code 0 after completion + // Orchestrator must NOT restart it (use K8s Job or Compose restart: "no") + // Config-as-Code: Uses injected config to run radosgw-admin commands + local init_container = + engine.container("ceph-init") + .with_image(images.ceph) + .with_environment({ + CLUSTER: $["ceph-cluster-id"], + FSID: $["ceph-fsid"], + KV_TYPE: "none", + RGW_ACCESS_KEY: $["ceph-access-key"], + RGW_SECRET_KEY: $["ceph-secret-key"], + }) + .with_limits("0.5", "512M") + .with_reservations("0.25", "256M") + .with_volume_mount(vol_init_config, "/etc/ceph") + .with_command([ + "bash", "-c", + inject_config + ||| + set -e + + # Wait for cluster health + echo "Waiting for Ceph cluster to be healthy..." + MAX_ATTEMPTS=60 + ATTEMPT=0 + until ceph --cluster ${CLUSTER} health 2>/dev/null | grep -q "HEALTH_OK\|HEALTH_WARN"; do + ATTEMPT=$((ATTEMPT+1)) + if [ $ATTEMPT -ge $MAX_ATTEMPTS ]; then + echo "ERROR: Cluster failed to become healthy after ${MAX_ATTEMPTS} attempts" + exit 1 + fi + echo "Attempt ${ATTEMPT}/${MAX_ATTEMPTS}: Cluster not ready, retrying in 5s..." + sleep 5 + done + echo "Cluster is healthy." + + # Wait for RGW availability + echo "Waiting for RGW to be ready..." + ATTEMPT=0 + until curl -sf http://ceph-rgw:7480 >/dev/null 2>&1; do + ATTEMPT=$((ATTEMPT+1)) + if [ $ATTEMPT -ge $MAX_ATTEMPTS ]; then + echo "ERROR: RGW failed to become ready after ${MAX_ATTEMPTS} attempts" + exit 1 + fi + echo "Attempt ${ATTEMPT}/${MAX_ATTEMPTS}: RGW not ready, retrying in 5s..." + sleep 5 + done + echo "RGW is ready." + + # Idempotent S3 user creation + echo "Provisioning S3 user: ${RGW_ACCESS_KEY}" + if radosgw-admin --cluster ${CLUSTER} user info --uid="${RGW_ACCESS_KEY}" >/dev/null 2>&1; then + echo "User ${RGW_ACCESS_KEY} already exists, skipping creation." + else + echo "Creating new S3 user: ${RGW_ACCESS_KEY}" + radosgw-admin --cluster ${CLUSTER} user create \ + --uid="${RGW_ACCESS_KEY}" \ + --display-name="Object Storage User" \ + --access-key="${RGW_ACCESS_KEY}" \ + --secret-key="${RGW_SECRET_KEY}" + echo "S3 user created successfully." + fi + + echo "Initialization complete. Exiting." + exit 0 + |||, + ]); + + // Container sets - each daemon gets its own for K8s node distribution + local mon_containerSet = engine.containers("ceph-mon", [mon_container]); + local mgr_containerSet = engine.containers("ceph-mgr", [mgr_container]); + local osd_containerSet = engine.containers("ceph-osd", [osd_container]); + local rgw_containerSet = engine.containers("ceph-rgw", [rgw_container]); + local init_containerSet = engine.containers("ceph-init", [init_container]); + + // Services - expose daemon ports for inter-daemon communication + local mon_service = + engine.service(mon_containerSet) + .with_port(6789, 6789, "mon") + .with_port(3300, 3300, "mon-msgr2"); + + local mgr_service = + engine.service(mgr_containerSet) + .with_port(7000, 7000, "mgr") + .with_port(8443, 8443, "dashboard") + .with_port(9283, 9283, "prometheus"); + + local osd_service = + engine.service(osd_containerSet) + .with_port(6800, 6800, "osd"); + + local rgw_service = + engine.service(rgw_containerSet) + .with_port(7480, 7480, "s3"); + + engine.resources([ + + // Data volumes + vol_mon, + vol_mgr, + vol_osd, + vol_rgw, + + // Config volumes (isolated, no sharing) + vol_mon_config, + vol_mgr_config, + vol_osd_config, + vol_rgw_config, + vol_init_config, + + // Container sets + mon_containerSet, + mgr_containerSet, + osd_containerSet, + rgw_containerSet, + init_containerSet, + + // Services + mon_service, + mgr_service, + osd_service, + rgw_service, + + ]) + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/falkordb.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/falkordb.jsonnet new file mode 100644 index 00000000..1d4176d8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/falkordb.jsonnet @@ -0,0 +1,38 @@ +local images = import "values/images.jsonnet"; + +{ + + "falkordb" +: { + + create:: function(engine) + + local vol = engine.volume("falkordb").with_size("20G"); + + local container = + engine.container("falkordb") + .with_image(images.falkordb) + .with_limits("1.0", "768M") + .with_reservations("0.5", "768M") + .with_port(6379, 6379, "api") + .with_port(3010, 3000, "ui") + .with_volume_mount(vol, "/data"); + + local containerSet = engine.containers( + "falkordb", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(6379, 6379, "api") + .with_port(3010, 3010, "ui"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/garage.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/garage.jsonnet new file mode 100644 index 00000000..9d339bfb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/garage.jsonnet @@ -0,0 +1,249 @@ +local images = import "values/images.jsonnet"; + +{ + + garage +: { + + // Garage S3 credentials - these are the actual access key ID and secret key + // Access Key ID must be in format: GK + 24 hex characters (12 bytes) + // Secret Key must be 64 hex characters (32 bytes) + // For production, generate secure random values and override these defaults + "access-key":: "GK000000000000000000000001", + "secret-key":: "b171f00be9be4c32c734f4c05fe64c527a8ab5eb823b376cfa8c2531f70fc427", + "rpc-secret":: "bbba746a9e289bad64a9e7a36a4299dac8d6e0b8cc2a6c2937fe756df4492008", + // For a production system, override this value + "admin-token":: "batts-rockhearted-unpartially", + region:: "garage", + "replication-factor":: "1", // Set to 1 for single-node, 3 for production + + // Storage volume sizes + "meta-size":: "2G", // Metadata volume size + "data-size":: "5G", // Data volume size (also used for cluster layout capacity) + + create:: function(engine) + + local accessKey = self["access-key"]; + local secretKey = self["secret-key"]; + local rpcSecret = self["rpc-secret"]; + local adminToken = self["admin-token"]; + local region = self.region; + local replicationFactor = self["replication-factor"]; + local metaSize = self["meta-size"]; + local dataSize = self["data-size"]; + + // Garage daemon configuration file - TOML format + local garage_conf = ||| + metadata_dir = "/var/lib/garage/meta" + data_dir = "/var/lib/garage/data" + + db_engine = "lmdb" + + replication_factor = %s + + compression_level = 1 + + rpc_bind_addr = "[::]:3901" + rpc_public_addr = "[::]:3901" + rpc_secret = "%s" + + [s3_api] + s3_region = "%s" + api_bind_addr = "[::]:3900" + root_domain = ".s3.garage.local" + + [s3_web] + bind_addr = "[::]:3902" + root_domain = ".web.garage.local" + index = "index.html" + + [k2v_api] + api_bind_addr = "[::]:3904" + + [admin] + api_bind_addr = "[::]:3903" + admin_token = "%s" + ||| % [replicationFactor, rpcSecret, region, adminToken]; + + // Config volume - contains the rendered garage.toml + local cfgVol = engine.configVolume( + "garage-cfg", "garage", + { + "garage.toml": garage_conf, + } + ); + + // Volumes - Garage stores metadata and data separately + local vol_meta = engine.volume("garage-meta").with_size(metaSize); + local vol_data = engine.volume("garage-data").with_size(dataSize); + + // Main Garage daemon container + local garage_container = + engine.container("garage") + .with_image(images.garage) + .with_command([ + "/garage", "-c", "/etc/garage/garage.toml", "server" + ]) + .with_environment({ + RUST_LOG: "garage=info", + }) + .with_limits("1.0", "512M") + .with_reservations("0.5", "512M") + .with_port(3900, 3900, "s3-api") + .with_port(3901, 3901, "rpc") + .with_port(3902, 3902, "web") + .with_port(3903, 3903, "admin") + .with_port(3904, 3904, "k2v") + .with_volume_mount(cfgVol, "/etc/garage/") + .with_volume_mount(vol_meta, "/var/lib/garage/meta") + .with_volume_mount(vol_data, "/var/lib/garage/data"); + + // Init container - configures cluster layout and creates S3 credentials + // IMPORTANT: This container exits with code 0 after completion + // Orchestrator must NOT restart it (use K8s Job or Compose restart: "no") + // Uses Alpine base image since garage container has no shell + local init_container = + engine.container("garage-init") + .with_image("docker.io/alpine:3.23.2") + .with_environment({ + GARAGE_ACCESS_KEY: accessKey, + GARAGE_SECRET_KEY: secretKey, + GARAGE_REGION: region, + GARAGE_ADMIN_TOKEN: adminToken, + GARAGE_RPC_SECRET: rpcSecret, + GARAGE_DATA_SIZE: dataSize, + }) + .with_limits("0.5", "256M") + .with_reservations("0.25", "128M") + .with_volume_mount(cfgVol, "/etc/garage/") + .with_command([ + "sh", "-c", ||| + set -e + + # Install required tools + echo "Installing curl, jq and downloading garage CLI..." + apk add --no-cache curl jq + + # Download garage binary (v2.1.0) for remote management + curl -fsSL "https://garagehq.deuxfleurs.fr/_releases/v2.1.0/x86_64-unknown-linux-musl/garage" \ + -o /usr/local/bin/garage + chmod +x /usr/local/bin/garage + + echo "Waiting for Garage daemon to be ready..." + MAX_ATTEMPTS=60 + ATTEMPT=0 + # Wait for /health to respond (even 503 is fine - means daemon is up) + until curl -s http://garage:3903/health >/dev/null 2>&1; do + ATTEMPT=$((ATTEMPT+1)) + if [ $ATTEMPT -ge $MAX_ATTEMPTS ]; then + echo "ERROR: Garage failed to become ready after ${MAX_ATTEMPTS} attempts" + exit 1 + fi + echo "Attempt ${ATTEMPT}/${MAX_ATTEMPTS}: Garage not ready, retrying in 2s..." + sleep 2 + done + echo "Garage daemon is ready." + + # Get the node ID via v2 Admin API + echo "Getting Garage node ID via Admin API..." + curl -s -H "Authorization: Bearer ${GARAGE_ADMIN_TOKEN}" \ + "http://garage:3903/v2/GetNodeInfo?node=self" > /tmp/garage-node-info.json + + # Extract node ID from response (the key in success map is the node ID) + NODE_ID=$(jq -r '.success | to_entries[0].value.nodeId' /tmp/garage-node-info.json) + echo "Node ID: ${NODE_ID}" + + if [ -z "$NODE_ID" ] || [ "$NODE_ID" = "null" ]; then + echo "ERROR: Failed to retrieve node ID" + exit 1 + fi + + # ===== LAYOUT MANAGEMENT VIA REMOTE RPC ===== + # Use garage CLI with -h and -s flags to connect remotely + # -h requires format: @: + # -s provides the RPC secret + + # Construct full RPC identifier + RPC_HOST="${NODE_ID}@garage:3901" + echo "RPC Host: ${RPC_HOST}" + + # Check current layout to see if node is already assigned (idempotent) + echo "Checking current cluster layout..." + LAYOUT_OUTPUT=$(garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" layout show 2>&1) + + # Check if node already has a role assigned + # Layout output shows abbreviated node ID (first 16 chars) + NODE_ID_SHORT="${NODE_ID:0:16}" + if echo "$LAYOUT_OUTPUT" | grep -q "$NODE_ID_SHORT"; then + echo "Node ${NODE_ID_SHORT}... already assigned in layout, skipping." + else + echo "Assigning node to cluster layout..." + # Assign node to zone dc1 with configured capacity + garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" \ + layout assign ${NODE_ID} -z dc1 -c ${GARAGE_DATA_SIZE} + + echo "Applying layout configuration..." + # Get current staged version and apply + garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" \ + layout apply --version 1 + + echo "Layout configured successfully." + # Wait for layout to stabilize + sleep 5 + fi + + # ===== KEY MANAGEMENT VIA REMOTE RPC ===== + + # Check if key already exists (idempotent) + # GARAGE_ACCESS_KEY is already a valid Garage Key ID (GK + 24 hex chars) + if garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" key info "${GARAGE_ACCESS_KEY}" >/dev/null 2>&1; then + echo "Access key ${GARAGE_ACCESS_KEY} already exists, skipping creation." + else + echo "Importing S3 access key: ${GARAGE_ACCESS_KEY}" + + # Import key with the provided credentials (both already in valid Garage format) + # GARAGE_ACCESS_KEY = Key ID (GK + 24 hex chars) + # GARAGE_SECRET_KEY = Secret (64 hex chars) + garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" \ + key import "${GARAGE_ACCESS_KEY}" "${GARAGE_SECRET_KEY}" --yes + + echo "Access key imported successfully." + fi + + # Grant permissions to the key + echo "Granting create-bucket permission to key..." + garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" \ + key allow --create-bucket "${GARAGE_ACCESS_KEY}" + + echo "" + echo "Garage initialization complete!" + echo "S3 Endpoint: http://garage:3900" + echo "Region: ${GARAGE_REGION}" + exit 0 + |||, + ]); + + // Container sets + local garage_containerSet = engine.containers("garage", [garage_container]); + local init_containerSet = engine.containers("garage-init", [init_container]); + + // Service - expose Garage ports + local garage_service = + engine.service(garage_containerSet) + .with_port(3900, 3900, "s3-api") + .with_port(3901, 3901, "rpc") + .with_port(3902, 3902, "web") + .with_port(3903, 3903, "admin") + .with_port(3904, 3904, "k2v"); + + engine.resources([ + cfgVol, + vol_meta, + vol_data, + garage_containerSet, + init_containerSet, + garage_service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/memgraph.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/memgraph.jsonnet new file mode 100644 index 00000000..eeed2e4e --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/memgraph.jsonnet @@ -0,0 +1,70 @@ +local images = import "values/images.jsonnet"; + +{ + + "memgraph" +: { + + create:: function(engine) + + local vol = engine.volume("memgraph").with_size("20G"); + + local container = + engine.container("memgraph") + .with_image(images.memgraph_mage) + .with_environment({ + MEMGRAPH: "--storage-properties-on-edges=true --storage-enable-edges-metadata=true" + }) + .with_limits("1.0", "1000M") + .with_reservations("0.5", "1000M") + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2") + .with_volume_mount(vol, "/var/lib/memgraph"); + + local containerSet = engine.containers( + "memgraph", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + + "memgraph-lab" +: { + + create:: function(engine) + + local container = + engine.container("lab") + .with_image(images.memgraph_lab) + .with_environment({ + QUICK_CONNECT_MG_HOST: "memgraph", + QUICK_CONNECT_MG_PORT: "7687", + }) + .with_limits("1.0", "512M") + .with_reservations("0.5", "512M") + .with_port(3010, 3000, "http"); + + local containerSet = engine.containers( + "lab", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(3010, 3010, "http"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/milvus.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/milvus.jsonnet new file mode 100644 index 00000000..5bed3820 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/milvus.jsonnet @@ -0,0 +1,89 @@ +local images = import "values/images.jsonnet"; +local minio = import "backends/minio.jsonnet"; + +minio { + + etcd +: { + + create:: function(engine) + + local vol = engine.volume("etcd").with_size("20G"); + + local container = + engine.container("etcd") + .with_image(images.etcd) + .with_command([ + "etcd", + "-advertise-client-urls=http://127.0.0.1:2379", + "-listen-client-urls", + "http://0.0.0.0:2379", + "--data-dir", + "/etcd", + ]) + .with_environment({ + ETCD_AUTO_COMPACTION_MODE: "revision", + ETCD_AUTO_COMPACTION_RETENTION: "1000", + ETCD_QUOTA_BACKEND_BYTES: "4294967296", + ETCD_SNAPSHOT_COUNT: "50000" + }) + .with_limits("1.0", "128M") + .with_reservations("0.25", "128M") + .with_port(2379, 2379, "api") + .with_volume_mount(vol, "/etcd"); + + local containerSet = engine.containers( + "etcd", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(2379, 2379, "api"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + + milvus +: { + + create:: function(engine) + + local vol = engine.volume("milvus").with_size("20G"); + + local container = + engine.container("milvus") + .with_image(images.milvus) + .with_command([ + "milvus", "run", "standalone" + ]) + .with_environment({ + ETCD_ENDPOINTS: "etcd:2379", + MINIO_ADDRESS: "minio:9000", + }) + .with_limits("1.0", "256M") + .with_reservations("0.5", "256M") + .with_port(9091, 9091, "api") + .with_port(19530, 19530, "api2") + .with_volume_mount(vol, "/var/lib/milvus"); + + local containerSet = engine.containers( + "milvus", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9091, 9091, "api") + .with_port(19530, 19530, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/minio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/minio.jsonnet new file mode 100644 index 00000000..b38bb81f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/minio.jsonnet @@ -0,0 +1,48 @@ +local images = import "values/images.jsonnet"; + +{ + + minio +: { + + create:: function(engine) + + local vol = engine.volume("minio-data").with_size("20G"); + + local container = + engine.container("minio") + .with_image(images.minio) + .with_command([ + "minio", + "server", + "/minio_data", + "--console-address", + ":9001", + ]) + .with_environment({ + MINIO_ROOT_USER: "minioadmin", + MINIO_ROOT_PASSWORD: "minioadmin", + }) + .with_limits("0.5", "128M") + .with_reservations("0.25", "128M") + .with_port(9000, 9000, "api") + .with_port(9001, 9001, "console") + .with_volume_mount(vol, "/minio_data"); + + local containerSet = engine.containers( + "minio", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9000, 9000, "api") + .with_port(9001, 9001, "console"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/neo4j.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/neo4j.jsonnet new file mode 100644 index 00000000..46c61e0f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/neo4j.jsonnet @@ -0,0 +1,46 @@ +local images = import "values/images.jsonnet"; + +{ + + "neo4j" +: { + + create:: function(engine) + + local vol = engine.volume("neo4j").with_size("20G"); + + local container = + engine.container("neo4j") + .with_image(images.neo4j) + .with_environment({ + NEO4J_AUTH: "neo4j/password", + NEO4J_server_memory_pagecache_size: "512m", + NEO4J_server_memory_heap_max__size: "512m", + // NEO4J_server_bolt_listen__address: "0.0.0.0:7687", + // NEO4J_server_default__listen__address: "0.0.0.0", + // NEO4J_server_http_listen__address: "0.0.0.0:7474", + }) + .with_limits("1.0", "1536M") + .with_reservations("0.5", "1536M") + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2") + .with_volume_mount(vol, "/data"); + + local containerSet = engine.containers( + "neo4j", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/pinecone.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/pinecone.jsonnet new file mode 100644 index 00000000..2bef70fb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/pinecone.jsonnet @@ -0,0 +1,45 @@ +local images = import "values/images.jsonnet"; + +{ + + "pinecone" +: { + + create:: function(engine) + + local container = + engine.container("pinecone") + .with_image(images.pinecone) + .with_environment({ + + PORT: "5080", + + INDEX_TYPE: "serverless", + + // Dimension is fixed, 384 is right for miniLM + // sentence embedding + DIMENSION: 384, + + METRIC: "cosine" + + }) + .with_limits("1.0", "256M") + .with_reservations("0.5", "256M") + .with_port(5080, 5080, "api"); + + local containerSet = engine.containers( + "pinecone", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(5080, 5080, "api"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/qdrant.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/qdrant.jsonnet new file mode 100644 index 00000000..2e6761f8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/backends/qdrant.jsonnet @@ -0,0 +1,65 @@ +local images = import "values/images.jsonnet"; + +{ + + "qdrant" +: { + + // Memory settings (can be overridden by memory-profile) + "memory-limit":: "1024M", + "memory-reservation":: "1024M", + + // Mmap settings for low-memory mode (trades latency for memory) + // Set to null to disable, or string value to enable + "memmap-threshold-kb":: null, + "on-disk-payload":: null, + + create:: function(engine) + + // Capture memory settings into locals + local memLimit = self["memory-limit"]; + local memReserv = self["memory-reservation"]; + local mmapThreshold = self["memmap-threshold-kb"]; + local onDiskPayload = self["on-disk-payload"]; + + local vol = engine.volume("qdrant").with_size("20G"); + + // Build environment with optional mmap settings + local baseEnv = {}; + local env = baseEnv + + (if mmapThreshold != null then { + QDRANT__STORAGE__MEMMAP_THRESHOLD_KB: mmapThreshold, + } else {}) + + (if onDiskPayload != null then { + QDRANT__STORAGE__ON_DISK_PAYLOAD: onDiskPayload, + } else {}); + + local container = + engine.container("qdrant") + .with_image(images.qdrant) + .with_limits("1.0", memLimit) + .with_reservations("0.5", memReserv) + .with_port(6333, 6333, "api") + .with_port(6334, 6334, "api2") + .with_volume_mount(vol, "/qdrant/storage") + + (if std.length(env) > 0 then { + environment+: env, + } else {}); + + local containerSet = engine.containers( + "qdrant", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(6333, 6333, "api") + .with_port(6334, 6334, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/components.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/components.jsonnet new file mode 100644 index 00000000..03a55208 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/components.jsonnet @@ -0,0 +1,121 @@ +{ + + // Essentials + "trustgraph-base": import "core/trustgraph.jsonnet", + "rev-gateway": import "core/rev-gateway.jsonnet", + "pulsar": import "pulsar/pulsar.jsonnet", + + // LLMs + "azure": import "llm/azure.jsonnet", + "azure-openai": import "llm/azure-openai.jsonnet", + "bedrock": import "llm/bedrock.jsonnet", + "claude": import "llm/claude.jsonnet", + "cohere": import "llm/cohere.jsonnet", + "googleaistudio": import "llm/googleaistudio.jsonnet", + "llamafile": import "llm/llamafile.jsonnet", + "lmstudio": import "llm/lmstudio.jsonnet", + "mistral": import "llm/mistral.jsonnet", + "ollama": import "llm/ollama.jsonnet", + "openai": import "llm/openai.jsonnet", + "vertexai": import "llm/vertexai.jsonnet", + "tgi": import "llm/tgi.jsonnet", + "vllm": import "llm/vllm.jsonnet", + + // Embeddings + "embeddings-ollama": import "embeddings/embeddings-ollama.jsonnet", + "embeddings-hf": import "embeddings/embeddings-hf.jsonnet", + "embeddings-fastembed": import "embeddings/embeddings-fastembed.jsonnet", + + // OCR options + "ocr": import "ocr/ocr.jsonnet", + "mistral-ocr": import "ocr/mistral-ocr.jsonnet", + + // Vector stores + "vector-store-milvus": import "vector-store/milvus.jsonnet", + "vector-store-qdrant": import "vector-store/qdrant.jsonnet", + "vector-store-pinecone": import "vector-store/pinecone.jsonnet", + + // Triples stores + "triple-store-cassandra": import "triple-store/cassandra.jsonnet", + "triple-store-neo4j": import "triple-store/neo4j.jsonnet", + "triple-store-falkordb": import "triple-store/falkordb.jsonnet", + "triple-store-memgraph": import "triple-store/memgraph.jsonnet", + + // Object stores + "row-store-cassandra": import "row-store/cassandra.jsonnet", + + // Observability support + "grafana": import "monitoring/grafana.jsonnet", + "loki": import "monitoring/loki.jsonnet", + + // Pulsar manager is a UI for Pulsar. Uses a LOT of memory + "pulsar-manager": import "pulsar/pulsar-manager.jsonnet", + + "override-recursive-chunker": import "core/chunker-recursive.jsonnet", + + // The prompt manager + "prompt-overrides": import "core/prompt-overrides.jsonnet", + + // Extra MCP services + "ddg-mcp-server": import "mcp/ddg-mcp-server.jsonnet", + + // Does nothing. But, can be a hack to overwrite parameters + "null": {}, + + // Passthrough: returns parameters directly, preserving +: merge syntax + // Also supports JSON-safe routing with prefixed parameters like "cassandra-heap" + "override": { + local route = function(target) + function(prefix, k, v) + local suffix = std.substr(k, std.length(prefix), std.length(k) - std.length(prefix)); + { [target] +: { [suffix]:: v } }, + + local routes = { + "cassandra-": route("cassandra"), + "pulsar-": route("pulsar"), + "qdrant-": route("qdrant"), + "api-gateway-": route("api-gateway"), + "librarian-": route("librarian"), + }, + + with_params:: function(pars) + std.foldl( + function(acc, k) + local matchingPrefixes = [p for p in std.objectFields(routes) if std.startsWith(k, p)]; + if std.length(matchingPrefixes) > 0 then + local prefix = matchingPrefixes[0]; + acc + routes[prefix](prefix, k, pars[k]) + else + acc + { [k]:: pars[k] }, + std.objectFields(pars), + {} + ), + }, + + // Memory profiles + "memory-profile-low": import "profiles/memory-profile-low.jsonnet", + + // Model hosting + + "hosting-intel-battlemage-vllm": + import "model-hosting/intel-battlemage-vllm.jsonnet", + + "hosting-cpu-tgi": + import "model-hosting/cpu-tgi.jsonnet", + + "hosting-intel-xpu-tgi": + import "model-hosting/intel-xpu-tgi.jsonnet", + + "hosting-intel-gaudi-tgi": + import "model-hosting/intel-gaudi-tgi.jsonnet", + + "hosting-intel-xpu-vllm": + import "model-hosting/intel-xpu-vllm.jsonnet", + + "hosting-intel-gaudi-vllm": + import "model-hosting/intel-gaudi-vllm.jsonnet", + + "hosting-nvidia-gpu-vllm": + import "model-hosting/nvidia-gpu-vllm.jsonnet", + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/agent-manager-react.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/agent-manager-react.jsonnet new file mode 100644 index 00000000..954d80a7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/agent-manager-react.jsonnet @@ -0,0 +1,47 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "agent-manager" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("agent-manager") + .with_image(images.trustgraph_flow) + .with_command([ + "agent-manager-react", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "agent-manager", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/chunker-recursive.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/chunker-recursive.jsonnet new file mode 100644 index 00000000..0b924aba --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/chunker-recursive.jsonnet @@ -0,0 +1,55 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; + +{ + + "chunk-size":: 2000, + "chunk-overlap":: 100, + + "chunker" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("chunker") + .with_image(images.trustgraph_flow) + .with_command([ + "chunker-recursive", + "-p", + url.pulsar, + "--chunk-size", + std.toString($["chunk-size"]), + "--chunk-overlap", + std.toString($["chunk-overlap"]), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "chunker", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/configuration.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/configuration.jsonnet new file mode 100644 index 00000000..75afebb9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/configuration.jsonnet @@ -0,0 +1,57 @@ + +// This puts the default configuration together. References many things, +// flow classes, a default flow, token costs, prompts, agent tools + +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "init-trustgraph" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local cfgVol = engine.configVolume( + "trustgraph-cfg", "trustgraph", + { + "config.json": importstr "trustgraph/config.json", + } + ); + + local container = + engine.container("init-trustgraph") + .with_image(images.trustgraph_flow) + .with_command( + [ + "tg-init-trustgraph", + "-p", + url.pulsar_admin, + "--config-file", + "/trustgraph/config.json", + ] + ) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation) + .with_volume_mount(cfgVol, "/trustgraph/"); + + local containerSet = engine.containers( + "init-trustgraph", [ container ] + ); + + engine.resources([ + cfgVol, + containerSet, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/document-rag.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/document-rag.jsonnet new file mode 100644 index 00000000..e466e3a4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/document-rag.jsonnet @@ -0,0 +1,92 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; + +{ + + "document-rag" +: { + + "doc-limit":: 20, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local docLimit = self["doc-limit"]; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("document-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "document-rag", + "-p", + url.pulsar, + "--doc-limit", + std.toString(docLimit), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "document-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "document-embeddings" +: { + + "cpu-limit":: "1.0", + "cpu-reservation":: "0.5", + "memory-limit":: "512M", + "memory-reservation":: "512M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("document-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "document-embeddings", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "document-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/graph-rag.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/graph-rag.jsonnet new file mode 100644 index 00000000..718441ea --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/graph-rag.jsonnet @@ -0,0 +1,283 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "kg-extract-definitions" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-extract-definitions") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-definitions", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-extract-definitions", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-relationships" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-extract-relationships") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-relationships", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-extract-relationships", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-agent" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-extract-agent") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-agent", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-extract-agent", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-ontology" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "300M", + "memory-reservation":: "300M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-extract-ontology") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-ontology", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-extract-ontology", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "graph-rag" +: { + + concurrency:: 1, + "entity-limit":: 50, + "triple-limit":: 30, + "max-subgraph-size":: 400, + "max-path-length":: 2, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local entityLimit = self["entity-limit"]; + local tripleLimit = self["triple-limit"]; + local maxSubgraphSize = self["max-subgraph-size"]; + local maxPathLength = self["max-path-length"]; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("graph-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "graph-rag", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--entity-limit", + std.toString(entityLimit), + "--triple-limit", + std.toString(tripleLimit), + "--max-subgraph-size", + std.toString(maxSubgraphSize), + "--max-path-length", + std.toString(maxPathLength), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "graph-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "graph-embeddings" +: { + + "cpu-limit":: "1.0", + "cpu-reservation":: "0.5", + "memory-limit":: "512M", + "memory-reservation":: "512M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "graph-embeddings", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/librarian.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/librarian.jsonnet new file mode 100644 index 00000000..2ecda5a8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/librarian.jsonnet @@ -0,0 +1,58 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local garage = import "backends/garage.jsonnet"; +local cassandra = import "backends/cassandra.jsonnet"; + +{ + + "librarian" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "256M", + "memory-reservation":: "256M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("librarian") + .with_image(images.trustgraph_flow) + .with_command([ + "librarian", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + "--object-store-endpoint", + url.object_store, + "--object-store-access-key", + $.garage["access-key"], + "--object-store-secret-key", + $.garage["secret-key"], + "--object-store-region", + $.garage.region, + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "librarian", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +// Garage and Cassandra are used by the Librarian +} + garage + cassandra + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/mcp-server.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/mcp-server.jsonnet new file mode 100644 index 00000000..9bf7e3bd --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/mcp-server.jsonnet @@ -0,0 +1,54 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "mcp-server" +: { + + port:: 8000, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "256M", + "memory-reservation":: "256M", + + create:: function(engine) + + local port = self.port; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local envSecrets = engine.envSecrets("mcp-server-secret") + .with_env_var("MCP_SERVER_SECRET", "mcp-server-secret") + .with_env_var("GATEWAY_SECRET", "gateway-secret"); + + local container = + engine.container("mcp-server") + .with_image(images.trustgraph_mcp) + .with_command([ + "mcp-server", + "--port", + std.toString(port), + ]) + .with_env_var_secrets(envSecrets) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation) + .with_port(port, port, "mcp"); + + local containerSet = engine.containers( + "mcp-server", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(port, port, "mcp"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/prompt-overrides.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/prompt-overrides.jsonnet new file mode 100644 index 00000000..852ec09d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/prompt-overrides.jsonnet @@ -0,0 +1,24 @@ +local default_prompts = import "prompts/default-prompts.jsonnet"; + +{ + + with:: function(key, value) + if (key == "system-template") then + self + { + prompts +:: { + "system-template": value, + } + } + else + self + { + prompts +:: { + templates +:: { + [key] +:: { + prompt: value + } + } + } + }, + +} + default_prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/prompt-template.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/prompt-template.jsonnet new file mode 100644 index 00000000..3cd79d59 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/prompt-template.jsonnet @@ -0,0 +1,97 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "prompt" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("prompt") + .with_image(images.trustgraph_flow) + .with_command([ + "prompt-template", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "prompt", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "prompt-rag" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("prompt-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "prompt-template", + "-p", + url.pulsar, + "--id", + "prompt-rag", + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "prompt-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/rev-gateway.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/rev-gateway.jsonnet new file mode 100644 index 00000000..fbd9f77a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/rev-gateway.jsonnet @@ -0,0 +1,62 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "rev-gateway" +: { + + // Invalid, but at least means the rev-gateway won't connect to anything + // it shouldn't. + token:: "INVALID_TOKEN", + uri:: "wss://127.0.0.1/api/v1/relay?token=" + self.token, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "256M", + "memory-reservation":: "256M", + + create:: function(engine) + + local uri = self.uri; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local envSecrets = engine.envSecrets("rev-gateway-secret") + .with_env_var("REV_GATEWAY_SECRET", "rev-gateway-secret"); + + local container = + engine.container("api-gateway") + .with_image(images.trustgraph_flow) + .with_command([ + "rev-gateway", + "-p", + url.pulsar, + "--websocket-uri", + std.toString(uri), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation) + .with_port(8000, 8000, "metrics") + .with_port(port, port, "api"); + + local containerSet = engine.containers( + "api-gateway", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics") + .with_port(port, port, "api"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/structured-data.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/structured-data.jsonnet new file mode 100644 index 00000000..8b23372f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/structured-data.jsonnet @@ -0,0 +1,171 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "nlp-query" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("nlp-query") + .with_image(images.trustgraph_flow) + .with_command([ + "nlp-query", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "nlp-query", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "structured-query" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("structured-query") + .with_image(images.trustgraph_flow) + .with_command([ + "structured-query", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "structured-query", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "structured-diag" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "96M", + "memory-reservation":: "96M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("structured-diag") + .with_image(images.trustgraph_flow) + .with_command([ + "structured-diag", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "structured-diag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-objects" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-extract-objects") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-objects", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-extract-objects", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/trustgraph.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/trustgraph.jsonnet new file mode 100644 index 00000000..3cf12d26 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/core/trustgraph.jsonnet @@ -0,0 +1,473 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +local config_initialiser = import "configuration.jsonnet"; +local config = import "../runtime-config/trustgraph-config.jsonnet"; +local librarian = import "librarian.jsonnet"; +local mcp_server = import "mcp-server.jsonnet"; +local workbench = import "../ui/workbench-ui.jsonnet"; +local graphrag = import "graph-rag.jsonnet"; +local documentrag = import "document-rag.jsonnet"; +local prompt_template = import "prompt-template.jsonnet"; +local agent_manager = import "agent-manager-react.jsonnet"; +local structured_data = import "structured-data.jsonnet"; +local ddg = import "mcp/ddg-mcp-server.jsonnet"; + +// Helper to create a routing function for a target object +local route = function(target) + function(prefix, k, v) + local suffix = std.substr(k, std.length(prefix), std.length(k) - std.length(prefix)); + { [target] +: { [suffix]:: v } }; + +// Parameter prefix -> target object routing table +local routes = { + "prompt-rag-": route("prompt-rag"), + "prompt-": route("prompt"), + "text-completion-rag-": route("text-completion-rag"), + "text-completion-": route("text-completion"), + "embeddings-": route("embeddings"), + "api-gateway-": route("api-gateway"), + "chunk-": route("chunker"), + "graph-rag-": route("graph-rag"), + "graph-embeddings-": route("graph-embeddings"), + "kg-extract-definitions-": route("kg-extract-definitions"), + "kg-extract-relationships-": route("kg-extract-relationships"), + "kg-extract-agent-": route("kg-extract-agent"), + "kg-extract-ontology-": route("kg-extract-ontology"), + "kg-extract-objects-": route("kg-extract-objects"), + "garage-": route("garage"), + "config-svc-": route("config-svc"), + "pdf-decoder-": route("pdf-decoder"), + "mcp-tool-": route("mcp-tool"), + "mcp-server-": route("mcp-server"), + "metering-rag-": route("metering-rag"), + "metering-": route("metering"), + "kg-store-": route("kg-store"), + "kg-manager-": route("kg-manager"), + "librarian-": route("librarian"), + "agent-manager-": route("agent-manager"), + "document-rag-": route("document-rag"), + "document-embeddings-": route("document-embeddings"), + "rev-gateway-": route("rev-gateway"), + "nlp-query-": route("nlp-query"), + "structured-query-": route("structured-query"), + "structured-diag-": route("structured-diag"), + "init-trustgraph-": route("init-trustgraph"), +}; + +// Find longest matching prefix (most specific first) +local findRoute = function(k) + local prefixes = std.objectFields(routes); + local matching = std.filter(function(p) std.startsWith(k, p), prefixes); + local sorted = std.sort(matching, function(x) -std.length(x)); + if std.length(sorted) > 0 then sorted[0] else null; + +{ + + // Route parameters to appropriate internal objects based on prefix + with:: function(k, v) + local prefix = findRoute(k); + if prefix != null then + self + routes[prefix](prefix, k, v) + else + self + { [k]:: v }, + + "log-level":: "INFO", + + // Base objects with concurrency defaults (LLM/embeddings components merge into these) + "text-completion" +: { concurrency:: 1 }, + "text-completion-rag" +: { concurrency:: 1 }, + embeddings +: { concurrency:: 1 }, + + "api-gateway" +: { + + port:: 8088, + timeout:: 600, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "512M", + "memory-reservation":: "512M", + + create:: function(engine) + + local port = self.port; + local timeout = self.timeout; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local envSecrets = engine.envSecrets("gateway-secret") + .with_env_var("GATEWAY_SECRET", "gateway-secret"); + + local container = + engine.container("api-gateway") + .with_image(images.trustgraph_flow) + .with_command([ + "api-gateway", + "-p", + url.pulsar, + "--timeout", + std.toString(timeout), + "--port", + std.toString(port), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation) + .with_port(port, port, "api"); + + local containerSet = engine.containers( + "api-gateway", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics") + .with_port(port, port, "api"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "chunker" +: { + + size:: 2000, + overlap:: 50, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local size = self.size; + local overlap = self.overlap; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("chunker") + .with_image(images.trustgraph_flow) + .with_command([ + "chunker-recursive", + "-p", + url.pulsar, + "--chunk-size", + std.toString(size), + "--chunk-overlap", + std.toString(overlap), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "chunker", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "config-svc" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local cpuLimit = self["cpu-limit"]; + local cpuReservation = self["cpu-reservation"]; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("config-svc") + .with_image(images.trustgraph_flow) + .with_command([ + "config-svc", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(cpuLimit, memoryLimit) + .with_reservations(cpuReservation, memoryReservation); + + local containerSet = engine.containers( + "config-svc", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "pdf-decoder" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "512M", + "memory-reservation":: "512M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("pdf-decoder") + .with_image(images.trustgraph_flow) + .with_command([ + "pdf-decoder", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "pdf-decoder", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "mcp-tool" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("mcp-tool") + .with_image(images.trustgraph_flow) + .with_command([ + "mcp-tool", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "mcp-tool", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "metering" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("metering") + .with_image(images.trustgraph_flow) + .with_command([ + "metering", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "metering", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "metering-rag" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("metering-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "metering", + "-p", + url.pulsar, + "--id", + "metering-rag", + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "metering-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-store" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-store") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-store", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-store", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-manager" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-manager") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-manager", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-manager", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + librarian + mcp_server + workbench + graphrag + + documentrag + prompt_template + agent_manager + structured_data + + config_initialiser + config + + ddg + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/embeddings/embeddings-fastembed.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/embeddings/embeddings-fastembed.jsonnet new file mode 100644 index 00000000..37116de0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/embeddings/embeddings-fastembed.jsonnet @@ -0,0 +1,49 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/embeddings-fastembed.jsonnet"; + +{ + + "fastembed-models":: models, + + "embeddings-models" +:: $["fastembed-models"], + + embeddings +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local container = + engine.container("embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "embeddings-fastembed", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits("1.0", "400M") + .with_reservations("0.5", "400M"); + + local containerSet = engine.containers( + "embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/embeddings/embeddings-hf.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/embeddings/embeddings-hf.jsonnet new file mode 100644 index 00000000..492f05cb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/embeddings/embeddings-hf.jsonnet @@ -0,0 +1,47 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/embeddings-huggingface.jsonnet"; + +{ + + "huggingface-embeddings-models":: models, + + "embeddings-models" +:: $["huggingface-embeddings-models"], + + embeddings +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local container = + engine.container("embeddings") + .with_image(images.trustgraph_hf) + .with_command([ + "embeddings-hf", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + ]) + .with_limits("1.0", "400M") + .with_reservations("0.5", "400M"); + + local containerSet = engine.containers( + "embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/embeddings/embeddings-ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/embeddings/embeddings-ollama.jsonnet new file mode 100644 index 00000000..05cbed72 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/embeddings/embeddings-ollama.jsonnet @@ -0,0 +1,52 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local models = import "parameters/embeddings-ollama.jsonnet"; + +{ + + "ollama-url":: "${OLLAMA_HOST}", + + "ollama-models":: models, + + "embeddings-models" +:: $["ollama-models"], + + embeddings +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local container = + engine.container("embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "embeddings-ollama", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "-r", + $["ollama-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/engine/aks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/engine/aks-k8s.jsonnet new file mode 100644 index 00000000..07e050f2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/engine/aks-k8s.jsonnet @@ -0,0 +1,45 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "disk.csi.azure.com", + parameters: { + // Standard disks (spinning magnetic), Locally Redundant Storage + // Cheapest, basically + skuName: "Standard_LRS", + }, + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/engine/docker-compose.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/engine/docker-compose.jsonnet new file mode 100644 index 00000000..334b3517 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/engine/docker-compose.jsonnet @@ -0,0 +1,260 @@ +{ + + // Extract resources using the engine + package:: function(patterns) + std.foldl( + function(state, p) state + p.create(self), + std.objectValues(patterns), + {} + ), + + container:: function(name) + { + + local container = self, + + name:: name, + + with_image:: function(x) self + { image: x }, + + with_user:: function(x) self + { user: x }, + + with_group:: function(x) self + + if std.objectHas(container, "group_add") then + { group_add: container.group_add + [x] } + else + { group_add: [x] }, + + with_command:: function(x) self + { + command: + if std.isString(x) then + std.strReplace(x, "$", "$$") + else if std.isArray(x) then + std.map(function(s) std.strReplace(s, "$", "$$"), x) + else + x + }, + + with_entrypoint:: function(x) self + { entrypoint: x }, + + with_runtime:: function(x) self + { runtime: x }, + + with_privileged:: function(x) self + { privileged: x }, + + with_ipc:: function(x) self + { ipc: x }, + + with_capability:: function(x) self + + if std.objectHas(container, "capability") then + { cap_add: container.capability + x } + else + { cap_add: [x], }, + + with_environment:: function(x) self + + if std.objectHas(container, "environment") then + { environment: container.environment + x } + else + { environment: x, }, + + with_device:: function(hdev, cdev) self + + if std.objectHas(container, "devices") then + { devices: container.devices + [ "%s:%s" % [hdev, cdev] ] } + else + { devices: [ "%s:%s" % [hdev, cdev] ], }, + + with_limits:: function(c, m) self + { + deploy +: { resources +: { + limits: { cpus: c, memory: m } + } }, + }, + + with_reservations:: function(c, m) self + { + deploy +: { resources +: { + reservations: { cpus: c, memory: m } + } }, + }, + + with_volume_mount:: + function(vol, mnt) + self + { + volumes: + if std.objectHas(container, "volumes") then + container.volumes + [ + "%s:%s" % [vol.volid, mnt] + ] + else + [ + "%s:%s" % [vol.volid, mnt] + ] + }, + + with_bind_mount:: + function(src, dest) + self + { + volumes: + if std.objectHas(container, "volumes") then + container.volumes + [ + "%s:%s" % [src, dest] + ] + else + [ + "%s:%s" % [src, dest] + ] + }, + + with_port:: + function(src, dest, name) + self + { + ports: + if std.objectHas(container, "ports") then + container.ports + [ "%d:%d" % [src, dest] ] + else + [ "%d:%d" % [src, dest] ] + }, + + with_env_var_secrets:: + function(vars) + std.foldl( + function(obj, x) obj.with_environment( + { [x]: "${" + x + "}" } + ), + vars.variables, + self + ), + + restart: "on-failure:100", + + add:: function() { + services +: { + [container.name]: container, + } + } + + }, + + internalService:: function(containers) + { + + local service = self, + + name: containers.name, + + with_port:: function(src, dest, name) + self + { port: [src, dest] }, + + add:: function() { + } + + }, + + service:: function(containers) + { + + local service = self, + + name: containers.name, + + with_port:: function(src, dest, name) + self + { port: [src, dest] }, + + add:: function() { + } + + }, + + volume:: function(name) + { + + local volume = self, + + name: name, + + volid:: name, + + with_size:: function(size) self + { size: size }, + + add:: function() { + volumes +: { + [volume.name]: {} + } + } + + }, + + configVolume:: function(name, dir, parts) + { + + local volume = self, + + name: dir, + + volid:: "./" + dir, + + with_size:: function(size) self + { size: size }, + + add:: function() { + } + + }, + + secretVolume:: function(name, dir, parts) + { + + local volume = self, + + name: dir, + + volid:: dir, + + with_size:: function(size) self + { size: size }, + + add:: function() { + } + + }, + + envSecrets:: function(name) + { + + local volume = self, + + name: name, + + volid:: name, + + variables:: [], + + with_env_var:: + function(name, key) self + { + variables: super.variables + [name], + }, + + add:: function() { + } + + }, + + containers:: function(name, containers) + { + + local cont = self, + + name: name, + containers: containers, + + add:: function() std.foldl( + function(state, c) state + c.add(), + cont.containers, + {} + ), + + }, + + resources:: function(res) + std.foldl( + function(state, c) state + c.add(), + res, + {} + ), + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/engine/eks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/engine/eks-k8s.jsonnet new file mode 100644 index 00000000..3fc3f035 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/engine/eks-k8s.jsonnet @@ -0,0 +1,46 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "ebs.csi.aws.com", + parameters: { + type: "gp3", + encrypted: "true", + iops: "6000", + throughput: "400", + }, + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/engine/gcp-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/engine/gcp-k8s.jsonnet new file mode 100644 index 00000000..71792426 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/engine/gcp-k8s.jsonnet @@ -0,0 +1,44 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "pd.csi.storage.gke.io", + parameters: { + type: "pd-balanced", + "csi.storage.k8s.io/fstype": "ext4", + }, + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/engine/k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/engine/k8s.jsonnet new file mode 100644 index 00000000..2067f6ac --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/engine/k8s.jsonnet @@ -0,0 +1,393 @@ +{ + + container:: function(name) + { + + local container = self, + + name: name, + limits: {}, + reservations: {}, + ports: [], + volumes: [], + bindMounts: [], + groups: [], + environment: [], + + with_image:: function(x) self + { image: x }, + + with_user:: function(x) self + { user: x }, + + with_group:: function(x) self + { groups: super.groups + [x] }, + + with_privileged:: function(x) self + { privileged: x }, + + with_command:: function(x) self + { command: x }, + + with_entrypoint:: function(x) self + { entrypoint: x }, + + with_environment:: function(x) self + { + environment: super.environment + [ + { + name: v.key, value: v.value + } + for v in std.objectKeysValues(x) + ], + }, + + with_limits:: function(c, m) self + { limits: { cpu: c, memory: m } }, + + with_reservations:: + function(c, m) self + { reservations: { cpu: c, memory: m } }, + + with_volume_mount:: + function(vol, mnt) + self + { + volumes: super.volumes + [{ + volume: vol, mount: mnt + }] + }, + + with_bind_mount:: + function(src, dest) + local name = "bind-" + std.strReplace(std.strReplace(src, "/", "-"), ".", "-"); + self + { + bindMounts: super.bindMounts + [{ + name: name, src: src, dest: dest + }] + }, + + with_port:: + function(src, dest, name) self + { + ports: super.ports + [ + { src: src, dest: dest, name : name } + ] + }, + + with_env_var_secrets:: + function(vars) + std.foldl( + function(obj, x) obj + { + environment: super.environment + [{ + name: x, + valueFrom: { + secretKeyRef: { + name: vars.name, + key: vars.keyMap[x], + } + } + }] + }, + vars.variables, + self + ), + + add:: function() [ + + { + apiVersion: "apps/v1", + kind: "Deployment", + metadata: { + name: container.name, + namespace: "trustgraph", + labels: { + app: container.name + } + }, + spec: { + replicas: 1, + selector: { + matchLabels: { + app: container.name, + } + }, + template: { + metadata: { + labels: { + app: container.name, + } + }, + spec: { + containers: [ + { + name: container.name, + image: container.image, + + // FIXME: Make everything run as + // root. Needed to get filesystems + // to be accessible. There's a + // better way of doing this? + securityContext: { + runAsUser: 0, + runAsGroup: 0, + } + ( + if std.objectHas(container, "privileged") && container.privileged then + { privileged: true } + else {} + ), + + resources: { + requests: container.reservations, + limits: container.limits + }, + } + ( + if std.length(container.ports) > 0 then + { + ports: [ + { + hostPort: port.src, + containerPort: port.dest, + } + for port in container.ports + ] + } else + {}) + + + (if std.objectHas(container, "entrypoint") then + // Entrypoint is set - use command for entrypoint, args for command + (if std.isString(container.entrypoint) && container.entrypoint == "" then + { command: [] } + else if std.isArray(container.entrypoint) then + { command: container.entrypoint } + else + { command: [container.entrypoint] } + ) + (if std.objectHas(container, "command") then + { args: container.command } + else {}) + else if std.objectHas(container, "command") then + { command: container.command } + else {}) + + + (if std.length(container.environment) > 0 then + { + env: container.environment, + } + else {}) + + + (if std.length(container.volumes) > 0 || std.length(container.bindMounts) > 0 then + { + volumeMounts: [ + { + mountPath: vol.mount, + name: vol.volume.name, + } + for vol in container.volumes + ] + [ + { + mountPath: bm.dest, + name: bm.name, + } + for bm in container.bindMounts + ] + } + + else + {} + ) + ], + volumes: [ + vol.volume.volRef() + for vol in container.volumes + ] + [ + { + name: bm.name, + hostPath: { path: bm.src } + } + for bm in container.bindMounts + ] + } + ( + if std.length(container.groups) > 0 then + { securityContext: { supplementalGroups: container.groups } } + else {} + ) + }, + } + {} + + } + + ] + + }, + + // Just an alias + internalService:: self.service, + + service:: function(containers) + { + + local service = self, + + name: containers.name, + + ports: [], + + with_port:: + function(src, dest, name) + self + { + ports: super.ports + [ + { src: src, dest: dest, name: name } + ] + }, + + add:: function() [ + + { + + apiVersion: "v1", + kind: "Service", + metadata: { + name: service.name, + namespace: "trustgraph", + }, + spec: { + selector: { + app: service.name, + }, + ports: [ + { + port: port.src, + targetPort: port.dest, + name: port.name, + } + for port in service.ports + ], + } + } + ], + + }, + + volume:: function(name) + { + + local volume = self, + + name: name, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + { + apiVersion: "v1", + kind: "PersistentVolumeClaim", + metadata: { + name: volume.name, + namespace: "trustgraph", + }, + spec: { + storageClassName: "tg", + accessModes: [ "ReadWriteOnce" ], + resources: { + requests: { + storage: volume.size, + } + }, + } + } + ], + + volRef:: function() { + name: volume.name, + persistentVolumeClaim: { claimName: volume.name }, + } + + }, + + configVolume:: function(name, dir, parts) + { + + local volume = self, + + name: name, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + { + apiVersion: "v1", + kind: "ConfigMap", + metadata: { + name: volume.name, + namespace: "trustgraph", + }, + data: parts + }, + ], + + + volRef:: function() { + name: volume.name, + configMap: { name: volume.name }, + } + + }, + + secretVolume:: function(name, dir, parts) + { + + local volume = self, + + name: name, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + ], + + volRef:: function() { + name: volume.name, + secret: { secretName: volume.name }, + } + + }, + + envSecrets:: function(name) + { + + local volume = self, + + name: name, + + variables: [], + keyMap: {}, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + ], + + volRef:: function() { + name: volume.name, + secret: { secretName: volume.name }, + }, + + with_env_var:: + function(name, key) self + { + variables: super.variables + [name], + keyMap: super.keyMap + { [name]: key }, + }, + + }, + + containers:: function(name, containers) + { + + local cont = self, + + name: name, + containers: containers, + + add:: function() std.flattenArrays( + [ c.add() for c in cont.containers ] + ), + + }, + + resources:: function(res) + + std.flattenArrays( + [ c.add() for c in res ] + ), + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/engine/minikube-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/engine/minikube-k8s.jsonnet new file mode 100644 index 00000000..858b17ad --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/engine/minikube-k8s.jsonnet @@ -0,0 +1,115 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList, + + volume:: function(name) + { + local volume = self, + name: name, + with_size:: function(size) self + { size: size }, + add:: function() [ + { + apiVersion: "v1", + kind: "PersistentVolume", + metadata: { + name: volume.name, + }, + spec: { + accessModes: [ "ReadWriteOnce" ], + capacity: { + storage: volume.size, + }, + persistentVolumeReclaimPolicy: "Delete", + hostPath: { + path: "/data/pv-" + volume.name, + }, + } + }, + { + apiVersion: "v1", + kind: "PersistentVolumeClaim", + metadata: { + name: volume.name, + namespace: "trustgraph", + }, + spec: { + accessModes: [ "ReadWriteOnce" ], + resources: { + requests: { + storage: volume.size, + } + }, + } + } + ], + + volRef:: function() { + name: volume.name, + persistentVolumeClaim: { claimName: volume.name }, + } + + }, + + service:: function(containers) + { + local service = self, + name: containers.name, + ports: [], + with_port:: + function(src, dest, name) + self + { + ports: super.ports + [ + { src: src, dest: dest, name: name } + ] + }, + add:: function() [ + { + apiVersion: "v1", + kind: "Service", + metadata: { + name: service.name, + namespace: "trustgraph", + }, + spec: { + selector: { + app: service.name, + }, + type: "LoadBalancer", + ports: [ + { + port: port.src, + targetPort: port.dest, + name: port.name, + } + for port in service.ports + ], + } + } + ], + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/engine/noop.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/engine/noop.jsonnet new file mode 100644 index 00000000..1f384648 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/engine/noop.jsonnet @@ -0,0 +1,79 @@ +{ + + // Extract resources usnig the engine + package:: function(patterns) {}, + + container:: function(name) { + + with_image:: function(x) self + {}, + + with_user:: function(x) self + {}, + + with_command:: function(x) self + {}, + + with_runtime:: function(x) self + {}, + + with_privileged:: function(x) self + {}, + + with_ipc:: function(x) self + {}, + + with_capability:: function(x) self + {}, + + with_environment:: function(x) self + {}, + + with_device:: function(hdev, cdev) self + {}, + + with_limits:: function(c, m) self + {}, + + with_reservations:: function(c, m) self + {}, + + with_volume_mount:: self + {}, + + with_port:: function(src, dest, name) self + {}, + + with_env_var_secrets:: function(vars) self + {}, + + add:: function() {}, + }, + + internalService:: function(containers) { + with_port:: function(src, dest, name) self + {}, + add:: function() {}, + }, + + service:: function(containers) { + with_port:: function(src, dest, name) self + {}, + add:: function() {}, + }, + + volume:: function(name) { + with_size:: function(size) self + {}, + add:: function() {}, + }, + + configVolume:: function(name, dir, parts) { + add:: function() {}, + }, + + secretVolume:: function(name, dir, parts) { + add:: function() {}, + }, + + envSecrets:: function(name) { + with_env_var:: function(name, key) self + {}, + add:: function() {}, + }, + + containers:: function(name, containers) { + add:: function() {}, + }, + + resources:: function(res) + std.foldl( + function(state, c) state + c.add(), + res, + {} + ), + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/engine/ovh-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/engine/ovh-k8s.jsonnet new file mode 100644 index 00000000..15d57c83 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/engine/ovh-k8s.jsonnet @@ -0,0 +1,45 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "cinder.csi.openstack.org", + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", + parameters: { + availability: "nova", + fsType: "ext4", + type: "high-speed", + }, +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: [ns, sc] + resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/engine/scw-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/engine/scw-k8s.jsonnet new file mode 100644 index 00000000..eed23968 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/engine/scw-k8s.jsonnet @@ -0,0 +1,40 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "csi.scaleway.com", + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: [ns, sc] + resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/agent-extract.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/agent-extract.jsonnet new file mode 100644 index 00000000..4c5785c8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/agent-extract.jsonnet @@ -0,0 +1,35 @@ +// Agent-based extraction module +// Uses AI agents for more sophisticated knowledge extraction from text +// Leverages agent tools and reasoning for complex extraction tasks + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; + +{ + // No external interfaces - internal agent extraction service + "interfaces" +: { + }, + + // No configurable parameters for agent extraction + "parameters" +: { + }, + + // Flow-level processors for agent-based extraction + "flow" +: { + // Agent-based knowledge extraction processor + // Uses AI agents with tools to extract structured knowledge + "kg-extract-agent:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output knowledge triples + "entity-contexts": flow("entity-contexts-load:{id}"), // Entity context information + "agent-request": request("agent:{id}"), // Agent service requests + "agent-response": response("agent:{id}"), // Agent service responses + }, + }, + + // No blueprint-level processors needed + "blueprint" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/agent.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/agent.jsonnet new file mode 100644 index 00000000..fbb06695 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/agent.jsonnet @@ -0,0 +1,59 @@ +// Agent management module +// Provides AI agent orchestration and tool integration +// Manages agent conversations, tool calls, and response coordination +// Supports MCP tools, GraphRAG, and structured queries + +local helpers = import "helpers.jsonnet"; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +{ + // External interfaces for agent operations + "interfaces" +: { + "agent": request_response("agent:{id}"), // Main agent service interface + "mcp-tool": request_response("mcp-tool:{id}"), // MCP tool execution interface + }, + + // No configurable parameters for agent management + "parameters" +: { + }, + + // Flow-level processors for agent management + "flow" +: { + // Agent manager orchestrates agent conversations and tool usage + "agent-manager:{id}": { + // Agent communication channels + request: request("agent:{id}"), // Incoming agent requests + next: request("agent:{id}"), // Multi-turn conversation support + response: response("agent:{id}"), // Agent responses + + // LLM and prompt services + "text-completion-request": request("text-completion:{id}"), // LLM requests + "text-completion-response": response("text-completion:{id}"), // LLM responses + "prompt-request": request("prompt:{id}"), // Prompt processing + "prompt-response": response("prompt:{id}"), + + // Tool integrations + "mcp-tool-request": request("mcp-tool:{id}"), // MCP tool calls + "mcp-tool-response": response("mcp-tool:{id}"), + "graph-rag-request": request("graph-rag:{id}"), // GraphRAG queries + "graph-rag-response": response("graph-rag:{id}"), + "structured-query-request": request("structured-query:{id}"), // Structured data queries + "structured-query-response": response("structured-query:{id}"), + }, + + // MCP tool executor for agent tool usage + "mcp-tool:{id}": { + request: request("mcp-tool:{id}"), // Tool invocation requests + response: response("mcp-tool:{id}"), // Tool execution results + "text-completion-request": request("text-completion:{id}"), // LLM for tool reasoning + "text-completion-response": response("text-completion:{id}"), + }, + + }, + + // Blueprint-level processors for agent-related services + "blueprint" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/documentrag.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/documentrag.jsonnet new file mode 100644 index 00000000..32597c79 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/documentrag.jsonnet @@ -0,0 +1,100 @@ +// Document RAG (Retrieval Augmented Generation) module +// Implements document-based RAG using chunk embeddings +// Provides semantic search and context-aware question answering +// Supports MCP (Model Context Protocol) tool integration + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; +local llm_parameters = import "llm-parameters.jsonnet"; + +{ + // External interfaces for document RAG functionality + "interfaces" +: { + // Document embedding storage and retrieval + "document-embeddings-store": flow("document-embeddings-store:{id}"), // Embedding storage stream + "document-rag": request_response("document-rag:{id}"), // Main document RAG interface + "document-embeddings": request_response("document-embeddings:{id}"), // Document embedding queries + + // Supporting services + "embeddings": request_response("embeddings:{id}"), // General embedding service + "prompt": request_response("prompt:{id}"), // Prompt processing + "mcp-tool": request_response("mcp-tool:{id}"), // MCP tool integration + "text-completion": request_response("text-completion:{id}"), // LLM text completion + }, + + // Parameters that can be configured for this flow + "parameters" +: llm_parameters, + + // Flow-level processors for document embedding and storage + "flow" +: { + "document-embeddings:{id}": { + input: flow("chunk-load:{id}"), + output: flow("document-embeddings-store:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + }, + "de-write:{id}": { + input: flow("document-embeddings-store:{id}"), + }, + "text-completion:{id}": { + request: request("text-completion:{id}"), + response: response("text-completion:{id}"), + model: "{llm-model}", + }, + "text-completion-rag:{id}": { + request: request("text-completion-rag:{id}"), + response: response("text-completion-rag:{id}"), + model: "{llm-rag-model}", + }, + "embeddings:{id}": { + request: request("embeddings:{id}"), + response: response("embeddings:{id}"), + model: "{embeddings-model}", + }, + "document-rag:{id}": { + request: request("document-rag:{id}"), + response: response("document-rag:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + "prompt-request": request("prompt-rag:{id}"), + "prompt-response": response("prompt-rag:{id}"), + "document-embeddings-request": request("document-embeddings:{id}"), + "document-embeddings-response": response("document-embeddings:{id}"), + }, + "de-query:{id}": { + request: request("document-embeddings:{id}"), + response: response("document-embeddings:{id}"), + }, + "prompt:{id}": { + request: request("prompt:{id}"), + response: response("prompt:{id}"), + "text-completion-request": request("text-completion:{id}"), + "text-completion-response": response("text-completion:{id}"), + }, + "prompt-rag:{id}": { + request: request("prompt-rag:{id}"), + response: response("prompt-rag:{id}"), + "text-completion-request": request("text-completion-rag:{id}"), + "text-completion-response": response("text-completion-rag:{id}"), + }, + "mcp-tool:{id}": { + request: request("mcp-tool:{id}"), + response: response("mcp-tool:{id}"), + "text-completion-request": request("text-completion:{id}"), + "text-completion-response": response("text-completion:{id}"), + }, + "metering:{id}": { + input: response("text-completion:{id}"), + }, + "metering-rag:{id}": { + input: response("text-completion-rag:{id}"), + }, + }, + + // Blueprint-level processors for document RAG operations + "blueprint" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/flow-blueprints.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/flow-blueprints.jsonnet new file mode 100644 index 00000000..abeb11ba --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/flow-blueprints.jsonnet @@ -0,0 +1,105 @@ +// TrustGraph Flow Blueprints Configuration +// Defines different flow combinations for various use cases +// Each flow blueprint combines multiple functional modules to create complete processing pipelines +// +// Available modules: +// - graphrag: Graph-based RAG with knowledge graphs +// - documentrag: Document-based RAG with chunk embeddings +// - structured: Structured data processing and NLP queries +// - agent: AI agent orchestration and tool integration +// - load: Document loading and preprocessing +// - kg-base: Basic knowledge extraction from text +// - agent-extract: Agent-based knowledge extraction +// - kgcore: Knowledge graph core storage + +// Import all the modular flow components +local graphrag_part = import "graphrag.jsonnet"; +local kg_base_part = import "kg-base.jsonnet"; +local onto_base_part = import "onto-base.jsonnet"; +local agent_extract_part = import "agent-extract.jsonnet"; +local structured_part = import "structured.jsonnet"; +local documentrag_part = import "documentrag.jsonnet"; +local agent_part = import "agent.jsonnet"; +local load_part = import "load.jsonnet"; +local kgcore_part = import "kgcore.jsonnet"; + +{ + + // Complete TrustGraph system with all capabilities + // Includes GraphRAG, DocumentRAG, structured data processing, and knowledge cores + "everything": { + description: "GraphRAG, DocumentRAG, structured data + knowledge cores", + tags: [ + "document-rag", "graph-rag", "knowledge-extraction", + "structured-data", "kgcore" + ], + } + + graphrag_part + documentrag_part + agent_part + load_part + + kg_base_part + structured_part, + + // Dual RAG system without knowledge core creation + // Combines both document and graph-based retrieval + "document-rag+graph-rag": { + description: "Supports GraphRAG and document RAG, no core creation", + tags: ["document-rag", "graph-rag", "knowledge-extraction"], + } + + graphrag_part + documentrag_part + agent_part + load_part + kg_base_part, + + // Graph-based RAG only + // Uses knowledge graphs for context-aware question answering + "graph-rag": { + description: "GraphRAG only", + tags: ["graph-rag", "knowledge-extraction"], + } + + graphrag_part + agent_part + load_part + kg_base_part, + + // Graph-based RAG only + // Uses knowledge graphs for context-aware question answering + "onto-rag": { + description: "Ontology RAG only", + tags: ["graph-rag", "knowledge-extraction"], + } + + graphrag_part + agent_part + load_part + onto_base_part, + + // Document-based RAG only + // Uses document embeddings for semantic search and answers + "document-rag": { + description: "DocumentRAG only", + tags: ["document-rag"], + } + + documentrag_part + load_part, + + // Full RAG system with knowledge core creation + // Includes both RAG types plus persistent knowledge storage + "document-rag+graph-rag+kgcore": { + description: "GraphRAG + DocumentRAG + knowledge core creation", + tags: ["document-rag", "graph-rag", "knowledge-extraction"], + } + + graphrag_part + documentrag_part + agent_part + load_part + + kgcore_part + kg_base_part, + + // GraphRAG with advanced agent-based extraction + // Uses AI agents for sophisticated knowledge extraction + "graph-rag+agent-extract": { + description: "GraphRAG + agent extract", + tags: ["graph-rag", "knowledge-extraction", "agent-extract"], + } + + graphrag_part + agent_part + load_part + agent_extract_part, + + // GraphRAG with structured data processing + // Combines knowledge graphs with structured data queries + "graph-rag+structured-data": { + description: "GraphRAG + structured data", + tags: ["graph-rag", "knowledge-extraction", "structured-data"], + } + + graphrag_part + agent_part + load_part + structured_part, + + // Structured data processing only + // Handles structured data extraction and NLP queries + "structured-data": { + description: "Structured data only", + tags: ["knowledge-extraction", "structured-data"], + } + + agent_part + load_part + structured_part, + +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/graphrag.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/graphrag.jsonnet new file mode 100644 index 00000000..32ce5cf0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/graphrag.jsonnet @@ -0,0 +1,107 @@ +// GraphRAG flow configuration module +// Implements graph-based retrieval augmented generation (GraphRAG) functionality +// Handles knowledge graph storage, embeddings, and graph-based question answering + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; +local llm_parameters = import "llm-parameters.jsonnet"; + +{ + // External interfaces exposed by the GraphRAG flow + "interfaces" +: { + // Data ingestion interfaces for graph construction + "entity-contexts-load": flow("entity-contexts-load:{id}"), // Entity context data stream + "triples-store": flow("triples-store:{id}"), // RDF triples storage stream + "graph-embeddings-store": flow("graph-embeddings-store:{id}"), // Graph embedding storage + + // Query interfaces for graph-based operations + "graph-rag": request_response("graph-rag:{id}"), // Main GraphRAG query interface + "triples": request_response("triples:{id}"), // Triple store queries + "graph-embeddings": request_response("graph-embeddings:{id}"), // Graph embedding queries + + // Supporting services + "embeddings": request_response("embeddings:{id}"), // General embedding service + "prompt": request_response("prompt:{id}"), // Prompt processing service + "text-completion": request_response("text-completion:{id}"), // LLM text completion + }, + + + // Parameters that can be configured for this flow + "parameters" +: llm_parameters, + + // Flow-level processors - handle data streams for a specific flow instance + "flow" +: { + "graph-embeddings:{id}": { + input: flow("entity-contexts-load:{id}"), + output: flow("graph-embeddings-store:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + }, + "triples-write:{id}": { + input: flow("triples-store:{id}"), + }, + "ge-write:{id}": { + input: flow("graph-embeddings-store:{id}"), + }, + "text-completion:{id}": { + request: request("text-completion:{id}"), + response: response("text-completion:{id}"), + model: "{llm-model}", + }, + "text-completion-rag:{id}": { + request: request("text-completion-rag:{id}"), + response: response("text-completion-rag:{id}"), + model: "{llm-rag-model}", + }, + "embeddings:{id}": { + request: request("embeddings:{id}"), + response: response("embeddings:{id}"), + model: "{embeddings-model}", + }, + "graph-rag:{id}": { + request: request("graph-rag:{id}"), + response: response("graph-rag:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + "prompt-request": request("prompt-rag:{id}"), + "prompt-response": response("prompt-rag:{id}"), + "graph-embeddings-request": request("graph-embeddings:{id}"), + "graph-embeddings-response": response("graph-embeddings:{id}"), + "triples-request": request("triples:{id}"), + "triples-response": response("triples:{id}"), + }, + "triples-query:{id}": { + request: request("triples:{id}"), + response: response("triples:{id}"), + }, + "ge-query:{id}": { + request: request("graph-embeddings:{id}"), + response: response("graph-embeddings:{id}"), + }, + "prompt:{id}": { + request: request("prompt:{id}"), + response: response("prompt:{id}"), + "text-completion-request": request("text-completion:{id}"), + "text-completion-response": response("text-completion:{id}"), + }, + "prompt-rag:{id}": { + request: request("prompt-rag:{id}"), + response: response("prompt-rag:{id}"), + "text-completion-request": request("text-completion-rag:{id}"), + "text-completion-response": response("text-completion-rag:{id}"), + }, + "metering:{id}": { + input: response("text-completion:{id}"), + }, + "metering-rag:{id}": { + input: response("text-completion-rag:{id}"), + }, + }, + + // Blueprint-level processors - shared across all flow instances of this blueprint + "blueprint" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/helpers.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/helpers.jsonnet new file mode 100644 index 00000000..eabb3bf4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/helpers.jsonnet @@ -0,0 +1,29 @@ +// Helper functions for flow configuration +// Provides utility functions for constructing flow, request, and response URIs +// used throughout the TrustGraph flow configuration system + +// Creates a persistent flow URI for data streams +// Persistent flows retain messages until consumed +local flow(x) = "persistent://tg/flow/" + x; + +// Creates a non-persistent request URI for request-response patterns +// Non-persistent means messages are not retained if no consumer is present +local request(x) = "non-persistent://tg/request/" + x; + +// Creates a non-persistent response URI for request-response patterns +local response(x) = "non-persistent://tg/response/" + x; + +// Creates a request-response pair for bidirectional communication +// Returns an object with both request and response URIs +local request_response(x) = { + request: request(x), + response: response(x), +}; + +// Export all helper functions for use in other modules +{ + flow: flow, + request: request, + response: response, + request_response: request_response, +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/kg-base.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/kg-base.jsonnet new file mode 100644 index 00000000..144513b7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/kg-base.jsonnet @@ -0,0 +1,44 @@ +// Knowledge Graph Base extraction module +// Provides basic knowledge extraction capabilities from text chunks +// Extracts entity definitions and relationships using prompt-based processing + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; + +{ + // No external interfaces - this module provides internal extraction services + "interfaces" +: { + }, + + // No configurable parameters for basic KG extraction + "parameters" +: { + }, + + // Flow-level processors for knowledge extraction + "flow" +: { + // Extracts entity definitions from text chunks + // Identifies and defines key entities mentioned in the text + "kg-extract-definitions:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output definition triples + "entity-contexts": flow("entity-contexts-load:{id}"), // Entity context information + "prompt-request": request("prompt:{id}"), // Definition extraction prompts + "prompt-response": response("prompt:{id}"), + }, + + // Extracts relationships between entities + // Identifies how entities are connected and interact + "kg-extract-relationships:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output relationship triples + "prompt-request": request("prompt:{id}"), // Relationship extraction prompts + "prompt-response": response("prompt:{id}"), + }, + }, + + // No blueprint-level processors needed + "blueprint" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/kgcore.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/kgcore.jsonnet new file mode 100644 index 00000000..bfc01ada --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/kgcore.jsonnet @@ -0,0 +1,31 @@ +// Knowledge Graph Core storage module +// Handles persistent storage of knowledge graph data +// Consolidates triples and graph embeddings into permanent storage +// Creates the core knowledge base for long-term use + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; + +{ + // No external interfaces - internal storage service + "interfaces" +: { + }, + + // No configurable parameters for core storage + "parameters" +: { + }, + + // Flow-level processors for knowledge graph storage + "flow" +: { + // Knowledge graph store consolidates extracted knowledge + // Takes processed triples and embeddings and stores them permanently + "kg-store:{id}": { + "triples-input": flow("triples-store:{id}"), // Input RDF triples stream + "graph-embeddings-input": flow("graph-embeddings-store:{id}"), // Input graph embeddings + }, + }, + + // No blueprint-level processors needed + "blueprint" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/llm-parameters.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/llm-parameters.jsonnet new file mode 100644 index 00000000..a99f72ae --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/llm-parameters.jsonnet @@ -0,0 +1,59 @@ +{ + + // LLM model selection for normal LLM + "llm-model": { + "type": "llm-model", + "description": "LLM model", + "order": 1, + "advanced": false, + }, + + // LLM model for RAG operations + "llm-rag-model": { + "type": "llm-model", + "description": "LLM model for RAG", + "order": 2, + "advanced": true, + "controlled-by": "llm-model", + }, + + // LLM model selection for normal LLM + "llm-temperature": { + "type": "llm-temperature", + "description": "LLM temperature", + "order": 3, + "advanced": true, + }, + + // LLM model selection for normal LLM + "llm-rag-temperature": { + "type": "llm-temperature", + "description": "LLM temperature for RAG", + "order": 4, + "advanced": true, + }, + + "embeddings-model": { + "type": "embeddings-model", + "description": "Embeddings model", + "order": 5, + "advanced": true, + }, + + // LLM model selection for normal LLM + "chunk-size": { + "type": "chunk-size", + "description": "Chunk size", + "order": 6, + "advanced": true, + }, + + // LLM model selection for normal LLM + "chunk-overlap": { + "type": "chunk-overlap", + "description": "Chunk overlap", + "order": 7, + "advanced": true, + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/load.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/load.jsonnet new file mode 100644 index 00000000..00328b7f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/load.jsonnet @@ -0,0 +1,50 @@ +// Document loading and preprocessing module +// Handles document ingestion, format conversion, and chunking +// Converts PDFs to text and splits documents into processable chunks + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +{ + + // External interfaces for document loading + "interfaces" +: { + "document-load": flow("document-load:{id}"), // Raw document input stream + "text-load": flow("text-document-load:{id}"), // Text document stream + "embeddings": request_response("embeddings:{id}"), // Embedding service for chunks + }, + + // No configurable parameters for document loading + "parameters" +: { + }, + + // Flow-level processors for document preprocessing + "flow" +: { + // PDF decoder converts PDF documents to text + "pdf-decoder:{id}": { + input: flow("document-load:{id}"), // Raw PDF input + output: flow("text-document-load:{id}"), // Extracted text output + }, + + // Chunker splits documents into smaller, processable pieces + "chunker:{id}": { + input: flow("text-document-load:{id}"), // Full text documents + output: flow("chunk-load:{id}"), // Document chunks for processing + "chunk-size": "{chunk-size}", // Chunk size + "chunk-overlap": "{chunk-overlap}", // Overlap between chunks + }, + // Embedding service for converting text chunks to vectors + "embeddings:{id}": { + request: request("embeddings:{id}"), // Embedding requests + response: response("embeddings:{id}"), // Embedding responses + model: "{embeddings-model}", + }, + }, + + // Blueprint-level processors for document loading services + "blueprint" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/onto-base.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/onto-base.jsonnet new file mode 100644 index 00000000..d11daf64 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/onto-base.jsonnet @@ -0,0 +1,39 @@ +// Knowledge Graph Base extraction module +// Provides basic knowledge extraction capabilities from text chunks +// Extracts entity definitions and relationships using prompt-based processing + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; + +{ + // No external interfaces - this module provides internal extraction services + "interfaces" +: { + }, + + // No configurable parameters for basic KG extraction + "parameters" +: { + }, + + // Flow-level processors for knowledge extraction + "flow" +: { + // Extracts using ontology definitions + "kg-extract-ontology:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output triples + "entity-contexts": flow("entity-contexts-load:{id}"), // Entity context information + "prompt-request": request("prompt:{id}"), // Definition + // extraction prompts + "prompt-response": response("prompt:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + }, + + }, + + // No blueprint-level processors needed + "blueprint" +: { + } +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/ontorag.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/ontorag.jsonnet new file mode 100644 index 00000000..32ce5cf0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/ontorag.jsonnet @@ -0,0 +1,107 @@ +// GraphRAG flow configuration module +// Implements graph-based retrieval augmented generation (GraphRAG) functionality +// Handles knowledge graph storage, embeddings, and graph-based question answering + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; +local llm_parameters = import "llm-parameters.jsonnet"; + +{ + // External interfaces exposed by the GraphRAG flow + "interfaces" +: { + // Data ingestion interfaces for graph construction + "entity-contexts-load": flow("entity-contexts-load:{id}"), // Entity context data stream + "triples-store": flow("triples-store:{id}"), // RDF triples storage stream + "graph-embeddings-store": flow("graph-embeddings-store:{id}"), // Graph embedding storage + + // Query interfaces for graph-based operations + "graph-rag": request_response("graph-rag:{id}"), // Main GraphRAG query interface + "triples": request_response("triples:{id}"), // Triple store queries + "graph-embeddings": request_response("graph-embeddings:{id}"), // Graph embedding queries + + // Supporting services + "embeddings": request_response("embeddings:{id}"), // General embedding service + "prompt": request_response("prompt:{id}"), // Prompt processing service + "text-completion": request_response("text-completion:{id}"), // LLM text completion + }, + + + // Parameters that can be configured for this flow + "parameters" +: llm_parameters, + + // Flow-level processors - handle data streams for a specific flow instance + "flow" +: { + "graph-embeddings:{id}": { + input: flow("entity-contexts-load:{id}"), + output: flow("graph-embeddings-store:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + }, + "triples-write:{id}": { + input: flow("triples-store:{id}"), + }, + "ge-write:{id}": { + input: flow("graph-embeddings-store:{id}"), + }, + "text-completion:{id}": { + request: request("text-completion:{id}"), + response: response("text-completion:{id}"), + model: "{llm-model}", + }, + "text-completion-rag:{id}": { + request: request("text-completion-rag:{id}"), + response: response("text-completion-rag:{id}"), + model: "{llm-rag-model}", + }, + "embeddings:{id}": { + request: request("embeddings:{id}"), + response: response("embeddings:{id}"), + model: "{embeddings-model}", + }, + "graph-rag:{id}": { + request: request("graph-rag:{id}"), + response: response("graph-rag:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + "prompt-request": request("prompt-rag:{id}"), + "prompt-response": response("prompt-rag:{id}"), + "graph-embeddings-request": request("graph-embeddings:{id}"), + "graph-embeddings-response": response("graph-embeddings:{id}"), + "triples-request": request("triples:{id}"), + "triples-response": response("triples:{id}"), + }, + "triples-query:{id}": { + request: request("triples:{id}"), + response: response("triples:{id}"), + }, + "ge-query:{id}": { + request: request("graph-embeddings:{id}"), + response: response("graph-embeddings:{id}"), + }, + "prompt:{id}": { + request: request("prompt:{id}"), + response: response("prompt:{id}"), + "text-completion-request": request("text-completion:{id}"), + "text-completion-response": response("text-completion:{id}"), + }, + "prompt-rag:{id}": { + request: request("prompt-rag:{id}"), + response: response("prompt-rag:{id}"), + "text-completion-request": request("text-completion-rag:{id}"), + "text-completion-response": response("text-completion-rag:{id}"), + }, + "metering:{id}": { + input: response("text-completion:{id}"), + }, + "metering-rag:{id}": { + input: response("text-completion-rag:{id}"), + }, + }, + + // Blueprint-level processors - shared across all flow instances of this blueprint + "blueprint" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/structured.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/structured.jsonnet new file mode 100644 index 00000000..d56027ca --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/flows/structured.jsonnet @@ -0,0 +1,108 @@ +// Structured data processing module +// Handles extraction and querying of structured data objects +// Provides natural language to GraphQL query capabilities +// Supports structured data storage and retrieval + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; +local llm_parameters = import "llm-parameters.jsonnet"; + +{ + // External interfaces for structured data operations + "interfaces" +: { + // Supporting services + "embeddings": request_response("embeddings:{id}"), // Embedding service + "prompt": request_response("prompt:{id}"), // Prompt processing + "text-completion": request_response("text-completion:{id}"), // LLM completion + + // Structured data storage and querying + "objects-store": flow("objects-store:{id}"), // Object storage stream + "objects": request_response("objects:{id}"), // Object query service + + // Query interfaces + "nlp-query": request_response("nlp-query:{id}"), // NLP to GraphQL translation + "structured-query": request_response("structured-query:{id}"), // Structured query execution + "structured-diag": request_response("structured-diag:{id}"), // Query diagnostics + }, + + + // Parameters that can be configured for this flow + "parameters" +: llm_parameters, + + // Flow-level processors for structured data extraction + "flow" +: { + "kg-extract-objects:{id}": { + input: flow("chunk-load:{id}"), + output: flow("objects-store:{id}"), + "entity-contexts": flow("entity-contexts-load:{id}"), + "prompt-request": request("prompt:{id}"), + "prompt-response": response("prompt:{id}"), + }, + "objects-write:{id}": { + input: flow("objects-store:{id}"), + }, + "text-completion:{id}": { + request: request("text-completion:{id}"), + response: response("text-completion:{id}"), + model: "{llm-model}", + }, + "text-completion-rag:{id}": { + request: request("text-completion-rag:{id}"), + response: response("text-completion-rag:{id}"), + model: "{llm-rag-model}", + }, + "objects-query:{id}": { + request: request("objects:{id}"), + response: response("objects:{id}"), + }, + "nlp-query:{id}": { + request: request("nlp-query:{id}"), + response: response("nlp-query:{id}"), + "prompt-request": request("prompt-rag:{id}"), + "prompt-response": response("prompt-rag:{id}"), + }, + "structured-query:{id}": { + request: request("structured-query:{id}"), + response: response("structured-query:{id}"), + "nlp-query-request": request("nlp-query:{id}"), + "nlp-query-response": response("nlp-query:{id}"), + "objects-query-request": request("objects:{id}"), + "objects-query-response": response("objects:{id}"), + }, + "structured-diag:{id}": { + request: request("structured-diag:{id}"), + response: response("structured-diag:{id}"), + "prompt-request": request("prompt:{id}"), + "prompt-response": response("prompt:{id}"), + }, + "embeddings:{id}": { + request: request("embeddings:{id}"), + response: response("embeddings:{id}"), + model: "{embeddings-model}", + }, + "prompt:{id}": { + request: request("prompt:{id}"), + response: response("prompt:{id}"), + "text-completion-request": request("text-completion:{id}"), + "text-completion-response": response("text-completion:{id}"), + }, + "prompt-rag:{id}": { + request: request("prompt-rag:{id}"), + response: response("prompt-rag:{id}"), + "text-completion-request": request("text-completion-rag:{id}"), + "text-completion-response": response("text-completion-rag:{id}"), + }, + "metering:{id}": { + input: response("text-completion:{id}"), + }, + "metering-rag:{id}": { + input: response("text-completion-rag:{id}"), + }, + }, + // Blueprint-level processors for structured data operations + "blueprint" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/azure-openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/azure-openai.jsonnet new file mode 100644 index 00000000..bee0fe47 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/azure-openai.jsonnet @@ -0,0 +1,112 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/azure-openai.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["azure-openai-" + key]:: value, + }, + +// Strategy is to specify the model with the AZURE_MODEL environment +// variable. This isn't something that can just be specified dynamically, +// it has to match what was provisioned in Azure. + + "azure-openai-max-output-tokens":: 4192, + "azure-openai-temperature":: 0.0, + "azure-openai-models":: models, + + "llm-models" +:: $["azure-openai-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-openai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_MODEL", "azure-model") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure-openai", + "-p", + url.pulsar, + "-x", + std.toString($["azure-openai-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-openai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_MODEL", "azure-model") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure-openai", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["azure-openai-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/azure.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/azure.jsonnet new file mode 100644 index 00000000..f4db7500 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/azure.jsonnet @@ -0,0 +1,106 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/azure.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["azure-" + key]:: value, + }, + + "azure-max-output-tokens":: 4096, + "azure-temperature":: 0.0, + "azure-models":: models, + + "llm-models" +:: $["azure-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-ai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure", + "-p", + url.pulsar, + "-x", + std.toString($["azure-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-ai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["azure-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/bedrock.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/bedrock.jsonnet new file mode 100644 index 00000000..0db1475b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/bedrock.jsonnet @@ -0,0 +1,104 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/bedrock.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["bedrock-" + key]:: value, + }, + + "bedrock-max-output-tokens":: 4096, + "bedrock-temperature":: 0.0, + "bedrock-models":: models, + + "llm-models" +:: $["bedrock-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("bedrock-credentials") + .with_env_var("AWS_ACCESS_KEY_ID", "aws-id-key") + .with_env_var("AWS_SECRET_ACCESS_KEY", "aws-secret") + .with_env_var("AWS_DEFAULT_REGION", "aws-region"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_bedrock) + .with_command([ + "text-completion-bedrock", + "-p", + url.pulsar, + "-x", + std.toString($["bedrock-max-output-tokens"]), + "-t", + "%0.3f" % $["bedrock-temperature"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("bedrock-credentials") + .with_env_var("AWS_ACCESS_KEY_ID", "aws-id-key") + .with_env_var("AWS_SECRET_ACCESS_KEY", "aws-secret") + .with_env_var("AWS_DEFAULT_REGION", "aws-region"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_bedrock) + .with_command([ + "text-completion-bedrock", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["bedrock-max-output-tokens"]), + "-t", + "%0.3f" % $["bedrock-temperature"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/claude.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/claude.jsonnet new file mode 100644 index 00000000..f3125e96 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/claude.jsonnet @@ -0,0 +1,104 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/claude.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["claude-" + key]:: value, + }, + + "claude-max-output-tokens":: 4096, + "claude-temperature":: 0.0, + "claude-models":: models, + + "llm-models" +:: $["claude-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("claude-credentials") + .with_env_var("CLAUDE_KEY", "claude-key"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-claude", + "-p", + url.pulsar, + "-x", + std.toString($["claude-max-output-tokens"]), + "-t", + "%0.3f" % $["claude-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("claude-credentials") + .with_env_var("CLAUDE_KEY", "claude-key"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-claude", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["claude-max-output-tokens"]), + "-t", + "%0.3f" % $["claude-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/cohere.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/cohere.jsonnet new file mode 100644 index 00000000..6517f7a0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/cohere.jsonnet @@ -0,0 +1,97 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/cohere.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["cohere-" + key]:: value, + }, + + "cohere-temperature":: 0.0, + "cohere-models":: models, + + "llm-models" +:: $["cohere-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("cohere-credentials") + .with_env_var("COHERE_KEY", "cohere-key"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-cohere", + "-p", + url.pulsar, + "-t", + "%0.3f" % $["cohere-temperature"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("cohere-credentials") + .with_env_var("COHERE_KEY", "cohere-key"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-cohere", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-t", + "%0.3f" % $["cohere-temperature"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/googleaistudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/googleaistudio.jsonnet new file mode 100644 index 00000000..f7e8d578 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/googleaistudio.jsonnet @@ -0,0 +1,104 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/googleaistudio.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["googleaistudio-" + key]:: value, + }, + + "googleaistudio-max-output-tokens":: 4096, + "googleaistudio-temperature":: 0.0, + "googleaistudio-models":: models, + + "llm-models" +:: $["googleaistudio-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("googleaistudio-credentials") + .with_env_var("GOOGLE_AI_STUDIO_KEY", "googleaistudio-key"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-googleaistudio", + "-p", + url.pulsar, + "-x", + std.toString($["googleaistudio-max-output-tokens"]), + "-t", + "%0.3f" % $["googleaistudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("googleaistudio-credentials") + .with_env_var("GOOGLE_AI_STUDIO_KEY", "googleaistudio-key"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-googleaistudio", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["googleaistudio-max-output-tokens"]), + "-t", + "%0.3f" % $["googleaistudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/llamafile.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/llamafile.jsonnet new file mode 100644 index 00000000..f3bee2cc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/llamafile.jsonnet @@ -0,0 +1,94 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/slm.jsonnet"; +local models = import "parameters/llamafile.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["llamafile-" + key]:: value, + }, + + "llamafile-models":: models, + + "llm-models" +:: $["llamafile-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("llamafile-credentials") + .with_env_var("LLAMAFILE_URL", "llamafile-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-llamafile", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("llamafile-credentials") + .with_env_var("LLAMAFILE_URL", "llamafile-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-llamafile", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/lmstudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/lmstudio.jsonnet new file mode 100644 index 00000000..4cf78caa --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/lmstudio.jsonnet @@ -0,0 +1,104 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/lmstudio.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["lmstudio-" + key]:: value, + }, + + "lmstudio-max-output-tokens":: 4096, + "lmstudio-temperature":: 0.0, + "lmstudio-models":: models, + + "llm-models" +:: $["lmstudio-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("lmstudio-credentials") + .with_env_var("LMSTUDIO_URL", "lmstudio-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-lmstudio", + "-p", + url.pulsar, + "-x", + std.toString($["lmstudio-max-output-tokens"]), + "-t", + "%0.3f" % $["lmstudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("lmstudio-credentials") + .with_env_var("LMSTUDIO_URL", "lmstudio-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-lmstudio", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["lmstudio-max-output-tokens"]), + "-t", + "%0.3f" % $["lmstudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/mistral.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/mistral.jsonnet new file mode 100644 index 00000000..2fb8c562 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/mistral.jsonnet @@ -0,0 +1,104 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/mistral.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["mistral-" + key]:: value, + }, + + "mistral-max-output-tokens":: 4096, + "mistral-temperature":: 0.0, + "mistral-models":: models, + + "llm-models" +:: $["mistral-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("mistral-credentials") + .with_env_var("MISTRAL_TOKEN", "mistral-token"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-mistral", + "-p", + url.pulsar, + "-x", + std.toString($["mistral-max-output-tokens"]), + "-t", + "%0.3f" % $["mistral-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("mistral-credentials") + .with_env_var("MISTRAL_TOKEN", "mistral-token"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-mistral", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["mistral-max-output-tokens"]), + "-t", + "%0.3f" % $["mistral-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/ollama.jsonnet new file mode 100644 index 00000000..6143f5cc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/ollama.jsonnet @@ -0,0 +1,102 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/ollama.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["ollama-" + key]:: value, + }, + + "ollama-models":: models, + + "llm-models" +:: $["ollama-models"], + + "text-completion" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("ollama-credentials") + .with_env_var("OLLAMA_HOST", "ollama-host"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-ollama", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("ollama-credentials") + .with_env_var("OLLAMA_HOST", "ollama-host"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-ollama", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/openai.jsonnet new file mode 100644 index 00000000..c3d482fd --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/openai.jsonnet @@ -0,0 +1,106 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/openai.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["openai-" + key]:: value, + }, + + "openai-max-output-tokens":: 4096, + "openai-temperature":: 0.0, + "openai-models":: models, + + "llm-models" +:: $["openai-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("openai-credentials") + .with_env_var("OPENAI_TOKEN", "openai-token") + .with_env_var("OPENAI_BASE_URL", "openai-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-openai", + "-p", + url.pulsar, + "-x", + std.toString($["openai-max-output-tokens"]), + "-t", + "%0.3f" % $["openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("openai-credentials") + .with_env_var("OPENAI_TOKEN", "openai-token") + .with_env_var("OPENAI_BASE_URL", "openai-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-openai", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["openai-max-output-tokens"]), + "-t", + "%0.3f" % $["openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/tgi.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/tgi.jsonnet new file mode 100644 index 00000000..91588dff --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/tgi.jsonnet @@ -0,0 +1,108 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-" + key]:: value, + }, + + "tgi-max-output-tokens":: 1024, + "tgi-temperature":: 0.0, + + "text-completion" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("tgi-credentials") + .with_env_var("TGI_BASE_URL", "tgi-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-tgi", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "-x", + std.toString($["tgi-max-output-tokens"]), + "-t", + "%0.3f" % $["tgi-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("tgi-credentials") + .with_env_var("TGI_BASE_URL", "tgi-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-tgi", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "--concurrency", + std.toString(concurrency), + "-x", + std.toString($["tgi-max-output-tokens"]), + "-t", + "%0.3f" % $["tgi-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/vertexai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/vertexai.jsonnet new file mode 100644 index 00000000..cb8c141f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/vertexai.jsonnet @@ -0,0 +1,120 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/vertexai.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vertexai-" + key]:: value, + }, + + "vertexai-private-key":: "/vertexai/private.json", + "vertexai-region":: "us-central1", + "vertexai-max-output-tokens":: 4096, + "vertexai-temperature":: 0.0, + "vertexai-models":: models, + + "llm-models" +:: $["vertexai-models"], + + "text-completion" +: { + + create:: function(engine) + + local cfgVol = engine.secretVolume( + "vertexai-creds", + "./vertexai", + { + "private.json": importstr "vertexai/private.json", + } + ); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_vertexai) + .with_command([ + "text-completion-vertexai", + "-p", + url.pulsar, + "-k", + $["vertexai-private-key"], + "-r", + $["vertexai-region"], + "-x", + std.toString($["vertexai-max-output-tokens"]), + "-t", + "%0.3f" % $["vertexai-temperature"], + ]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_volume_mount(cfgVol, "/vertexai"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + cfgVol, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local cfgVol = engine.secretVolume( + "vertexai-creds", + "./vertexai", + { + "private.json": importstr "vertexai/private.json", + } + ); + + local container = + engine.container("text-completion-rag") + .with_image(images.trustgraph_vertexai) + .with_command([ + "text-completion-vertexai", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-k", + $["vertexai-private-key"], + "-r", + $["vertexai-region"], + "-x", + std.toString($["vertexai-max-output-tokens"]), + "-t", + "%0.3f" % $["vertexai-temperature"], + ]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_volume_mount(cfgVol, "/vertexai"); + + local containerSet = engine.containers( + "text-completion-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + cfgVol, + containerSet, + service, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/vllm.jsonnet new file mode 100644 index 00000000..8b846bbf --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/llm/vllm.jsonnet @@ -0,0 +1,113 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/vllm.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-" + key]:: value, + }, + + "vllm-models":: models, + + "llm-models" +:: $["vllm-models"], + + "vllm-max-output-tokens":: 1024, + "vllm-temperature":: 0.0, + + "text-completion" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("vllm-credentials") + .with_env_var("VLLM_BASE_URL", "vllm-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-vllm", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "-x", + std.toString($["vllm-max-output-tokens"]), + "-t", + "%0.3f" % $["vllm-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("vllm-credentials") + .with_env_var("VLLM_BASE_URL", "vllm-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-vllm", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "--concurrency", + std.toString(concurrency), + "-x", + std.toString($["vllm-max-output-tokens"]), + "-t", + "%0.3f" % $["vllm-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/mcp/ddg-mcp-server.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/mcp/ddg-mcp-server.jsonnet new file mode 100644 index 00000000..04176f15 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/mcp/ddg-mcp-server.jsonnet @@ -0,0 +1,47 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "ddg-mcp-server-port":: 9870, + + "ddg-mcp-server" +: { + + create:: function(engine) + + local port = $["ddg-mcp-server-port"]; + + local container = + engine.container("ddg-mcp-server") + .with_image(images["ddg-mcp-server"]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_port(port, port, "mcp"); + + local containerSet = engine.containers( + "ddg-mcp-server", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(port, port, "mcp"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + mcp +:: { + "duckduckgo": { + "remote-name": "search", + local port = $["ddg-mcp-server-port"], + local url = "http://ddg-mcp-server:%s/mcp" % port, + "url": url, + } + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/model-hosting/cpu-tgi.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/model-hosting/cpu-tgi.jsonnet new file mode 100644 index 00000000..04b61566 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/model-hosting/cpu-tgi.jsonnet @@ -0,0 +1,68 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-service-" + key]:: value, + }, + + "tgi-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "tgi-service-cpus":: "8.0", + "tgi-service-memory":: "16G", + "tgi-service-storage":: "20G", + "tgi-service-hf-token":: null, + + "tgi-service" +: { + + create:: function(engine) + + local vol = engine.volume("tgi-storage") + .with_size($["tgi-service-storage"]); + + local container = + engine.container("tgi-service") + .with_image(images["tgi-service-cpu"]) + .with_command([ + "--model-id", + $["tgi-service-model"], + "--hostname", + "0.0.0.0", + "--port", + "7000", + "--cuda-graphs", + "0", + ]) + .with_environment({ + } + ( + if $["tgi-service-hf-token"] != null + then { HF_TOKEN: $["tgi-service-hf-token"] } + else {} + )) + .with_limits( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_reservations( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_port(7000, 7000, "tgi") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "tgi-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "tgi"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/model-hosting/intel-battlemage-vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/model-hosting/intel-battlemage-vllm.jsonnet new file mode 100644 index 00000000..7646398c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/model-hosting/intel-battlemage-vllm.jsonnet @@ -0,0 +1,98 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-service-" + key]:: value, + }, + + "vllm-service-model":: "mistralai/Mistral-Nemo-Instruct-2407", + "vllm-service-cpus":: "32.0", + "vllm-service-memory":: "48G", + "vllm-service-storage":: "48G", + "vllm-service-tokenizer-mode":: "mistral", + "vllm-service-datatype":: "float16", + "vllm-service-quantization":: "woq_int4", + "vllm-service-hf-token":: null, + "vllm-service-max-model-len":: 8192, + "vllm-service-max-num-seqs":: 16, + + "vllm-service" +: { + + create:: function(engine) + + local vol = engine.volume("vllm-storage") + .with_size($["vllm-service-storage"]); + + local container = + engine.container("vllm-service") + .with_image(images["vllm-service-intel-battlemage"]) + .with_entrypoint("") // Clear default entrypoint + .with_command([ + "python", + "-m", + "ipex_llm.vllm.xpu.entrypoints.openai.api_server", + "--model", + $["vllm-service-model"], + "--served-model-name", + "model", + "--host", + "0.0.0.0", + "--port", + "7000", + "--device", + "xpu", + "--dtype", + $["vllm-service-datatype"], + "--enforce-eager", + "--max-model-len", + std.toString($["vllm-service-max-model-len"]), + "--max-num-seqs", + std.toString($["vllm-service-max-num-seqs"]), + "--load-in-low-bit", + $["vllm-service-quantization"], + "--trust-remote-code", + "--tokenizer-mode", + $["vllm-service-tokenizer-mode"], + "--disable-sliding-window", + ]) + .with_environment({ + VLLM_WORKER_MULTIPROC_METHOD: "spawn", + SYCL_PI_LEVEL_ZERO_USE_IMMEDIATE_COMMANDLISTS: "1", + } + ( + if $["vllm-service-hf-token"] != null + then { HF_TOKEN: $["vllm-service-hf-token"] } + else {} + )) + .with_privileged(true) + .with_device("/dev/dri", "/dev/dri") + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_reservations( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_port(7000, 7000, "vllm") + .with_bind_mount("/dev/dri/by-path", "/dev/dri/by-path") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "vllm-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "vllm"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/model-hosting/intel-gaudi-tgi.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/model-hosting/intel-gaudi-tgi.jsonnet new file mode 100644 index 00000000..5af36b78 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/model-hosting/intel-gaudi-tgi.jsonnet @@ -0,0 +1,96 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-service-" + key]:: value, + }, + + "tgi-service-model":: "meta-llama/Llama-3.3-70B-Instruct", + "tgi-service-cpus":: "64.0", + "tgi-service-memory":: "64G", + "tgi-service-storage":: "50G", + "tgi-service-num-shard":: 8, + "tgi-service-max-input-tokens":: 4096, + "tgi-service-max-total-tokens":: 4096, + "tgi-service-max-batch-size":: 128, + "tgi-service-max-concurrent-requests":: 512, + "tgi-service-hf-token":: null, + + "tgi-service" +: { + + create:: function(engine) + + local vol = engine.volume("tgi-storage") + .with_size($["tgi-service-storage"]); + + local container = + engine.container("tgi-service") + .with_image(images["tgi-service-gaudi"]) + .with_command([ + "--model-id", + $["tgi-service-model"], + "--hostname", + "0.0.0.0", + "--port", + "7000", + "--sharded", + "true", + "--num-shard", + std.toString($["tgi-service-num-shard"]), + "--max-input-tokens", + std.toString($["tgi-service-max-input-tokens"]), + "--max-total-tokens", + std.toString($["tgi-service-max-total-tokens"]), + "--max-batch-size", + std.toString($["tgi-service-max-batch-size"]), + "--max-waiting-tokens", + "7", + "--max-concurrent-requests", + std.toString($["tgi-service-max-concurrent-requests"]), + "--cuda-graphs", + "0", + ]) + .with_runtime("habana") + .with_environment({ + HABANA_VISIBLE_DEVICES: "all", + OMPI_MCA_btl_vader_single_copy_mechanism: "none", + ENABLE_HPU_GRAPH: "true", + LIMIT_HPU_GRAPH: "true", + USE_FLASH_ATTENTION: "true", + FLASH_ATTENTION_RECOMPUTE: "true", + } + ( + if $["tgi-service-hf-token"] != null + then { HF_TOKEN: $["tgi-service-hf-token"] } + else {} + )) + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_reservations( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_port(7000, 7000, "tgi") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "tgi-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "tgi"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/model-hosting/intel-gaudi-vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/model-hosting/intel-gaudi-vllm.jsonnet new file mode 100644 index 00000000..81d31c14 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/model-hosting/intel-gaudi-vllm.jsonnet @@ -0,0 +1,76 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-service-" + key]:: value, + }, + + "vllm-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "vllm-service-cpus":: "64.0", + "vllm-service-memory":: "64G", + "vllm-service-storage":: "50G", + "vllm-service-tensor-parallel-size":: 8, + "vllm-service-hf-token":: null, + + "vllm-service" +: { + + create:: function(engine) + + local vol = engine.volume("vllm-storage") + .with_size($["vllm-service-storage"]); + + local container = + engine.container("vllm-service") + .with_image(images["vllm-service-gaudi"]) + .with_command([ + "--model", + $["vllm-service-model"], + "--served-model-name", + "model", + "--host", + "0.0.0.0", + "--port", + "7000", + "--tensor-parallel-size", + std.toString($["vllm-service-tensor-parallel-size"]), + ]) + .with_runtime("habana") + .with_environment({ + VLLM_SKIP_WARMUP: "true", + HABANA_VISIBLE_DEVICES: "all", + } + ( + if $["vllm-service-hf-token"] != null + then { HF_TOKEN: $["vllm-service-hf-token"] } + else {} + )) + .with_privileged(true) + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_reservations( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_port(7000, 7000, "vllm") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "vllm-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "vllm"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/model-hosting/intel-xpu-tgi.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/model-hosting/intel-xpu-tgi.jsonnet new file mode 100644 index 00000000..bc806874 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/model-hosting/intel-xpu-tgi.jsonnet @@ -0,0 +1,75 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-service-" + key]:: value, + }, + + "tgi-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "tgi-service-cpus":: "8.0", + "tgi-service-memory":: "16G", + "tgi-service-storage":: "20G", + "tgi-service-hf-token":: null, + + "tgi-service" +: { + + create:: function(engine) + + local vol = engine.volume("tgi-storage") + .with_size($["tgi-service-storage"]); + + local container = + engine.container("tgi-service") + .with_image(images["tgi-service-intel-xpu"]) + .with_command([ + "--model-id", + $["tgi-service-model"], + "--hostname", + "0.0.0.0", + "--port", + "7000", + "--cuda-graphs", + "0", + ]) + .with_environment({ + } + ( + if $["tgi-service-hf-token"] != null + then { HF_TOKEN: $["tgi-service-hf-token"] } + else {} + )) + .with_privileged(true) + .with_device("/dev/dri", "/dev/dri") + .with_ipc("host") + .with_group("video") + .with_group("render") + .with_capability("SYS_NICE") + .with_limits( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_reservations( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_port(7000, 7000, "tgi") + .with_bind_mount("/dev/dri/by-path", "/dev/dri/by-path") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "tgi-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "tgi"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/model-hosting/intel-xpu-vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/model-hosting/intel-xpu-vllm.jsonnet new file mode 100644 index 00000000..f6e82905 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/model-hosting/intel-xpu-vllm.jsonnet @@ -0,0 +1,97 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-service-" + key]:: value, + }, + + "vllm-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "vllm-service-cpus":: "8.0", + "vllm-service-memory":: "16G", + "vllm-service-storage":: "20G", + "vllm-service-datatype":: "float16", + "vllm-service-max-model-len":: 4096, + "vllm-service-max-num-seqs":: 16, + "vllm-service-hf-token":: null, + + "vllm-service" +: { + + create:: function(engine) + + local vol = engine.volume("vllm-storage") + .with_size($["vllm-service-storage"]); + + local container = + engine.container("vllm-service") + .with_image(images["vllm-service-intel-xpu"]) + .with_command([ + "python", + "-m", + "vllm.entrypoints.openai.api_server", + "--model", + $["vllm-service-model"], + "--served-model-name", + "model", + "--host", + "0.0.0.0", + "--port", + "7000", + "--device", + "xpu", + "--dtype", + $["vllm-service-datatype"], + "--enforce-eager", + "--max-model-len", + std.toString($["vllm-service-max-model-len"]), + "--max-num-seqs", + std.toString($["vllm-service-max-num-seqs"]), + "--block-size", + "64", + "--gpu-memory-util", + "0.85", + "--trust-remote-code", + "--disable-sliding-window", + ]) + .with_environment({ + VLLM_USE_V1: "1", + VLLM_WORKER_MULTIPROC_METHOD: "spawn", + } + ( + if $["vllm-service-hf-token"] != null + then { HF_TOKEN: $["vllm-service-hf-token"] } + else {} + )) + .with_privileged(true) + .with_device("/dev/dri", "/dev/dri") + .with_ipc("host") + .with_group("video") + .with_group("render") + .with_capability("SYS_NICE") + .with_limits( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_reservations( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_port(7000, 7000, "vllm") + .with_bind_mount("/dev/dri/by-path", "/dev/dri/by-path") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "vllm-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "vllm"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/model-hosting/nvidia-gpu-vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/model-hosting/nvidia-gpu-vllm.jsonnet new file mode 100644 index 00000000..d04876c4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/model-hosting/nvidia-gpu-vllm.jsonnet @@ -0,0 +1,72 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-service-" + key]:: value, + }, + + "vllm-service-model":: "mistralai/Mistral-7B-Instruct-v0.3", + "vllm-service-cpus":: "0.5", + "vllm-service-memory":: "1G", + "vllm-service-storage":: "50G", + "vllm-service-hf-token":: null, + + "vllm-service" +: { + + create:: function(engine) + + local vol = engine.volume("vllm-storage") + .with_size($["vllm-service-storage"]); + + local container = + engine.container("vllm-service") + .with_image(images["vllm-service-nvidia"]) + .with_command([ + "--model", + $["vllm-service-model"], + "--served-model-name", + "model", + "--host", + "0.0.0.0", + "--port", + "7000", + ]) + .with_runtime("nvidia") + .with_environment({ + VLLM_SKIP_WARMUP: "true", + } + ( + if $["vllm-service-hf-token"] != null + then { HF_TOKEN: $["vllm-service-hf-token"] } + else {} + )) + .with_privileged(true) + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_reservations( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_port(7000, 7000, "vllm") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "vllm-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "vllm"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/monitoring/grafana.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/monitoring/grafana.jsonnet new file mode 100644 index 00000000..dd0dddd7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/monitoring/grafana.jsonnet @@ -0,0 +1,124 @@ +local images = import "values/images.jsonnet"; +local loki = import "loki.jsonnet"; + +{ + + "prometheus" +: { + + create:: function(engine) + + local vol = engine.volume("prometheus-data").with_size("20G"); + + local cfgVol = engine.configVolume( + "prometheus-cfg", "prometheus", + { + "prometheus.yml": importstr "prometheus/prometheus.yml", + } + ); + + local container = + engine.container("prometheus") + .with_image(images.prometheus) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M") + .with_port(9090, 9090, "http") + .with_volume_mount(cfgVol, "/etc/prometheus/") + .with_volume_mount(vol, "/prometheus"); + + local containerSet = engine.containers( + "prometheus", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9090, 9090, "http"); + + engine.resources([ + cfgVol, + vol, + containerSet, + service, + ]) + + }, + + "grafana" +: { + + create:: function(engine) + + local vol = engine.volume("grafana-storage").with_size("20G"); + + local provDashVol = engine.configVolume( + "prov-dash", "grafana/provisioning/", + { + "dashboard.yml": + importstr "grafana/provisioning/dashboard.yml", + } + + ); + + local provDataVol = engine.configVolume( + "prov-data", "grafana/provisioning/", + { + "datasource.yml": + importstr "grafana/provisioning/datasource.yml", + } + + ); + + local dashVol = engine.configVolume( + "dashboards", "grafana/dashboards/", + { + "overview-dashboard.json": + importstr "grafana/dashboards/overview-dashboard.json", + "log-dashboard.json": + importstr "grafana/dashboards/log-dashboard.json", + } + + ); + + local container = + engine.container("grafana") + .with_image(images.grafana) + .with_environment({ + // GF_AUTH_ANONYMOUS_ORG_ROLE: "Admin", + // GF_AUTH_ANONYMOUS_ENABLED: "true", + // GF_ORG_ROLE: "Admin", + GF_ORG_NAME: "trustgraph.ai", + // GF_SERVER_ROOT_URL: "https://example.com", + }) + .with_limits("1.0", "256M") + .with_reservations("0.5", "256M") + .with_port(3000, 3000, "cassandra") + .with_volume_mount(vol, "/var/lib/grafana") + .with_volume_mount( + provDashVol, "/etc/grafana/provisioning/dashboards/" + ) + .with_volume_mount( + provDataVol, "/etc/grafana/provisioning/datasources/" + ) + .with_volume_mount( + dashVol, "/var/lib/grafana/dashboards/" + ); + + local containerSet = engine.containers( + "grafana", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(3000, 3000, "http"); + + engine.resources([ + vol, + provDashVol, + provDataVol, + dashVol, + containerSet, + service, + ]) + + }, + +} + loki + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/monitoring/loki.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/monitoring/loki.jsonnet new file mode 100644 index 00000000..0774f6cf --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/monitoring/loki.jsonnet @@ -0,0 +1,45 @@ +local images = import "values/images.jsonnet"; + +{ + + "loki" +: { + + create:: function(engine) + + local vol = engine.volume("loki-data").with_size("20G"); + + local cfgVol = engine.configVolume( + "loki-cfg", "loki", + { + "local-config.yaml": importstr "loki/local-config.yaml", + } + ); + + local container = + engine.container("loki") + .with_image(images.loki) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_port(3100, 3100, "http") + .with_volume_mount(cfgVol, "/etc/loki/") + .with_volume_mount(vol, "/loki"); + + local containerSet = engine.containers( + "loki", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(3100, 3100, "http"); + + engine.resources([ + cfgVol, + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/ocr/mistral-ocr.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/ocr/mistral-ocr.jsonnet new file mode 100644 index 00000000..f70e0cd6 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/ocr/mistral-ocr.jsonnet @@ -0,0 +1,49 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["mistral-" + key]:: value, + }, + + "pdf-decoder" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("mistral-credentials") + .with_env_var("MISTRAL_TOKEN", "mistral-token"); + + local container = + engine.container("mistral-ocr") + .with_image(images.trustgraph_flow) + .with_command([ + "pdf-ocr-mistral", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "mistral-ocr", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/ocr/ocr.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/ocr/ocr.jsonnet new file mode 100644 index 00000000..4f53aa5a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/ocr/ocr.jsonnet @@ -0,0 +1,37 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "pdf-decoder" +: { + + create:: function(engine) + + local container = + engine.container("pdf-ocr") + .with_image(images.trustgraph_ocr) + .with_command([ + "pdf-ocr", + "-p", + url.pulsar, + ]) + .with_limits("1.0", "512M") + .with_reservations("0.1", "512M"); + + local containerSet = engine.containers( + "pdf-ocr", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/azure-openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/azure-openai.jsonnet new file mode 100644 index 00000000..0e3ea907 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/azure-openai.jsonnet @@ -0,0 +1,9 @@ +// Azure OpenAI LLM Model Definitions +// Model input is just text + +{ + "type": "string", + "description": "LLM model to use", + "default": "gpt-4o", + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/azure.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/azure.jsonnet new file mode 100644 index 00000000..6ffa5fdf --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/azure.jsonnet @@ -0,0 +1,9 @@ +// Azure LLM Model Definitions +// Model input is just text + +{ + "type": "string", + "description": "LLM model to use", + "default": "phi4:14b", + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/bedrock.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/bedrock.jsonnet new file mode 100644 index 00000000..df930e59 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/bedrock.jsonnet @@ -0,0 +1,63 @@ +// AWS Bedrock LLM Model Definitions +// Defines available models and their configurations for AWS Bedrock + +{ + "type": "string", + "description": "LLM model to use", + "default": "global.anthropic.claude-sonnet-4-5-20250929-v1:0", + "enum": [ + { + id: "global.anthropic.claude-sonnet-4-5-20250929-v1:0", + description: "Claude Sonnet 4.5 (smartest for complex agents and coding)" + }, + { + id: "global.anthropic.claude-opus-4-5-20251101-v1:0", + description: "Claude Opus 4.5 (maximum intelligence)" + }, + { + id: "global.anthropic.claude-haiku-4-5-20251001-v1:0", + description: "Claude Haiku 4.5 (fastest with near-frontier intelligence)" + }, + { + id: "global.anthropic.claude-opus-4-1-20250805-v1:0", + description: "Claude Opus 4.1 (specialized reasoning)" + }, + { + id: "global.anthropic.claude-sonnet-4-20250514-v1:0", + description: "Claude Sonnet 4.0" + }, + { + id: "global.anthropic.claude-opus-4-20250514-v1:0", + description: "Claude Opus 4.0" + }, + { + id: "anthropic.claude-3-5-haiku-20241022-v1:0", + description: "Claude 3.5 Haiku" + }, + { + id: "anthropic.claude-3-haiku-20240307-v1:0", + description: "Claude 3 Haiku" + }, + { + id: "meta.llama3-1-405b-instruct-v1:0", + description: "Llama 3.1 405B Instruct" + }, + { + id: "meta.llama3-1-70b-instruct-v1:0", + description: "Llama 3.1 70B Instruct" + }, + { + id: "meta.llama3-1-8b-instruct-v1:0", + description: "Llama 3.1 8B Instruct" + }, + { + id: "mistral.mistral-large-2407-v1:0", + description: "Mistral Large" + }, + { + id: "mistral.mixtral-8x7b-instruct-v0:1", + description: "Mixtral 8x7B Instruct" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/chunking-param-types.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/chunking-param-types.jsonnet new file mode 100644 index 00000000..f82fa3f7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/chunking-param-types.jsonnet @@ -0,0 +1,25 @@ +// Chunk parameter type definitions + +{ + "chunk-size": { + "type": "integer", + "description": "Chunk size", + "placeholder": 2000, + "helper": "An integer, usually 2000 .. 8000", + "default": 2000, + "min": 0, + "max": 32768, + "required": true + }, + "chunk-overlap": { + "type": "integer", + "description": "Chunk overlap", + "placeholder": 50, + "helper": "An integer, usually 50 .. 100", + "default": 50, + "min": 0, + "max": 8000, + "required": true + }, +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/claude.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/claude.jsonnet new file mode 100644 index 00000000..40fd434b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/claude.jsonnet @@ -0,0 +1,35 @@ +// Claude LLM Model Definitions +// Defines available models and their configurations for Anthropic's Claude + +{ + "type": "string", + "description": "LLM model to use", + "default": "claude-sonnet-4-5-20250929", + "enum": [ + { + id: "claude-sonnet-4-5-20250929", + description: "Claude Sonnet 4.5 (complex agents + coding)" + }, + { + id: "claude-opus-4-5-20251101", + description: "Claude Opus 4.5 (maximum intelligence)" + }, + { + id: "claude-haiku-4-5-20251001", + description: "Claude Haiku 4.5 (fast)" + }, + { + id: "claude-opus-4-1-20250805", + description: "Claude Opus 4.1 (specialized reasoning)" + }, + { + id: "claude-sonnet-4-20250514", + description: "Claude Sonnet 4.0" + }, + { + id: "claude-3-5-haiku-20241022", + description: "Claude 3.5 Haiku" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/cohere.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/cohere.jsonnet new file mode 100644 index 00000000..5a5865a3 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/cohere.jsonnet @@ -0,0 +1,35 @@ +// Cohere LLM Model Definitions +// Defines available models and their configurations for Cohere + +{ + "type": "string", + "description": "LLM model to use", + "default": "command-r-plus-08-2024", + "enum": [ + { + id: "command-r-plus-08-2024", + description: "Command R+ (August 2024)" + }, + { + id: "command-r-08-2024", + description: "Command R (August 2024)" + }, + { + id: "command-r-plus", + description: "Command R+ (legacy)" + }, + { + id: "command-r", + description: "Command R (legacy)" + }, + { + id: "command", + description: "Command" + }, + { + id: "command-light", + description: "Command Light" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/embeddings-fastembed.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/embeddings-fastembed.jsonnet new file mode 100644 index 00000000..477e25dc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/embeddings-fastembed.jsonnet @@ -0,0 +1,127 @@ +// Embeddings model definitions for fastembed +// Defines available models and their configurations for Fastembed + +{ + "type": "string", + "description": "Embeddings model to use", + "default": "sentence-transformers/all-MiniLM-L6-v2", + "enum": [ + { + "id": "sentence-transformers/all-MiniLM-L6-v2", + "description": "all-MiniLM-L6-v2" + }, + { + "id": "BAAI/bge-small-en-v1.5", + "description": "bge-small-en-v1.5" + }, + { + "id": "BAAI/bge-small-zh-v1.5", + "description": "bge-small-zh-v1.5" + }, + { + "id": "snowflake/snowflake-arctic-embed-xs", + "description": "snowflake-arctic-embed-xs" + }, + { + "id": "jinaai/jina-embeddings-v2-small-en", + "description": "jina-embeddings-v2-small-en" + }, + { + "id": "nomic-ai/nomic-embed-text-v1.5-Q", + "description": "nomic-embed-text-v1.5-Q" + }, + { + "id": "snowflake/snowflake-arctic-embed-s", + "description": "snowflake-arctic-embed-s" + }, + { + "id": "BAAI/bge-small-en", + "description": "bge-small-en" + }, + { + "id": "BAAI/bge-base-en-v1.5", + "description": "bge-base-en-v1.5" + }, + { + "id": "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2", + "description": "paraphrase-multilingual-MiniLM-L12-v2" + }, + { + "id": "Qdrant/clip-ViT-B-32-text", + "description": "clip-ViT-B-32-text" + }, + { + "id": "jinaai/jina-embeddings-v2-base-de", + "description": "jina-embeddings-v2-base-de" + }, + { + "id": "BAAI/bge-base-en", + "description": "bge-base-en" + }, + { + "id": "snowflake/snowflake-arctic-embed-m", + "description": "snowflake-arctic-embed-m" + }, + { + "id": "thenlper/gte-base", + "description": "gte-base" + }, + { + "id": "jinaai/jina-embeddings-v2-base-en", + "description": "jina-embeddings-v2-base-en" + }, + { + "id": "nomic-ai/nomic-embed-text-v1", + "description": "nomic-embed-text-v1" + }, + { + "id": "nomic-ai/nomic-embed-text-v1.5", + "description": "nomic-embed-text-v1.5" + }, + { + "id": "snowflake/snowflake-arctic-embed-m-long", + "description": "snowflake-arctic-embed-m-long" + }, + { + "id": "jinaai/jina-clip-v1", + "description": "jina-clip-v1" + }, + { + "id": "mixedbread-ai/mxbai-embed-large-v1", + "description": "mxbai-embed-large-v1" + }, + { + "id": "jinaai/jina-embeddings-v2-base-es", + "description": "jina-embeddings-v2-base-es" + }, + { + "id": "jinaai/jina-embeddings-v2-base-code", + "description": "jina-embeddings-v2-base-code" + }, + { + "id": "jinaai/jina-embeddings-v2-base-zh", + "description": "jina-embeddings-v2-base-zh" + }, + { + "id": "sentence-transformers/paraphrase-multilingual-mpnet-base-v2", + "description": "paraphrase-multilingual-mpnet-base-v2" + }, + { + "id": "snowflake/snowflake-arctic-embed-l", + "description": "snowflake-arctic-embed-l" + }, + { + "id": "BAAI/bge-large-en-v1.5", + "description": "bge-large-en-v1.5" + }, + { + "id": "thenlper/gte-large", + "description": "gte-large" + }, + { + "id": "intfloat/multilingual-e5-large", + "description": "multilingual-e5-large" + } + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/embeddings-huggingface.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/embeddings-huggingface.jsonnet new file mode 100644 index 00000000..32254a2b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/embeddings-huggingface.jsonnet @@ -0,0 +1,31 @@ +// Embeddings model definitions for fastembed +// Defines available models and their configurations for Fastembed + +{ + "type": "string", + "description": "Embeddings model to use", + "default": "sentence-transformers/all-MiniLM-L6-v2", + "enum": [ + { + "id": "all-MiniLM-L6-v2", + "description": "all-MiniLM-L6-v2" + }, + { + "id": "all-mpnet-base-v2", + "description": "all-mpnet-base-v2" + }, + { + "id": "all-distilroberta-v1", + "description": "all-distilroberta-v1" + }, + { + "id": "stsb-bert-large", + "description": "stsb-bert-large" + }, + { + "id": "sentence-camembert-large", + "description": "sentence-camembert-large" + } + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/embeddings-ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/embeddings-ollama.jsonnet new file mode 100644 index 00000000..c3f29fbb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/embeddings-ollama.jsonnet @@ -0,0 +1,23 @@ +// Embeddings model definitions for Ollama +// Defines available models and their configurations for Ollama + +{ + "type": "string", + "description": "Embeddings model to use", + "default": "all-minilm:latest", + "enum": [ + { + "id": "all-minilm:latest", + "description": "all-MiniLM-L6-v2" + }, + { + "id": "nomic-ai/nomic-embed-text-v1.5-Q", + "description": "nomic-embed-text-v1.5-Q" + }, + { + "id": "mixedbread-ai/mxbai-embed-large-v1", + "description": "mxbai-embed-large-v1" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/googleaistudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/googleaistudio.jsonnet new file mode 100644 index 00000000..a8891932 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/googleaistudio.jsonnet @@ -0,0 +1,67 @@ +// Google AI Studio LLM Model Definitions +// Defines available models and their configurations for Google AI Studio + +{ + "type": "string", + "description": "LLM model to use", + "default": "gemini-2.5-flash-lite", + "enum": [ + // Gemini 2.5 models (latest generation) + { + id: "gemini-2.5-pro", + description: "Gemini 2.5 Pro" + }, + { + id: "gemini-2.5-flash", + description: "Gemini 2.5 Flash" + }, + { + id: "gemini-2.5-flash-lite", + description: "Gemini 2.5 Flash Lite" + }, + + // Gemini 2.0 models + { + id: "gemini-2.0-flash-exp", + description: "Gemini 2.0 Flash (experimental)" + }, + + + // Claude models on VertexAI + { + id: "claude-3-5-sonnet@20241022", + description: "Claude 3.5 Sonnet (via VertexAI)" + }, + { + id: "claude-3-5-haiku@20241022", + description: "Claude 3.5 Haiku (via VertexAI)" + }, + { + id: "claude-3-opus@20240229", + description: "Claude 3 Opus (via VertexAI)" + }, + { + id: "claude-3-sonnet@20240229", + description: "Claude 3 Sonnet (via VertexAI)" + }, + { + id: "claude-3-haiku@20240307", + description: "Claude 3 Haiku (via VertexAI)" + }, + + // Llama models on VertexAI + { + id: "llama3-405b-instruct-maas", + description: "Llama 3 405B Instruct (via VertexAI)" + }, + { + id: "llama3-70b-instruct-maas", + description: "Llama 3 70B Instruct (via VertexAI)" + }, + { + id: "llama3-8b-instruct-maas", + description: "Llama 3 8B Instruct (via VertexAI)" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/llamafile.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/llamafile.jsonnet new file mode 100644 index 00000000..f6480aa9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/llamafile.jsonnet @@ -0,0 +1,9 @@ +// LlamaFile LLM Model Definitions +// Model input is just text + +{ + "type": "string", + "description": "LLM model to use", + "default": "phi4:14b", + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/lmstudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/lmstudio.jsonnet new file mode 100644 index 00000000..1c88da3c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/lmstudio.jsonnet @@ -0,0 +1,150 @@ +// LMStudio LLM Model Definitions +// Defines available models and their configurations for LMStudio + +{ + "type": "string", + "description": "LLM model to use", + "default": "llama3.1:70b", + "enum": [ + // Gemma3 models + { + id: "gemma3:1b", + description: "Gemma3 1B" + }, + { + id: "gemma3:4b", + description: "Gemma3 4B" + }, + { + id: "gemma3:12b", + description: "Gemma3 12B" + }, + { + id: "gemma3:27b", + description: "Gemma3 27B" + }, + + // Phi4 models + { + id: "phi4:mini:3.8b", + description: "Phi4 Mini 3.8B" + }, + { + id: "phi4:14b", + description: "Phi4 14B" + }, + + // DeepSeek-R1 models + { + id: "deepseek-r1:671b", + description: "DeepSeek-R1 671B" + }, + { + id: "deepseek-r1:70b", + description: "DeepSeek-R1 70B" + }, + { + id: "deepseek-r1:32b", + description: "DeepSeek-R1 32B" + }, + { + id: "deepseek-r1:14b", + description: "DeepSeek-R1 14B" + }, + { + id: "deepseek-r1:8b", + description: "DeepSeek-R1 8B" + }, + { + id: "deepseek-r1:7b", + description: "DeepSeek-R1 7B" + }, + { + id: "deepseek-r1:1.5b", + description: "DeepSeek-R1 1.5B" + }, + + // Llama3.1 models + { + id: "llama3.1:405b", + description: "Llama 3.1 405B" + }, + { + id: "llama3.1:70b", + description: "Llama 3.1 70B" + }, + { + id: "llama3.1:8b", + description: "Llama 3.1 8B" + }, + + // Gemma2 models + { + id: "gemma2:2b", + description: "Gemma2 2B" + }, + { + id: "gemma2:9b", + description: "Gemma2 9B" + }, + { + id: "gemma2:27b", + description: "Gemma2 27B" + }, + + // Qwen2.5 models + { + id: "qwen2.5:0.5b", + description: "Qwen2.5 0.5B" + }, + { + id: "qwen2.5:1.5b", + description: "Qwen2.5 1.5B" + }, + { + id: "qwen2.5:3b", + description: "Qwen2.5 3B" + }, + { + id: "qwen2.5:7b", + description: "Qwen2.5 7B" + }, + { + id: "qwen2.5:14b", + description: "Qwen2.5 14B" + }, + { + id: "qwen2.5:32b", + description: "Qwen2.5 32B" + }, + { + id: "qwen2.5:72b", + description: "Qwen2.5 72B" + }, + + // Mistral models + { + id: "mistral:7b", + description: "Mistral 7B" + }, + { + id: "mistral-nemo:12b", + description: "Mistral Nemo 12B" + }, + { + id: "mistral-large:123b", + description: "Mistral Large 123B" + }, + + // Command-R models + { + id: "command-r:35b", + description: "Command-R 35B" + }, + { + id: "command-r-plus:104b", + description: "Command-R Plus 104B" + }, + ], + "required": true +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/mistral.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/mistral.jsonnet new file mode 100644 index 00000000..07f75b81 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/mistral.jsonnet @@ -0,0 +1,61 @@ +// Mistral LLM Model Definitions +// Defines available models and their configurations for Mistral AI + +{ + "type": "string", + "description": "LLM model to use", + "default": "mistral-medium-2508", + "enum": [ + // Featured models + { + id: "mistral-medium-2508", + description: "Mistral Medium 3.1" + }, + { + id: "mistral-large-2512", + description: "Mistral Large 3" + }, + { + id: "mistral-small-2506", + description: "Mistral Small 3.2" + }, + { + id: "ministral-14b-2512", + description: "Ministral 3 14B" + }, + { + id: "ministral-8b-2512", + description: "Ministral 3 8B" + }, + { + id: "ministral-3b-2512", + description: "Ministral 3 3B" + }, + { + id: "magistral-medium-2509", + description: "Magistral Medium 1.2 (reasoning)" + }, + { + id: "magistral-small-2509", + description: "Magistral Small 1.2 (reasoning)" + }, + { + id: "devstral-2512", + description: "Devstral 2 (code)" + }, + // Other models + { + id: "codestral-2508", + description: "Codestral (code)" + }, + { + id: "pixtral-large-2411", + description: "Pixtral Large (vision)" + }, + { + id: "open-mistral-nemo", + description: "Open Mistral Nemo" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/ollama.jsonnet new file mode 100644 index 00000000..4ef98a6a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/ollama.jsonnet @@ -0,0 +1,150 @@ +// Ollama LLM Model Definitions +// Defines available models and their configurations for Ollama + +{ + "type": "string", + "description": "LLM model to use", + "default": "gemma3:12b", + "enum": [ + // Gemma3 models + { + id: "gemma3:1b", + description: "Gemma3 1B" + }, + { + id: "gemma3:4b", + description: "Gemma3 4B" + }, + { + id: "gemma3:12b", + description: "Gemma3 12B" + }, + { + id: "gemma3:27b", + description: "Gemma3 27B" + }, + + // Phi4 models + { + id: "phi4:mini:3.8b", + description: "Phi4 Mini 3.8B" + }, + { + id: "phi4:14b", + description: "Phi4 14B" + }, + + // DeepSeek-R1 models + { + id: "deepseek-r1:671b", + description: "DeepSeek-R1 671B" + }, + { + id: "deepseek-r1:70b", + description: "DeepSeek-R1 70B" + }, + { + id: "deepseek-r1:32b", + description: "DeepSeek-R1 32B" + }, + { + id: "deepseek-r1:14b", + description: "DeepSeek-R1 14B" + }, + { + id: "deepseek-r1:8b", + description: "DeepSeek-R1 8B" + }, + { + id: "deepseek-r1:7b", + description: "DeepSeek-R1 7B" + }, + { + id: "deepseek-r1:1.5b", + description: "DeepSeek-R1 1.5B" + }, + + // Llama3.1 models + { + id: "llama3.1:405b", + description: "Llama 3.1 405B" + }, + { + id: "llama3.1:70b", + description: "Llama 3.1 70B" + }, + { + id: "llama3.1:8b", + description: "Llama 3.1 8B" + }, + + // Gemma2 models + { + id: "gemma2:2b", + description: "Gemma2 2B" + }, + { + id: "gemma2:9b", + description: "Gemma2 9B" + }, + { + id: "gemma2:27b", + description: "Gemma2 27B" + }, + + // Qwen2.5 models + { + id: "qwen2.5:0.5b", + description: "Qwen2.5 0.5B" + }, + { + id: "qwen2.5:1.5b", + description: "Qwen2.5 1.5B" + }, + { + id: "qwen2.5:3b", + description: "Qwen2.5 3B" + }, + { + id: "qwen2.5:7b", + description: "Qwen2.5 7B" + }, + { + id: "qwen2.5:14b", + description: "Qwen2.5 14B" + }, + { + id: "qwen2.5:32b", + description: "Qwen2.5 32B" + }, + { + id: "qwen2.5:72b", + description: "Qwen2.5 72B" + }, + + // Mistral models + { + id: "mistral:7b", + description: "Mistral 7B" + }, + { + id: "mistral-nemo:12b", + description: "Mistral Nemo 12B" + }, + { + id: "mistral-large:123b", + description: "Mistral Large 123B" + }, + + // Command-R models + { + id: "command-r:35b", + description: "Command-R 35B" + }, + { + id: "command-r-plus:104b", + description: "Command-R Plus 104B" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/openai.jsonnet new file mode 100644 index 00000000..75b2670f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/openai.jsonnet @@ -0,0 +1,31 @@ +// OpenAI LLM Model Definitions +// Defines available models and their configurations for OpenAI's platform + +{ + "type": "string", + "description": "LLM model to use", + "default": "gpt-4o", + "enum": [ + { + id: "gpt-4o", + description: "GPT-4o (latest)" + }, + { + id: "gpt-4o-mini", + description: "GPT-4o Mini" + }, + { + id: "gpt-4-turbo", + description: "GPT-4 Turbo" + }, + { + id: "gpt-4", + description: "GPT-4" + }, + { + id: "gpt-3.5-turbo", + description: "GPT-3.5 Turbo" + }, + ], + "required": true +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/temperature-param-types.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/temperature-param-types.jsonnet new file mode 100644 index 00000000..f5523e88 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/temperature-param-types.jsonnet @@ -0,0 +1,15 @@ +// LLM temperature definitions + +{ + "llm-temperature": { + "type": "float", + "description": "LLM temperature", + "placeholder": 0.3, + "helper": "A floating point number between 0 and 1", + "default": 0.3, + "min": 0.0, + "max": 10.0, + "required": true + } +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/vertexai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/vertexai.jsonnet new file mode 100644 index 00000000..6d90f8d3 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/vertexai.jsonnet @@ -0,0 +1,68 @@ +// VertexAI LLM Model Definitions +// Defines available models and their configurations for Google's VertexAI platform + +{ + "type": "string", + "description": "LLM model to use", + "default": "gemini-2.5-flash-lite", + "enum": [ + // Gemini 2.5 models (latest generation) + { + id: "gemini-2.5-pro", + description: "Gemini 2.5 Pro" + }, + { + id: "gemini-2.5-flash", + description: "Gemini 2.5 Flash" + }, + { + id: "gemini-2.5-flash-lite", + description: "Gemini 2.5 Flash Lite" + }, + + // Gemini 2.0 models + { + id: "gemini-2.0-flash-exp", + description: "Gemini 2.0 Flash (experimental)" + }, + + + // Claude models on VertexAI + { + id: "claude-3-5-sonnet@20241022", + description: "Claude 3.5 Sonnet (via VertexAI)" + }, + { + id: "claude-3-5-haiku@20241022", + description: "Claude 3.5 Haiku (via VertexAI)" + }, + { + id: "claude-3-opus@20240229", + description: "Claude 3 Opus (via VertexAI)" + }, + { + id: "claude-3-sonnet@20240229", + description: "Claude 3 Sonnet (via VertexAI)" + }, + { + id: "claude-3-haiku@20240307", + description: "Claude 3 Haiku (via VertexAI)" + }, + + // Llama models on VertexAI + { + id: "llama3-405b-instruct-maas", + description: "Llama 3 405B Instruct (via VertexAI)" + }, + { + id: "llama3-70b-instruct-maas", + description: "Llama 3 70B Instruct (via VertexAI)" + }, + { + id: "llama3-8b-instruct-maas", + description: "Llama 3 8B Instruct (via VertexAI)" + }, + ], + "required": true +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/vllm.jsonnet new file mode 100644 index 00000000..120342b2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/parameters/vllm.jsonnet @@ -0,0 +1,18 @@ +// vLLM Model Definitions +// Defines available models and their configurations for vLLM +// vLLM works with any HuggingFace model. We have to use the model +// vLLM was initialised with + +{ + "type": "string", + "description": "LLM model to use", + "default": "model", + "enum": [ + // Llama 3.1 models + { + id: "model", + description: "Pre-defined model" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/profiles/memory-profile-low.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/profiles/memory-profile-low.jsonnet new file mode 100644 index 00000000..e6341062 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/profiles/memory-profile-low.jsonnet @@ -0,0 +1,169 @@ +// Low memory profile - reduces memory allocation across components. +// Include this at the END of your configuration to override default memory +// settings with lower values suitable for memory-constrained environments. +// +// Usage: {"name": "memory-profile-low", "parameters": {}} +// +// Note: This trades some performance headroom for reduced memory usage. +// Monitor for OOM errors under heavy load. + +{ + + // Override Pulsar stack memory settings + "pulsar" +: { + + // Zookeeper: 512M -> 300M + "zk-memory-limit":: "300M", + "zk-memory-reservation":: "200M", + "zk-heap":: "128m", + "zk-direct-memory":: "64m", + + // Bookie: 1024M -> 600M + "bookie-memory-limit":: "600M", + "bookie-memory-reservation":: "400M", + "bookie-heap":: "128m", + "bookie-direct-memory":: "128m", + + // Broker: 800M -> 512M + "broker-memory-limit":: "512M", + "broker-memory-reservation":: "400M", + "broker-heap":: "192m", + "broker-direct-memory":: "192m", + + // Pulsar-init: 256M -> 128M + "init-memory-limit":: "128M", + "init-memory-reservation":: "128M", + "init-heap":: "64m", + "init-direct-memory":: "64m", + + }, + + // Override Cassandra memory settings: 1000M -> 600M + "cassandra" +: { + "memory-limit":: "600M", + "memory-reservation":: "500M", + "heap":: "200M", + }, + + // Override Qdrant memory settings: 1024M -> 600M + // Also enables mmap for vectors/payloads (trades latency for memory) + "qdrant" +: { + "memory-limit":: "600M", + "memory-reservation":: "500M", + "memmap-threshold-kb":: "1", + "on-disk-payload":: "true", + }, + + // TrustGraph core services - 50% memory reservations + "api-gateway" +: { + "memory-reservation":: "256M", // 512M -> 256M + }, + + "chunker" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "config-svc" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "pdf-decoder" +: { + "memory-reservation":: "256M", // 512M -> 256M + }, + + "mcp-tool" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "mcp-server" +: { + "memory-reservation":: "128M", // 256M -> 128M + }, + + "metering" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "metering-rag" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "kg-store" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "kg-manager" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "prompt" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "prompt-rag" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "document-rag" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "document-embeddings" +: { + "memory-reservation":: "256M", // 512M -> 256M + }, + + "librarian" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "agent-manager" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + // Graph RAG services + "kg-extract-definitions" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "kg-extract-relationships" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "kg-extract-agent" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "kg-extract-ontology" +: { + "memory-reservation":: "150M", // 300M -> 150M + }, + + "kg-extract-objects" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "graph-rag" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "graph-embeddings" +: { + "memory-reservation":: "256M", // 512M -> 256M + }, + + // Structured data services + "nlp-query" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "structured-query" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "structured-diag" +: { + "memory-reservation":: "48M", // 96M -> 48M + }, + + // Init service + "init-trustgraph" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/agent-prompt.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/agent-prompt.txt new file mode 100644 index 00000000..9155d20f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/agent-prompt.txt @@ -0,0 +1,104 @@ +# ReAct Agent System Prompt + +You are an AI assistant that uses the ReAct (Reasoning + Acting) framework to solve problems through systematic reasoning and tool use. + +## Core Instructions + +For each user query, work through the problem step-by-step using this cycle: +1. **Thought**: Reason about the current situation and determine what you need to do next +2. **Action**: Take ONE specific action using an available tool +3. Wait for **Observation**: The system will provide the result of your action +4. Continue with the next **Thought** based on the observation + +**CRITICAL**: Generate exactly ONE Thought followed by ONE Action, then STOP. Do not generate multiple Thought/Action pairs in a single response. Do not generate Observations yourself - the system will provide them. + +## Response Format + +Use this exact format for each step: + +``` +Thought: [Your reasoning about what to do next - be specific about why this action is needed] +Action: [tool_name] +Args: { + "parameter_name": "value", + "another_parameter": 123, + "list_parameter": ["item1", "item2"] +} +``` + +When you have finished provide the final answer: + +``` +Thought: [Your reasoning about why the process is complete] +Final Answer: [The final answer] +``` + +When providing a final answer, do not provide an Action or Args. + +## Action Format Rules + +1. **Tool Name**: Write "Action: " followed by the exact tool name on its own line +2. **Arguments**: Write "Args: " followed by a valid JSON object containing all parameters +3. **JSON Requirements**: + - Use double quotes for all string keys and values + - Numbers don't need quotes: `"count": 5` + - Booleans: `"enabled": true` or `"enabled": false` + - Arrays: `"items": ["a", "b", "c"]` + - Nested objects: `"config": {"setting": "value"}` + - Null values: `"optional_field": null` +4. **Required Parameters**: Include all required parameters for the tool +5. **No Extra Text**: Don't add explanations or comments within the Action block +1. **Final answer**: Write "Final Answer: " followed by the final answer + +## Available Tools + +{% for tool in tools %}- **{{ tool.name }}**: {{ tool.description }} +{% for arg in tool.arguments %} - Required: `"{{ arg.name }}"` ({{ arg.type }}): {{ arg.description }} +{% endfor %} +{% endfor %} + +## Behavior Rules + +1. **One Step at a Time**: Generate exactly one Thought and one Action, then wait for the system to provide an Observation +2. **Be Specific**: Your Thought should clearly explain why you're taking the specific action +3. **Use Context**: Build on previous Observations to inform your next steps +4. **Error Handling**: If an action fails, reason about the error and try a different approach +5. **Completion**: When you have enough information to fully answer the user's query, generate a final Thought explaining your conclusion, but do not take further actions + +## Error Responses + +If an action fails, you'll see: +``` +Observation: Error: [specific error message] +``` + +When this happens: +- Generate a Thought analyzing what went wrong +- Take a corrective Action with different parameters or a different tool +- If a tool is completely unavailable, explain this limitation in your next Thought + +## Termination + +The conversation ends when: +- You determine you have sufficient information to answer the user's query completely and provide a final answer. +- You encounter an unrecoverable error that prevents task completion +- The system reaches the maximum iteration limit + +## Important Notes + +- **Never generate Observations yourself** - only the system provides these +- **Always validate your JSON** - malformed JSON will cause action failures +- **Stay focused** - each Thought should directly relate to solving the user's query +- **Be efficient** - choose actions that gather the most relevant information for the task + +# Proceed + +Question: {{question}} + +{% for h in history %} +Action: "{{h.action}}" +Args: { +{% for k, v in h.arguments.items() %} "{{k}}": "{{v}}" +{% endfor %}} +Observation: "{{h.observation}}" +{% endfor %} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/cohere.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/cohere.jsonnet new file mode 100644 index 00000000..9541e4c2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/cohere.jsonnet @@ -0,0 +1,42 @@ +// For Cohere. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/default-prompts.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/default-prompts.jsonnet new file mode 100644 index 00000000..35dda682 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/default-prompts.jsonnet @@ -0,0 +1,234 @@ + +// Prompt templates. For tidy JSONNET use, don't change these templates +// here, but use over-rides in the prompt directory + +{ + + "system-template":: "You are a helpful assistant.", + + "templates":: { + + "question":: { + "prompt": "{{question}}", + }, + + "extract-definitions":: { + "prompt": "\nStudy the following text and derive definitions for any discovered entities.\nDo not provide definitions for entities whose definitions are incomplete\nor unknown.\nOutput relationships in JSON format as an array of objects with fields:\n- entity: the name of the entity\n- definition: English text which defines the entity\n\n\n\n{{text}}\n\n\n\nYou will respond only with raw JSON format data. Do not provide\nexplanations. Do not use special characters in the abstract text. The\nabstract will be written as plain text. Do not add markdown formatting\nor headers or prefixes. Do not include null or unknown definitions.\n", + "response-type": "json", + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "entity": { + "type": "string" + }, + "definition": { + "type": "string" + } + }, + "required": [ + "entity", + "definition" + ] + } + } + }, + + "extract-relationships":: { + "prompt": "\nStudy the following text and derive entity relationships. For each\nrelationship, derive the subject, predicate and object of the relationship.\nOutput relationships in JSON format as an array of objects with fields:\n- subject: the subject of the relationship\n- predicate: the predicate\n- object: the object of the relationship\n- object-entity: false if the object is a simple data type: name, value or date. true if it is an entity.\n\n\n\n{{text}}\n\n\n\nYou will respond only with raw JSON format data. Do not provide\nexplanations. Do not use special characters in the abstract text. The\nabstract must be written as plain text. Do not add markdown formatting\nor headers or prefixes.\n", + "response-type": "json", + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "subject": { + "type": "string" + }, + "predicate": { + "type": "string" + }, + "object": { + "type": "string" + }, + "object-entity": { + "type": "boolean" + }, + }, + "required": [ + "subject", + "predicate", + "object", + "object-entity" + ] + } + } + }, + + "extract-topics":: { + "prompt": "You are a helpful assistant that performs information extraction tasks for a provided text.\nRead the provided text. You will identify topics and their definitions in JSON.\n\nReading Instructions:\n- Ignore document formatting in the provided text.\n- Study the provided text carefully.\n\nHere is the text:\n{{text}}\n\nResponse Instructions: \n- Do not respond with special characters.\n- Return only topics that are concepts and unique to the provided text.\n- Respond only with well-formed JSON.\n- The JSON response shall be an array of objects with keys \"topic\" and \"definition\". \n- The JSON response shall use the following structure:\n\n```json\n[{\"topic\": string, \"definition\": string}]\n```\n\n- Do not write any additional text or explanations.", + "response-type": "json", + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "topic": { + "type": "string" + }, + "definition": { + "type": "string" + } + }, + "required": [ + "topic", + "definition" + ] + } + } + }, + + "extract-rows":: { + "prompt": "\nStudy the following text and derive objects which match the schema provided.\n\nYou must output an array of JSON objects for each object you discover\nwhich matches the schema. For each object, output a JSON object whose fields\ncarry the name field specified in the schema.\n\n\n\n{{schema}}\n\n\n\n{{text}}\n\n\n\nYou will respond only with raw JSON format data. Do not provide\nexplanations. Do not add markdown formatting or headers or prefixes.\n", + "response-type": "json", + }, + + "kg-prompt":: { + "prompt": "Study the following set of knowledge statements. The statements are written in Cypher format that has been extracted from a knowledge graph. Use only the provided set of knowledge statements in your response. Do not speculate if the answer is not found in the provided set of knowledge statements.\n\nHere's the knowledge statements:\n{% for edge in knowledge %}({{edge.s}})-[{{edge.p}}]->({{edge.o}})\n{%endfor%}\n\nUse only the provided knowledge statements to respond to the following:\n{{query}}\n", + "response-type": "text", + }, + + "document-prompt":: { + "prompt": "Study the following context. Use only the information provided in the context in your response. Do not speculate if the answer is not found in the provided set of knowledge statements.\n\nHere is the context:\n{{documents}}\n\nUse only the provided knowledge statements to respond to the following:\n{{query}}\n", + "response-type": "text", + }, + + "agent-react":: { + "prompt": importstr "agent-prompt.txt", + "response-type": "text" + }, + + "agent-kg-extract":: { + "prompt": "Analyze the following text and extract both entity definitions and relationships. Return the results as JSON with 'definitions' and 'relationships' arrays.\n\nFor definitions, extract entities and their explanations or descriptions.\nFor relationships, extract subject-predicate-object triples where subjects and objects are entities, and predicates are relationship types.\n\nText: {{text}}\n\nReturn JSON only, no other text. Use this exact format:\n{\n \"definitions\": [\n {\n \"entity\": \"entity_name\",\n \"definition\": \"definition_text\"\n }\n ],\n \"relationships\": [\n {\n \"subject\": \"subject_entity\",\n \"predicate\": \"relationship_type\",\n \"object\": \"object_entity_or_literal\",\n \"object-entity\": true\n }\n ]\n}\n", + "response-type": "json", + "schema": { + "type": "object", + "properties": { + "definitions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "entity": { + "type": "string" + }, + "definition": { + "type": "string" + } + }, + "required": [ + "entity", + "definition" + ] + } + }, + "relationships": { + "type": "array", + "items": { + "type": "object", + "properties": { + "subject": { + "type": "string" + }, + "predicate": { + "type": "string" + }, + "object": { + "type": "string" + }, + "object-entity": { + "type": "boolean" + } + }, + "required": [ + "subject", + "predicate", + "object" + ] + } + } + }, + "required": [ + "definitions", + "relationships" + ] + } + }, + + "schema-selection":: { + "prompt": importstr "schema-selection.txt", + "response-type": "json", + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "description": "An array of schema names that are relevant to answering the given question" + } + }, + + "graphql-generation":: { + "prompt": importstr "graphql-generation.txt", + "response-type": "json", + "schema": { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "The GraphQL query string generated to answer the question" + }, + "variables": { + "type": "object", + "description": "Object containing any GraphQL variables needed for the query", + "additionalProperties": true + }, + "confidence": { + "type": "number", + "minimum": 0.0, + "maximum": 1.0, + "description": "Float between 0.0-1.0 indicating confidence in the generated query" + } + }, + "required": ["query", "variables", "confidence"], + "additionalProperties": false + } + }, + + "diagnose-structured-data":: { + "prompt": importstr "diagnose-structured-data.txt", + "response-type": "json", + }, + + "diagnose-xml":: { + "prompt": importstr "diagnose-xml.txt", + "response-type": "json", + }, + "diagnose-json":: { + "prompt": importstr "diagnose-json.txt", + "response-type": "json", + }, + "diagnose-csv":: { + "prompt": importstr "diagnose-csv.txt", + "response-type": "json", + }, + + "extract-with-ontologies":: { + "prompt": importstr "ontology-prompt.txt", + "response-type": "json", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/diagnose-csv.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/diagnose-csv.txt new file mode 100644 index 00000000..ad9725ba --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/diagnose-csv.txt @@ -0,0 +1,370 @@ +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for data import pipelines, with particular expertise in CSV processing and delimiter-separated value formats. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured CSV data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## CSV Processing Expertise + +When working with CSV data, you must: + +1. **Analyze CSV Structure** - Examine headers, delimiters, quoting, and data patterns +2. **Identify Column Mappings** - Map source column names to target fields +3. **Handle Complex CSV Patterns** - Support various CSV formats including: + - Standard comma-separated values: `name,age,city` + - Alternative delimiters: tab-separated (TSV), pipe-separated, semicolon-separated + - Quoted fields with embedded delimiters: `"Last, First",25,"New York, NY"` + - Headers with spaces or special characters: `"Customer Name","Order Date","Total Amount"` + - Files with or without headers + - Multi-line fields with embedded newlines + +## CSV Format Configuration Guidelines + +For CSV format configurations, use these patterns: + +**Basic CSV Configuration:** +```json +{ + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + "delimiter": ",", + "quote_char": "\"", + "has_header": true, + "skip_rows": 0 + } + } +} +``` + +**Advanced CSV Options:** +```json +{ + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + "delimiter": "\t", // Tab-separated + "quote_char": "\"", + "escape_char": "\\", + "has_header": true, + "skip_rows": 2, // Skip metadata rows + "null_values": ["", "NULL", "N/A"], + "trim_whitespace": true, + "skip_blank_lines": true + } + } +} +``` + +**CRITICAL: Source Field Names in Mappings** + +When processing CSV files, the parser uses column headers (if present) or generates column indices as field names. Your source field names in mappings must match exactly: + +**CORRECT Example with Headers:** +CSV file: +```csv +Customer Name,Order Date,Total Amount,Status +John Smith,2024-01-15,1000.50,Active +Jane Doe,2024-01-16,750.25,Pending +``` + +Becomes parsed data: +```json +{ + "Customer Name": "John Smith", + "Order Date": "2024-01-15", + "Total Amount": "1000.50", + "Status": "Active" +} +``` + +Your mappings should use: +```json +{ + "source_field": "Customer Name", // ✅ Correct - matches header exactly + "source_field": "Order Date", // ✅ Correct - matches header exactly + "source_field": "Total Amount", // ✅ Correct - matches header exactly + "source_field": "Status" // ✅ Correct - matches header exactly +} +``` + +**CORRECT Example without Headers:** +CSV file without headers uses column indices: +```csv +John Smith,2024-01-15,1000.50,Active +Jane Doe,2024-01-16,750.25,Pending +``` + +Becomes parsed data: +```json +{ + "0": "John Smith", + "1": "2024-01-15", + "2": "1000.50", + "3": "Active" +} +``` + +Your mappings should use: +```json +{ + "source_field": "0", // ✅ Correct - first column + "source_field": "1", // ✅ Correct - second column + "source_field": "2", // ✅ Correct - third column + "source_field": "3" // ✅ Correct - fourth column +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **Source Data Format** + - Delimiter character (comma, tab, pipe, semicolon, etc.) + - Quote character and escape character + - **For CSV**: Does the file have headers? Sample structure + - Text encoding (UTF-8, Windows-1252, etc.) + - Any rows to skip (metadata, blank lines) + - How null/empty values are represented + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source column → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + - Date/time format conversions + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or invalid data + - Duplicate handling strategy + - Row-level validation rules + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## CSV Structure Analysis + +When presented with CSV data, analyze: + +1. **Delimiter Detection**: What character separates the fields? +2. **Header Presence**: Does the first row contain column names? +3. **Quote Pattern**: Are fields quoted? What quote character is used? +4. **Data Types**: What types are present in each column? +5. **Null Representation**: How are empty/null values represented? +6. **Special Characters**: Are there embedded commas, quotes, or newlines? +7. **Encoding Issues**: Are there any character encoding problems? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + // CSV-specific parsing options + // delimiter, quote_char, has_header, skip_rows, etc. + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` +- `split`, `strip_quotes` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` +- `parse_number`, `parse_currency` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` +- `clean_whitespace`, `normalize_encoding` + +**Date/Time Operations:** +- `parse_date`, `format_date`, `date_component` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` +- `numeric_range`, `date_range` + +## CSV-Specific Best Practices + +1. **Detect delimiters accurately** - test with sample data to confirm delimiter +2. **Handle quoted fields properly** - account for embedded delimiters and quotes +3. **Trim whitespace consistently** - decide whether to preserve or remove extra spaces +4. **Validate data types early** - catch type conversion errors at the field level +5. **Handle empty values explicitly** - distinguish between empty strings and nulls +6. **Account for encoding issues** - especially with international characters +7. **Validate row structure** - ensure consistent column counts across rows + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes +9. **Handle CSV-specific edge cases** like malformed quotes, inconsistent delimiters +10. **Test with sample data** to validate parsing configuration + +## Complete CSV Example + +Given this CSV structure: +```csv +Customer Name,Order Date,Total Amount,Currency,Status +"Smith, John",2024-01-15,1000.50,USD,Active +"Doe, Jane",2024-01-16,750.25,EUR,Pending +"Johnson, Bob",2024-01-17,,USD,Cancelled +``` + +The parser will: +1. Use `delimiter: ","` and `quote_char: "\""` to parse fields correctly +2. Use `has_header: true` to treat first row as column names +3. Create this parsed data structure for each record: + ```json + { + "Customer Name": "Smith, John", + "Order Date": "2024-01-15", + "Total Amount": "1000.50", + "Currency": "USD", + "Status": "Active" + } + ``` + +Generate this COMPLETE configuration: +```json +{ + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + "delimiter": ",", + "quote_char": "\"", + "has_header": true, + "trim_whitespace": true, + "null_values": ["", "NULL"] + } + }, + "mappings": [ + { + "source_field": "Customer Name", // ✅ Matches header exactly + "target_field": "customer_name", + "transforms": [{"type": "trim"}] + }, + { + "source_field": "Order Date", // ✅ Matches header exactly + "target_field": "order_date", + "transforms": [{"type": "to_date", "format": "YYYY-MM-DD"}], + "validation": [{"type": "required"}] + }, + { + "source_field": "Total Amount", // ✅ Matches header exactly + "target_field": "amount", + "transforms": [ + {"type": "to_float"}, + {"type": "default", "value": 0.0} + ] + }, + { + "source_field": "Currency", // ✅ Matches header exactly + "target_field": "currency_code", + "transforms": [{"type": "upper"}], + "validation": [{"type": "in_list", "values": ["USD", "EUR", "GBP"]}] + }, + { + "source_field": "Status", // ✅ Matches header exactly + "target_field": "order_status", + "transforms": [{"type": "lower"}] + } + ] +} +``` + +**KEY RULE: source_field names must match the column headers exactly, or use column indices (0, 1, 2, etc.) for files without headers.** + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the CSV structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to delimiter detection, header identification, quoting patterns, and data type inference: + +{{sample}} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/diagnose-json.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/diagnose-json.txt new file mode 100644 index 00000000..2fe6341d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/diagnose-json.txt @@ -0,0 +1,327 @@ +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for data import pipelines, with particular expertise in JSON processing and JSONPath expressions. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured JSON data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## JSON Processing Expertise + +When working with JSON data, you must: + +1. **Analyze JSON Structure** - Examine the hierarchy, array patterns, and object nesting +2. **Generate Proper JSONPath Expressions** - Create efficient JSONPath selectors for record extraction +3. **Handle Complex JSON Patterns** - Support various JSON formats including: + - Array of objects: `[{"name": "John", "age": 30}, {...}]` + - Nested object arrays: `{"data": {"records": [{"id": 1}, {...}]}}` + - Mixed hierarchies with both arrays and nested objects + - Single object records: `{"record": {"field1": "value1"}}` + +## JSONPath Expression Guidelines + +For JSON format configurations, use these JSONPath patterns: + +**Record Path Examples:** +- Root array: `$[*]` (for arrays at the root level) +- Nested arrays: `$.data.records[*]` or `$.response.items[*]` +- Single object: `$.record` (when there's one record per file) +- Deep nesting: `$.data.results.items[*]` + +**Field Access Patterns:** +- Direct properties: Use property names directly in mappings +- Nested properties: Use dot notation like `address.street` or `contact.email` +- Array elements: Use bracket notation like `tags[0]` for first element + +**CRITICAL: Source Field Names in Mappings** + +When processing JSON, the parser creates a flat or nested dictionary based on the record structure. Your source field names in mappings must match the actual property names in the parsed records: + +**CORRECT Example:** +```json +{ + "Country or Area": "Albania", + "Trade (USD)": "1000.50", + "metadata": { + "source": "UN", + "year": 2024 + } +} +``` + +Your mappings should use: +```json +{ + "source_field": "Country or Area", // ✅ Correct - matches property name + "source_field": "Trade (USD)", // ✅ Correct - matches property name + "source_field": "metadata.source", // ✅ Correct - nested property access + "source_field": "metadata.year" // ✅ Correct - nested property access +} +``` + +**JSON Format Configuration Template:** +```json +{ + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + "record_path": "$[*]", // JSONPath to extract records + "flatten_nested": true, // Whether to flatten nested objects + "array_handling": "expand" // How to handle arrays: expand, first, concat + } + } +} +``` + +**Alternative JSON Options:** +```json +{ + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + "record_path": "$.data.items[*]", // For nested array structures + "flatten_nested": false, // Keep nested structure + "null_value_handling": "skip" // skip, empty_string, or preserve + } + } +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **Source Data Format** + - JSON structure type (array of objects, nested objects, single records) + - **For JSON**: Sample structure, nesting patterns, array locations + - Sample data or field descriptions + - Any format-specific details (encoding, special null handling, etc.) + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source field → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + - Nested object flattening requirements + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or null values + - Duplicate handling strategy + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## JSON Structure Analysis + +When presented with JSON data, analyze: + +1. **Root Structure**: Is it an array, object, or nested structure? +2. **Record Location**: Where are individual records located in the hierarchy? +3. **Field Pattern**: How are field names and values structured? + - Direct properties: `{"name": "John"}` + - Nested objects: `{"contact": {"email": "john@example.com"}}` + - Arrays: `{"tags": ["red", "blue"]}` + - Mixed types: `{"data": [{"id": 1, "details": {"name": "John"}}]}` +4. **Data Types**: What types are present (strings, numbers, booleans, nulls, arrays, objects)? +5. **Hierarchy Depth**: How deeply nested are the records and fields? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + // JSON-specific parsing options + // record_path (JSONPath), flatten_nested, array_handling, etc. + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` +- `flatten_object`, `extract_array_element`, `join_array` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` + +## JSON-Specific Best Practices + +1. **Use efficient JSONPath expressions** - Prefer specific paths over broad searches +2. **Handle nested objects appropriately** - decide whether to flatten or preserve structure +3. **Consider array handling strategies** - expand arrays to multiple records or extract specific elements +4. **Account for null vs undefined** values in field mappings +5. **Handle mixed data types** within the same field across records +6. **Use appropriate flattening** for deeply nested structures + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes +9. **Handle JSON-specific edge cases** like empty arrays, null objects, mixed types + +## Complete JSON Example + +Given this JSON structure: +```json +{ + "data": { + "records": [ + { + "Country": "USA", + "Year": 2024, + "Amount": 1000.50, + "metadata": { + "source": "World Bank", + "confidence": 0.95 + } + } + ] + } +} +``` + +The parser will: +1. Use `record_path: "$.data.records[*]"` to extract record objects from the array +2. Create this parsed data structure for each record: + ```json + { + "Country": "USA", + "Year": 2024, + "Amount": 1000.50, + "metadata.source": "World Bank", // If flattened + "metadata.confidence": 0.95 // If flattened + } + ``` + +Generate this COMPLETE configuration: +```json +{ + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + "record_path": "$.data.records[*]", + "flatten_nested": true, + "array_handling": "expand" + } + }, + "mappings": [ + { + "source_field": "Country", // ✅ Matches property name + "target_field": "country_name" + }, + { + "source_field": "Year", // ✅ Matches property name + "target_field": "year", + "transforms": [{"type": "to_int"}] + }, + { + "source_field": "Amount", // ✅ Matches property name + "target_field": "amount", + "transforms": [{"type": "to_float"}] + }, + { + "source_field": "metadata.source", // ✅ Flattened nested field + "target_field": "data_source" + } + ] +} +``` + +**KEY RULE: source_field names must match the actual property names in the parsed JSON records, using dot notation for nested properties when flattened.** + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the JSON structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to JSON hierarchy, object patterns, array structures, and generate appropriate JSONPath expressions: + +{{sample}} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/diagnose-structured-data.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/diagnose-structured-data.txt new file mode 100644 index 00000000..84c4b8be --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/diagnose-structured-data.txt @@ -0,0 +1,309 @@ + +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for data import pipelines, with particular expertise in XML processing and XPath expressions. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## XML Processing Expertise + +When working with XML data, you must: + +1. **Analyze XML Structure** - Examine the hierarchy, namespaces, and element patterns +2. **Generate Proper XPath Expressions** - Create efficient XPath selectors for record extraction +3. **Handle Complex XML Patterns** - Support various XML formats including: + - Standard element structures: `John` + - Attribute-based fields: `USA` + - Mixed content and nested hierarchies + - Namespaced XML documents + +## XPath Expression Guidelines + +For XML format configurations, use these XPath patterns: + +**Record Path Examples:** +- Simple records: `//record` or `//customer` +- Nested records: `//data/records/record` or `//customers/customer` +- Absolute paths: `/ROOT/data/record` (will be converted to relative paths automatically) +- With namespaces: `//ns:record` or `//soap:Body/data/record` + +**Field Attribute Patterns:** +- When fields use name attributes: set `field_attribute: "name"` for `value` +- For other attribute patterns: set appropriate attribute name + +**CRITICAL: Source Field Names in Mappings** + +When using `field_attribute`, the XML parser extracts field names from the attribute values and creates a flat dictionary. Your source field names in mappings must match these extracted names: + +**CORRECT Example:** +```xml +Albania +1000.50 +``` + +Becomes parsed data: +```json +{ + "Country or Area": "Albania", + "Trade (USD)": "1000.50" +} +``` + +So your mappings should use: +```json +{ + "source_field": "Country or Area", // ✅ Correct - matches parsed field name + "source_field": "Trade (USD)" // ✅ Correct - matches parsed field name +} +``` + +**INCORRECT Example:** +```json +{ + "source_field": "Field[@name='Country or Area']", // ❌ Wrong - XPath not needed here + "source_field": "field[@name='Trade (USD)']" // ❌ Wrong - XPath not needed here +} +``` + +**XML Format Configuration Template:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//data/record", // XPath to find record elements + "field_attribute": "name" // For value pattern + } + } +} +``` + +**Alternative XML Options:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//customer", // Direct element-based records + // No field_attribute needed for standard XML + } + } +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **Source Data Format** + - File type (CSV, JSON, XML, Excel, fixed-width, etc.) + - **For XML**: Sample structure, namespace prefixes, record element patterns + - Sample data or field descriptions + - Any format-specific details (delimiters, encoding, namespaces, etc.) + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source field → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or invalid data + - Duplicate handling strategy + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## XML Structure Analysis + +When presented with XML data, analyze: + +1. **Document Root**: What is the root element? +2. **Record Container**: Where are individual records located? +3. **Field Pattern**: How are field names and values structured? + - Direct child elements: `John` + - Attribute-based: `John` + - Mixed patterns +4. **Namespaces**: Are there any namespace prefixes? +5. **Hierarchy Depth**: How deeply nested are the records? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "[csv|json|xml|fixed-width|excel]", + "encoding": "utf-8", + "options": { + // Format-specific parsing options + // For XML: record_path (XPath), field_attribute (if applicable) + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` + +## XML-Specific Best Practices + +1. **Use efficient XPath expressions** - Prefer specific paths over broad searches +2. **Handle namespace prefixes** when present +3. **Identify field attribute patterns** correctly +4. **Test XPath expressions** mentally against the provided structure +5. **Consider XML element vs attribute data** in field mappings +6. **Account for mixed content** and nested structures + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes + +## Complete XML Example + +Given this XML structure: +```xml + + + + USA + 2024 + 1000.50 + + + +``` + +The parser will: +1. Use `record_path: "/ROOT/data/record"` to find record elements +2. Use `field_attribute: "name"` to extract field names from the name attribute +3. Create this parsed data structure: `{"Country": "USA", "Year": "2024", "Amount": "1000.50"}` + +Generate this COMPLETE configuration: +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "/ROOT/data/record", + "field_attribute": "name" + } + }, + "mappings": [ + { + "source_field": "Country", // ✅ Matches parsed field name + "target_field": "country_name" + }, + { + "source_field": "Year", // ✅ Matches parsed field name + "target_field": "year", + "transforms": [{"type": "to_int"}] + }, + { + "source_field": "Amount", // ✅ Matches parsed field name + "target_field": "amount", + "transforms": [{"type": "to_float"}] + } + ] +} +``` + +**KEY RULE: source_field names must match the extracted field names, NOT the XML element structure.** + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the XML structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to XML hierarchy, element patterns, and generate appropriate XPath expressions: + +{{sample}} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/diagnose-xml.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/diagnose-xml.txt new file mode 100644 index 00000000..a3d8efa4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/diagnose-xml.txt @@ -0,0 +1,453 @@ +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for XML data import pipelines, with particular expertise in XML processing and XPath expressions. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured XML data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## XML Processing Expertise + +When working with XML data, you must: + +1. **Analyze XML Structure** - Examine the hierarchy, namespaces, and element patterns +2. **Generate Proper XPath Expressions** - Create efficient XPath selectors for record extraction +3. **Handle Complex XML Patterns** - Support various XML formats including: + - Standard element structures: `John` + - Attribute-based fields: `USA` + - Mixed content and nested hierarchies + - Namespaced XML documents + - CDATA sections and text nodes + +## XPath Expression Guidelines + +For XML format configurations, use these XPath patterns: + +**Record Path Examples:** +- Simple records: `//record` or `//customer` +- Nested records: `//data/records/record` or `//customers/customer` +- Absolute paths: `/ROOT/data/record` +- With namespaces: `//ns:record` or `//soap:Body/data/record` +- Attribute-filtered: `//record[@type='customer']` + +**Field Attribute Patterns:** +- When fields use name attributes: set `field_attribute: "name"` for `value` +- For other attribute patterns: set appropriate attribute name like `field_attribute: "id"` or `field_attribute: "type"` + +## CRITICAL: Source Field Names in Mappings + +The behavior depends on whether you use `field_attribute`: + +### With field_attribute (Attribute-Based Fields) + +When using `field_attribute`, the XML parser extracts field names from the attribute values and creates a flat dictionary: + +**Example:** +```xml + + Albania + 1000.50 + Active + +``` + +Parser configuration: +```json +{ + "record_path": "//record", + "field_attribute": "name" +} +``` + +Becomes parsed data: +```json +{ + "Country or Area": "Albania", + "Trade (USD)": "1000.50", + "Status": "Active" +} +``` + +Your mappings should use: +```json +{ + "source_field": "Country or Area", // ✅ Correct - matches parsed field name + "source_field": "Trade (USD)", // ✅ Correct - matches parsed field name + "source_field": "Status" // ✅ Correct - matches parsed field name +} +``` + +### Without field_attribute (Element-Based Fields) + +When NOT using `field_attribute`, use direct element names or XPath expressions: + +**Example:** +```xml + + Albania + 1000.50 + Active + + UN + 2024 + + +``` + +Parser configuration: +```json +{ + "record_path": "//record" + // No field_attribute specified +} +``` + +Your mappings should use: +```json +{ + "source_field": "country", // ✅ Direct element name + "source_field": "trade_amount", // ✅ Direct element name + "source_field": "metadata/source", // ✅ Nested element path + "source_field": "metadata/year" // ✅ Nested element path +} +``` + +## XML Format Configuration Templates + +**For Attribute-Based Fields:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//data/record", // XPath to find record elements + "field_attribute": "name", // Extract field names from 'name' attribute + "namespace_prefixes": { // Optional: define namespaces + "ns": "http://example.com/namespace" + } + } + } +} +``` + +**For Element-Based Fields:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//customer", // XPath to find record elements + "preserve_namespaces": true, // Optional: keep namespace info + "ignore_attributes": false // Optional: include element attributes + } + } +} +``` + +**For Complex XML with Namespaces:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//soap:Body//data:record", + "namespace_prefixes": { + "soap": "http://schemas.xmlsoap.org/soap/envelope/", + "data": "http://example.com/data" + }, + "field_attribute": "name" + } + } +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **XML Structure Details** + - Sample XML structure or schema + - Root element and record location + - Field organization (elements vs attributes) + - Namespace declarations and prefixes + - Text encoding (UTF-8, ISO-8859-1, etc.) + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source field → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or invalid data + - Duplicate handling strategy + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## XML Structure Analysis + +When presented with XML data, analyze: + +1. **Document Root**: What is the root element? +2. **Namespaces**: Are there namespace declarations? What prefixes are used? +3. **Record Container**: Where are individual records located in the hierarchy? +4. **Field Pattern**: How are field names and values structured? + - Direct child elements: `John` + - Attribute-based: `John` + - Mixed patterns: `John` +5. **Data Location**: Are values in element text, attributes, or both? +6. **Hierarchy Depth**: How deeply nested are the records? +7. **Special Content**: Any CDATA sections, mixed content, or special characters? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + // XML-specific parsing options + // record_path (XPath), field_attribute (if applicable), namespace_prefixes + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` +- `strip_cdata`, `decode_html_entities` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` +- `parse_xml_date`, `parse_iso_date` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` +- `extract_attribute`, `get_text_content` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` +- `valid_xml_name`, `namespace_check` + +## XML-Specific Best Practices + +1. **Use efficient XPath expressions** - Prefer specific paths over broad searches like `//` when possible +2. **Handle namespace prefixes** when present - define them in `namespace_prefixes` +3. **Identify field attribute patterns** correctly - determine if using `field_attribute` +4. **Test XPath expressions** mentally against the provided structure +5. **Consider XML element vs attribute data** in field mappings +6. **Account for mixed content** and nested structures +7. **Handle CDATA sections** and special characters properly +8. **Preserve or normalize whitespace** as appropriate +9. **Consider XML schema validation** if XSD is available + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes +9. **Handle XML-specific edge cases** like empty elements, mixed content, processing instructions + +## Complete XML Examples + +### Example 1: Attribute-Based Fields + +Given this XML structure: +```xml + + + + USA + 2024 + 1000.50 + + + +``` + +The parser will: +1. Use `record_path: "//data/record"` to find record elements +2. Use `field_attribute: "name"` to extract field names from the name attribute +3. Create this parsed data structure: `{"Country": "USA", "Year": "2024", "Amount": "1000.50"}` + +Generate this configuration: +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//data/record", + "field_attribute": "name" + } + }, + "mappings": [ + { + "source_field": "Country", // ✅ Matches parsed field name + "target_field": "country_name" + }, + { + "source_field": "Year", // ✅ Matches parsed field name + "target_field": "year", + "transforms": [{"type": "to_int"}] + }, + { + "source_field": "Amount", // ✅ Matches parsed field name + "target_field": "amount", + "transforms": [{"type": "to_float"}] + } + ] +} +``` + +### Example 2: Element-Based Fields + +Given this XML structure: +```xml + + + John Smith + john@example.com +
+ 123 Main St + New York +
+ + + 456 + 100.50 + + +
+
+``` + +Generate this configuration: +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//customer" + } + }, + "mappings": [ + { + "source_field": "name", // ✅ Direct element name + "target_field": "customer_name" + }, + { + "source_field": "email", // ✅ Direct element name + "target_field": "email_address" + }, + { + "source_field": "address/street", // ✅ Nested element path + "target_field": "street_address" + }, + { + "source_field": "address/city", // ✅ Nested element path + "target_field": "city" + }, + { + "source_field": "@id", // ✅ Attribute reference + "target_field": "customer_id", + "transforms": [{"type": "to_int"}] + } + ] +} +``` + +**KEY RULES:** +- With `field_attribute`: source_field names must match the extracted attribute values +- Without `field_attribute`: use direct element names, nested paths, or XPath expressions +- Use `@attribute` notation for XML element attributes + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the XML structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to XML hierarchy, element patterns, namespace usage, and generate appropriate XPath expressions: + +{{sample}} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/gemini.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/gemini.jsonnet new file mode 100644 index 00000000..b9a1e0c0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/gemini.jsonnet @@ -0,0 +1,42 @@ +// For VertexAI Gemini. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/graphql-generation.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/graphql-generation.txt new file mode 100644 index 00000000..58af61dc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/graphql-generation.txt @@ -0,0 +1,59 @@ +You are a GraphQL query generation expert. Given a natural language question and provided database schemas, generate a valid GraphQL query embedded in valid JSON. + +## Question: +{{ question }} + +## Provided Schemas: +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %}- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif %}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif %}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ field.enum_values|join(', ') }}]{% endif %} +{% endfor %} +{% endfor %} + +## GraphQL Query Rules: +1. Use the schema names as GraphQL query fields (e.g., `customers`, `orders`) +2. Apply filters using the `where` parameter with nested filter objects +3. Available filter operators per field type: + - String fields: `eq`, `contains`, `startsWith`, `endsWith`, `in`, `not`, `not_in` + - Integer/Float fields: `eq`, `gt`, `gte`, `lt`, `lte`, `in`, `not`, `not_in` +4. Use `order_by` for sorting (field name as string) +5. Use `direction` for sort direction: `ASC` or `DESC` +6. Use `limit` to restrict number of results +7. Select specific fields in the query body + +## Task Instructions: +1. Analyze the question to identify: + - What data to retrieve (which fields to select) + - What filters to apply (where conditions) + - What sorting is needed (order_by, direction) + - How many results (limit) + +2. Generate a GraphQL query that: + - Uses only the provided schema names and field names + - Applies appropriate filters based on the question + - Selects relevant fields for the response + - Includes reasonable limits (default 100 if not specified) + +3. If variables are needed, include them in the response + +## Output Format: +Return ONLY a valid JSON object (no markdown, no code blocks) with these fields: +- "query": the GraphQL query string +- "variables": object with any GraphQL variables (empty object if none) +- "confidence": float between 0.0-1.0 indicating confidence in the query + +Important: Return raw JSON only, with no markdown formatting, no code blocks, and no backticks. + +## Examples: + +Question: "Show me customers from California" +{"query": "query { customers(where: {state: {eq: \"California\"}}, limit: 100) { customer_id name email state } }", "variables": {}, "confidence": 0.92} + +Question: "Top 10 products by price" +{"query": "query { products(order_by: \"price\", direction: DESC, limit: 10) { product_id name price category } }", "variables": {}, "confidence": 0.88} + +Question: "Recent orders over $100" +{"query": "query { orders(where: {total_amount: {gt: 100}, order_date: {gte: \"2024-01-01\"}}, order_by: \"order_date\", direction: DESC, limit: 50) { order_id customer_id total_amount order_date status } }", "variables": {}, "confidence": 0.96} + +Now generate the GraphQL query for the question above. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/mixtral.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/mixtral.jsonnet new file mode 100644 index 00000000..cd56e7ef --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/mixtral.jsonnet @@ -0,0 +1,42 @@ +// For Mixtral. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/ontology-prompt.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/ontology-prompt.txt new file mode 100644 index 00000000..5a0fcce0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/ontology-prompt.txt @@ -0,0 +1,126 @@ +You are a knowledge extraction expert. Your task is to find entities, relationships, and attributes in text based on a provided schema. + +## Entity Types + +These are the types of entities you should look for: + +{% for class_id, class_def in classes.items() %} +- **{{class_id}}**{% if class_def.subclass_of %} (subclass of {{class_def.subclass_of}}){% endif %}{% if class_def.comment %}: {{class_def.comment}}{% endif %} +{% endfor %} + +## Relationships + +These relationships connect entities to other entities: + +{% for prop_id, prop_def in object_properties.items() %} +- **{{prop_id}}**{% if prop_def.domain and prop_def.range %} ({{prop_def.domain}} → {{prop_def.range}}){% endif %}{% if prop_def.comment %}: {{prop_def.comment}}{% endif %} +{% endfor %} + +## Attributes + +These attributes describe entity properties (text, numbers, etc.): + +{% for prop_id, prop_def in datatype_properties.items() %} +- **{{prop_id}}**{% if prop_def.domain and prop_def.range %} ({{prop_def.domain}} → {{prop_def.range}}){% endif %}{% if prop_def.comment %}: {{prop_def.comment}}{% endif %} +{% endfor %} + +## Text to Analyze + +{{text}} + +## Your Task + +Extract the following from the text above: + +1. **Entities**: Things mentioned in the text and their types +2. **Relationships**: How entities relate to each other +3. **Attributes**: Properties of entities (like quantities, descriptions, etc.) + +## Output Format + +Return ONLY valid JSON (no markdown, no code blocks, no backticks) with this structure: + +``` +{ + "entities": [ + { + "entity": "entity name as it appears in text", + "type": "EntityType" + } + ], + "relationships": [ + { + "subject": "subject entity name", + "subject-type": "SubjectType", + "relation": "relationship_name", + "object": "object entity name", + "object-type": "ObjectType" + } + ], + "attributes": [ + { + "entity": "entity name", + "entity-type": "EntityType", + "attribute": "attribute_name", + "value": "literal value" + } + ] +} +``` + +## Important Rules + +1. **Entity names**: Use the exact text as it appears (e.g., "Cornish pasty", "beef") +2. **Types**: Use the EXACT type identifiers from the schema above (e.g., "fo/Recipe", "fo/Food") +3. **Relationships**: Use the EXACT relationship names from the schema (e.g., "fo/has_ingredient") +4. **Attributes**: Use the EXACT attribute names from the schema (e.g., "fo/serves") +5. **Type disambiguation**: If the same entity name appears with different types, list it separately for each type +6. **All arrays can be empty** if nothing is found + +## Example + +Input text: "Cornish pasty is a savory pastry filled with beef and potatoes. This recipe serves 4 people." + +Expected output: +{ + "entities": [ + { + "entity": "Cornish pasty", + "type": "fo/Recipe" + }, + { + "entity": "beef", + "type": "fo/Food" + }, + { + "entity": "potatoes", + "type": "fo/Food" + } + ], + "relationships": [ + { + "subject": "Cornish pasty", + "subject-type": "fo/Recipe", + "relation": "fo/has_ingredient", + "object": "beef", + "object-type": "fo/Food" + }, + { + "subject": "Cornish pasty", + "subject-type": "fo/Recipe", + "relation": "fo/has_ingredient", + "object": "potatoes", + "object-type": "fo/Food" + } + ], + "attributes": [ + { + "entity": "Cornish pasty", + "entity-type": "fo/Recipe", + "attribute": "fo/serves", + "value": "4 people" + } + ] +} + +Now extract entities, relationships, and attributes from the text above. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/openai.jsonnet new file mode 100644 index 00000000..5d232337 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/openai.jsonnet @@ -0,0 +1,42 @@ +// For OpenAI LLMs. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/schema-selection.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/schema-selection.txt new file mode 100644 index 00000000..39b180e5 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/schema-selection.txt @@ -0,0 +1,25 @@ +You are a database schema selection expert. Given a natural language question and available +database schemas, your job is to identify which schemas are most relevant to answer the question. + +## Available Schemas: +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}): {{ field.description }} +{% endfor %} + +{% endfor %} + +## Question: +{{ question }} + +## Instructions: +1. Analyze the question to understand what data is being requested +2. Examine each schema to understand what data it contains +3. Select ONLY the schemas that are directly relevant to answering the question +4. Return your answer as a JSON array of schema names + +## Response Format: +Return ONLY a JSON array of schema names, nothing else. +Example: ["customers", "orders", "products"] diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/slm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/slm.jsonnet new file mode 100644 index 00000000..48eb96d0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/prompts/slm.jsonnet @@ -0,0 +1,44 @@ +// For SLM. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + + + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/pulsar/pulsar-manager.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/pulsar/pulsar-manager.jsonnet new file mode 100644 index 00000000..c1ca4515 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/pulsar/pulsar-manager.jsonnet @@ -0,0 +1,40 @@ +local images = import "values/images.jsonnet"; + +{ + + "pulsar" +: { + + create:: function(engine) + +// FIXME: Should persist something? +// local volume = engine.volume(...) + + local container = + engine.container("pulsar") + .with_image(images.pulsar_manager) + .with_environment({ + SPRING_CONFIGURATION_FILE: "/pulsar-manager/pulsar-manager/application.properties", + }) + .with_limits("0.5", "1.4G") + .with_reservations("0.1", "1.4G") + .with_port(9527, 9527, "api") + .with_port(7750, 7750, "api2"); + + local containerSet = engine.containers( + "pulsar", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9527, 9527, "api") + .with_port(7750, 7750, "api2); + + engine.resources([ + containerSet, + service, + ]) + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/pulsar/pulsar.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/pulsar/pulsar.jsonnet new file mode 100644 index 00000000..d417322d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/pulsar/pulsar.jsonnet @@ -0,0 +1,222 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +// This is a Pulsar configuration. Non-standalone mode so we deploy +// individual components: bookkeeper, broker and zookeeper. +// +// This also deploys the TrustGraph 'admin' container which initialises +// TrustGraph-specific namespaces etc. + +{ + + "pulsar" +: { + + // Zookeeper memory settings (can be overridden by memory-profile) + "zk-memory-limit":: "512M", + "zk-memory-reservation":: "512M", + "zk-heap":: "256m", + "zk-direct-memory":: "256m", + + // Bookie memory settings (can be overridden by memory-profile) + "bookie-memory-limit":: "1024M", + "bookie-memory-reservation":: "1024M", + "bookie-heap":: "256m", + "bookie-direct-memory":: "256m", + + // Broker memory settings (can be overridden by memory-profile) + "broker-memory-limit":: "800M", + "broker-memory-reservation":: "800M", + "broker-heap":: "384m", + "broker-direct-memory":: "384m", + + // Pulsar-init memory settings (can be overridden by memory-profile) + "init-memory-limit":: "256M", + "init-memory-reservation":: "256M", + "init-heap":: "128m", + "init-direct-memory":: "128m", + + create:: function(engine) + + // Capture memory settings into locals (self refers to pulsar object here) + local zkMemLimit = self["zk-memory-limit"]; + local zkMemReserv = self["zk-memory-reservation"]; + local zkHeap = self["zk-heap"]; + local zkDirect = self["zk-direct-memory"]; + + local bookieMemLimit = self["bookie-memory-limit"]; + local bookieMemReserv = self["bookie-memory-reservation"]; + local bookieHeap = self["bookie-heap"]; + local bookieDirect = self["bookie-direct-memory"]; + + local brokerMemLimit = self["broker-memory-limit"]; + local brokerMemReserv = self["broker-memory-reservation"]; + local brokerHeap = self["broker-heap"]; + local brokerDirect = self["broker-direct-memory"]; + + local initMemLimit = self["init-memory-limit"]; + local initMemReserv = self["init-memory-reservation"]; + local initHeap = self["init-heap"]; + local initDirect = self["init-direct-memory"]; + + // Zookeeper volume + local zkVolume = engine.volume("zookeeper").with_size("1G"); + + // Zookeeper container + local zkContainer = + engine.container("zookeeper") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "bin/apply-config-from-env.py conf/zookeeper.conf && bin/generate-zookeeper-config.sh conf/zookeeper.conf && exec bin/pulsar zookeeper" + ]) + .with_limits("1", zkMemLimit) + .with_reservations("0.05", zkMemReserv) + .with_user("0:1000") + .with_volume_mount(zkVolume, "/pulsar/data/zookeeper") + .with_environment({ + "metadataStoreUrl": "zk:zookeeper:2181", + "PULSAR_MEM": "-Xms%s -Xmx%s -XX:MaxDirectMemorySize=%s" % [ + zkHeap, zkHeap, zkDirect, + ], + }) + .with_port(2181, 2181, "zookeeper") + .with_port(2888, 2888, "zookeeper2") + .with_port(3888, 3888, "zookeeper3"); + + // Pulsar cluster init container + local initContainer = + engine.container("pulsar-init") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "sleep 10 && bin/pulsar initialize-cluster-metadata --cluster cluster-a --zookeeper zookeeper:2181 --configuration-store zookeeper:2181 --web-service-url http://pulsar:8080 --broker-service-url pulsar://pulsar:6650", + ]) + .with_limits("1", initMemLimit) + .with_reservations("0.05", initMemReserv) + .with_environment({ + "PULSAR_MEM": "-Xms%s -Xmx%s -XX:MaxDirectMemorySize=%s" % [ + initHeap, initHeap, initDirect, + ], + }); + + + // Bookkeeper volume + local bookieVolume = engine.volume("bookie").with_size("20G"); + + // Bookkeeper container + local bookieContainer = + engine.container("bookie") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "bin/apply-config-from-env.py conf/bookkeeper.conf && exec bin/pulsar bookie" + // false ^ causes this to be a 'failure' exit. + ]) + .with_limits("1", bookieMemLimit) + .with_reservations("0.1", bookieMemReserv) + .with_user("0:1000") + .with_volume_mount(bookieVolume, "/pulsar/data/bookkeeper") + .with_environment({ + "clusterName": "cluster-a", + "zkServers": "zookeeper:2181", + "bookieId": "bookie", + "metadataStoreUri": "metadata-store:zk:zookeeper:2181", + "advertisedAddress": "bookie", + "BOOKIE_MEM": "-Xms%s -Xmx%s -XX:MaxDirectMemorySize=%s" % [ + bookieHeap, bookieHeap, bookieDirect, + ], + }) + .with_port(3181, 3181, "bookie"); + + // Pulsar broker, stateless (uses ZK and Bookkeeper for state) + local brokerContainer = + engine.container("pulsar") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "bin/apply-config-from-env.py conf/broker.conf && exec bin/pulsar broker" + ]) + .with_limits("1", brokerMemLimit) + .with_reservations("0.1", brokerMemReserv) + .with_environment({ + "metadataStoreUrl": "zk:zookeeper:2181", + "zookeeperServers": "zookeeper:2181", + "clusterName": "cluster-a", + "managedLedgerDefaultEnsembleSize": "1", + "managedLedgerDefaultWriteQuorum": "1", + "managedLedgerDefaultAckQuorum": "1", + "advertisedAddress": "pulsar", + "advertisedListeners": "external:pulsar://pulsar:6650,localhost:pulsar://localhost:6650", + "PULSAR_MEM": "-Xms%s -Xmx%s -XX:MaxDirectMemorySize=%s" % [ + brokerHeap, brokerHeap, brokerDirect, + ], + }) + .with_port(6650, 6650, "pulsar") + .with_port(8080, 8080, "admin"); + + // Container sets + local zkContainerSet = engine.containers( + "zookeeper", + [ + zkContainer, + ] + ); + + local initContainerSet = engine.containers( + "init-pulsar", + [ + initContainer, + ] + ); + + local bookieContainerSet = engine.containers( + "bookie", + [ + bookieContainer, + ] + ); + + local brokerContainerSet = engine.containers( + "pulsar", + [ + brokerContainer, + ] + ); + + // Zookeeper service + local zkService = + engine.service(zkContainerSet) + .with_port(2181, 2181, "zookeeper") + .with_port(2888, 2888, "zookeeper2") + .with_port(3888, 3888, "zookeeper3"); + + // Bookkeeper service + local bookieService = + engine.service(bookieContainerSet) + .with_port(3181, 3181, "bookie"); + + // Pulsar broker service + local brokerService = + engine.service(brokerContainerSet) + .with_port(6650, 6650, "pulsar") + .with_port(8080, 8080, "admin"); + + engine.resources([ + zkVolume, + bookieVolume, + zkContainerSet, + initContainerSet, + bookieContainerSet, + brokerContainerSet, + zkService, + bookieService, + brokerService, + ]) + + } + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-additionals.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-additionals.jsonnet new file mode 100644 index 00000000..3e9f3481 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-additionals.jsonnet @@ -0,0 +1,134 @@ +local decode = import "decode-config.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Custom engine that collects configVolume parts +local engine = { + + // Collection of all configVolume parts + configVolumes:: [], + + // Implement all required engine methods as no-ops + container:: function(name) { + with_image:: function(x) self, + with_command:: function(x) self, + with_environment:: function(x) self, + with_limits:: function(c, m) self, + with_reservations:: function(c, m) self, + with_port:: function(src, dest, name) self, + with_volume_mount:: function(vol, mnt) self, + with_user:: function(x) self, + with_runtime:: function(x) self, + with_privileged:: function(x) self, + with_ipc:: function(x) self, + with_capability:: function(x) self, + with_device:: function(hdev, cdev) self, + with_env_var_secrets:: function(vars) self, + }, + + volume:: function(name) { + with_size:: function(size) self, + }, + + // The key method - collects configVolume parts + configVolume:: function(name, dir, parts) + local collector = self + { + configVolumes: super.configVolumes + [ + { + dir: dir, + parts: parts, + } + ] + }; + { + // Return a dummy volume that has the collector in it + name: name, + with_size:: function(size) collector, + // Provide a way to get back to the collector + getCollector:: function() collector, + }, + + secretVolume:: function(name, dir, parts) { + with_size:: function(size) self, + }, + + envSecrets:: function(name) { + with_env_var:: function(name, key) self, + }, + + containers:: function(name, containers) self, + + service:: function(containers) { + with_port:: function(src, dest, name) self, + }, + + internalService:: function(containers) { + with_port:: function(src, dest, name) self, + }, + + resources:: function(res) + // Fold over resources and collect any configVolume state + local collected = std.foldl( + function(state, r) + if std.objectHasAll(r, 'getCollector') then + // Merge the configVolumes from the volume's collector into our state + local volumeCollector = r.getCollector(); + state + { + configVolumes: state.configVolumes + volumeCollector.configVolumes + } + else + state, + res, + self + ); + collected, +}; + +// Execute all component create() functions with our collecting engine +// Note: create:: is a hidden field, so we must use objectHasAll not objectHas +local result = std.foldl( + function(state, p) + if std.objectHasAll(p, 'create') then + // Pattern has create directly - call it + p.create(state) + else + state, + std.objectValues(patterns), + engine +); + +// Debug: show what we collected +local debug = { + numPatterns: std.length(std.objectValues(patterns)), + numConfigVolumes: std.length(result.configVolumes), +}; + +// Transform collected data into output format +local allFiles = std.flattenArrays([ + [ + { + // Remove trailing slash from dir to avoid double slashes + path: std.join("/", [std.rstripChars(cv.dir, "/"), filename]), + content: cv.parts[filename] + } + for filename in std.objectFields(cv.parts) + ] + for cv in result.configVolumes +]); + +// Deduplicate by path - use a map to keep only unique paths +local uniqueMap = std.foldl( + function(acc, item) acc + { [item.path]: item }, + allFiles, + {} +); + +// Convert back to array +local additionals = std.objectValues(uniqueMap); + +// Output the array +additionals diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-aks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-aks-k8s.jsonnet new file mode 100644 index 00000000..bcec1cbb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-aks-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "../engine/aks-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-docker-compose.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-docker-compose.jsonnet new file mode 100644 index 00000000..7f0dbabe --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-docker-compose.jsonnet @@ -0,0 +1,20 @@ + +local engine = import "../engine/docker-compose.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resources = std.foldl( + function(state, p) state + p.create(engine), + std.objectValues(patterns), + {} +); + +resources + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-eks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-eks-k8s.jsonnet new file mode 100644 index 00000000..f9de59cb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-eks-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "../engine/eks-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-gcp-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-gcp-k8s.jsonnet new file mode 100644 index 00000000..50037a5c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-gcp-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "../engine/gcp-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-minikube-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-minikube-k8s.jsonnet new file mode 100644 index 00000000..6fa64706 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-minikube-k8s.jsonnet @@ -0,0 +1,26 @@ + +local engine = import "../engine/minikube-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +// Extract resources using the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-noop.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-noop.jsonnet new file mode 100644 index 00000000..3ad735d9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-noop.jsonnet @@ -0,0 +1,20 @@ + +local engine = import "../engine/noop.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resources = std.foldl( + function(state, p) state + p.create(engine), + std.objectValues(patterns), + {} +); + +resources + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-ovh-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-ovh-k8s.jsonnet new file mode 100644 index 00000000..92f61041 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-ovh-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "../engine/ovh-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-podman-compose.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-podman-compose.jsonnet new file mode 100644 index 00000000..7f0dbabe --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-podman-compose.jsonnet @@ -0,0 +1,20 @@ + +local engine = import "../engine/docker-compose.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resources = std.foldl( + function(state, p) state + p.create(engine), + std.objectValues(patterns), + {} +); + +resources + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-scw-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-scw-k8s.jsonnet new file mode 100644 index 00000000..4f6c1d7a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-scw-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "../engine/scw-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-tg-configuration.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-tg-configuration.jsonnet new file mode 100644 index 00000000..59b4732a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/config-to-tg-configuration.jsonnet @@ -0,0 +1,15 @@ + +local engine = import "../engine/noop.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract configuration directly from patterns +patterns.configuration.configuration + + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/decode-config.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/decode-config.jsonnet new file mode 100644 index 00000000..759513c4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/renderers/decode-config.jsonnet @@ -0,0 +1,31 @@ + +local components = import "../components.jsonnet"; + +local apply = function(p, components) + + local base = { + + with:: function(k, v) self + { + [k] +:: v + }, + + with_params:: function(pars) + self + std.foldl( + function(obj, par) obj.with(par.key, par.value), + std.objectKeysValues(pars), + self + ), + + }; + + local component = base + components[p.name]; + + component.with_params(p.parameters); + +local decode = function(config) + local add = function(state, c) state + apply(c, components); + local patterns = std.foldl(add, config, {}); + patterns; + +decode + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/row-store/cassandra.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/row-store/cassandra.jsonnet new file mode 100644 index 00000000..7fdf433e --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/row-store/cassandra.jsonnet @@ -0,0 +1,77 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local cassandra = import "backends/cassandra.jsonnet"; + +cassandra + { + + "store-objects" +: { + + create:: function(engine) + + local container = + engine.container("store-objects") + .with_image(images.trustgraph_flow) + .with_command([ + "objects-write-cassandra", + "-p", + url.pulsar, + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-objects", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-objects" +: { + + create:: function(engine) + + local container = + engine.container("query-objects") + .with_image(images.trustgraph_flow) + .with_command([ + "objects-query-cassandra", + "-p", + url.pulsar, + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "512M") + .with_reservations("0.1", "512M"); + + local containerSet = engine.containers( + "query-objects", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/runtime-config/config-composer.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/runtime-config/config-composer.jsonnet new file mode 100644 index 00000000..e93ff044 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/runtime-config/config-composer.jsonnet @@ -0,0 +1,97 @@ +// Configuration Composer Module +// Orchestrates the complete configuration building process +// Combines all components into the final TrustGraph configuration + +local flow_builder = import "flow-builder.jsonnet"; +local interface_builder = import "interface-builder.jsonnet"; + +{ + // Main function to build the complete configuration + build: function(config_spec) + // Extract configuration parameters + local flow_blueprints = config_spec.flow_blueprints; + local default_flow_blueprint = config_spec.default_flow_blueprint; + local default_flow_id = config_spec.default_flow_id; + local flow_init_parameters = config_spec.flow_init_parameters; + + // Build all processors for the default flow + local blueprint_processors = flow_builder.build_blueprint_processors( + flow_blueprints, + default_flow_blueprint, + flow_init_parameters + ); + + local flow_processors = flow_builder.build_flow_processors( + flow_blueprints, + default_flow_blueprint, + default_flow_id, + flow_init_parameters + ); + + // Combine processors into flow objects + local processor_array = blueprint_processors + flow_processors; + local flow_objects = flow_builder.build_flow_objects(processor_array); + local active_flows = flow_builder.merge_flow_objects(flow_objects); + + // Build interfaces for the default flow + local default_flow_interfaces = interface_builder.build_interfaces( + flow_blueprints, + default_flow_blueprint, + default_flow_id, + flow_init_parameters + ); + + // Return object with nested configuration (for backwards compatibility) + { + // Create function (for backwards compatibility) + create: function(engine) {}, + + // The actual configuration object + configuration: { + // Prompts configuration + prompt: { + "system": config_spec.prompts["system-template"], + "template-index": std.objectFieldsAll(config_spec.prompts.templates), + } + { + ["template." + template.key]: template.value + for template in std.objectKeysValuesAll(config_spec.prompts.templates) + }, + + // Tools configuration + tool: { + [tool.id]: tool + for tool in config_spec.tools + }, + + // MCP configuration + mcp: config_spec.mcp, + + // Flow blueprints reference + "flow-blueprint": flow_blueprints, + + // Interface descriptions + "interface-description": config_spec.interface_descriptions, + + // Flow instances + "flow": { + [default_flow_id]: { + "description": "Default processing flow", + "blueprint-name": default_flow_blueprint, + "interfaces": default_flow_interfaces, + "parameters": flow_init_parameters, + }, + }, + + // Active flow processors + "active-flow": active_flows, + + // Token costs and parameter types + "token-cost": config_spec.token_costs, + "parameter-type": config_spec.parameter_types, + + // Collections configuration + "collection": config_spec.collection, + + }, + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/runtime-config/flow-builder.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/runtime-config/flow-builder.jsonnet new file mode 100644 index 00000000..0b142750 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/runtime-config/flow-builder.jsonnet @@ -0,0 +1,72 @@ +// Flow Builder Module +// Processes flow blueprints and builds complete flow configurations +// Handles {blueprint}, {id}, and parameter substitutions + +local param_processor = import "parameter-processor.jsonnet"; + +{ + // Builds blueprint-level processors with parameter substitution + // Processes the 'blueprint' section of flow blueprints + build_blueprint_processors: function(flow_blueprints, blueprint_name, parameters) + [ + [ + // Replace {blueprint} in the processor key + local key = std.strReplace(processor.key, "{blueprint}", blueprint_name); + local parts = std.splitLimit(key, ":", 2); + parts, + { + // Process each field in the processor configuration + [field.key]: + // First replace {blueprint}, then substitute parameters + local blueprint_replaced = std.strReplace(field.value, "{blueprint}", blueprint_name); + param_processor.substitute_parameters(blueprint_replaced, parameters) + for field in std.objectKeysValuesAll(processor.value) + } + ] + for processor in std.objectKeysValuesAll(flow_blueprints[blueprint_name].blueprint) + ], + + // Builds flow-level processors with parameter substitution + // Processes the 'flow' section of flow blueprints + build_flow_processors: function(flow_blueprints, blueprint_name, flow_id, parameters) + [ + [ + // Replace both {blueprint} and {id} in the processor key + local key = std.strReplace( + std.strReplace(processor.key, "{blueprint}", blueprint_name), + "{id}", flow_id + ); + local parts = std.splitLimit(key, ":", 2); + parts, + { + // Process each field in the processor configuration + [field.key]: + // Replace {blueprint} and {id}, then substitute parameters + local blueprint_replaced = std.strReplace(field.value, "{blueprint}", blueprint_name); + local id_replaced = std.strReplace(blueprint_replaced, "{id}", flow_id); + param_processor.substitute_parameters(id_replaced, parameters) + for field in std.objectKeysValuesAll(processor.value) + } + ] + for processor in std.objectKeysValuesAll(flow_blueprints[blueprint_name].flow) + ], + + // Combines blueprint and flow processors into flow objects + build_flow_objects: function(processor_array) + std.map( + function(item) { + [item[0][0]] +: { + [item[0][1]]: item[1] + } + }, + processor_array + ), + + // Merges all flow objects into a single flows_active configuration + merge_flow_objects: function(flow_objects) + std.foldr( + function(a, b) a + b, + flow_objects, + {} + ), +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/runtime-config/interface-builder.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/runtime-config/interface-builder.jsonnet new file mode 100644 index 00000000..2143e600 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/runtime-config/interface-builder.jsonnet @@ -0,0 +1,30 @@ +// Interface Builder Module +// Processes flow class interfaces with parameter substitution +// Handles both string interfaces and nested object interfaces + +local param_processor = import "parameter-processor.jsonnet"; + +{ + // Builds interfaces for a specific flow class and instance + // Processes the 'interfaces' section of flow classes + build_interfaces: function(flow_classes, class_name, flow_id, parameters) + local interface_spec = flow_classes[class_name].interfaces; + { + [interface.key]: + if std.isString(interface.value) then + // Simple string interface - apply all substitutions + local class_replaced = std.strReplace(interface.value, "{class}", class_name); + local id_replaced = std.strReplace(class_replaced, "{id}", flow_id); + param_processor.substitute_parameters(id_replaced, parameters) + else + // Complex object interface - process nested fields + { + [field.key]: + local class_replaced = std.strReplace(field.value, "{class}", class_name); + local id_replaced = std.strReplace(class_replaced, "{id}", flow_id); + param_processor.substitute_parameters(id_replaced, parameters) + for field in std.objectKeysValuesAll(interface.value) + } + for interface in std.objectKeysValuesAll(interface_spec) + }, +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/runtime-config/interface-descriptions.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/runtime-config/interface-descriptions.jsonnet new file mode 100644 index 00000000..f9dda8b0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/runtime-config/interface-descriptions.jsonnet @@ -0,0 +1,89 @@ +// Interface Descriptions Module +// Defines all external interfaces available in TrustGraph flows +// These are the 'endpoints' that external systems can interact with + +{ + // Document loading interfaces - for data ingestion + "document-load": { + "description": "Document loader", + "kind": "send", + "visible": true, + }, + "text-load": { + "description": "Text document loader", + "kind": "send", + "visible": true, + }, + + // Data storage interfaces - for processed data streams + "entity-contexts-load": { + "description": "Entity contexts loader", + "kind": "send", + }, + "triples-store": { + "description": "Triples loader", + "kind": "send", + }, + "graph-embeddings-store": { + "description": "Graph embeddings loader", + "kind": "send", + }, + "document-embeddings-store": { + "description": "Document embeddings loader", + "kind": "send", + }, + "objects-store": { + "description": "Object store", + "kind": "request-response", + }, + + // Query interfaces - for retrieving information + "graph-rag": { + "description": "GraphRAG service", + "kind": "request-response", + }, + "document-rag": { + "description": "ChunkRAG service", + "kind": "request-response", + }, + "triples": { + "description": "Triples query service", + "kind": "request-response", + }, + "graph-embeddings": { + "description": "Graph embeddings service", + "kind": "request-response", + }, + "document-embeddings": { + "description": "Document embeddings service", + "kind": "request-response", + }, + "objects": { + "description": "Object query service", + "kind": "request-response", + }, + + // Processing services - for text and data processing + "prompt": { + "description": "Prompt service", + "kind": "request-response", + }, + "agent": { + "description": "Agent service", + "kind": "request-response", + }, + "text-completion": { + "description": "Text completion service", + "kind": "request-response", + }, + + // Query translation services - for natural language queries + "nlp-query": { + "description": "NLP question to GraphQL service", + "kind": "request-response", + }, + "structured-query": { + "description": "Structured query service", + "kind": "request-response", + }, +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/runtime-config/parameter-processor.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/runtime-config/parameter-processor.jsonnet new file mode 100644 index 00000000..f317ae9f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/runtime-config/parameter-processor.jsonnet @@ -0,0 +1,38 @@ +// Parameter Processing Module +// Handles dynamic parameter replacement in configuration values +// Replaces {parameter_name} placeholders with actual parameter values + +{ + // Applies parameter substitutions to string values + // Only processes strings - leaves other types unchanged + substitute_parameters: function(value, parameters) + if std.isString(value) then + std.foldl( + function(acc, param) + // Only do string replacement if param.value is a string + if std.isString(param.value) then + std.strReplace(acc, "{" + param.key + "}", param.value) + else + acc, // Skip replacement for non-string parameter values + std.objectKeysValuesAll(parameters), + value + ) + else + value, + + // Applies parameter substitutions to all values in an object + // Recursively processes nested objects and arrays + substitute_parameters_in_object: function(obj, parameters) + if std.isObject(obj) then + { + [key]: $.substitute_parameters_in_object(obj[key], parameters) + for key in std.objectFields(obj) + } + else if std.isArray(obj) then + [ + $.substitute_parameters_in_object(item, parameters) + for item in obj + ] + else + $.substitute_parameters(obj, parameters), +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/runtime-config/tools.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/runtime-config/tools.jsonnet new file mode 100644 index 00000000..a84cd0bb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/runtime-config/tools.jsonnet @@ -0,0 +1,52 @@ +// Tools Configuration Module +// Defines all available tools that can be used by agents and flows +// Each tool specifies its interface, arguments, and behavior + +[ + // Knowledge extraction tool - extracts structured knowledge from text + { + id: "knowledge-extraction", + name: "Knowledge extraction", + description: "Takes a chunk of text and extracts knowledge in definition and relationship formats. The input is a text chunk", + type: "prompt", + template: "agent-kg-extract", + arguments: [ + { + "name": "text", + "type": "string", + "description": "The text chunk", + } + ], + }, + + // Knowledge query tool - queries the knowledge base + { + id: "knowledge-query", + name: "Knowledge query", + description: "This tool queries a knowledge base that holds information about domain-specific information. The question should be a natural language question.", + type: "knowledge-query", + collection: "default", + arguments: [ + { + name: "question", + type: "string", + description: "A simple natural language question.", + } + ] + }, + + // LLM completion tool - general purpose text completion + { + id: "llm-completion", + name: "LLM text completion", + type: "text-completion", + description: "This tool queries an LLM for non-domain-specific information. The question should be a natural language question.", + arguments: [ + { + name: "question", + type: "string", + description: "The question which should be asked of the LLM.", + } + ] + } +] \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/runtime-config/trustgraph-config.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/runtime-config/trustgraph-config.jsonnet new file mode 100644 index 00000000..d860ab3c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/runtime-config/trustgraph-config.jsonnet @@ -0,0 +1,90 @@ +// TrustGraph Main Configuration +// Clean, modular composition of TrustGraph configuration +// Uses specialized modules for different aspects of config building + +// Import dependencies +local images = import "../values/images.jsonnet"; +local url = import "../values/url.jsonnet"; +local prompts = import "../prompts/mixtral.jsonnet"; +local default_prompts = import "../prompts/default-prompts.jsonnet"; +local token_costs = import "../values/token-costs.jsonnet"; +local flow_blueprints = import "../flows/flow-blueprints.jsonnet"; +local config_composer = import "config-composer.jsonnet"; +local interface_descriptions = import "interface-descriptions.jsonnet"; +local tools = import "tools.jsonnet"; +local temperature_params = import "../parameters/temperature-param-types.jsonnet"; +local chunking_params = import "../parameters/chunking-param-types.jsonnet"; + +// Main configuration object +local configuration = { + + // Prompt templates + prompts:: default_prompts, + + // Tool definitions + tools:: tools, + + // MCP configuration + mcp:: {}, + + // Flow classes reference + "flow-blueprints":: flow_blueprints, + + // LLM model parameters + "llm-models" +:: {}, + + // Embeddings model parameters + "embeddings-models" +:: {}, + + collections +:: { + "trustgraph:default": { + "user": "default-user", + "collection": "default", + "name": "Default Collection", + "description": "Default collection", + "tags": ["default"], + }, + }, + + // Default model and flow parameters + flow_init_parameters:: { + "llm-model": $["llm-models"].default, + "llm-rag-model": $["llm-models"].default, + "llm-temperature": "%0.3f" % $["parameter-types"]["llm-temperature"].default, + "llm-rag-temperature": "%0.3f" % $["parameter-types"]["llm-temperature"].default, + "chunk-size": std.toString($["parameter-types"]["chunk-size"].default), + "chunk-overlap": std.toString($["parameter-types"]["chunk-overlap"].default), + "embeddings-model": $["embeddings-models"].default, + }, + + // Interface descriptions for external endpoints + "interface-descriptions":: interface_descriptions, + + // Parameter type definitions + "parameter-types":: { + "llm-model": $["llm-models"], + "embeddings-model": $["embeddings-models"], + } + chunking_params + temperature_params, + + // Token costs + "token-costs":: token_costs, + + // Build the complete configuration using the composer + configuration:: config_composer.build({ + flow_blueprints: $["flow-blueprints"], + default_flow_blueprint: "everything", + default_flow_id: "default", + flow_init_parameters: $["flow_init_parameters"], + prompts: $["prompts"], + tools: $["tools"], + mcp: $["mcp"], + interface_descriptions: $["interface-descriptions"], + parameter_types: $["parameter-types"], + token_costs: $["token-costs"], + collection: $["collections"], + }), + +} + default_prompts; + +// Export the final configuration +configuration diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/triple-store/cassandra.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/triple-store/cassandra.jsonnet new file mode 100644 index 00000000..05f4fd27 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/triple-store/cassandra.jsonnet @@ -0,0 +1,77 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local cassandra = import "backends/cassandra.jsonnet"; + +cassandra + { + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-cassandra", + "-p", + url.pulsar, + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-cassandra", + "-p", + url.pulsar, + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "512M") + .with_reservations("0.1", "512M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/triple-store/falkordb.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/triple-store/falkordb.jsonnet new file mode 100644 index 00000000..b4bace2d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/triple-store/falkordb.jsonnet @@ -0,0 +1,79 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local falkordb = import "backends/falkordb.jsonnet"; + +falkordb + { + + "falkordb-url":: "falkor://falkordb:6379", + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-falkordb", + "-p", + url.pulsar, + "-g", + $["falkordb-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-falkordb", + "-p", + url.pulsar, + "-g", + $["falkordb-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/triple-store/memgraph.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/triple-store/memgraph.jsonnet new file mode 100644 index 00000000..72597d72 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/triple-store/memgraph.jsonnet @@ -0,0 +1,84 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local memgraph = import "backends/memgraph.jsonnet"; + +memgraph + { + + "memgraph-url":: "bolt://memgraph:7687", + "memgraph-database":: "memgraph", + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-memgraph", + "-p", + url.pulsar, + "-g", + $["memgraph-url"], + "--database", + $["memgraph-database"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-memgraph", + "-p", + url.pulsar, + "-g", + $["memgraph-url"], + "--database", + $["memgraph-database"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/triple-store/neo4j.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/triple-store/neo4j.jsonnet new file mode 100644 index 00000000..08904715 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/triple-store/neo4j.jsonnet @@ -0,0 +1,79 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local neo4j = import "backends/neo4j.jsonnet"; + +neo4j + { + + "neo4j-url":: "bolt://neo4j:7687", + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-neo4j", + "-p", + url.pulsar, + "-g", + $["neo4j-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-neo4j", + "-p", + url.pulsar, + "-g", + $["neo4j-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/ui/workbench-ui.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/ui/workbench-ui.jsonnet new file mode 100644 index 00000000..f2048e47 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/ui/workbench-ui.jsonnet @@ -0,0 +1,32 @@ +local images = import "values/images.jsonnet"; + +{ + + "workbench-ui" +: { + + create:: function(engine) + + local container = + engine.container("workbench-ui") + .with_image(images["workbench-ui"]) + .with_limits("0.1", "256M") + .with_reservations("0.1", "256M") + .with_port(8888, 8888, "ui"); + + local containerSet = engine.containers( + "workbench-ui", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8888, 8888, "ui"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/values/images.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/values/images.jsonnet new file mode 100644 index 00000000..8b8b75a0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/values/images.jsonnet @@ -0,0 +1,36 @@ +local version = import "version.jsonnet"; +{ + cassandra: "docker.io/cassandra:4.1.10", +// Not working +// ceph: "quay.io/ceph/daemon:latest-reef", + neo4j: "docker.io/neo4j:2025.08.0-community-bullseye", + pulsar: "docker.io/apachepulsar/pulsar:4.1.0", + pulsar_manager: "docker.io/apachepulsar/pulsar-manager:v0.4.0", + etcd: "quay.io/coreos/etcd:v3.6.4", + minio: "docker.io/minio/minio:RELEASE.2025-09-07T16-13-09Z", + garage: "docker.io/dxflrs/garage:v2.1.0", + milvus: "docker.io/milvusdb/milvus:v2.5.17", + prometheus: "docker.io/prom/prometheus:v3.8.0", + grafana: "docker.io/grafana/grafana:12.3.0", + loki: "docker.io/grafana/loki:3.6.2", + trustgraph_base: "docker.io/trustgraph/trustgraph-base:" + version, + trustgraph_flow: "docker.io/trustgraph/trustgraph-flow:" + version, + trustgraph_ocr: "docker.io/trustgraph/trustgraph-ocr:" + version, + trustgraph_bedrock: "docker.io/trustgraph/trustgraph-bedrock:" + version, + trustgraph_vertexai: "docker.io/trustgraph/trustgraph-vertexai:" + version, + trustgraph_hf: "docker.io/trustgraph/trustgraph-hf:" + version, + trustgraph_mcp: "docker.io/trustgraph/trustgraph-mcp:" + version, + qdrant: "docker.io/qdrant/qdrant:v1.15.4", + memgraph_mage: "docker.io/memgraph/memgraph-mage:3.5", + memgraph_lab: "docker.io/memgraph/lab:3.5.0", + falkordb: "docker.io/falkordb/falkordb:v4.12.5", + "workbench-ui": "docker.io/trustgraph/workbench-ui:1.5.5", + "ddg-mcp-server": "docker.io/trustgraph/ddg-mcp-server:0.1.0", + "tgi-service-intel-xpu": "ghcr.io/huggingface/text-generation-inference:3.3.1-intel-xpu", + "tgi-service-cpu": "ghcr.io/huggingface/text-generation-inference:3.3.1-intel-cpu", + "tgi-service-gaudi": "ghcr.io/huggingface/text-generation-inference:sha-f140440-gaudi", + "vllm-service-intel-xpu": "docker.io/intel/vllm:0.8.0-xpu", + "vllm-service-gaudi": "docker.io/trustgraph/vllm-hpu:027f5645", + "vllm-service-nvidia": "docker.io/vllm/vllm-openai:latest", + "vllm-service-intel-battlemage": "docker.io/intelanalytics/ipex-llm-serving-xpu:0.2.0-b6", +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/values/token-costs.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/values/token-costs.jsonnet new file mode 100644 index 00000000..84c70373 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/values/token-costs.jsonnet @@ -0,0 +1,102 @@ +{ + "mistral.mistral-large-2407-v1:0": { + "model_name": "mistral.mistral-large-2407-v1:0", + "input_price": 0.000004, + "output_price": 0.000012 + }, + "meta.llama3-1-405b-instruct-v1:0": { + "model_name": "meta.llama3-1-405b-instruct-v1:0", + "input_price": 0.00000532, + "output_price": 0.000016 + }, + "mistral.mixtral-8x7b-instruct-v0:1": { + "model_name": "mistral.mixtral-8x7b-instruct-v0:1", + "input_price": 0.00000045, + "output_price": 0.0000007 + }, + "meta.llama3-1-70b-instruct-v1:0": { + "model_name": "meta.llama3-1-70b-instruct-v1:0", + "input_price": 0.00000099, + "output_price": 0.00000099 + }, + "meta.llama3-1-8b-instruct-v1:0": { + "model_name": "meta.llama3-1-8b-instruct-v1:0", + "input_price": 0.00000022, + "output_price": 0.00000022 + }, + "anthropic.claude-3-haiku-20240307-v1:0": { + "model_name": "anthropic.claude-3-haiku-20240307-v1:0", + "input_price": 0.00000025, + "output_price": 0.00000125 + }, + "anthropic.claude-3-5-sonnet-20240620-v1:0": { + "model_name": "anthropic.claude-3-5-sonnet-20240620-v1:0", + "input_price": 0.000003, + "output_price": 0.000015 + }, + "cohere.command-r-plus-v1:0": { + "model_name": "cohere.command-r-plus-v1:0", + "input_price": 0.0000030, + "output_price": 0.0000150 + }, + "ollama": { + "model_name": "ollama", + "input_price": 0, + "output_price": 0 + }, + "claude-3-haiku-20240307": { + "model_name": "claude-3-haiku-20240307", + "input_price": 0.00000025, + "output_price": 0.00000125 + }, + "claude-3-5-sonnet-20240620": { + "model_name": "claude-3-5-sonnet-20240620", + "input_price": 0.000003, + "output_price": 0.000015 + }, + "claude-3-opus-20240229": { + "model_name": "claude-3-opus-20240229", + "input_price": 0.000015, + "output_price": 0.000075 + }, + "claude-3-sonnet-20240229": { + "model_name": "claude-3-sonnet-20240229", + "input_price": 0.000003, + "output_price": 0.000015 + }, + "command-r-08-202": { + "model_name": "command-r-08-202", + "input_price": 0.0000025, + "output_price": 0.000010 + }, + "c4ai-aya-23-8b": { + "model_name": "c4ai-aya-23-8b", + "input_price": 0, + "output_price": 0 + }, + "llama.cpp": { + "model_name": "llama.cpp", + "input_price": 0, + "output_price": 0 + }, + "gpt-4o": { + "model_name": "gpt-4o", + "input_price": 0.000005, + "output_price": 0.000015 + }, + "gpt-4o-2024-08-06": { + "model_name": "gpt-4o-2024-08-06", + "input_price": 0.0000025, + "output_price": 0.000010 + }, + "gpt-4o-2024-05-13": { + "model_name": "gpt-4o-2024-05-13", + "input_price": 0.000005, + "output_price": 0.000015 + }, + "gpt-4o-mini": { + "model_name": "gpt-4o-mini", + "input_price": 0.00000015, + "output_price": 0.0000006 + } +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/values/url.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/values/url.jsonnet new file mode 100644 index 00000000..c3d4ad97 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/values/url.jsonnet @@ -0,0 +1,7 @@ +{ + pulsar: "pulsar://pulsar:6650", + pulsar_admin: "http://pulsar:8080", + milvus: "http://milvus:19530", + qdrant: "http://qdrant:6333", + object_store: "garage:3900", +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/vector-store/milvus.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/vector-store/milvus.jsonnet new file mode 100644 index 00000000..213027d1 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/vector-store/milvus.jsonnet @@ -0,0 +1,146 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local milvus = import "backends/milvus.jsonnet"; + +milvus + { + + "store-graph-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("store-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-write-milvus", + "-p", + url.pulsar, + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-graph-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("query-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-query-milvus", + "-p", + url.pulsar, + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "store-doc-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("store-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-write-milvus", + "-p", + url.pulsar, + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-doc-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("query-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-query-milvus", + "-p", + url.pulsar, + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/vector-store/pinecone.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/vector-store/pinecone.jsonnet new file mode 100644 index 00000000..3803bdcb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/vector-store/pinecone.jsonnet @@ -0,0 +1,160 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; + +{ + + "pinecone-cloud":: "aws", + "pinecone-region":: "us-east-1", + + "store-graph-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("store-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-write-pinecone", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "query-graph-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("query-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-query-pinecone", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "store-doc-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("store-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-write-pinecone", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "query-doc-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("query-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-query-pinecone", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/vector-store/qdrant.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/vector-store/qdrant.jsonnet new file mode 100644 index 00000000..24d6b040 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/vector-store/qdrant.jsonnet @@ -0,0 +1,170 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local qdrant = import "backends/qdrant.jsonnet"; + +qdrant + { + + "store-graph-embeddings" +: { + + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("store-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-write-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", memoryLimit) + .with_reservations("0.1", memoryReservation); + + local containerSet = engine.containers( + "store-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-graph-embeddings" +: { + + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("query-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-query-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", memoryLimit) + .with_reservations("0.1", memoryReservation); + + local containerSet = engine.containers( + "query-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "store-doc-embeddings" +: { + + "memory-limit":: "256M", + "memory-reservation":: "256M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("store-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-write-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", memoryLimit) + .with_reservations("0.1", memoryReservation); + + local containerSet = engine.containers( + "store-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-doc-embeddings" +: { + + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("query-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-query-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", memoryLimit) + .with_reservations("0.1", memoryReservation); + + local containerSet = engine.containers( + "query-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/zip-readme.md b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/zip-readme.md new file mode 100644 index 00000000..0b117792 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.8/zip-readme.md @@ -0,0 +1,28 @@ + +Note! this is a subset of possible configurations, to generate your own +launch config use the config util... + +- Production: https://config-ui.demo.trustgraph.ai +- Early release: https://dev.config-ui.demo.trustgraph.ai + +The config util auto-generates deployment instructions for your +configuration, so that's the recommended way to deploy. + +---------------------------------------------------------------------------- + +These are launch configurations for TrustGraph. See https://trustgraph.ai for +the quickstart using docker compose. + +Hint for Linux: There are files here which get mounted as volumes inside +Docker Compose containers. This may trigger SELinux rules on your system, to +permit access insider the containers, use a command like this... + +chcon -Rt svirt_sandbox_file_t grafana/ prometheus/ + +The file vertexai/private.json is a placeholder for real GCP credentials if +you are using the VertexAI LLM. If you're using that in Docker Compose, +replace with your real credentials, and don't forget to permit access if you +are using Linux: + +chcon -Rt svirt_sandbox_file_t vertexai/ + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/README.md b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/README.md new file mode 100644 index 00000000..23039e9a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/README.md @@ -0,0 +1,125 @@ + +# TrustGraph template generation + +There are two utilities here: + +- `generate`: Generates a single Docker Compose launch configuration + based on configuration you provide. +- `generate-all`: Generates the release bundle for releases. You won't + need to use this unless you are managing releases. + +## `generate-all` + +Previously, this generates a full set of all vector DB / triple store / LLM +combinations, and put them in a single ZIP file. But this got out of +hand, so at the time of writing, this generates a single configuraton +using Qdrant vector DB, Ollama LLM support and Cassandra for a triple store. + +The combinations are contained withing the code, it takes two arguments: +- output ZIP file (is over-written) +- TrustGraph version number + +``` +templates/generate-all output.zip 0.18.11 +``` + +## `generate` + +This utility takes a configuration file describing the components to bundle, +and outputs a Docker Compose YAML file. + +### Input configuration + +The input configuration is a JSON file, an array of components to pull into +the configuration. For each component, there is a name and a (possibly empty) +object describing addtional parameters for that component. + +Example: + +``` +[ + { + "name": "cassandra", + "parameters": {} + }, + { + "name": "pulsar", + "parameters": {} + }, + { + "name": "qdrant", + "parameters": {} + }, + { + "name": "embeddings-hf", + "parameters": {} + }, + { + "name": "graph-rag", + "parameters": {} + }, + { + "name": "grafana", + "parameters": {} + }, + { + "name": "trustgraph", + "parameters": {} + }, + { + "name": "googleaistudio", + "parameters": { + "googleaistudio-temperature": 0.3, + "googleaistudio-max-output-tokens": 2048, + "googleaistudio-model": "gemini-1.5-pro-002" + } + }, + { + "name": "prompt-template", + "parameters": {} + }, + { + "name": "override-recursive-chunker", + "parameters": { + "chunk-size": 1000, + "chunk-overlap": 50 + } + }, + { + "name": "workbench-ui", + "parameters": {} + }, + { + "name": "agent-manager-react", + "parameters": {} + } +] +``` + +If you want to make your own configuration you could try changing the +configuration above: +- Components which are essential: pulsar, trustgraph, graph-rag, grafana, + agent-manager-react +- You need a triple store, one of: cassandra, memgraph, falkordb, neo4j +- You need a vector store, one of: qdrant, pinecone +- You need an LLM, one of: azure, azure-openai, bedrock, claude, cohere, + llamafile, ollama, openai, vertexai. +- You need an embeddings implementation, one of: embeddings-hf, + embeddings-ollama +- Optionally add the Workbench tool: workbench-ui + +Components have over-ridable parameters, look in the component definition +in `templates/components/` to see what you can override. + +### Invocation + +Two parameters: +- The output ZIP file +- The version number + +The configuration file described above is provided on standard input + +``` +templates/generate out.zip 0.18.9 < config.json +``` + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/cassandra.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/cassandra.jsonnet new file mode 100644 index 00000000..60f262d3 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/cassandra.jsonnet @@ -0,0 +1,50 @@ +local images = import "values/images.jsonnet"; + +{ + + "cassandra" +: { + + // Memory settings (can be overridden by memory-profile) + "memory-limit":: "1000M", + "memory-reservation":: "1000M", + "heap":: "300M", + + create:: function(engine) + + // Capture memory settings into locals + local memLimit = self["memory-limit"]; + local memReserv = self["memory-reservation"]; + local heap = self["heap"]; + + local vol = engine.volume("cassandra").with_size("20G"); + + local container = + engine.container("cassandra") + .with_image(images.cassandra) + .with_environment({ + JVM_OPTS: "-Xms%s -Xmx%s -Dcassandra.skip_wait_for_gossip_to_settle=0" % [ + heap, heap, + ], + }) + .with_limits("1.0", memLimit) + .with_reservations("0.5", memReserv) + .with_port(9042, 9042, "cassandra") + .with_volume_mount(vol, "/var/lib/cassandra"); + + local containerSet = engine.containers( + "cassandra", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9042, 9042, "api"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/ceph.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/ceph.jsonnet new file mode 100644 index 00000000..42406a95 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/ceph.jsonnet @@ -0,0 +1,446 @@ + +// +// This majorly does not work +// + +// This configuration fails primarily because it tries to treat Ceph +// like a stateless web app. You are currently pointing mon host to a +// generic service name (ceph-mon), but you aren't telling the Monitor +// process to assume that service identity. + +// To make this work with the "Service Name" approach across any +// engine, you need to fix the binding logic and the messenger protocol. + +// 1. The ceph.conf Fix + +// Need to enable Messenger v2 (modern) and tell the cluster to use +// the service names for its initial quorum. + +//Change this section: +// Ini, TOML + +// [global] +// fsid = %s +// mon initial members = mon0 +// mon host = ceph-mon:6789 +// ... + +// To this: +// Ini, TOML + +// [global] +// fsid = %s +// # Use the actual service names as members +// mon initial members = ceph-mon +// # Explicitly use the Service name (VIP) +// mon host = ceph-mon +// # Force modern protocol +// ms_bind_msgr2 = true +// ms_bind_msgr1 = true + +// 2. The MON Environment & Command Fix + +// This is the most critical part. Your current config sets MON_IP: +// "0.0.0.0". This causes the MON to bind to the Pod IP, which breaks +// when the Pod restarts. You must force it to bind to the Service IP. + +// Update mon_env: +// Code snippet + +// local mon_env = cluster_env + { +// CEPH_DAEMON: "MON", +// MON_NAME: "mon0", +// # Remove MON_IP: "0.0.0.0" +// # Add these: +// MON_ADDR: "ceph-mon", // This says to resolve the service name +// }; + +// Update mon_container command: You are currently wiping the MON data +// on every start (rm -rf /var/lib/ceph/mon/*). Stop doing that. If you +// wipe the data, you lose the cluster state and the OSDs will refuse to +// talk to the "new" MON. + +// Code snippet + +// .with_command([ +// "bash", "-c", +// # 1. Resolve the Service IP at runtime +// "export MON_IP=$(getent hosts ceph-mon | awk '{ print $1 }'); " + +// inject_mon_config + +// # 2. Start the daemon telling it its PUBLIC address is the Service VIP +// "exec /opt/ceph-container/bin/entrypoint.sh" +// ]) + +// 3. Why your current config "Majorly does not work" + +// The "Wipe" Logic: By running rm -rf /var/lib/ceph/mon/* in the +// MON container, you are creating a "New Cluster" every time the +// container starts. Since the OSDs store the fsid and cluster +// secrets, they will reject the "new" MON. + +// DNS Race Condition: Your OSD/MGR/RGW containers wait for +// ceph-mon DNS, which is good. However, if ceph-mon resolves to a +// Round Robin IP (multiple pods) rather than a stable ClusterIP, the +// connection will be flaky. + +// Messenger Protocol: Without ms_bind_msgr2, Ceph defaults to the +// old v1 protocol which is much more sensitive to NAT/Container IP +// mismatches. + +// SUMMARY + +// Component: ceph.conf +// Change: Add ms_bind_msgr2 = true +// Why: Supports modern container networking better. + +// Component: MON Start +// Change: Remove rm -rf +// Why: Ceph MONs must keep their database to maintain the cluster. + +// Component: MON Address +// Change: Use getent hosts ceph-mon +// Why: Forces the MON to advertise the Service VIP instead of its own Pod IP. + +// Component: MON Keyring +// Change: Ensure /etc/ceph/ceph.mon.keyring exists +// Why: MONs need their specific key to start. + +local images = import "values/images.jsonnet"; + +{ + with:: function(key, value) + self + { + ["ceph-" + key]:: value, + }, + + // Ceph credentials and cluster settings + "ceph-access-key":: "object-user", + "ceph-secret-key":: "object-password", + "ceph-cluster-id":: "ceph", + "ceph-fsid":: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", + + // Pool redundancy settings + // size: 2 = two replicas for fault tolerance + // min_size: 1 = allow degraded I/O if one OSD is down (prevents cluster freeze) + "ceph-pool-size":: "2", + "ceph-pool-min-size":: "1", + + ceph +: { + create:: function(engine) + // Pre-Shared Cryptographic Material - Config-as-Code Approach + // These keys are generated once and distributed to all daemons + // This ensures cryptographic consistency across the shared-nothing architecture + local admin_key = "AQBpxSBlAAAAABAAU99V6D8vS7Uu9y1S8W0iBg=="; + local mon_key = "AQBpxSBlAAAAABAAn7pL/pG9oT+X6vO7V1S6bg=="; + + // Ceph configuration file - rendered from Jsonnet variables + local ceph_conf = ||| + [global] + fsid = %s + mon initial members = mon0 + mon host = ceph-mon:6789 + public network = 0.0.0.0/0 + cluster network = 0.0.0.0/0 + osd pool default size = %s + osd pool default min size = %s + osd crush chooseleaf type = 0 + auth cluster required = cephx + auth service required = cephx + auth client required = cephx + ||| % [$["ceph-fsid"], $["ceph-pool-size"], $["ceph-pool-min-size"]]; + + // Admin keyring - distributed to all daemons + local admin_keyring = ||| + [client.admin] + key = %s + caps mds = "allow *" + caps mgr = "allow *" + caps mon = "allow *" + caps osd = "allow *" + ||| % [admin_key]; + + // Monitor keyring - used by MON for cluster operations + local mon_keyring = ||| + [mon.] + key = %s + caps mon = "allow *" + ||| % [mon_key]; + + // Config injection command - writes files before entrypoint + local inject_config = "printf '%s' > /etc/ceph/ceph.conf; printf '%s' > /etc/ceph/ceph.client.admin.keyring; " % [ceph_conf, admin_keyring]; + local inject_mon_config = inject_config + ("printf '%s' > /etc/ceph/ceph.mon.keyring; " % [mon_keyring]); + + // Data volumes - sized appropriately for production workloads + local vol_mon = engine.volume("ceph-mon").with_size("20G"); + local vol_mgr = engine.volume("ceph-mgr").with_size("20G"); + local vol_osd = engine.volume("ceph-osd").with_size("100G"); + local vol_rgw = engine.volume("ceph-rgw").with_size("20G"); + + // Isolated config volumes per daemon (ReadWriteOnce compatible) + // Each daemon gets its own non-shared config volume to support + // multi-node scheduling in K8s and other orchestrators + local vol_mon_config = engine.volume("ceph-mon-config").with_size("500M"); + local vol_mgr_config = engine.volume("ceph-mgr-config").with_size("500M"); + local vol_osd_config = engine.volume("ceph-osd-config").with_size("500M"); + local vol_rgw_config = engine.volume("ceph-rgw-config").with_size("500M"); + local vol_init_config = engine.volume("ceph-init-config").with_size("500M"); + + // Simplified cluster environment - Config-as-Code model + // No fetch logic needed - config is injected before entrypoint runs + local cluster_env = { + CLUSTER: $["ceph-cluster-id"], + FSID: $["ceph-fsid"], + KV_TYPE: "none", // No external coordination + }; + + // MON-specific environment + // Config-as-Code: MON uses injected config files, not fetch logic + // + // CRITICAL: MON_DATA_AVAIL="0" forces fresh cluster bootstrap + // The ceph/daemon entrypoint script (variables_stack.sh) uses this as a gate: + // - MON_DATA_AVAIL="0" -> run mkfs, create new cluster with our FSID + // - MON_DATA_AVAIL="1" -> attempt to join existing cluster (infinite probe loop) + // + // Network configuration for monmap generation + local mon_env = cluster_env + { + CEPH_DAEMON: "MON", + MON_NAME: "mon0", + MON_PORT: "6789", + MON_DATA_AVAIL: "0", + MON_IP: "0.0.0.0", + NETWORK_AUTO_DETECT: "4", + CEPH_PUBLIC_NETWORK: "0.0.0.0/0", + }; + + // Simplified daemon environments - Config-as-Code model + // All daemons receive config via injection, not fetch from MON + // This eliminates "static mode" errors and networking complexity + + // MGR-specific environment + local mgr_env = cluster_env + { + CEPH_DAEMON: "MGR", + MGR_NAME: "mgr0", + }; + + // OSD-specific environment + local osd_env = cluster_env + { + CEPH_DAEMON: "OSD", + OSD_TYPE: "directory", + }; + + // RGW-specific environment + local rgw_env = cluster_env + { + CEPH_DAEMON: "RGW", + RGW_NAME: "rgw0", + RGW_FRONTEND_PORT: "7480", + }; + + // MON (Monitor) container - cluster state and quorum + // Config-as-Code: Injects pre-shared keys before entrypoint + // CRITICAL: Wipes /var/lib/ceph/mon/* on every start to force fresh bootstrap + // This ensures MON always uses our FSID and doesn't inherit stale cluster state + local mon_container = + engine.container("ceph-mon") + .with_image(images.ceph) + .with_environment(mon_env) + .with_command([ + "bash", "-c", + "rm -rf /var/lib/ceph/mon/*; " + + inject_mon_config + + "exec /opt/ceph-container/bin/entrypoint.sh" + ]) + .with_limits("1.0", "1536M") + .with_reservations("0.5", "1024M") + .with_port(6789, 6789, "mon") + .with_port(3300, 3300, "mon-msgr2") + .with_volume_mount(vol_mon, "/var/lib/ceph/mon") + .with_volume_mount(vol_mon_config, "/etc/ceph"); + + // MGR (Manager) container - cluster management and dashboard + // Config-as-Code: Uses injected config files with pre-shared keys + // DNS wait ensures MON is available before MGR connects + local mgr_container = + engine.container("ceph-mgr") + .with_image(images.ceph) + .with_environment(mgr_env) + .with_command([ + "bash", "-c", + "until getent hosts ceph-mon; do echo 'Waiting for MON DNS...'; sleep 2; done; " + + inject_config + + "exec /opt/ceph-container/bin/entrypoint.sh" + ]) + .with_limits("1.0", "1536M") + .with_reservations("0.5", "1024M") + .with_port(7000, 7000, "mgr") + .with_port(8443, 8443, "dashboard") + .with_port(9283, 9283, "prometheus") + .with_volume_mount(vol_mgr, "/var/lib/ceph/mgr") + .with_volume_mount(vol_mgr_config, "/etc/ceph"); + + // OSD (Object Storage Daemon) - actual data storage + // Config-as-Code: Uses injected config files with pre-shared keys + // Increased resources to prevent OOM during recovery operations + // DNS wait ensures MON is available before OSD connects + local osd_container = + engine.container("ceph-osd") + .with_image(images.ceph) + .with_environment(osd_env) + .with_command([ + "bash", "-c", + "until getent hosts ceph-mon; do echo 'Waiting for MON DNS...'; sleep 2; done; " + + inject_config + + "exec /opt/ceph-container/bin/entrypoint.sh" + ]) + .with_limits("2.0", "4096M") + .with_reservations("1.0", "2048M") + .with_port(6800, 6800, "osd") + .with_volume_mount(vol_osd, "/var/lib/ceph/osd") + .with_volume_mount(vol_osd_config, "/etc/ceph"); + + // RGW (RADOS Gateway) - S3 API endpoint + // Config-as-Code: Uses injected config files with pre-shared keys + // DNS wait ensures MON is available before RGW connects + local rgw_container = + engine.container("ceph-rgw") + .with_image(images.ceph) + .with_environment(rgw_env) + .with_command([ + "bash", "-c", + "until getent hosts ceph-mon; do echo 'Waiting for MON DNS...'; sleep 2; done; " + + inject_config + + "exec /opt/ceph-container/bin/entrypoint.sh" + ]) + .with_limits("1.0", "1536M") + .with_reservations("0.5", "1024M") + .with_port(7480, 7480, "s3") + .with_volume_mount(vol_rgw, "/var/lib/ceph/radosgw") + .with_volume_mount(vol_rgw_config, "/etc/ceph"); + + // Init container - one-time S3 user provisioning + // IMPORTANT: This container exits with code 0 after completion + // Orchestrator must NOT restart it (use K8s Job or Compose restart: "no") + // Config-as-Code: Uses injected config to run radosgw-admin commands + local init_container = + engine.container("ceph-init") + .with_image(images.ceph) + .with_environment({ + CLUSTER: $["ceph-cluster-id"], + FSID: $["ceph-fsid"], + KV_TYPE: "none", + RGW_ACCESS_KEY: $["ceph-access-key"], + RGW_SECRET_KEY: $["ceph-secret-key"], + }) + .with_limits("0.5", "512M") + .with_reservations("0.25", "256M") + .with_volume_mount(vol_init_config, "/etc/ceph") + .with_command([ + "bash", "-c", + inject_config + ||| + set -e + + # Wait for cluster health + echo "Waiting for Ceph cluster to be healthy..." + MAX_ATTEMPTS=60 + ATTEMPT=0 + until ceph --cluster ${CLUSTER} health 2>/dev/null | grep -q "HEALTH_OK\|HEALTH_WARN"; do + ATTEMPT=$((ATTEMPT+1)) + if [ $ATTEMPT -ge $MAX_ATTEMPTS ]; then + echo "ERROR: Cluster failed to become healthy after ${MAX_ATTEMPTS} attempts" + exit 1 + fi + echo "Attempt ${ATTEMPT}/${MAX_ATTEMPTS}: Cluster not ready, retrying in 5s..." + sleep 5 + done + echo "Cluster is healthy." + + # Wait for RGW availability + echo "Waiting for RGW to be ready..." + ATTEMPT=0 + until curl -sf http://ceph-rgw:7480 >/dev/null 2>&1; do + ATTEMPT=$((ATTEMPT+1)) + if [ $ATTEMPT -ge $MAX_ATTEMPTS ]; then + echo "ERROR: RGW failed to become ready after ${MAX_ATTEMPTS} attempts" + exit 1 + fi + echo "Attempt ${ATTEMPT}/${MAX_ATTEMPTS}: RGW not ready, retrying in 5s..." + sleep 5 + done + echo "RGW is ready." + + # Idempotent S3 user creation + echo "Provisioning S3 user: ${RGW_ACCESS_KEY}" + if radosgw-admin --cluster ${CLUSTER} user info --uid="${RGW_ACCESS_KEY}" >/dev/null 2>&1; then + echo "User ${RGW_ACCESS_KEY} already exists, skipping creation." + else + echo "Creating new S3 user: ${RGW_ACCESS_KEY}" + radosgw-admin --cluster ${CLUSTER} user create \ + --uid="${RGW_ACCESS_KEY}" \ + --display-name="Object Storage User" \ + --access-key="${RGW_ACCESS_KEY}" \ + --secret-key="${RGW_SECRET_KEY}" + echo "S3 user created successfully." + fi + + echo "Initialization complete. Exiting." + exit 0 + |||, + ]); + + // Container sets - each daemon gets its own for K8s node distribution + local mon_containerSet = engine.containers("ceph-mon", [mon_container]); + local mgr_containerSet = engine.containers("ceph-mgr", [mgr_container]); + local osd_containerSet = engine.containers("ceph-osd", [osd_container]); + local rgw_containerSet = engine.containers("ceph-rgw", [rgw_container]); + local init_containerSet = engine.containers("ceph-init", [init_container]); + + // Services - expose daemon ports for inter-daemon communication + local mon_service = + engine.service(mon_containerSet) + .with_port(6789, 6789, "mon") + .with_port(3300, 3300, "mon-msgr2"); + + local mgr_service = + engine.service(mgr_containerSet) + .with_port(7000, 7000, "mgr") + .with_port(8443, 8443, "dashboard") + .with_port(9283, 9283, "prometheus"); + + local osd_service = + engine.service(osd_containerSet) + .with_port(6800, 6800, "osd"); + + local rgw_service = + engine.service(rgw_containerSet) + .with_port(7480, 7480, "s3"); + + engine.resources([ + + // Data volumes + vol_mon, + vol_mgr, + vol_osd, + vol_rgw, + + // Config volumes (isolated, no sharing) + vol_mon_config, + vol_mgr_config, + vol_osd_config, + vol_rgw_config, + vol_init_config, + + // Container sets + mon_containerSet, + mgr_containerSet, + osd_containerSet, + rgw_containerSet, + init_containerSet, + + // Services + mon_service, + mgr_service, + osd_service, + rgw_service, + + ]) + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/falkordb.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/falkordb.jsonnet new file mode 100644 index 00000000..1d4176d8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/falkordb.jsonnet @@ -0,0 +1,38 @@ +local images = import "values/images.jsonnet"; + +{ + + "falkordb" +: { + + create:: function(engine) + + local vol = engine.volume("falkordb").with_size("20G"); + + local container = + engine.container("falkordb") + .with_image(images.falkordb) + .with_limits("1.0", "768M") + .with_reservations("0.5", "768M") + .with_port(6379, 6379, "api") + .with_port(3010, 3000, "ui") + .with_volume_mount(vol, "/data"); + + local containerSet = engine.containers( + "falkordb", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(6379, 6379, "api") + .with_port(3010, 3010, "ui"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/garage.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/garage.jsonnet new file mode 100644 index 00000000..9d339bfb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/garage.jsonnet @@ -0,0 +1,249 @@ +local images = import "values/images.jsonnet"; + +{ + + garage +: { + + // Garage S3 credentials - these are the actual access key ID and secret key + // Access Key ID must be in format: GK + 24 hex characters (12 bytes) + // Secret Key must be 64 hex characters (32 bytes) + // For production, generate secure random values and override these defaults + "access-key":: "GK000000000000000000000001", + "secret-key":: "b171f00be9be4c32c734f4c05fe64c527a8ab5eb823b376cfa8c2531f70fc427", + "rpc-secret":: "bbba746a9e289bad64a9e7a36a4299dac8d6e0b8cc2a6c2937fe756df4492008", + // For a production system, override this value + "admin-token":: "batts-rockhearted-unpartially", + region:: "garage", + "replication-factor":: "1", // Set to 1 for single-node, 3 for production + + // Storage volume sizes + "meta-size":: "2G", // Metadata volume size + "data-size":: "5G", // Data volume size (also used for cluster layout capacity) + + create:: function(engine) + + local accessKey = self["access-key"]; + local secretKey = self["secret-key"]; + local rpcSecret = self["rpc-secret"]; + local adminToken = self["admin-token"]; + local region = self.region; + local replicationFactor = self["replication-factor"]; + local metaSize = self["meta-size"]; + local dataSize = self["data-size"]; + + // Garage daemon configuration file - TOML format + local garage_conf = ||| + metadata_dir = "/var/lib/garage/meta" + data_dir = "/var/lib/garage/data" + + db_engine = "lmdb" + + replication_factor = %s + + compression_level = 1 + + rpc_bind_addr = "[::]:3901" + rpc_public_addr = "[::]:3901" + rpc_secret = "%s" + + [s3_api] + s3_region = "%s" + api_bind_addr = "[::]:3900" + root_domain = ".s3.garage.local" + + [s3_web] + bind_addr = "[::]:3902" + root_domain = ".web.garage.local" + index = "index.html" + + [k2v_api] + api_bind_addr = "[::]:3904" + + [admin] + api_bind_addr = "[::]:3903" + admin_token = "%s" + ||| % [replicationFactor, rpcSecret, region, adminToken]; + + // Config volume - contains the rendered garage.toml + local cfgVol = engine.configVolume( + "garage-cfg", "garage", + { + "garage.toml": garage_conf, + } + ); + + // Volumes - Garage stores metadata and data separately + local vol_meta = engine.volume("garage-meta").with_size(metaSize); + local vol_data = engine.volume("garage-data").with_size(dataSize); + + // Main Garage daemon container + local garage_container = + engine.container("garage") + .with_image(images.garage) + .with_command([ + "/garage", "-c", "/etc/garage/garage.toml", "server" + ]) + .with_environment({ + RUST_LOG: "garage=info", + }) + .with_limits("1.0", "512M") + .with_reservations("0.5", "512M") + .with_port(3900, 3900, "s3-api") + .with_port(3901, 3901, "rpc") + .with_port(3902, 3902, "web") + .with_port(3903, 3903, "admin") + .with_port(3904, 3904, "k2v") + .with_volume_mount(cfgVol, "/etc/garage/") + .with_volume_mount(vol_meta, "/var/lib/garage/meta") + .with_volume_mount(vol_data, "/var/lib/garage/data"); + + // Init container - configures cluster layout and creates S3 credentials + // IMPORTANT: This container exits with code 0 after completion + // Orchestrator must NOT restart it (use K8s Job or Compose restart: "no") + // Uses Alpine base image since garage container has no shell + local init_container = + engine.container("garage-init") + .with_image("docker.io/alpine:3.23.2") + .with_environment({ + GARAGE_ACCESS_KEY: accessKey, + GARAGE_SECRET_KEY: secretKey, + GARAGE_REGION: region, + GARAGE_ADMIN_TOKEN: adminToken, + GARAGE_RPC_SECRET: rpcSecret, + GARAGE_DATA_SIZE: dataSize, + }) + .with_limits("0.5", "256M") + .with_reservations("0.25", "128M") + .with_volume_mount(cfgVol, "/etc/garage/") + .with_command([ + "sh", "-c", ||| + set -e + + # Install required tools + echo "Installing curl, jq and downloading garage CLI..." + apk add --no-cache curl jq + + # Download garage binary (v2.1.0) for remote management + curl -fsSL "https://garagehq.deuxfleurs.fr/_releases/v2.1.0/x86_64-unknown-linux-musl/garage" \ + -o /usr/local/bin/garage + chmod +x /usr/local/bin/garage + + echo "Waiting for Garage daemon to be ready..." + MAX_ATTEMPTS=60 + ATTEMPT=0 + # Wait for /health to respond (even 503 is fine - means daemon is up) + until curl -s http://garage:3903/health >/dev/null 2>&1; do + ATTEMPT=$((ATTEMPT+1)) + if [ $ATTEMPT -ge $MAX_ATTEMPTS ]; then + echo "ERROR: Garage failed to become ready after ${MAX_ATTEMPTS} attempts" + exit 1 + fi + echo "Attempt ${ATTEMPT}/${MAX_ATTEMPTS}: Garage not ready, retrying in 2s..." + sleep 2 + done + echo "Garage daemon is ready." + + # Get the node ID via v2 Admin API + echo "Getting Garage node ID via Admin API..." + curl -s -H "Authorization: Bearer ${GARAGE_ADMIN_TOKEN}" \ + "http://garage:3903/v2/GetNodeInfo?node=self" > /tmp/garage-node-info.json + + # Extract node ID from response (the key in success map is the node ID) + NODE_ID=$(jq -r '.success | to_entries[0].value.nodeId' /tmp/garage-node-info.json) + echo "Node ID: ${NODE_ID}" + + if [ -z "$NODE_ID" ] || [ "$NODE_ID" = "null" ]; then + echo "ERROR: Failed to retrieve node ID" + exit 1 + fi + + # ===== LAYOUT MANAGEMENT VIA REMOTE RPC ===== + # Use garage CLI with -h and -s flags to connect remotely + # -h requires format: @: + # -s provides the RPC secret + + # Construct full RPC identifier + RPC_HOST="${NODE_ID}@garage:3901" + echo "RPC Host: ${RPC_HOST}" + + # Check current layout to see if node is already assigned (idempotent) + echo "Checking current cluster layout..." + LAYOUT_OUTPUT=$(garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" layout show 2>&1) + + # Check if node already has a role assigned + # Layout output shows abbreviated node ID (first 16 chars) + NODE_ID_SHORT="${NODE_ID:0:16}" + if echo "$LAYOUT_OUTPUT" | grep -q "$NODE_ID_SHORT"; then + echo "Node ${NODE_ID_SHORT}... already assigned in layout, skipping." + else + echo "Assigning node to cluster layout..." + # Assign node to zone dc1 with configured capacity + garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" \ + layout assign ${NODE_ID} -z dc1 -c ${GARAGE_DATA_SIZE} + + echo "Applying layout configuration..." + # Get current staged version and apply + garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" \ + layout apply --version 1 + + echo "Layout configured successfully." + # Wait for layout to stabilize + sleep 5 + fi + + # ===== KEY MANAGEMENT VIA REMOTE RPC ===== + + # Check if key already exists (idempotent) + # GARAGE_ACCESS_KEY is already a valid Garage Key ID (GK + 24 hex chars) + if garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" key info "${GARAGE_ACCESS_KEY}" >/dev/null 2>&1; then + echo "Access key ${GARAGE_ACCESS_KEY} already exists, skipping creation." + else + echo "Importing S3 access key: ${GARAGE_ACCESS_KEY}" + + # Import key with the provided credentials (both already in valid Garage format) + # GARAGE_ACCESS_KEY = Key ID (GK + 24 hex chars) + # GARAGE_SECRET_KEY = Secret (64 hex chars) + garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" \ + key import "${GARAGE_ACCESS_KEY}" "${GARAGE_SECRET_KEY}" --yes + + echo "Access key imported successfully." + fi + + # Grant permissions to the key + echo "Granting create-bucket permission to key..." + garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" \ + key allow --create-bucket "${GARAGE_ACCESS_KEY}" + + echo "" + echo "Garage initialization complete!" + echo "S3 Endpoint: http://garage:3900" + echo "Region: ${GARAGE_REGION}" + exit 0 + |||, + ]); + + // Container sets + local garage_containerSet = engine.containers("garage", [garage_container]); + local init_containerSet = engine.containers("garage-init", [init_container]); + + // Service - expose Garage ports + local garage_service = + engine.service(garage_containerSet) + .with_port(3900, 3900, "s3-api") + .with_port(3901, 3901, "rpc") + .with_port(3902, 3902, "web") + .with_port(3903, 3903, "admin") + .with_port(3904, 3904, "k2v"); + + engine.resources([ + cfgVol, + vol_meta, + vol_data, + garage_containerSet, + init_containerSet, + garage_service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/memgraph.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/memgraph.jsonnet new file mode 100644 index 00000000..eeed2e4e --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/memgraph.jsonnet @@ -0,0 +1,70 @@ +local images = import "values/images.jsonnet"; + +{ + + "memgraph" +: { + + create:: function(engine) + + local vol = engine.volume("memgraph").with_size("20G"); + + local container = + engine.container("memgraph") + .with_image(images.memgraph_mage) + .with_environment({ + MEMGRAPH: "--storage-properties-on-edges=true --storage-enable-edges-metadata=true" + }) + .with_limits("1.0", "1000M") + .with_reservations("0.5", "1000M") + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2") + .with_volume_mount(vol, "/var/lib/memgraph"); + + local containerSet = engine.containers( + "memgraph", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + + "memgraph-lab" +: { + + create:: function(engine) + + local container = + engine.container("lab") + .with_image(images.memgraph_lab) + .with_environment({ + QUICK_CONNECT_MG_HOST: "memgraph", + QUICK_CONNECT_MG_PORT: "7687", + }) + .with_limits("1.0", "512M") + .with_reservations("0.5", "512M") + .with_port(3010, 3000, "http"); + + local containerSet = engine.containers( + "lab", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(3010, 3010, "http"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/milvus.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/milvus.jsonnet new file mode 100644 index 00000000..5bed3820 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/milvus.jsonnet @@ -0,0 +1,89 @@ +local images = import "values/images.jsonnet"; +local minio = import "backends/minio.jsonnet"; + +minio { + + etcd +: { + + create:: function(engine) + + local vol = engine.volume("etcd").with_size("20G"); + + local container = + engine.container("etcd") + .with_image(images.etcd) + .with_command([ + "etcd", + "-advertise-client-urls=http://127.0.0.1:2379", + "-listen-client-urls", + "http://0.0.0.0:2379", + "--data-dir", + "/etcd", + ]) + .with_environment({ + ETCD_AUTO_COMPACTION_MODE: "revision", + ETCD_AUTO_COMPACTION_RETENTION: "1000", + ETCD_QUOTA_BACKEND_BYTES: "4294967296", + ETCD_SNAPSHOT_COUNT: "50000" + }) + .with_limits("1.0", "128M") + .with_reservations("0.25", "128M") + .with_port(2379, 2379, "api") + .with_volume_mount(vol, "/etcd"); + + local containerSet = engine.containers( + "etcd", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(2379, 2379, "api"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + + milvus +: { + + create:: function(engine) + + local vol = engine.volume("milvus").with_size("20G"); + + local container = + engine.container("milvus") + .with_image(images.milvus) + .with_command([ + "milvus", "run", "standalone" + ]) + .with_environment({ + ETCD_ENDPOINTS: "etcd:2379", + MINIO_ADDRESS: "minio:9000", + }) + .with_limits("1.0", "256M") + .with_reservations("0.5", "256M") + .with_port(9091, 9091, "api") + .with_port(19530, 19530, "api2") + .with_volume_mount(vol, "/var/lib/milvus"); + + local containerSet = engine.containers( + "milvus", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9091, 9091, "api") + .with_port(19530, 19530, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/minio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/minio.jsonnet new file mode 100644 index 00000000..b38bb81f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/minio.jsonnet @@ -0,0 +1,48 @@ +local images = import "values/images.jsonnet"; + +{ + + minio +: { + + create:: function(engine) + + local vol = engine.volume("minio-data").with_size("20G"); + + local container = + engine.container("minio") + .with_image(images.minio) + .with_command([ + "minio", + "server", + "/minio_data", + "--console-address", + ":9001", + ]) + .with_environment({ + MINIO_ROOT_USER: "minioadmin", + MINIO_ROOT_PASSWORD: "minioadmin", + }) + .with_limits("0.5", "128M") + .with_reservations("0.25", "128M") + .with_port(9000, 9000, "api") + .with_port(9001, 9001, "console") + .with_volume_mount(vol, "/minio_data"); + + local containerSet = engine.containers( + "minio", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9000, 9000, "api") + .with_port(9001, 9001, "console"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/neo4j.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/neo4j.jsonnet new file mode 100644 index 00000000..46c61e0f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/neo4j.jsonnet @@ -0,0 +1,46 @@ +local images = import "values/images.jsonnet"; + +{ + + "neo4j" +: { + + create:: function(engine) + + local vol = engine.volume("neo4j").with_size("20G"); + + local container = + engine.container("neo4j") + .with_image(images.neo4j) + .with_environment({ + NEO4J_AUTH: "neo4j/password", + NEO4J_server_memory_pagecache_size: "512m", + NEO4J_server_memory_heap_max__size: "512m", + // NEO4J_server_bolt_listen__address: "0.0.0.0:7687", + // NEO4J_server_default__listen__address: "0.0.0.0", + // NEO4J_server_http_listen__address: "0.0.0.0:7474", + }) + .with_limits("1.0", "1536M") + .with_reservations("0.5", "1536M") + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2") + .with_volume_mount(vol, "/data"); + + local containerSet = engine.containers( + "neo4j", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/pinecone.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/pinecone.jsonnet new file mode 100644 index 00000000..2bef70fb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/pinecone.jsonnet @@ -0,0 +1,45 @@ +local images = import "values/images.jsonnet"; + +{ + + "pinecone" +: { + + create:: function(engine) + + local container = + engine.container("pinecone") + .with_image(images.pinecone) + .with_environment({ + + PORT: "5080", + + INDEX_TYPE: "serverless", + + // Dimension is fixed, 384 is right for miniLM + // sentence embedding + DIMENSION: 384, + + METRIC: "cosine" + + }) + .with_limits("1.0", "256M") + .with_reservations("0.5", "256M") + .with_port(5080, 5080, "api"); + + local containerSet = engine.containers( + "pinecone", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(5080, 5080, "api"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/qdrant.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/qdrant.jsonnet new file mode 100644 index 00000000..2e6761f8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/backends/qdrant.jsonnet @@ -0,0 +1,65 @@ +local images = import "values/images.jsonnet"; + +{ + + "qdrant" +: { + + // Memory settings (can be overridden by memory-profile) + "memory-limit":: "1024M", + "memory-reservation":: "1024M", + + // Mmap settings for low-memory mode (trades latency for memory) + // Set to null to disable, or string value to enable + "memmap-threshold-kb":: null, + "on-disk-payload":: null, + + create:: function(engine) + + // Capture memory settings into locals + local memLimit = self["memory-limit"]; + local memReserv = self["memory-reservation"]; + local mmapThreshold = self["memmap-threshold-kb"]; + local onDiskPayload = self["on-disk-payload"]; + + local vol = engine.volume("qdrant").with_size("20G"); + + // Build environment with optional mmap settings + local baseEnv = {}; + local env = baseEnv + + (if mmapThreshold != null then { + QDRANT__STORAGE__MEMMAP_THRESHOLD_KB: mmapThreshold, + } else {}) + + (if onDiskPayload != null then { + QDRANT__STORAGE__ON_DISK_PAYLOAD: onDiskPayload, + } else {}); + + local container = + engine.container("qdrant") + .with_image(images.qdrant) + .with_limits("1.0", memLimit) + .with_reservations("0.5", memReserv) + .with_port(6333, 6333, "api") + .with_port(6334, 6334, "api2") + .with_volume_mount(vol, "/qdrant/storage") + + (if std.length(env) > 0 then { + environment+: env, + } else {}); + + local containerSet = engine.containers( + "qdrant", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(6333, 6333, "api") + .with_port(6334, 6334, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/components.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/components.jsonnet new file mode 100644 index 00000000..03a55208 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/components.jsonnet @@ -0,0 +1,121 @@ +{ + + // Essentials + "trustgraph-base": import "core/trustgraph.jsonnet", + "rev-gateway": import "core/rev-gateway.jsonnet", + "pulsar": import "pulsar/pulsar.jsonnet", + + // LLMs + "azure": import "llm/azure.jsonnet", + "azure-openai": import "llm/azure-openai.jsonnet", + "bedrock": import "llm/bedrock.jsonnet", + "claude": import "llm/claude.jsonnet", + "cohere": import "llm/cohere.jsonnet", + "googleaistudio": import "llm/googleaistudio.jsonnet", + "llamafile": import "llm/llamafile.jsonnet", + "lmstudio": import "llm/lmstudio.jsonnet", + "mistral": import "llm/mistral.jsonnet", + "ollama": import "llm/ollama.jsonnet", + "openai": import "llm/openai.jsonnet", + "vertexai": import "llm/vertexai.jsonnet", + "tgi": import "llm/tgi.jsonnet", + "vllm": import "llm/vllm.jsonnet", + + // Embeddings + "embeddings-ollama": import "embeddings/embeddings-ollama.jsonnet", + "embeddings-hf": import "embeddings/embeddings-hf.jsonnet", + "embeddings-fastembed": import "embeddings/embeddings-fastembed.jsonnet", + + // OCR options + "ocr": import "ocr/ocr.jsonnet", + "mistral-ocr": import "ocr/mistral-ocr.jsonnet", + + // Vector stores + "vector-store-milvus": import "vector-store/milvus.jsonnet", + "vector-store-qdrant": import "vector-store/qdrant.jsonnet", + "vector-store-pinecone": import "vector-store/pinecone.jsonnet", + + // Triples stores + "triple-store-cassandra": import "triple-store/cassandra.jsonnet", + "triple-store-neo4j": import "triple-store/neo4j.jsonnet", + "triple-store-falkordb": import "triple-store/falkordb.jsonnet", + "triple-store-memgraph": import "triple-store/memgraph.jsonnet", + + // Object stores + "row-store-cassandra": import "row-store/cassandra.jsonnet", + + // Observability support + "grafana": import "monitoring/grafana.jsonnet", + "loki": import "monitoring/loki.jsonnet", + + // Pulsar manager is a UI for Pulsar. Uses a LOT of memory + "pulsar-manager": import "pulsar/pulsar-manager.jsonnet", + + "override-recursive-chunker": import "core/chunker-recursive.jsonnet", + + // The prompt manager + "prompt-overrides": import "core/prompt-overrides.jsonnet", + + // Extra MCP services + "ddg-mcp-server": import "mcp/ddg-mcp-server.jsonnet", + + // Does nothing. But, can be a hack to overwrite parameters + "null": {}, + + // Passthrough: returns parameters directly, preserving +: merge syntax + // Also supports JSON-safe routing with prefixed parameters like "cassandra-heap" + "override": { + local route = function(target) + function(prefix, k, v) + local suffix = std.substr(k, std.length(prefix), std.length(k) - std.length(prefix)); + { [target] +: { [suffix]:: v } }, + + local routes = { + "cassandra-": route("cassandra"), + "pulsar-": route("pulsar"), + "qdrant-": route("qdrant"), + "api-gateway-": route("api-gateway"), + "librarian-": route("librarian"), + }, + + with_params:: function(pars) + std.foldl( + function(acc, k) + local matchingPrefixes = [p for p in std.objectFields(routes) if std.startsWith(k, p)]; + if std.length(matchingPrefixes) > 0 then + local prefix = matchingPrefixes[0]; + acc + routes[prefix](prefix, k, pars[k]) + else + acc + { [k]:: pars[k] }, + std.objectFields(pars), + {} + ), + }, + + // Memory profiles + "memory-profile-low": import "profiles/memory-profile-low.jsonnet", + + // Model hosting + + "hosting-intel-battlemage-vllm": + import "model-hosting/intel-battlemage-vllm.jsonnet", + + "hosting-cpu-tgi": + import "model-hosting/cpu-tgi.jsonnet", + + "hosting-intel-xpu-tgi": + import "model-hosting/intel-xpu-tgi.jsonnet", + + "hosting-intel-gaudi-tgi": + import "model-hosting/intel-gaudi-tgi.jsonnet", + + "hosting-intel-xpu-vllm": + import "model-hosting/intel-xpu-vllm.jsonnet", + + "hosting-intel-gaudi-vllm": + import "model-hosting/intel-gaudi-vllm.jsonnet", + + "hosting-nvidia-gpu-vllm": + import "model-hosting/nvidia-gpu-vllm.jsonnet", + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/agent-manager-react.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/agent-manager-react.jsonnet new file mode 100644 index 00000000..954d80a7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/agent-manager-react.jsonnet @@ -0,0 +1,47 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "agent-manager" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("agent-manager") + .with_image(images.trustgraph_flow) + .with_command([ + "agent-manager-react", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "agent-manager", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/chunker-recursive.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/chunker-recursive.jsonnet new file mode 100644 index 00000000..0b924aba --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/chunker-recursive.jsonnet @@ -0,0 +1,55 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; + +{ + + "chunk-size":: 2000, + "chunk-overlap":: 100, + + "chunker" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("chunker") + .with_image(images.trustgraph_flow) + .with_command([ + "chunker-recursive", + "-p", + url.pulsar, + "--chunk-size", + std.toString($["chunk-size"]), + "--chunk-overlap", + std.toString($["chunk-overlap"]), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "chunker", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/configuration.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/configuration.jsonnet new file mode 100644 index 00000000..75afebb9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/configuration.jsonnet @@ -0,0 +1,57 @@ + +// This puts the default configuration together. References many things, +// flow classes, a default flow, token costs, prompts, agent tools + +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "init-trustgraph" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local cfgVol = engine.configVolume( + "trustgraph-cfg", "trustgraph", + { + "config.json": importstr "trustgraph/config.json", + } + ); + + local container = + engine.container("init-trustgraph") + .with_image(images.trustgraph_flow) + .with_command( + [ + "tg-init-trustgraph", + "-p", + url.pulsar_admin, + "--config-file", + "/trustgraph/config.json", + ] + ) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation) + .with_volume_mount(cfgVol, "/trustgraph/"); + + local containerSet = engine.containers( + "init-trustgraph", [ container ] + ); + + engine.resources([ + cfgVol, + containerSet, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/document-rag.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/document-rag.jsonnet new file mode 100644 index 00000000..e466e3a4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/document-rag.jsonnet @@ -0,0 +1,92 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; + +{ + + "document-rag" +: { + + "doc-limit":: 20, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local docLimit = self["doc-limit"]; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("document-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "document-rag", + "-p", + url.pulsar, + "--doc-limit", + std.toString(docLimit), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "document-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "document-embeddings" +: { + + "cpu-limit":: "1.0", + "cpu-reservation":: "0.5", + "memory-limit":: "512M", + "memory-reservation":: "512M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("document-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "document-embeddings", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "document-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/graph-rag.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/graph-rag.jsonnet new file mode 100644 index 00000000..718441ea --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/graph-rag.jsonnet @@ -0,0 +1,283 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "kg-extract-definitions" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-extract-definitions") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-definitions", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-extract-definitions", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-relationships" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-extract-relationships") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-relationships", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-extract-relationships", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-agent" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-extract-agent") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-agent", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-extract-agent", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-ontology" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "300M", + "memory-reservation":: "300M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-extract-ontology") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-ontology", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-extract-ontology", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "graph-rag" +: { + + concurrency:: 1, + "entity-limit":: 50, + "triple-limit":: 30, + "max-subgraph-size":: 400, + "max-path-length":: 2, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local entityLimit = self["entity-limit"]; + local tripleLimit = self["triple-limit"]; + local maxSubgraphSize = self["max-subgraph-size"]; + local maxPathLength = self["max-path-length"]; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("graph-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "graph-rag", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--entity-limit", + std.toString(entityLimit), + "--triple-limit", + std.toString(tripleLimit), + "--max-subgraph-size", + std.toString(maxSubgraphSize), + "--max-path-length", + std.toString(maxPathLength), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "graph-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "graph-embeddings" +: { + + "cpu-limit":: "1.0", + "cpu-reservation":: "0.5", + "memory-limit":: "512M", + "memory-reservation":: "512M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "graph-embeddings", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/librarian.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/librarian.jsonnet new file mode 100644 index 00000000..2ecda5a8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/librarian.jsonnet @@ -0,0 +1,58 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local garage = import "backends/garage.jsonnet"; +local cassandra = import "backends/cassandra.jsonnet"; + +{ + + "librarian" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "256M", + "memory-reservation":: "256M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("librarian") + .with_image(images.trustgraph_flow) + .with_command([ + "librarian", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + "--object-store-endpoint", + url.object_store, + "--object-store-access-key", + $.garage["access-key"], + "--object-store-secret-key", + $.garage["secret-key"], + "--object-store-region", + $.garage.region, + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "librarian", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +// Garage and Cassandra are used by the Librarian +} + garage + cassandra + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/mcp-server.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/mcp-server.jsonnet new file mode 100644 index 00000000..9bf7e3bd --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/mcp-server.jsonnet @@ -0,0 +1,54 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "mcp-server" +: { + + port:: 8000, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "256M", + "memory-reservation":: "256M", + + create:: function(engine) + + local port = self.port; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local envSecrets = engine.envSecrets("mcp-server-secret") + .with_env_var("MCP_SERVER_SECRET", "mcp-server-secret") + .with_env_var("GATEWAY_SECRET", "gateway-secret"); + + local container = + engine.container("mcp-server") + .with_image(images.trustgraph_mcp) + .with_command([ + "mcp-server", + "--port", + std.toString(port), + ]) + .with_env_var_secrets(envSecrets) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation) + .with_port(port, port, "mcp"); + + local containerSet = engine.containers( + "mcp-server", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(port, port, "mcp"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/prompt-overrides.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/prompt-overrides.jsonnet new file mode 100644 index 00000000..852ec09d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/prompt-overrides.jsonnet @@ -0,0 +1,24 @@ +local default_prompts = import "prompts/default-prompts.jsonnet"; + +{ + + with:: function(key, value) + if (key == "system-template") then + self + { + prompts +:: { + "system-template": value, + } + } + else + self + { + prompts +:: { + templates +:: { + [key] +:: { + prompt: value + } + } + } + }, + +} + default_prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/prompt-template.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/prompt-template.jsonnet new file mode 100644 index 00000000..3cd79d59 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/prompt-template.jsonnet @@ -0,0 +1,97 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "prompt" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("prompt") + .with_image(images.trustgraph_flow) + .with_command([ + "prompt-template", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "prompt", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "prompt-rag" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("prompt-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "prompt-template", + "-p", + url.pulsar, + "--id", + "prompt-rag", + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "prompt-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/rev-gateway.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/rev-gateway.jsonnet new file mode 100644 index 00000000..fbd9f77a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/rev-gateway.jsonnet @@ -0,0 +1,62 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "rev-gateway" +: { + + // Invalid, but at least means the rev-gateway won't connect to anything + // it shouldn't. + token:: "INVALID_TOKEN", + uri:: "wss://127.0.0.1/api/v1/relay?token=" + self.token, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "256M", + "memory-reservation":: "256M", + + create:: function(engine) + + local uri = self.uri; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local envSecrets = engine.envSecrets("rev-gateway-secret") + .with_env_var("REV_GATEWAY_SECRET", "rev-gateway-secret"); + + local container = + engine.container("api-gateway") + .with_image(images.trustgraph_flow) + .with_command([ + "rev-gateway", + "-p", + url.pulsar, + "--websocket-uri", + std.toString(uri), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation) + .with_port(8000, 8000, "metrics") + .with_port(port, port, "api"); + + local containerSet = engine.containers( + "api-gateway", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics") + .with_port(port, port, "api"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/structured-data.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/structured-data.jsonnet new file mode 100644 index 00000000..8b23372f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/structured-data.jsonnet @@ -0,0 +1,171 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "nlp-query" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("nlp-query") + .with_image(images.trustgraph_flow) + .with_command([ + "nlp-query", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "nlp-query", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "structured-query" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("structured-query") + .with_image(images.trustgraph_flow) + .with_command([ + "structured-query", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "structured-query", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "structured-diag" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "96M", + "memory-reservation":: "96M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("structured-diag") + .with_image(images.trustgraph_flow) + .with_command([ + "structured-diag", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "structured-diag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-objects" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-extract-objects") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-objects", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-extract-objects", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/trustgraph.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/trustgraph.jsonnet new file mode 100644 index 00000000..3cf12d26 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/core/trustgraph.jsonnet @@ -0,0 +1,473 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +local config_initialiser = import "configuration.jsonnet"; +local config = import "../runtime-config/trustgraph-config.jsonnet"; +local librarian = import "librarian.jsonnet"; +local mcp_server = import "mcp-server.jsonnet"; +local workbench = import "../ui/workbench-ui.jsonnet"; +local graphrag = import "graph-rag.jsonnet"; +local documentrag = import "document-rag.jsonnet"; +local prompt_template = import "prompt-template.jsonnet"; +local agent_manager = import "agent-manager-react.jsonnet"; +local structured_data = import "structured-data.jsonnet"; +local ddg = import "mcp/ddg-mcp-server.jsonnet"; + +// Helper to create a routing function for a target object +local route = function(target) + function(prefix, k, v) + local suffix = std.substr(k, std.length(prefix), std.length(k) - std.length(prefix)); + { [target] +: { [suffix]:: v } }; + +// Parameter prefix -> target object routing table +local routes = { + "prompt-rag-": route("prompt-rag"), + "prompt-": route("prompt"), + "text-completion-rag-": route("text-completion-rag"), + "text-completion-": route("text-completion"), + "embeddings-": route("embeddings"), + "api-gateway-": route("api-gateway"), + "chunk-": route("chunker"), + "graph-rag-": route("graph-rag"), + "graph-embeddings-": route("graph-embeddings"), + "kg-extract-definitions-": route("kg-extract-definitions"), + "kg-extract-relationships-": route("kg-extract-relationships"), + "kg-extract-agent-": route("kg-extract-agent"), + "kg-extract-ontology-": route("kg-extract-ontology"), + "kg-extract-objects-": route("kg-extract-objects"), + "garage-": route("garage"), + "config-svc-": route("config-svc"), + "pdf-decoder-": route("pdf-decoder"), + "mcp-tool-": route("mcp-tool"), + "mcp-server-": route("mcp-server"), + "metering-rag-": route("metering-rag"), + "metering-": route("metering"), + "kg-store-": route("kg-store"), + "kg-manager-": route("kg-manager"), + "librarian-": route("librarian"), + "agent-manager-": route("agent-manager"), + "document-rag-": route("document-rag"), + "document-embeddings-": route("document-embeddings"), + "rev-gateway-": route("rev-gateway"), + "nlp-query-": route("nlp-query"), + "structured-query-": route("structured-query"), + "structured-diag-": route("structured-diag"), + "init-trustgraph-": route("init-trustgraph"), +}; + +// Find longest matching prefix (most specific first) +local findRoute = function(k) + local prefixes = std.objectFields(routes); + local matching = std.filter(function(p) std.startsWith(k, p), prefixes); + local sorted = std.sort(matching, function(x) -std.length(x)); + if std.length(sorted) > 0 then sorted[0] else null; + +{ + + // Route parameters to appropriate internal objects based on prefix + with:: function(k, v) + local prefix = findRoute(k); + if prefix != null then + self + routes[prefix](prefix, k, v) + else + self + { [k]:: v }, + + "log-level":: "INFO", + + // Base objects with concurrency defaults (LLM/embeddings components merge into these) + "text-completion" +: { concurrency:: 1 }, + "text-completion-rag" +: { concurrency:: 1 }, + embeddings +: { concurrency:: 1 }, + + "api-gateway" +: { + + port:: 8088, + timeout:: 600, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "512M", + "memory-reservation":: "512M", + + create:: function(engine) + + local port = self.port; + local timeout = self.timeout; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local envSecrets = engine.envSecrets("gateway-secret") + .with_env_var("GATEWAY_SECRET", "gateway-secret"); + + local container = + engine.container("api-gateway") + .with_image(images.trustgraph_flow) + .with_command([ + "api-gateway", + "-p", + url.pulsar, + "--timeout", + std.toString(timeout), + "--port", + std.toString(port), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation) + .with_port(port, port, "api"); + + local containerSet = engine.containers( + "api-gateway", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics") + .with_port(port, port, "api"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "chunker" +: { + + size:: 2000, + overlap:: 50, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local size = self.size; + local overlap = self.overlap; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("chunker") + .with_image(images.trustgraph_flow) + .with_command([ + "chunker-recursive", + "-p", + url.pulsar, + "--chunk-size", + std.toString(size), + "--chunk-overlap", + std.toString(overlap), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "chunker", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "config-svc" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local cpuLimit = self["cpu-limit"]; + local cpuReservation = self["cpu-reservation"]; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("config-svc") + .with_image(images.trustgraph_flow) + .with_command([ + "config-svc", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(cpuLimit, memoryLimit) + .with_reservations(cpuReservation, memoryReservation); + + local containerSet = engine.containers( + "config-svc", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "pdf-decoder" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "512M", + "memory-reservation":: "512M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("pdf-decoder") + .with_image(images.trustgraph_flow) + .with_command([ + "pdf-decoder", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "pdf-decoder", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "mcp-tool" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("mcp-tool") + .with_image(images.trustgraph_flow) + .with_command([ + "mcp-tool", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "mcp-tool", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "metering" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("metering") + .with_image(images.trustgraph_flow) + .with_command([ + "metering", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "metering", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "metering-rag" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("metering-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "metering", + "-p", + url.pulsar, + "--id", + "metering-rag", + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "metering-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-store" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-store") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-store", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-store", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-manager" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-manager") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-manager", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-manager", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + librarian + mcp_server + workbench + graphrag + + documentrag + prompt_template + agent_manager + structured_data + + config_initialiser + config + + ddg + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/embeddings/embeddings-fastembed.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/embeddings/embeddings-fastembed.jsonnet new file mode 100644 index 00000000..37116de0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/embeddings/embeddings-fastembed.jsonnet @@ -0,0 +1,49 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/embeddings-fastembed.jsonnet"; + +{ + + "fastembed-models":: models, + + "embeddings-models" +:: $["fastembed-models"], + + embeddings +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local container = + engine.container("embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "embeddings-fastembed", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits("1.0", "400M") + .with_reservations("0.5", "400M"); + + local containerSet = engine.containers( + "embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/embeddings/embeddings-hf.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/embeddings/embeddings-hf.jsonnet new file mode 100644 index 00000000..492f05cb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/embeddings/embeddings-hf.jsonnet @@ -0,0 +1,47 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/embeddings-huggingface.jsonnet"; + +{ + + "huggingface-embeddings-models":: models, + + "embeddings-models" +:: $["huggingface-embeddings-models"], + + embeddings +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local container = + engine.container("embeddings") + .with_image(images.trustgraph_hf) + .with_command([ + "embeddings-hf", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + ]) + .with_limits("1.0", "400M") + .with_reservations("0.5", "400M"); + + local containerSet = engine.containers( + "embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/embeddings/embeddings-ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/embeddings/embeddings-ollama.jsonnet new file mode 100644 index 00000000..05cbed72 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/embeddings/embeddings-ollama.jsonnet @@ -0,0 +1,52 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local models = import "parameters/embeddings-ollama.jsonnet"; + +{ + + "ollama-url":: "${OLLAMA_HOST}", + + "ollama-models":: models, + + "embeddings-models" +:: $["ollama-models"], + + embeddings +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local container = + engine.container("embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "embeddings-ollama", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "-r", + $["ollama-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/engine/aks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/engine/aks-k8s.jsonnet new file mode 100644 index 00000000..07e050f2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/engine/aks-k8s.jsonnet @@ -0,0 +1,45 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "disk.csi.azure.com", + parameters: { + // Standard disks (spinning magnetic), Locally Redundant Storage + // Cheapest, basically + skuName: "Standard_LRS", + }, + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/engine/docker-compose.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/engine/docker-compose.jsonnet new file mode 100644 index 00000000..334b3517 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/engine/docker-compose.jsonnet @@ -0,0 +1,260 @@ +{ + + // Extract resources using the engine + package:: function(patterns) + std.foldl( + function(state, p) state + p.create(self), + std.objectValues(patterns), + {} + ), + + container:: function(name) + { + + local container = self, + + name:: name, + + with_image:: function(x) self + { image: x }, + + with_user:: function(x) self + { user: x }, + + with_group:: function(x) self + + if std.objectHas(container, "group_add") then + { group_add: container.group_add + [x] } + else + { group_add: [x] }, + + with_command:: function(x) self + { + command: + if std.isString(x) then + std.strReplace(x, "$", "$$") + else if std.isArray(x) then + std.map(function(s) std.strReplace(s, "$", "$$"), x) + else + x + }, + + with_entrypoint:: function(x) self + { entrypoint: x }, + + with_runtime:: function(x) self + { runtime: x }, + + with_privileged:: function(x) self + { privileged: x }, + + with_ipc:: function(x) self + { ipc: x }, + + with_capability:: function(x) self + + if std.objectHas(container, "capability") then + { cap_add: container.capability + x } + else + { cap_add: [x], }, + + with_environment:: function(x) self + + if std.objectHas(container, "environment") then + { environment: container.environment + x } + else + { environment: x, }, + + with_device:: function(hdev, cdev) self + + if std.objectHas(container, "devices") then + { devices: container.devices + [ "%s:%s" % [hdev, cdev] ] } + else + { devices: [ "%s:%s" % [hdev, cdev] ], }, + + with_limits:: function(c, m) self + { + deploy +: { resources +: { + limits: { cpus: c, memory: m } + } }, + }, + + with_reservations:: function(c, m) self + { + deploy +: { resources +: { + reservations: { cpus: c, memory: m } + } }, + }, + + with_volume_mount:: + function(vol, mnt) + self + { + volumes: + if std.objectHas(container, "volumes") then + container.volumes + [ + "%s:%s" % [vol.volid, mnt] + ] + else + [ + "%s:%s" % [vol.volid, mnt] + ] + }, + + with_bind_mount:: + function(src, dest) + self + { + volumes: + if std.objectHas(container, "volumes") then + container.volumes + [ + "%s:%s" % [src, dest] + ] + else + [ + "%s:%s" % [src, dest] + ] + }, + + with_port:: + function(src, dest, name) + self + { + ports: + if std.objectHas(container, "ports") then + container.ports + [ "%d:%d" % [src, dest] ] + else + [ "%d:%d" % [src, dest] ] + }, + + with_env_var_secrets:: + function(vars) + std.foldl( + function(obj, x) obj.with_environment( + { [x]: "${" + x + "}" } + ), + vars.variables, + self + ), + + restart: "on-failure:100", + + add:: function() { + services +: { + [container.name]: container, + } + } + + }, + + internalService:: function(containers) + { + + local service = self, + + name: containers.name, + + with_port:: function(src, dest, name) + self + { port: [src, dest] }, + + add:: function() { + } + + }, + + service:: function(containers) + { + + local service = self, + + name: containers.name, + + with_port:: function(src, dest, name) + self + { port: [src, dest] }, + + add:: function() { + } + + }, + + volume:: function(name) + { + + local volume = self, + + name: name, + + volid:: name, + + with_size:: function(size) self + { size: size }, + + add:: function() { + volumes +: { + [volume.name]: {} + } + } + + }, + + configVolume:: function(name, dir, parts) + { + + local volume = self, + + name: dir, + + volid:: "./" + dir, + + with_size:: function(size) self + { size: size }, + + add:: function() { + } + + }, + + secretVolume:: function(name, dir, parts) + { + + local volume = self, + + name: dir, + + volid:: dir, + + with_size:: function(size) self + { size: size }, + + add:: function() { + } + + }, + + envSecrets:: function(name) + { + + local volume = self, + + name: name, + + volid:: name, + + variables:: [], + + with_env_var:: + function(name, key) self + { + variables: super.variables + [name], + }, + + add:: function() { + } + + }, + + containers:: function(name, containers) + { + + local cont = self, + + name: name, + containers: containers, + + add:: function() std.foldl( + function(state, c) state + c.add(), + cont.containers, + {} + ), + + }, + + resources:: function(res) + std.foldl( + function(state, c) state + c.add(), + res, + {} + ), + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/engine/eks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/engine/eks-k8s.jsonnet new file mode 100644 index 00000000..3fc3f035 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/engine/eks-k8s.jsonnet @@ -0,0 +1,46 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "ebs.csi.aws.com", + parameters: { + type: "gp3", + encrypted: "true", + iops: "6000", + throughput: "400", + }, + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/engine/gcp-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/engine/gcp-k8s.jsonnet new file mode 100644 index 00000000..71792426 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/engine/gcp-k8s.jsonnet @@ -0,0 +1,44 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "pd.csi.storage.gke.io", + parameters: { + type: "pd-balanced", + "csi.storage.k8s.io/fstype": "ext4", + }, + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/engine/k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/engine/k8s.jsonnet new file mode 100644 index 00000000..2067f6ac --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/engine/k8s.jsonnet @@ -0,0 +1,393 @@ +{ + + container:: function(name) + { + + local container = self, + + name: name, + limits: {}, + reservations: {}, + ports: [], + volumes: [], + bindMounts: [], + groups: [], + environment: [], + + with_image:: function(x) self + { image: x }, + + with_user:: function(x) self + { user: x }, + + with_group:: function(x) self + { groups: super.groups + [x] }, + + with_privileged:: function(x) self + { privileged: x }, + + with_command:: function(x) self + { command: x }, + + with_entrypoint:: function(x) self + { entrypoint: x }, + + with_environment:: function(x) self + { + environment: super.environment + [ + { + name: v.key, value: v.value + } + for v in std.objectKeysValues(x) + ], + }, + + with_limits:: function(c, m) self + { limits: { cpu: c, memory: m } }, + + with_reservations:: + function(c, m) self + { reservations: { cpu: c, memory: m } }, + + with_volume_mount:: + function(vol, mnt) + self + { + volumes: super.volumes + [{ + volume: vol, mount: mnt + }] + }, + + with_bind_mount:: + function(src, dest) + local name = "bind-" + std.strReplace(std.strReplace(src, "/", "-"), ".", "-"); + self + { + bindMounts: super.bindMounts + [{ + name: name, src: src, dest: dest + }] + }, + + with_port:: + function(src, dest, name) self + { + ports: super.ports + [ + { src: src, dest: dest, name : name } + ] + }, + + with_env_var_secrets:: + function(vars) + std.foldl( + function(obj, x) obj + { + environment: super.environment + [{ + name: x, + valueFrom: { + secretKeyRef: { + name: vars.name, + key: vars.keyMap[x], + } + } + }] + }, + vars.variables, + self + ), + + add:: function() [ + + { + apiVersion: "apps/v1", + kind: "Deployment", + metadata: { + name: container.name, + namespace: "trustgraph", + labels: { + app: container.name + } + }, + spec: { + replicas: 1, + selector: { + matchLabels: { + app: container.name, + } + }, + template: { + metadata: { + labels: { + app: container.name, + } + }, + spec: { + containers: [ + { + name: container.name, + image: container.image, + + // FIXME: Make everything run as + // root. Needed to get filesystems + // to be accessible. There's a + // better way of doing this? + securityContext: { + runAsUser: 0, + runAsGroup: 0, + } + ( + if std.objectHas(container, "privileged") && container.privileged then + { privileged: true } + else {} + ), + + resources: { + requests: container.reservations, + limits: container.limits + }, + } + ( + if std.length(container.ports) > 0 then + { + ports: [ + { + hostPort: port.src, + containerPort: port.dest, + } + for port in container.ports + ] + } else + {}) + + + (if std.objectHas(container, "entrypoint") then + // Entrypoint is set - use command for entrypoint, args for command + (if std.isString(container.entrypoint) && container.entrypoint == "" then + { command: [] } + else if std.isArray(container.entrypoint) then + { command: container.entrypoint } + else + { command: [container.entrypoint] } + ) + (if std.objectHas(container, "command") then + { args: container.command } + else {}) + else if std.objectHas(container, "command") then + { command: container.command } + else {}) + + + (if std.length(container.environment) > 0 then + { + env: container.environment, + } + else {}) + + + (if std.length(container.volumes) > 0 || std.length(container.bindMounts) > 0 then + { + volumeMounts: [ + { + mountPath: vol.mount, + name: vol.volume.name, + } + for vol in container.volumes + ] + [ + { + mountPath: bm.dest, + name: bm.name, + } + for bm in container.bindMounts + ] + } + + else + {} + ) + ], + volumes: [ + vol.volume.volRef() + for vol in container.volumes + ] + [ + { + name: bm.name, + hostPath: { path: bm.src } + } + for bm in container.bindMounts + ] + } + ( + if std.length(container.groups) > 0 then + { securityContext: { supplementalGroups: container.groups } } + else {} + ) + }, + } + {} + + } + + ] + + }, + + // Just an alias + internalService:: self.service, + + service:: function(containers) + { + + local service = self, + + name: containers.name, + + ports: [], + + with_port:: + function(src, dest, name) + self + { + ports: super.ports + [ + { src: src, dest: dest, name: name } + ] + }, + + add:: function() [ + + { + + apiVersion: "v1", + kind: "Service", + metadata: { + name: service.name, + namespace: "trustgraph", + }, + spec: { + selector: { + app: service.name, + }, + ports: [ + { + port: port.src, + targetPort: port.dest, + name: port.name, + } + for port in service.ports + ], + } + } + ], + + }, + + volume:: function(name) + { + + local volume = self, + + name: name, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + { + apiVersion: "v1", + kind: "PersistentVolumeClaim", + metadata: { + name: volume.name, + namespace: "trustgraph", + }, + spec: { + storageClassName: "tg", + accessModes: [ "ReadWriteOnce" ], + resources: { + requests: { + storage: volume.size, + } + }, + } + } + ], + + volRef:: function() { + name: volume.name, + persistentVolumeClaim: { claimName: volume.name }, + } + + }, + + configVolume:: function(name, dir, parts) + { + + local volume = self, + + name: name, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + { + apiVersion: "v1", + kind: "ConfigMap", + metadata: { + name: volume.name, + namespace: "trustgraph", + }, + data: parts + }, + ], + + + volRef:: function() { + name: volume.name, + configMap: { name: volume.name }, + } + + }, + + secretVolume:: function(name, dir, parts) + { + + local volume = self, + + name: name, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + ], + + volRef:: function() { + name: volume.name, + secret: { secretName: volume.name }, + } + + }, + + envSecrets:: function(name) + { + + local volume = self, + + name: name, + + variables: [], + keyMap: {}, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + ], + + volRef:: function() { + name: volume.name, + secret: { secretName: volume.name }, + }, + + with_env_var:: + function(name, key) self + { + variables: super.variables + [name], + keyMap: super.keyMap + { [name]: key }, + }, + + }, + + containers:: function(name, containers) + { + + local cont = self, + + name: name, + containers: containers, + + add:: function() std.flattenArrays( + [ c.add() for c in cont.containers ] + ), + + }, + + resources:: function(res) + + std.flattenArrays( + [ c.add() for c in res ] + ), + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/engine/minikube-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/engine/minikube-k8s.jsonnet new file mode 100644 index 00000000..858b17ad --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/engine/minikube-k8s.jsonnet @@ -0,0 +1,115 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList, + + volume:: function(name) + { + local volume = self, + name: name, + with_size:: function(size) self + { size: size }, + add:: function() [ + { + apiVersion: "v1", + kind: "PersistentVolume", + metadata: { + name: volume.name, + }, + spec: { + accessModes: [ "ReadWriteOnce" ], + capacity: { + storage: volume.size, + }, + persistentVolumeReclaimPolicy: "Delete", + hostPath: { + path: "/data/pv-" + volume.name, + }, + } + }, + { + apiVersion: "v1", + kind: "PersistentVolumeClaim", + metadata: { + name: volume.name, + namespace: "trustgraph", + }, + spec: { + accessModes: [ "ReadWriteOnce" ], + resources: { + requests: { + storage: volume.size, + } + }, + } + } + ], + + volRef:: function() { + name: volume.name, + persistentVolumeClaim: { claimName: volume.name }, + } + + }, + + service:: function(containers) + { + local service = self, + name: containers.name, + ports: [], + with_port:: + function(src, dest, name) + self + { + ports: super.ports + [ + { src: src, dest: dest, name: name } + ] + }, + add:: function() [ + { + apiVersion: "v1", + kind: "Service", + metadata: { + name: service.name, + namespace: "trustgraph", + }, + spec: { + selector: { + app: service.name, + }, + type: "LoadBalancer", + ports: [ + { + port: port.src, + targetPort: port.dest, + name: port.name, + } + for port in service.ports + ], + } + } + ], + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/engine/noop.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/engine/noop.jsonnet new file mode 100644 index 00000000..1f384648 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/engine/noop.jsonnet @@ -0,0 +1,79 @@ +{ + + // Extract resources usnig the engine + package:: function(patterns) {}, + + container:: function(name) { + + with_image:: function(x) self + {}, + + with_user:: function(x) self + {}, + + with_command:: function(x) self + {}, + + with_runtime:: function(x) self + {}, + + with_privileged:: function(x) self + {}, + + with_ipc:: function(x) self + {}, + + with_capability:: function(x) self + {}, + + with_environment:: function(x) self + {}, + + with_device:: function(hdev, cdev) self + {}, + + with_limits:: function(c, m) self + {}, + + with_reservations:: function(c, m) self + {}, + + with_volume_mount:: self + {}, + + with_port:: function(src, dest, name) self + {}, + + with_env_var_secrets:: function(vars) self + {}, + + add:: function() {}, + }, + + internalService:: function(containers) { + with_port:: function(src, dest, name) self + {}, + add:: function() {}, + }, + + service:: function(containers) { + with_port:: function(src, dest, name) self + {}, + add:: function() {}, + }, + + volume:: function(name) { + with_size:: function(size) self + {}, + add:: function() {}, + }, + + configVolume:: function(name, dir, parts) { + add:: function() {}, + }, + + secretVolume:: function(name, dir, parts) { + add:: function() {}, + }, + + envSecrets:: function(name) { + with_env_var:: function(name, key) self + {}, + add:: function() {}, + }, + + containers:: function(name, containers) { + add:: function() {}, + }, + + resources:: function(res) + std.foldl( + function(state, c) state + c.add(), + res, + {} + ), + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/engine/ovh-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/engine/ovh-k8s.jsonnet new file mode 100644 index 00000000..15d57c83 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/engine/ovh-k8s.jsonnet @@ -0,0 +1,45 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "cinder.csi.openstack.org", + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", + parameters: { + availability: "nova", + fsType: "ext4", + type: "high-speed", + }, +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: [ns, sc] + resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/engine/scw-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/engine/scw-k8s.jsonnet new file mode 100644 index 00000000..eed23968 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/engine/scw-k8s.jsonnet @@ -0,0 +1,40 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "csi.scaleway.com", + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: [ns, sc] + resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/agent-extract.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/agent-extract.jsonnet new file mode 100644 index 00000000..4c5785c8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/agent-extract.jsonnet @@ -0,0 +1,35 @@ +// Agent-based extraction module +// Uses AI agents for more sophisticated knowledge extraction from text +// Leverages agent tools and reasoning for complex extraction tasks + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; + +{ + // No external interfaces - internal agent extraction service + "interfaces" +: { + }, + + // No configurable parameters for agent extraction + "parameters" +: { + }, + + // Flow-level processors for agent-based extraction + "flow" +: { + // Agent-based knowledge extraction processor + // Uses AI agents with tools to extract structured knowledge + "kg-extract-agent:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output knowledge triples + "entity-contexts": flow("entity-contexts-load:{id}"), // Entity context information + "agent-request": request("agent:{id}"), // Agent service requests + "agent-response": response("agent:{id}"), // Agent service responses + }, + }, + + // No blueprint-level processors needed + "blueprint" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/agent.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/agent.jsonnet new file mode 100644 index 00000000..6ed769b6 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/agent.jsonnet @@ -0,0 +1,51 @@ +// Agent management module +// Provides AI agent orchestration and tool integration +// Manages agent conversations, tool calls, and response coordination +// Supports MCP tools, GraphRAG, and structured queries + +local helpers = import "helpers.jsonnet"; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +// Import shared services (agent requires LLM for reasoning, MCP for tools) +local llm_services = import "llm-services.jsonnet"; +local mcp_service = import "mcp-service.jsonnet"; + +// Merge shared services with agent-specific configuration +llm_services + mcp_service + { + + // External interfaces for agent operations + "interfaces" +: { + "agent": request_response("agent:{id}"), + }, + + // Flow-level processors for agent management + "flow" +: { + // Agent manager orchestrates agent conversations and tool usage + "agent-manager:{id}": { + // Agent communication channels + request: request("agent:{id}"), + next: request("agent:{id}"), + response: response("agent:{id}"), + + // LLM and prompt services + "text-completion-request": request("text-completion:{id}"), + "text-completion-response": response("text-completion:{id}"), + "prompt-request": request("prompt:{id}"), + "prompt-response": response("prompt:{id}"), + + // Tool integrations + "mcp-tool-request": request("mcp-tool:{id}"), + "mcp-tool-response": response("mcp-tool:{id}"), + "graph-rag-request": request("graph-rag:{id}"), + "graph-rag-response": response("graph-rag:{id}"), + "structured-query-request": request("structured-query:{id}"), + "structured-query-response": response("structured-query:{id}"), + }, + }, + + // Blueprint-level processors for agent-related services + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/document-store.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/document-store.jsonnet new file mode 100644 index 00000000..676765b2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/document-store.jsonnet @@ -0,0 +1,56 @@ +// Document store module +// Infrastructure for document-based RAG using chunk embeddings +// Handles document embedding storage, retrieval, and question answering + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +// Import shared services +local llm_services = import "llm-services.jsonnet"; +local embeddings_service = import "embeddings-service.jsonnet"; + +// Merge shared services with document store configuration +llm_services + embeddings_service + { + + // External interfaces for document store + "interfaces" +: { + // Document embedding storage and retrieval + "document-embeddings-store": flow("document-embeddings-store:{id}"), + "document-rag": request_response("document-rag:{id}"), + "document-embeddings": request_response("document-embeddings:{id}"), + }, + + // Flow-level processors for document embedding and storage + "flow" +: { + "document-embeddings:{id}": { + input: flow("chunk-load:{id}"), + output: flow("document-embeddings-store:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + }, + "de-write:{id}": { + input: flow("document-embeddings-store:{id}"), + }, + "document-rag:{id}": { + request: request("document-rag:{id}"), + response: response("document-rag:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + "prompt-request": request("prompt-rag:{id}"), + "prompt-response": response("prompt-rag:{id}"), + "document-embeddings-request": request("document-embeddings:{id}"), + "document-embeddings-response": response("document-embeddings:{id}"), + }, + "de-query:{id}": { + request: request("document-embeddings:{id}"), + response: response("document-embeddings:{id}"), + }, + }, + + // Blueprint-level processors for document RAG operations + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/embeddings-service.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/embeddings-service.jsonnet new file mode 100644 index 00000000..1537e6c2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/embeddings-service.jsonnet @@ -0,0 +1,30 @@ +// Shared embeddings service module +// Provides vector embedding generation for text +// Import this module in any flow that requires embeddings + +local helpers = import "helpers.jsonnet"; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +{ + // Interfaces exposed by embeddings service + "interfaces" +: { + "embeddings": request_response("embeddings:{id}"), + }, + + "parameters" +: { + }, + + // Flow-level processor for embeddings + "flow" +: { + "embeddings:{id}": { + request: request("embeddings:{id}"), + response: response("embeddings:{id}"), + model: "{embeddings-model}", + }, + }, + + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/flow-blueprints.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/flow-blueprints.jsonnet new file mode 100644 index 00000000..4282fa23 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/flow-blueprints.jsonnet @@ -0,0 +1,48 @@ +// TrustGraph Flow Blueprints Configuration +// +// RAG Modes (4 types): +// - Document RAG: Uses document chunk embeddings +// - Graph RAG: Extracts definitions + relationships to graph +// - Ontology RAG: Extracts using ontology definitions to graph (mutually exclusive with Graph RAG) +// - Structured RAG: Extracts objects to object store +// +// Module structure: +// - *-store: Storage and query infrastructure +// - *-extract: Extraction methods + +// Import all the modular flow components +local graph_store = import "graph-store.jsonnet"; +local document_store = import "document-store.jsonnet"; +local structured_store = import "structured-store.jsonnet"; +local graphrag_extract = import "graphrag-extract.jsonnet"; +local ontorag_extract = import "ontorag-extract.jsonnet"; +local structured_extract = import "structured-extract.jsonnet"; +local agent = import "agent.jsonnet"; +local load = import "load.jsonnet"; +local kgcore = import "kgcore.jsonnet"; + +{ + + // Full system: Graph RAG + Document RAG + knowledge cores + "everything": { + description: "Graph RAG + Document RAG + knowledge cores", + tags: ["document-rag", "graph-rag", "kgcore"], + } + + graph_store + document_store + agent + load + + graphrag_extract + kgcore, + + // Structured RAG only + "structured": { + description: "Structured data extraction and querying", + tags: ["structured"], + } + + structured_store + structured_extract + agent + load, + + // Ontology RAG + knowledge cores + "ontology": { + description: "Ontology RAG + knowledge cores", + tags: ["onto-rag", "kgcore"], + } + + graph_store + ontorag_extract + agent + load + kgcore, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/graph-store.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/graph-store.jsonnet new file mode 100644 index 00000000..c6f16083 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/graph-store.jsonnet @@ -0,0 +1,70 @@ +// Graph store module +// Shared infrastructure for graph-based RAG (used by both GraphRAG and OntologyRAG) +// Handles knowledge graph storage, embeddings, and graph-based question answering + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +// Import shared services +local llm_services = import "llm-services.jsonnet"; +local embeddings_service = import "embeddings-service.jsonnet"; + +// Merge shared services with graph store configuration +llm_services + embeddings_service + { + + // External interfaces exposed by the graph store + "interfaces" +: { + // Data ingestion interfaces for graph construction + "entity-contexts-load": flow("entity-contexts-load:{id}"), + "triples-store": flow("triples-store:{id}"), + "graph-embeddings-store": flow("graph-embeddings-store:{id}"), + + // Query interfaces for graph-based operations + "graph-rag": request_response("graph-rag:{id}"), + "triples": request_response("triples:{id}"), + "graph-embeddings": request_response("graph-embeddings:{id}"), + }, + + // Flow-level processors - handle data streams for a specific flow instance + "flow" +: { + "graph-embeddings:{id}": { + input: flow("entity-contexts-load:{id}"), + output: flow("graph-embeddings-store:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + }, + "triples-write:{id}": { + input: flow("triples-store:{id}"), + }, + "ge-write:{id}": { + input: flow("graph-embeddings-store:{id}"), + }, + "graph-rag:{id}": { + request: request("graph-rag:{id}"), + response: response("graph-rag:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + "prompt-request": request("prompt-rag:{id}"), + "prompt-response": response("prompt-rag:{id}"), + "graph-embeddings-request": request("graph-embeddings:{id}"), + "graph-embeddings-response": response("graph-embeddings:{id}"), + "triples-request": request("triples:{id}"), + "triples-response": response("triples:{id}"), + }, + "triples-query:{id}": { + request: request("triples:{id}"), + response: response("triples:{id}"), + }, + "ge-query:{id}": { + request: request("graph-embeddings:{id}"), + response: response("graph-embeddings:{id}"), + }, + }, + + // Blueprint-level processors - shared across all flow instances of this blueprint + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/graphrag-extract.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/graphrag-extract.jsonnet new file mode 100644 index 00000000..6bcbca14 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/graphrag-extract.jsonnet @@ -0,0 +1,44 @@ +// GraphRAG extraction module +// Extraction method for GraphRAG - extracts definitions and relationships +// Mutually exclusive with OntologyRAG extraction (both write to graph store) + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; + +{ + // No external interfaces - this module provides internal extraction services + "interfaces" +: { + }, + + // No configurable parameters for basic KG extraction + "parameters" +: { + }, + + // Flow-level processors for knowledge extraction + "flow" +: { + // Extracts entity definitions from text chunks + // Identifies and defines key entities mentioned in the text + "kg-extract-definitions:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output definition triples + "entity-contexts": flow("entity-contexts-load:{id}"), // Entity context information + "prompt-request": request("prompt:{id}"), // Definition extraction prompts + "prompt-response": response("prompt:{id}"), + }, + + // Extracts relationships between entities + // Identifies how entities are connected and interact + "kg-extract-relationships:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output relationship triples + "prompt-request": request("prompt:{id}"), // Relationship extraction prompts + "prompt-response": response("prompt:{id}"), + }, + }, + + // No blueprint-level processors needed + "blueprint" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/helpers.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/helpers.jsonnet new file mode 100644 index 00000000..eabb3bf4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/helpers.jsonnet @@ -0,0 +1,29 @@ +// Helper functions for flow configuration +// Provides utility functions for constructing flow, request, and response URIs +// used throughout the TrustGraph flow configuration system + +// Creates a persistent flow URI for data streams +// Persistent flows retain messages until consumed +local flow(x) = "persistent://tg/flow/" + x; + +// Creates a non-persistent request URI for request-response patterns +// Non-persistent means messages are not retained if no consumer is present +local request(x) = "non-persistent://tg/request/" + x; + +// Creates a non-persistent response URI for request-response patterns +local response(x) = "non-persistent://tg/response/" + x; + +// Creates a request-response pair for bidirectional communication +// Returns an object with both request and response URIs +local request_response(x) = { + request: request(x), + response: response(x), +}; + +// Export all helper functions for use in other modules +{ + flow: flow, + request: request, + response: response, + request_response: request_response, +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/kgcore.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/kgcore.jsonnet new file mode 100644 index 00000000..bfc01ada --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/kgcore.jsonnet @@ -0,0 +1,31 @@ +// Knowledge Graph Core storage module +// Handles persistent storage of knowledge graph data +// Consolidates triples and graph embeddings into permanent storage +// Creates the core knowledge base for long-term use + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; + +{ + // No external interfaces - internal storage service + "interfaces" +: { + }, + + // No configurable parameters for core storage + "parameters" +: { + }, + + // Flow-level processors for knowledge graph storage + "flow" +: { + // Knowledge graph store consolidates extracted knowledge + // Takes processed triples and embeddings and stores them permanently + "kg-store:{id}": { + "triples-input": flow("triples-store:{id}"), // Input RDF triples stream + "graph-embeddings-input": flow("graph-embeddings-store:{id}"), // Input graph embeddings + }, + }, + + // No blueprint-level processors needed + "blueprint" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/llm-parameters.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/llm-parameters.jsonnet new file mode 100644 index 00000000..a99f72ae --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/llm-parameters.jsonnet @@ -0,0 +1,59 @@ +{ + + // LLM model selection for normal LLM + "llm-model": { + "type": "llm-model", + "description": "LLM model", + "order": 1, + "advanced": false, + }, + + // LLM model for RAG operations + "llm-rag-model": { + "type": "llm-model", + "description": "LLM model for RAG", + "order": 2, + "advanced": true, + "controlled-by": "llm-model", + }, + + // LLM model selection for normal LLM + "llm-temperature": { + "type": "llm-temperature", + "description": "LLM temperature", + "order": 3, + "advanced": true, + }, + + // LLM model selection for normal LLM + "llm-rag-temperature": { + "type": "llm-temperature", + "description": "LLM temperature for RAG", + "order": 4, + "advanced": true, + }, + + "embeddings-model": { + "type": "embeddings-model", + "description": "Embeddings model", + "order": 5, + "advanced": true, + }, + + // LLM model selection for normal LLM + "chunk-size": { + "type": "chunk-size", + "description": "Chunk size", + "order": 6, + "advanced": true, + }, + + // LLM model selection for normal LLM + "chunk-overlap": { + "type": "chunk-overlap", + "description": "Chunk overlap", + "order": 7, + "advanced": true, + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/llm-services.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/llm-services.jsonnet new file mode 100644 index 00000000..ee6e09ae --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/llm-services.jsonnet @@ -0,0 +1,66 @@ +// Shared LLM services module +// Provides text completion, prompt processing, and metering services +// Import this module in any flow that requires LLM functionality + +local helpers = import "helpers.jsonnet"; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; +local llm_parameters = import "llm-parameters.jsonnet"; + +{ + // Interfaces exposed by LLM services + "interfaces" +: { + "prompt": request_response("prompt:{id}"), + "text-completion": request_response("text-completion:{id}"), + }, + + // LLM configuration parameters + "parameters" +: llm_parameters, + + // Flow-level processors for LLM services + "flow" +: { + // Primary text completion service + "text-completion:{id}": { + request: request("text-completion:{id}"), + response: response("text-completion:{id}"), + model: "{llm-model}", + }, + + // RAG-specific text completion (may use different model) + "text-completion-rag:{id}": { + request: request("text-completion-rag:{id}"), + response: response("text-completion-rag:{id}"), + model: "{llm-rag-model}", + }, + + // Prompt processing service + "prompt:{id}": { + request: request("prompt:{id}"), + response: response("prompt:{id}"), + "text-completion-request": request("text-completion:{id}"), + "text-completion-response": response("text-completion:{id}"), + }, + + // RAG-specific prompt processing + "prompt-rag:{id}": { + request: request("prompt-rag:{id}"), + response: response("prompt-rag:{id}"), + "text-completion-request": request("text-completion-rag:{id}"), + "text-completion-response": response("text-completion-rag:{id}"), + }, + + // Usage metering for primary completion + "metering:{id}": { + input: response("text-completion:{id}"), + }, + + // Usage metering for RAG completion + "metering-rag:{id}": { + input: response("text-completion-rag:{id}"), + }, + }, + + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/load.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/load.jsonnet new file mode 100644 index 00000000..6c440792 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/load.jsonnet @@ -0,0 +1,43 @@ +// Document loading and preprocessing module +// Handles document ingestion, format conversion, and chunking +// Converts PDFs to text and splits documents into processable chunks + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +// Import shared services (load requires embeddings for chunk processing) +local embeddings_service = import "embeddings-service.jsonnet"; + +// Merge shared services with load-specific configuration +embeddings_service + { + + // External interfaces for document loading + "interfaces" +: { + "document-load": flow("document-load:{id}"), + "text-load": flow("text-document-load:{id}"), + }, + + // Flow-level processors for document preprocessing + "flow" +: { + // PDF decoder converts PDF documents to text + "pdf-decoder:{id}": { + input: flow("document-load:{id}"), + output: flow("text-document-load:{id}"), + }, + + // Chunker splits documents into smaller, processable pieces + "chunker:{id}": { + input: flow("text-document-load:{id}"), + output: flow("chunk-load:{id}"), + "chunk-size": "{chunk-size}", + "chunk-overlap": "{chunk-overlap}", + }, + }, + + // Blueprint-level processors for document loading services + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/mcp-service.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/mcp-service.jsonnet new file mode 100644 index 00000000..5d599a67 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/mcp-service.jsonnet @@ -0,0 +1,31 @@ +// Shared MCP (Model Context Protocol) tool service module +// Provides MCP tool execution capabilities for agents +// Import this module in any flow that requires MCP tool integration + +local helpers = import "helpers.jsonnet"; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +{ + // Interfaces exposed by MCP service + "interfaces" +: { + "mcp-tool": request_response("mcp-tool:{id}"), + }, + + "parameters" +: { + }, + + // Flow-level processor for MCP tool execution + "flow" +: { + "mcp-tool:{id}": { + request: request("mcp-tool:{id}"), + response: response("mcp-tool:{id}"), + "text-completion-request": request("text-completion:{id}"), + "text-completion-response": response("text-completion:{id}"), + }, + }, + + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/ontorag-extract.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/ontorag-extract.jsonnet new file mode 100644 index 00000000..e5521d8a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/ontorag-extract.jsonnet @@ -0,0 +1,39 @@ +// OntologyRAG extraction module +// Extraction method for OntologyRAG - extracts using ontology definitions +// Mutually exclusive with GraphRAG extraction (both write to graph store) + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; + +{ + // No external interfaces - this module provides internal extraction services + "interfaces" +: { + }, + + // No configurable parameters for basic KG extraction + "parameters" +: { + }, + + // Flow-level processors for knowledge extraction + "flow" +: { + // Extracts using ontology definitions + "kg-extract-ontology:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output triples + "entity-contexts": flow("entity-contexts-load:{id}"), // Entity context information + "prompt-request": request("prompt:{id}"), // Definition + // extraction prompts + "prompt-response": response("prompt:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + }, + + }, + + // No blueprint-level processors needed + "blueprint" +: { + } +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/structured-extract.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/structured-extract.jsonnet new file mode 100644 index 00000000..599a0c31 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/structured-extract.jsonnet @@ -0,0 +1,30 @@ +// Structured RAG extraction module +// Extracts structured objects from text chunks +// Outputs to objects-store for structured data querying + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; + +{ + "interfaces" +: { + }, + + "parameters" +: { + }, + + // Flow-level processor for structured object extraction + "flow" +: { + "kg-extract-objects:{id}": { + input: flow("chunk-load:{id}"), + output: flow("objects-store:{id}"), + "entity-contexts": flow("entity-contexts-load:{id}"), + "prompt-request": request("prompt:{id}"), + "prompt-response": response("prompt:{id}"), + }, + }, + + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/structured-store.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/structured-store.jsonnet new file mode 100644 index 00000000..e6dd1844 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/flows/structured-store.jsonnet @@ -0,0 +1,63 @@ +// Structured store module +// Shared infrastructure for structured data RAG +// Handles object storage, retrieval, and NLP query capabilities + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +// Import shared services +local llm_services = import "llm-services.jsonnet"; +local embeddings_service = import "embeddings-service.jsonnet"; + +// Merge shared services with structured store configuration +llm_services + embeddings_service + { + + // External interfaces for structured store + "interfaces" +: { + // Object storage and querying + "objects-store": flow("objects-store:{id}"), + "objects": request_response("objects:{id}"), + + // Query interfaces + "nlp-query": request_response("nlp-query:{id}"), + "structured-query": request_response("structured-query:{id}"), + "structured-diag": request_response("structured-diag:{id}"), + }, + + // Flow-level processors for structured storage and query + "flow" +: { + "objects-write:{id}": { + input: flow("objects-store:{id}"), + }, + "objects-query:{id}": { + request: request("objects:{id}"), + response: response("objects:{id}"), + }, + "nlp-query:{id}": { + request: request("nlp-query:{id}"), + response: response("nlp-query:{id}"), + "prompt-request": request("prompt-rag:{id}"), + "prompt-response": response("prompt-rag:{id}"), + }, + "structured-query:{id}": { + request: request("structured-query:{id}"), + response: response("structured-query:{id}"), + "nlp-query-request": request("nlp-query:{id}"), + "nlp-query-response": response("nlp-query:{id}"), + "objects-query-request": request("objects:{id}"), + "objects-query-response": response("objects:{id}"), + }, + "structured-diag:{id}": { + request: request("structured-diag:{id}"), + response: response("structured-diag:{id}"), + "prompt-request": request("prompt:{id}"), + "prompt-response": response("prompt:{id}"), + }, + }, + + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/azure-openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/azure-openai.jsonnet new file mode 100644 index 00000000..bee0fe47 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/azure-openai.jsonnet @@ -0,0 +1,112 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/azure-openai.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["azure-openai-" + key]:: value, + }, + +// Strategy is to specify the model with the AZURE_MODEL environment +// variable. This isn't something that can just be specified dynamically, +// it has to match what was provisioned in Azure. + + "azure-openai-max-output-tokens":: 4192, + "azure-openai-temperature":: 0.0, + "azure-openai-models":: models, + + "llm-models" +:: $["azure-openai-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-openai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_MODEL", "azure-model") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure-openai", + "-p", + url.pulsar, + "-x", + std.toString($["azure-openai-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-openai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_MODEL", "azure-model") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure-openai", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["azure-openai-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/azure.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/azure.jsonnet new file mode 100644 index 00000000..f4db7500 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/azure.jsonnet @@ -0,0 +1,106 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/azure.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["azure-" + key]:: value, + }, + + "azure-max-output-tokens":: 4096, + "azure-temperature":: 0.0, + "azure-models":: models, + + "llm-models" +:: $["azure-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-ai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure", + "-p", + url.pulsar, + "-x", + std.toString($["azure-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-ai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["azure-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/bedrock.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/bedrock.jsonnet new file mode 100644 index 00000000..0db1475b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/bedrock.jsonnet @@ -0,0 +1,104 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/bedrock.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["bedrock-" + key]:: value, + }, + + "bedrock-max-output-tokens":: 4096, + "bedrock-temperature":: 0.0, + "bedrock-models":: models, + + "llm-models" +:: $["bedrock-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("bedrock-credentials") + .with_env_var("AWS_ACCESS_KEY_ID", "aws-id-key") + .with_env_var("AWS_SECRET_ACCESS_KEY", "aws-secret") + .with_env_var("AWS_DEFAULT_REGION", "aws-region"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_bedrock) + .with_command([ + "text-completion-bedrock", + "-p", + url.pulsar, + "-x", + std.toString($["bedrock-max-output-tokens"]), + "-t", + "%0.3f" % $["bedrock-temperature"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("bedrock-credentials") + .with_env_var("AWS_ACCESS_KEY_ID", "aws-id-key") + .with_env_var("AWS_SECRET_ACCESS_KEY", "aws-secret") + .with_env_var("AWS_DEFAULT_REGION", "aws-region"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_bedrock) + .with_command([ + "text-completion-bedrock", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["bedrock-max-output-tokens"]), + "-t", + "%0.3f" % $["bedrock-temperature"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/claude.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/claude.jsonnet new file mode 100644 index 00000000..f3125e96 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/claude.jsonnet @@ -0,0 +1,104 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/claude.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["claude-" + key]:: value, + }, + + "claude-max-output-tokens":: 4096, + "claude-temperature":: 0.0, + "claude-models":: models, + + "llm-models" +:: $["claude-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("claude-credentials") + .with_env_var("CLAUDE_KEY", "claude-key"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-claude", + "-p", + url.pulsar, + "-x", + std.toString($["claude-max-output-tokens"]), + "-t", + "%0.3f" % $["claude-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("claude-credentials") + .with_env_var("CLAUDE_KEY", "claude-key"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-claude", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["claude-max-output-tokens"]), + "-t", + "%0.3f" % $["claude-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/cohere.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/cohere.jsonnet new file mode 100644 index 00000000..6517f7a0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/cohere.jsonnet @@ -0,0 +1,97 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/cohere.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["cohere-" + key]:: value, + }, + + "cohere-temperature":: 0.0, + "cohere-models":: models, + + "llm-models" +:: $["cohere-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("cohere-credentials") + .with_env_var("COHERE_KEY", "cohere-key"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-cohere", + "-p", + url.pulsar, + "-t", + "%0.3f" % $["cohere-temperature"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("cohere-credentials") + .with_env_var("COHERE_KEY", "cohere-key"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-cohere", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-t", + "%0.3f" % $["cohere-temperature"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/googleaistudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/googleaistudio.jsonnet new file mode 100644 index 00000000..f7e8d578 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/googleaistudio.jsonnet @@ -0,0 +1,104 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/googleaistudio.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["googleaistudio-" + key]:: value, + }, + + "googleaistudio-max-output-tokens":: 4096, + "googleaistudio-temperature":: 0.0, + "googleaistudio-models":: models, + + "llm-models" +:: $["googleaistudio-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("googleaistudio-credentials") + .with_env_var("GOOGLE_AI_STUDIO_KEY", "googleaistudio-key"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-googleaistudio", + "-p", + url.pulsar, + "-x", + std.toString($["googleaistudio-max-output-tokens"]), + "-t", + "%0.3f" % $["googleaistudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("googleaistudio-credentials") + .with_env_var("GOOGLE_AI_STUDIO_KEY", "googleaistudio-key"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-googleaistudio", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["googleaistudio-max-output-tokens"]), + "-t", + "%0.3f" % $["googleaistudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/llamafile.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/llamafile.jsonnet new file mode 100644 index 00000000..f3bee2cc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/llamafile.jsonnet @@ -0,0 +1,94 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/slm.jsonnet"; +local models = import "parameters/llamafile.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["llamafile-" + key]:: value, + }, + + "llamafile-models":: models, + + "llm-models" +:: $["llamafile-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("llamafile-credentials") + .with_env_var("LLAMAFILE_URL", "llamafile-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-llamafile", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("llamafile-credentials") + .with_env_var("LLAMAFILE_URL", "llamafile-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-llamafile", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/lmstudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/lmstudio.jsonnet new file mode 100644 index 00000000..4cf78caa --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/lmstudio.jsonnet @@ -0,0 +1,104 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/lmstudio.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["lmstudio-" + key]:: value, + }, + + "lmstudio-max-output-tokens":: 4096, + "lmstudio-temperature":: 0.0, + "lmstudio-models":: models, + + "llm-models" +:: $["lmstudio-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("lmstudio-credentials") + .with_env_var("LMSTUDIO_URL", "lmstudio-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-lmstudio", + "-p", + url.pulsar, + "-x", + std.toString($["lmstudio-max-output-tokens"]), + "-t", + "%0.3f" % $["lmstudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("lmstudio-credentials") + .with_env_var("LMSTUDIO_URL", "lmstudio-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-lmstudio", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["lmstudio-max-output-tokens"]), + "-t", + "%0.3f" % $["lmstudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/mistral.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/mistral.jsonnet new file mode 100644 index 00000000..2fb8c562 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/mistral.jsonnet @@ -0,0 +1,104 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/mistral.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["mistral-" + key]:: value, + }, + + "mistral-max-output-tokens":: 4096, + "mistral-temperature":: 0.0, + "mistral-models":: models, + + "llm-models" +:: $["mistral-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("mistral-credentials") + .with_env_var("MISTRAL_TOKEN", "mistral-token"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-mistral", + "-p", + url.pulsar, + "-x", + std.toString($["mistral-max-output-tokens"]), + "-t", + "%0.3f" % $["mistral-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("mistral-credentials") + .with_env_var("MISTRAL_TOKEN", "mistral-token"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-mistral", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["mistral-max-output-tokens"]), + "-t", + "%0.3f" % $["mistral-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/ollama.jsonnet new file mode 100644 index 00000000..6143f5cc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/ollama.jsonnet @@ -0,0 +1,102 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/ollama.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["ollama-" + key]:: value, + }, + + "ollama-models":: models, + + "llm-models" +:: $["ollama-models"], + + "text-completion" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("ollama-credentials") + .with_env_var("OLLAMA_HOST", "ollama-host"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-ollama", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("ollama-credentials") + .with_env_var("OLLAMA_HOST", "ollama-host"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-ollama", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/openai.jsonnet new file mode 100644 index 00000000..c3d482fd --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/openai.jsonnet @@ -0,0 +1,106 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/openai.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["openai-" + key]:: value, + }, + + "openai-max-output-tokens":: 4096, + "openai-temperature":: 0.0, + "openai-models":: models, + + "llm-models" +:: $["openai-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("openai-credentials") + .with_env_var("OPENAI_TOKEN", "openai-token") + .with_env_var("OPENAI_BASE_URL", "openai-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-openai", + "-p", + url.pulsar, + "-x", + std.toString($["openai-max-output-tokens"]), + "-t", + "%0.3f" % $["openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("openai-credentials") + .with_env_var("OPENAI_TOKEN", "openai-token") + .with_env_var("OPENAI_BASE_URL", "openai-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-openai", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["openai-max-output-tokens"]), + "-t", + "%0.3f" % $["openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/tgi.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/tgi.jsonnet new file mode 100644 index 00000000..91588dff --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/tgi.jsonnet @@ -0,0 +1,108 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-" + key]:: value, + }, + + "tgi-max-output-tokens":: 1024, + "tgi-temperature":: 0.0, + + "text-completion" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("tgi-credentials") + .with_env_var("TGI_BASE_URL", "tgi-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-tgi", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "-x", + std.toString($["tgi-max-output-tokens"]), + "-t", + "%0.3f" % $["tgi-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("tgi-credentials") + .with_env_var("TGI_BASE_URL", "tgi-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-tgi", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "--concurrency", + std.toString(concurrency), + "-x", + std.toString($["tgi-max-output-tokens"]), + "-t", + "%0.3f" % $["tgi-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/vertexai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/vertexai.jsonnet new file mode 100644 index 00000000..cb8c141f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/vertexai.jsonnet @@ -0,0 +1,120 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/vertexai.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vertexai-" + key]:: value, + }, + + "vertexai-private-key":: "/vertexai/private.json", + "vertexai-region":: "us-central1", + "vertexai-max-output-tokens":: 4096, + "vertexai-temperature":: 0.0, + "vertexai-models":: models, + + "llm-models" +:: $["vertexai-models"], + + "text-completion" +: { + + create:: function(engine) + + local cfgVol = engine.secretVolume( + "vertexai-creds", + "./vertexai", + { + "private.json": importstr "vertexai/private.json", + } + ); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_vertexai) + .with_command([ + "text-completion-vertexai", + "-p", + url.pulsar, + "-k", + $["vertexai-private-key"], + "-r", + $["vertexai-region"], + "-x", + std.toString($["vertexai-max-output-tokens"]), + "-t", + "%0.3f" % $["vertexai-temperature"], + ]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_volume_mount(cfgVol, "/vertexai"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + cfgVol, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local cfgVol = engine.secretVolume( + "vertexai-creds", + "./vertexai", + { + "private.json": importstr "vertexai/private.json", + } + ); + + local container = + engine.container("text-completion-rag") + .with_image(images.trustgraph_vertexai) + .with_command([ + "text-completion-vertexai", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-k", + $["vertexai-private-key"], + "-r", + $["vertexai-region"], + "-x", + std.toString($["vertexai-max-output-tokens"]), + "-t", + "%0.3f" % $["vertexai-temperature"], + ]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_volume_mount(cfgVol, "/vertexai"); + + local containerSet = engine.containers( + "text-completion-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + cfgVol, + containerSet, + service, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/vllm.jsonnet new file mode 100644 index 00000000..8b846bbf --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/llm/vllm.jsonnet @@ -0,0 +1,113 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/vllm.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-" + key]:: value, + }, + + "vllm-models":: models, + + "llm-models" +:: $["vllm-models"], + + "vllm-max-output-tokens":: 1024, + "vllm-temperature":: 0.0, + + "text-completion" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("vllm-credentials") + .with_env_var("VLLM_BASE_URL", "vllm-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-vllm", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "-x", + std.toString($["vllm-max-output-tokens"]), + "-t", + "%0.3f" % $["vllm-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("vllm-credentials") + .with_env_var("VLLM_BASE_URL", "vllm-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-vllm", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "--concurrency", + std.toString(concurrency), + "-x", + std.toString($["vllm-max-output-tokens"]), + "-t", + "%0.3f" % $["vllm-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/mcp/ddg-mcp-server.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/mcp/ddg-mcp-server.jsonnet new file mode 100644 index 00000000..04176f15 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/mcp/ddg-mcp-server.jsonnet @@ -0,0 +1,47 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "ddg-mcp-server-port":: 9870, + + "ddg-mcp-server" +: { + + create:: function(engine) + + local port = $["ddg-mcp-server-port"]; + + local container = + engine.container("ddg-mcp-server") + .with_image(images["ddg-mcp-server"]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_port(port, port, "mcp"); + + local containerSet = engine.containers( + "ddg-mcp-server", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(port, port, "mcp"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + mcp +:: { + "duckduckgo": { + "remote-name": "search", + local port = $["ddg-mcp-server-port"], + local url = "http://ddg-mcp-server:%s/mcp" % port, + "url": url, + } + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/model-hosting/cpu-tgi.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/model-hosting/cpu-tgi.jsonnet new file mode 100644 index 00000000..04b61566 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/model-hosting/cpu-tgi.jsonnet @@ -0,0 +1,68 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-service-" + key]:: value, + }, + + "tgi-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "tgi-service-cpus":: "8.0", + "tgi-service-memory":: "16G", + "tgi-service-storage":: "20G", + "tgi-service-hf-token":: null, + + "tgi-service" +: { + + create:: function(engine) + + local vol = engine.volume("tgi-storage") + .with_size($["tgi-service-storage"]); + + local container = + engine.container("tgi-service") + .with_image(images["tgi-service-cpu"]) + .with_command([ + "--model-id", + $["tgi-service-model"], + "--hostname", + "0.0.0.0", + "--port", + "7000", + "--cuda-graphs", + "0", + ]) + .with_environment({ + } + ( + if $["tgi-service-hf-token"] != null + then { HF_TOKEN: $["tgi-service-hf-token"] } + else {} + )) + .with_limits( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_reservations( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_port(7000, 7000, "tgi") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "tgi-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "tgi"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/model-hosting/intel-battlemage-vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/model-hosting/intel-battlemage-vllm.jsonnet new file mode 100644 index 00000000..7646398c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/model-hosting/intel-battlemage-vllm.jsonnet @@ -0,0 +1,98 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-service-" + key]:: value, + }, + + "vllm-service-model":: "mistralai/Mistral-Nemo-Instruct-2407", + "vllm-service-cpus":: "32.0", + "vllm-service-memory":: "48G", + "vllm-service-storage":: "48G", + "vllm-service-tokenizer-mode":: "mistral", + "vllm-service-datatype":: "float16", + "vllm-service-quantization":: "woq_int4", + "vllm-service-hf-token":: null, + "vllm-service-max-model-len":: 8192, + "vllm-service-max-num-seqs":: 16, + + "vllm-service" +: { + + create:: function(engine) + + local vol = engine.volume("vllm-storage") + .with_size($["vllm-service-storage"]); + + local container = + engine.container("vllm-service") + .with_image(images["vllm-service-intel-battlemage"]) + .with_entrypoint("") // Clear default entrypoint + .with_command([ + "python", + "-m", + "ipex_llm.vllm.xpu.entrypoints.openai.api_server", + "--model", + $["vllm-service-model"], + "--served-model-name", + "model", + "--host", + "0.0.0.0", + "--port", + "7000", + "--device", + "xpu", + "--dtype", + $["vllm-service-datatype"], + "--enforce-eager", + "--max-model-len", + std.toString($["vllm-service-max-model-len"]), + "--max-num-seqs", + std.toString($["vllm-service-max-num-seqs"]), + "--load-in-low-bit", + $["vllm-service-quantization"], + "--trust-remote-code", + "--tokenizer-mode", + $["vllm-service-tokenizer-mode"], + "--disable-sliding-window", + ]) + .with_environment({ + VLLM_WORKER_MULTIPROC_METHOD: "spawn", + SYCL_PI_LEVEL_ZERO_USE_IMMEDIATE_COMMANDLISTS: "1", + } + ( + if $["vllm-service-hf-token"] != null + then { HF_TOKEN: $["vllm-service-hf-token"] } + else {} + )) + .with_privileged(true) + .with_device("/dev/dri", "/dev/dri") + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_reservations( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_port(7000, 7000, "vllm") + .with_bind_mount("/dev/dri/by-path", "/dev/dri/by-path") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "vllm-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "vllm"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/model-hosting/intel-gaudi-tgi.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/model-hosting/intel-gaudi-tgi.jsonnet new file mode 100644 index 00000000..5af36b78 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/model-hosting/intel-gaudi-tgi.jsonnet @@ -0,0 +1,96 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-service-" + key]:: value, + }, + + "tgi-service-model":: "meta-llama/Llama-3.3-70B-Instruct", + "tgi-service-cpus":: "64.0", + "tgi-service-memory":: "64G", + "tgi-service-storage":: "50G", + "tgi-service-num-shard":: 8, + "tgi-service-max-input-tokens":: 4096, + "tgi-service-max-total-tokens":: 4096, + "tgi-service-max-batch-size":: 128, + "tgi-service-max-concurrent-requests":: 512, + "tgi-service-hf-token":: null, + + "tgi-service" +: { + + create:: function(engine) + + local vol = engine.volume("tgi-storage") + .with_size($["tgi-service-storage"]); + + local container = + engine.container("tgi-service") + .with_image(images["tgi-service-gaudi"]) + .with_command([ + "--model-id", + $["tgi-service-model"], + "--hostname", + "0.0.0.0", + "--port", + "7000", + "--sharded", + "true", + "--num-shard", + std.toString($["tgi-service-num-shard"]), + "--max-input-tokens", + std.toString($["tgi-service-max-input-tokens"]), + "--max-total-tokens", + std.toString($["tgi-service-max-total-tokens"]), + "--max-batch-size", + std.toString($["tgi-service-max-batch-size"]), + "--max-waiting-tokens", + "7", + "--max-concurrent-requests", + std.toString($["tgi-service-max-concurrent-requests"]), + "--cuda-graphs", + "0", + ]) + .with_runtime("habana") + .with_environment({ + HABANA_VISIBLE_DEVICES: "all", + OMPI_MCA_btl_vader_single_copy_mechanism: "none", + ENABLE_HPU_GRAPH: "true", + LIMIT_HPU_GRAPH: "true", + USE_FLASH_ATTENTION: "true", + FLASH_ATTENTION_RECOMPUTE: "true", + } + ( + if $["tgi-service-hf-token"] != null + then { HF_TOKEN: $["tgi-service-hf-token"] } + else {} + )) + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_reservations( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_port(7000, 7000, "tgi") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "tgi-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "tgi"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/model-hosting/intel-gaudi-vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/model-hosting/intel-gaudi-vllm.jsonnet new file mode 100644 index 00000000..81d31c14 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/model-hosting/intel-gaudi-vllm.jsonnet @@ -0,0 +1,76 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-service-" + key]:: value, + }, + + "vllm-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "vllm-service-cpus":: "64.0", + "vllm-service-memory":: "64G", + "vllm-service-storage":: "50G", + "vllm-service-tensor-parallel-size":: 8, + "vllm-service-hf-token":: null, + + "vllm-service" +: { + + create:: function(engine) + + local vol = engine.volume("vllm-storage") + .with_size($["vllm-service-storage"]); + + local container = + engine.container("vllm-service") + .with_image(images["vllm-service-gaudi"]) + .with_command([ + "--model", + $["vllm-service-model"], + "--served-model-name", + "model", + "--host", + "0.0.0.0", + "--port", + "7000", + "--tensor-parallel-size", + std.toString($["vllm-service-tensor-parallel-size"]), + ]) + .with_runtime("habana") + .with_environment({ + VLLM_SKIP_WARMUP: "true", + HABANA_VISIBLE_DEVICES: "all", + } + ( + if $["vllm-service-hf-token"] != null + then { HF_TOKEN: $["vllm-service-hf-token"] } + else {} + )) + .with_privileged(true) + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_reservations( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_port(7000, 7000, "vllm") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "vllm-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "vllm"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/model-hosting/intel-xpu-tgi.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/model-hosting/intel-xpu-tgi.jsonnet new file mode 100644 index 00000000..bc806874 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/model-hosting/intel-xpu-tgi.jsonnet @@ -0,0 +1,75 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-service-" + key]:: value, + }, + + "tgi-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "tgi-service-cpus":: "8.0", + "tgi-service-memory":: "16G", + "tgi-service-storage":: "20G", + "tgi-service-hf-token":: null, + + "tgi-service" +: { + + create:: function(engine) + + local vol = engine.volume("tgi-storage") + .with_size($["tgi-service-storage"]); + + local container = + engine.container("tgi-service") + .with_image(images["tgi-service-intel-xpu"]) + .with_command([ + "--model-id", + $["tgi-service-model"], + "--hostname", + "0.0.0.0", + "--port", + "7000", + "--cuda-graphs", + "0", + ]) + .with_environment({ + } + ( + if $["tgi-service-hf-token"] != null + then { HF_TOKEN: $["tgi-service-hf-token"] } + else {} + )) + .with_privileged(true) + .with_device("/dev/dri", "/dev/dri") + .with_ipc("host") + .with_group("video") + .with_group("render") + .with_capability("SYS_NICE") + .with_limits( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_reservations( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_port(7000, 7000, "tgi") + .with_bind_mount("/dev/dri/by-path", "/dev/dri/by-path") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "tgi-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "tgi"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/model-hosting/intel-xpu-vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/model-hosting/intel-xpu-vllm.jsonnet new file mode 100644 index 00000000..f6e82905 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/model-hosting/intel-xpu-vllm.jsonnet @@ -0,0 +1,97 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-service-" + key]:: value, + }, + + "vllm-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "vllm-service-cpus":: "8.0", + "vllm-service-memory":: "16G", + "vllm-service-storage":: "20G", + "vllm-service-datatype":: "float16", + "vllm-service-max-model-len":: 4096, + "vllm-service-max-num-seqs":: 16, + "vllm-service-hf-token":: null, + + "vllm-service" +: { + + create:: function(engine) + + local vol = engine.volume("vllm-storage") + .with_size($["vllm-service-storage"]); + + local container = + engine.container("vllm-service") + .with_image(images["vllm-service-intel-xpu"]) + .with_command([ + "python", + "-m", + "vllm.entrypoints.openai.api_server", + "--model", + $["vllm-service-model"], + "--served-model-name", + "model", + "--host", + "0.0.0.0", + "--port", + "7000", + "--device", + "xpu", + "--dtype", + $["vllm-service-datatype"], + "--enforce-eager", + "--max-model-len", + std.toString($["vllm-service-max-model-len"]), + "--max-num-seqs", + std.toString($["vllm-service-max-num-seqs"]), + "--block-size", + "64", + "--gpu-memory-util", + "0.85", + "--trust-remote-code", + "--disable-sliding-window", + ]) + .with_environment({ + VLLM_USE_V1: "1", + VLLM_WORKER_MULTIPROC_METHOD: "spawn", + } + ( + if $["vllm-service-hf-token"] != null + then { HF_TOKEN: $["vllm-service-hf-token"] } + else {} + )) + .with_privileged(true) + .with_device("/dev/dri", "/dev/dri") + .with_ipc("host") + .with_group("video") + .with_group("render") + .with_capability("SYS_NICE") + .with_limits( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_reservations( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_port(7000, 7000, "vllm") + .with_bind_mount("/dev/dri/by-path", "/dev/dri/by-path") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "vllm-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "vllm"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/model-hosting/nvidia-gpu-vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/model-hosting/nvidia-gpu-vllm.jsonnet new file mode 100644 index 00000000..d04876c4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/model-hosting/nvidia-gpu-vllm.jsonnet @@ -0,0 +1,72 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-service-" + key]:: value, + }, + + "vllm-service-model":: "mistralai/Mistral-7B-Instruct-v0.3", + "vllm-service-cpus":: "0.5", + "vllm-service-memory":: "1G", + "vllm-service-storage":: "50G", + "vllm-service-hf-token":: null, + + "vllm-service" +: { + + create:: function(engine) + + local vol = engine.volume("vllm-storage") + .with_size($["vllm-service-storage"]); + + local container = + engine.container("vllm-service") + .with_image(images["vllm-service-nvidia"]) + .with_command([ + "--model", + $["vllm-service-model"], + "--served-model-name", + "model", + "--host", + "0.0.0.0", + "--port", + "7000", + ]) + .with_runtime("nvidia") + .with_environment({ + VLLM_SKIP_WARMUP: "true", + } + ( + if $["vllm-service-hf-token"] != null + then { HF_TOKEN: $["vllm-service-hf-token"] } + else {} + )) + .with_privileged(true) + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_reservations( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_port(7000, 7000, "vllm") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "vllm-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "vllm"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/monitoring/grafana.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/monitoring/grafana.jsonnet new file mode 100644 index 00000000..dd0dddd7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/monitoring/grafana.jsonnet @@ -0,0 +1,124 @@ +local images = import "values/images.jsonnet"; +local loki = import "loki.jsonnet"; + +{ + + "prometheus" +: { + + create:: function(engine) + + local vol = engine.volume("prometheus-data").with_size("20G"); + + local cfgVol = engine.configVolume( + "prometheus-cfg", "prometheus", + { + "prometheus.yml": importstr "prometheus/prometheus.yml", + } + ); + + local container = + engine.container("prometheus") + .with_image(images.prometheus) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M") + .with_port(9090, 9090, "http") + .with_volume_mount(cfgVol, "/etc/prometheus/") + .with_volume_mount(vol, "/prometheus"); + + local containerSet = engine.containers( + "prometheus", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9090, 9090, "http"); + + engine.resources([ + cfgVol, + vol, + containerSet, + service, + ]) + + }, + + "grafana" +: { + + create:: function(engine) + + local vol = engine.volume("grafana-storage").with_size("20G"); + + local provDashVol = engine.configVolume( + "prov-dash", "grafana/provisioning/", + { + "dashboard.yml": + importstr "grafana/provisioning/dashboard.yml", + } + + ); + + local provDataVol = engine.configVolume( + "prov-data", "grafana/provisioning/", + { + "datasource.yml": + importstr "grafana/provisioning/datasource.yml", + } + + ); + + local dashVol = engine.configVolume( + "dashboards", "grafana/dashboards/", + { + "overview-dashboard.json": + importstr "grafana/dashboards/overview-dashboard.json", + "log-dashboard.json": + importstr "grafana/dashboards/log-dashboard.json", + } + + ); + + local container = + engine.container("grafana") + .with_image(images.grafana) + .with_environment({ + // GF_AUTH_ANONYMOUS_ORG_ROLE: "Admin", + // GF_AUTH_ANONYMOUS_ENABLED: "true", + // GF_ORG_ROLE: "Admin", + GF_ORG_NAME: "trustgraph.ai", + // GF_SERVER_ROOT_URL: "https://example.com", + }) + .with_limits("1.0", "256M") + .with_reservations("0.5", "256M") + .with_port(3000, 3000, "cassandra") + .with_volume_mount(vol, "/var/lib/grafana") + .with_volume_mount( + provDashVol, "/etc/grafana/provisioning/dashboards/" + ) + .with_volume_mount( + provDataVol, "/etc/grafana/provisioning/datasources/" + ) + .with_volume_mount( + dashVol, "/var/lib/grafana/dashboards/" + ); + + local containerSet = engine.containers( + "grafana", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(3000, 3000, "http"); + + engine.resources([ + vol, + provDashVol, + provDataVol, + dashVol, + containerSet, + service, + ]) + + }, + +} + loki + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/monitoring/loki.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/monitoring/loki.jsonnet new file mode 100644 index 00000000..0774f6cf --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/monitoring/loki.jsonnet @@ -0,0 +1,45 @@ +local images = import "values/images.jsonnet"; + +{ + + "loki" +: { + + create:: function(engine) + + local vol = engine.volume("loki-data").with_size("20G"); + + local cfgVol = engine.configVolume( + "loki-cfg", "loki", + { + "local-config.yaml": importstr "loki/local-config.yaml", + } + ); + + local container = + engine.container("loki") + .with_image(images.loki) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_port(3100, 3100, "http") + .with_volume_mount(cfgVol, "/etc/loki/") + .with_volume_mount(vol, "/loki"); + + local containerSet = engine.containers( + "loki", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(3100, 3100, "http"); + + engine.resources([ + cfgVol, + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/ocr/mistral-ocr.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/ocr/mistral-ocr.jsonnet new file mode 100644 index 00000000..f70e0cd6 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/ocr/mistral-ocr.jsonnet @@ -0,0 +1,49 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["mistral-" + key]:: value, + }, + + "pdf-decoder" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("mistral-credentials") + .with_env_var("MISTRAL_TOKEN", "mistral-token"); + + local container = + engine.container("mistral-ocr") + .with_image(images.trustgraph_flow) + .with_command([ + "pdf-ocr-mistral", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "mistral-ocr", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/ocr/ocr.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/ocr/ocr.jsonnet new file mode 100644 index 00000000..4f53aa5a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/ocr/ocr.jsonnet @@ -0,0 +1,37 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "pdf-decoder" +: { + + create:: function(engine) + + local container = + engine.container("pdf-ocr") + .with_image(images.trustgraph_ocr) + .with_command([ + "pdf-ocr", + "-p", + url.pulsar, + ]) + .with_limits("1.0", "512M") + .with_reservations("0.1", "512M"); + + local containerSet = engine.containers( + "pdf-ocr", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/azure-openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/azure-openai.jsonnet new file mode 100644 index 00000000..0e3ea907 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/azure-openai.jsonnet @@ -0,0 +1,9 @@ +// Azure OpenAI LLM Model Definitions +// Model input is just text + +{ + "type": "string", + "description": "LLM model to use", + "default": "gpt-4o", + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/azure.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/azure.jsonnet new file mode 100644 index 00000000..6ffa5fdf --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/azure.jsonnet @@ -0,0 +1,9 @@ +// Azure LLM Model Definitions +// Model input is just text + +{ + "type": "string", + "description": "LLM model to use", + "default": "phi4:14b", + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/bedrock.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/bedrock.jsonnet new file mode 100644 index 00000000..df930e59 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/bedrock.jsonnet @@ -0,0 +1,63 @@ +// AWS Bedrock LLM Model Definitions +// Defines available models and their configurations for AWS Bedrock + +{ + "type": "string", + "description": "LLM model to use", + "default": "global.anthropic.claude-sonnet-4-5-20250929-v1:0", + "enum": [ + { + id: "global.anthropic.claude-sonnet-4-5-20250929-v1:0", + description: "Claude Sonnet 4.5 (smartest for complex agents and coding)" + }, + { + id: "global.anthropic.claude-opus-4-5-20251101-v1:0", + description: "Claude Opus 4.5 (maximum intelligence)" + }, + { + id: "global.anthropic.claude-haiku-4-5-20251001-v1:0", + description: "Claude Haiku 4.5 (fastest with near-frontier intelligence)" + }, + { + id: "global.anthropic.claude-opus-4-1-20250805-v1:0", + description: "Claude Opus 4.1 (specialized reasoning)" + }, + { + id: "global.anthropic.claude-sonnet-4-20250514-v1:0", + description: "Claude Sonnet 4.0" + }, + { + id: "global.anthropic.claude-opus-4-20250514-v1:0", + description: "Claude Opus 4.0" + }, + { + id: "anthropic.claude-3-5-haiku-20241022-v1:0", + description: "Claude 3.5 Haiku" + }, + { + id: "anthropic.claude-3-haiku-20240307-v1:0", + description: "Claude 3 Haiku" + }, + { + id: "meta.llama3-1-405b-instruct-v1:0", + description: "Llama 3.1 405B Instruct" + }, + { + id: "meta.llama3-1-70b-instruct-v1:0", + description: "Llama 3.1 70B Instruct" + }, + { + id: "meta.llama3-1-8b-instruct-v1:0", + description: "Llama 3.1 8B Instruct" + }, + { + id: "mistral.mistral-large-2407-v1:0", + description: "Mistral Large" + }, + { + id: "mistral.mixtral-8x7b-instruct-v0:1", + description: "Mixtral 8x7B Instruct" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/chunking-param-types.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/chunking-param-types.jsonnet new file mode 100644 index 00000000..f82fa3f7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/chunking-param-types.jsonnet @@ -0,0 +1,25 @@ +// Chunk parameter type definitions + +{ + "chunk-size": { + "type": "integer", + "description": "Chunk size", + "placeholder": 2000, + "helper": "An integer, usually 2000 .. 8000", + "default": 2000, + "min": 0, + "max": 32768, + "required": true + }, + "chunk-overlap": { + "type": "integer", + "description": "Chunk overlap", + "placeholder": 50, + "helper": "An integer, usually 50 .. 100", + "default": 50, + "min": 0, + "max": 8000, + "required": true + }, +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/claude.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/claude.jsonnet new file mode 100644 index 00000000..40fd434b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/claude.jsonnet @@ -0,0 +1,35 @@ +// Claude LLM Model Definitions +// Defines available models and their configurations for Anthropic's Claude + +{ + "type": "string", + "description": "LLM model to use", + "default": "claude-sonnet-4-5-20250929", + "enum": [ + { + id: "claude-sonnet-4-5-20250929", + description: "Claude Sonnet 4.5 (complex agents + coding)" + }, + { + id: "claude-opus-4-5-20251101", + description: "Claude Opus 4.5 (maximum intelligence)" + }, + { + id: "claude-haiku-4-5-20251001", + description: "Claude Haiku 4.5 (fast)" + }, + { + id: "claude-opus-4-1-20250805", + description: "Claude Opus 4.1 (specialized reasoning)" + }, + { + id: "claude-sonnet-4-20250514", + description: "Claude Sonnet 4.0" + }, + { + id: "claude-3-5-haiku-20241022", + description: "Claude 3.5 Haiku" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/cohere.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/cohere.jsonnet new file mode 100644 index 00000000..5a5865a3 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/cohere.jsonnet @@ -0,0 +1,35 @@ +// Cohere LLM Model Definitions +// Defines available models and their configurations for Cohere + +{ + "type": "string", + "description": "LLM model to use", + "default": "command-r-plus-08-2024", + "enum": [ + { + id: "command-r-plus-08-2024", + description: "Command R+ (August 2024)" + }, + { + id: "command-r-08-2024", + description: "Command R (August 2024)" + }, + { + id: "command-r-plus", + description: "Command R+ (legacy)" + }, + { + id: "command-r", + description: "Command R (legacy)" + }, + { + id: "command", + description: "Command" + }, + { + id: "command-light", + description: "Command Light" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/embeddings-fastembed.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/embeddings-fastembed.jsonnet new file mode 100644 index 00000000..477e25dc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/embeddings-fastembed.jsonnet @@ -0,0 +1,127 @@ +// Embeddings model definitions for fastembed +// Defines available models and their configurations for Fastembed + +{ + "type": "string", + "description": "Embeddings model to use", + "default": "sentence-transformers/all-MiniLM-L6-v2", + "enum": [ + { + "id": "sentence-transformers/all-MiniLM-L6-v2", + "description": "all-MiniLM-L6-v2" + }, + { + "id": "BAAI/bge-small-en-v1.5", + "description": "bge-small-en-v1.5" + }, + { + "id": "BAAI/bge-small-zh-v1.5", + "description": "bge-small-zh-v1.5" + }, + { + "id": "snowflake/snowflake-arctic-embed-xs", + "description": "snowflake-arctic-embed-xs" + }, + { + "id": "jinaai/jina-embeddings-v2-small-en", + "description": "jina-embeddings-v2-small-en" + }, + { + "id": "nomic-ai/nomic-embed-text-v1.5-Q", + "description": "nomic-embed-text-v1.5-Q" + }, + { + "id": "snowflake/snowflake-arctic-embed-s", + "description": "snowflake-arctic-embed-s" + }, + { + "id": "BAAI/bge-small-en", + "description": "bge-small-en" + }, + { + "id": "BAAI/bge-base-en-v1.5", + "description": "bge-base-en-v1.5" + }, + { + "id": "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2", + "description": "paraphrase-multilingual-MiniLM-L12-v2" + }, + { + "id": "Qdrant/clip-ViT-B-32-text", + "description": "clip-ViT-B-32-text" + }, + { + "id": "jinaai/jina-embeddings-v2-base-de", + "description": "jina-embeddings-v2-base-de" + }, + { + "id": "BAAI/bge-base-en", + "description": "bge-base-en" + }, + { + "id": "snowflake/snowflake-arctic-embed-m", + "description": "snowflake-arctic-embed-m" + }, + { + "id": "thenlper/gte-base", + "description": "gte-base" + }, + { + "id": "jinaai/jina-embeddings-v2-base-en", + "description": "jina-embeddings-v2-base-en" + }, + { + "id": "nomic-ai/nomic-embed-text-v1", + "description": "nomic-embed-text-v1" + }, + { + "id": "nomic-ai/nomic-embed-text-v1.5", + "description": "nomic-embed-text-v1.5" + }, + { + "id": "snowflake/snowflake-arctic-embed-m-long", + "description": "snowflake-arctic-embed-m-long" + }, + { + "id": "jinaai/jina-clip-v1", + "description": "jina-clip-v1" + }, + { + "id": "mixedbread-ai/mxbai-embed-large-v1", + "description": "mxbai-embed-large-v1" + }, + { + "id": "jinaai/jina-embeddings-v2-base-es", + "description": "jina-embeddings-v2-base-es" + }, + { + "id": "jinaai/jina-embeddings-v2-base-code", + "description": "jina-embeddings-v2-base-code" + }, + { + "id": "jinaai/jina-embeddings-v2-base-zh", + "description": "jina-embeddings-v2-base-zh" + }, + { + "id": "sentence-transformers/paraphrase-multilingual-mpnet-base-v2", + "description": "paraphrase-multilingual-mpnet-base-v2" + }, + { + "id": "snowflake/snowflake-arctic-embed-l", + "description": "snowflake-arctic-embed-l" + }, + { + "id": "BAAI/bge-large-en-v1.5", + "description": "bge-large-en-v1.5" + }, + { + "id": "thenlper/gte-large", + "description": "gte-large" + }, + { + "id": "intfloat/multilingual-e5-large", + "description": "multilingual-e5-large" + } + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/embeddings-huggingface.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/embeddings-huggingface.jsonnet new file mode 100644 index 00000000..32254a2b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/embeddings-huggingface.jsonnet @@ -0,0 +1,31 @@ +// Embeddings model definitions for fastembed +// Defines available models and their configurations for Fastembed + +{ + "type": "string", + "description": "Embeddings model to use", + "default": "sentence-transformers/all-MiniLM-L6-v2", + "enum": [ + { + "id": "all-MiniLM-L6-v2", + "description": "all-MiniLM-L6-v2" + }, + { + "id": "all-mpnet-base-v2", + "description": "all-mpnet-base-v2" + }, + { + "id": "all-distilroberta-v1", + "description": "all-distilroberta-v1" + }, + { + "id": "stsb-bert-large", + "description": "stsb-bert-large" + }, + { + "id": "sentence-camembert-large", + "description": "sentence-camembert-large" + } + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/embeddings-ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/embeddings-ollama.jsonnet new file mode 100644 index 00000000..c3f29fbb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/embeddings-ollama.jsonnet @@ -0,0 +1,23 @@ +// Embeddings model definitions for Ollama +// Defines available models and their configurations for Ollama + +{ + "type": "string", + "description": "Embeddings model to use", + "default": "all-minilm:latest", + "enum": [ + { + "id": "all-minilm:latest", + "description": "all-MiniLM-L6-v2" + }, + { + "id": "nomic-ai/nomic-embed-text-v1.5-Q", + "description": "nomic-embed-text-v1.5-Q" + }, + { + "id": "mixedbread-ai/mxbai-embed-large-v1", + "description": "mxbai-embed-large-v1" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/googleaistudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/googleaistudio.jsonnet new file mode 100644 index 00000000..a8891932 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/googleaistudio.jsonnet @@ -0,0 +1,67 @@ +// Google AI Studio LLM Model Definitions +// Defines available models and their configurations for Google AI Studio + +{ + "type": "string", + "description": "LLM model to use", + "default": "gemini-2.5-flash-lite", + "enum": [ + // Gemini 2.5 models (latest generation) + { + id: "gemini-2.5-pro", + description: "Gemini 2.5 Pro" + }, + { + id: "gemini-2.5-flash", + description: "Gemini 2.5 Flash" + }, + { + id: "gemini-2.5-flash-lite", + description: "Gemini 2.5 Flash Lite" + }, + + // Gemini 2.0 models + { + id: "gemini-2.0-flash-exp", + description: "Gemini 2.0 Flash (experimental)" + }, + + + // Claude models on VertexAI + { + id: "claude-3-5-sonnet@20241022", + description: "Claude 3.5 Sonnet (via VertexAI)" + }, + { + id: "claude-3-5-haiku@20241022", + description: "Claude 3.5 Haiku (via VertexAI)" + }, + { + id: "claude-3-opus@20240229", + description: "Claude 3 Opus (via VertexAI)" + }, + { + id: "claude-3-sonnet@20240229", + description: "Claude 3 Sonnet (via VertexAI)" + }, + { + id: "claude-3-haiku@20240307", + description: "Claude 3 Haiku (via VertexAI)" + }, + + // Llama models on VertexAI + { + id: "llama3-405b-instruct-maas", + description: "Llama 3 405B Instruct (via VertexAI)" + }, + { + id: "llama3-70b-instruct-maas", + description: "Llama 3 70B Instruct (via VertexAI)" + }, + { + id: "llama3-8b-instruct-maas", + description: "Llama 3 8B Instruct (via VertexAI)" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/llamafile.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/llamafile.jsonnet new file mode 100644 index 00000000..f6480aa9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/llamafile.jsonnet @@ -0,0 +1,9 @@ +// LlamaFile LLM Model Definitions +// Model input is just text + +{ + "type": "string", + "description": "LLM model to use", + "default": "phi4:14b", + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/lmstudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/lmstudio.jsonnet new file mode 100644 index 00000000..1c88da3c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/lmstudio.jsonnet @@ -0,0 +1,150 @@ +// LMStudio LLM Model Definitions +// Defines available models and their configurations for LMStudio + +{ + "type": "string", + "description": "LLM model to use", + "default": "llama3.1:70b", + "enum": [ + // Gemma3 models + { + id: "gemma3:1b", + description: "Gemma3 1B" + }, + { + id: "gemma3:4b", + description: "Gemma3 4B" + }, + { + id: "gemma3:12b", + description: "Gemma3 12B" + }, + { + id: "gemma3:27b", + description: "Gemma3 27B" + }, + + // Phi4 models + { + id: "phi4:mini:3.8b", + description: "Phi4 Mini 3.8B" + }, + { + id: "phi4:14b", + description: "Phi4 14B" + }, + + // DeepSeek-R1 models + { + id: "deepseek-r1:671b", + description: "DeepSeek-R1 671B" + }, + { + id: "deepseek-r1:70b", + description: "DeepSeek-R1 70B" + }, + { + id: "deepseek-r1:32b", + description: "DeepSeek-R1 32B" + }, + { + id: "deepseek-r1:14b", + description: "DeepSeek-R1 14B" + }, + { + id: "deepseek-r1:8b", + description: "DeepSeek-R1 8B" + }, + { + id: "deepseek-r1:7b", + description: "DeepSeek-R1 7B" + }, + { + id: "deepseek-r1:1.5b", + description: "DeepSeek-R1 1.5B" + }, + + // Llama3.1 models + { + id: "llama3.1:405b", + description: "Llama 3.1 405B" + }, + { + id: "llama3.1:70b", + description: "Llama 3.1 70B" + }, + { + id: "llama3.1:8b", + description: "Llama 3.1 8B" + }, + + // Gemma2 models + { + id: "gemma2:2b", + description: "Gemma2 2B" + }, + { + id: "gemma2:9b", + description: "Gemma2 9B" + }, + { + id: "gemma2:27b", + description: "Gemma2 27B" + }, + + // Qwen2.5 models + { + id: "qwen2.5:0.5b", + description: "Qwen2.5 0.5B" + }, + { + id: "qwen2.5:1.5b", + description: "Qwen2.5 1.5B" + }, + { + id: "qwen2.5:3b", + description: "Qwen2.5 3B" + }, + { + id: "qwen2.5:7b", + description: "Qwen2.5 7B" + }, + { + id: "qwen2.5:14b", + description: "Qwen2.5 14B" + }, + { + id: "qwen2.5:32b", + description: "Qwen2.5 32B" + }, + { + id: "qwen2.5:72b", + description: "Qwen2.5 72B" + }, + + // Mistral models + { + id: "mistral:7b", + description: "Mistral 7B" + }, + { + id: "mistral-nemo:12b", + description: "Mistral Nemo 12B" + }, + { + id: "mistral-large:123b", + description: "Mistral Large 123B" + }, + + // Command-R models + { + id: "command-r:35b", + description: "Command-R 35B" + }, + { + id: "command-r-plus:104b", + description: "Command-R Plus 104B" + }, + ], + "required": true +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/mistral.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/mistral.jsonnet new file mode 100644 index 00000000..07f75b81 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/mistral.jsonnet @@ -0,0 +1,61 @@ +// Mistral LLM Model Definitions +// Defines available models and their configurations for Mistral AI + +{ + "type": "string", + "description": "LLM model to use", + "default": "mistral-medium-2508", + "enum": [ + // Featured models + { + id: "mistral-medium-2508", + description: "Mistral Medium 3.1" + }, + { + id: "mistral-large-2512", + description: "Mistral Large 3" + }, + { + id: "mistral-small-2506", + description: "Mistral Small 3.2" + }, + { + id: "ministral-14b-2512", + description: "Ministral 3 14B" + }, + { + id: "ministral-8b-2512", + description: "Ministral 3 8B" + }, + { + id: "ministral-3b-2512", + description: "Ministral 3 3B" + }, + { + id: "magistral-medium-2509", + description: "Magistral Medium 1.2 (reasoning)" + }, + { + id: "magistral-small-2509", + description: "Magistral Small 1.2 (reasoning)" + }, + { + id: "devstral-2512", + description: "Devstral 2 (code)" + }, + // Other models + { + id: "codestral-2508", + description: "Codestral (code)" + }, + { + id: "pixtral-large-2411", + description: "Pixtral Large (vision)" + }, + { + id: "open-mistral-nemo", + description: "Open Mistral Nemo" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/ollama.jsonnet new file mode 100644 index 00000000..4ef98a6a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/ollama.jsonnet @@ -0,0 +1,150 @@ +// Ollama LLM Model Definitions +// Defines available models and their configurations for Ollama + +{ + "type": "string", + "description": "LLM model to use", + "default": "gemma3:12b", + "enum": [ + // Gemma3 models + { + id: "gemma3:1b", + description: "Gemma3 1B" + }, + { + id: "gemma3:4b", + description: "Gemma3 4B" + }, + { + id: "gemma3:12b", + description: "Gemma3 12B" + }, + { + id: "gemma3:27b", + description: "Gemma3 27B" + }, + + // Phi4 models + { + id: "phi4:mini:3.8b", + description: "Phi4 Mini 3.8B" + }, + { + id: "phi4:14b", + description: "Phi4 14B" + }, + + // DeepSeek-R1 models + { + id: "deepseek-r1:671b", + description: "DeepSeek-R1 671B" + }, + { + id: "deepseek-r1:70b", + description: "DeepSeek-R1 70B" + }, + { + id: "deepseek-r1:32b", + description: "DeepSeek-R1 32B" + }, + { + id: "deepseek-r1:14b", + description: "DeepSeek-R1 14B" + }, + { + id: "deepseek-r1:8b", + description: "DeepSeek-R1 8B" + }, + { + id: "deepseek-r1:7b", + description: "DeepSeek-R1 7B" + }, + { + id: "deepseek-r1:1.5b", + description: "DeepSeek-R1 1.5B" + }, + + // Llama3.1 models + { + id: "llama3.1:405b", + description: "Llama 3.1 405B" + }, + { + id: "llama3.1:70b", + description: "Llama 3.1 70B" + }, + { + id: "llama3.1:8b", + description: "Llama 3.1 8B" + }, + + // Gemma2 models + { + id: "gemma2:2b", + description: "Gemma2 2B" + }, + { + id: "gemma2:9b", + description: "Gemma2 9B" + }, + { + id: "gemma2:27b", + description: "Gemma2 27B" + }, + + // Qwen2.5 models + { + id: "qwen2.5:0.5b", + description: "Qwen2.5 0.5B" + }, + { + id: "qwen2.5:1.5b", + description: "Qwen2.5 1.5B" + }, + { + id: "qwen2.5:3b", + description: "Qwen2.5 3B" + }, + { + id: "qwen2.5:7b", + description: "Qwen2.5 7B" + }, + { + id: "qwen2.5:14b", + description: "Qwen2.5 14B" + }, + { + id: "qwen2.5:32b", + description: "Qwen2.5 32B" + }, + { + id: "qwen2.5:72b", + description: "Qwen2.5 72B" + }, + + // Mistral models + { + id: "mistral:7b", + description: "Mistral 7B" + }, + { + id: "mistral-nemo:12b", + description: "Mistral Nemo 12B" + }, + { + id: "mistral-large:123b", + description: "Mistral Large 123B" + }, + + // Command-R models + { + id: "command-r:35b", + description: "Command-R 35B" + }, + { + id: "command-r-plus:104b", + description: "Command-R Plus 104B" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/openai.jsonnet new file mode 100644 index 00000000..75b2670f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/openai.jsonnet @@ -0,0 +1,31 @@ +// OpenAI LLM Model Definitions +// Defines available models and their configurations for OpenAI's platform + +{ + "type": "string", + "description": "LLM model to use", + "default": "gpt-4o", + "enum": [ + { + id: "gpt-4o", + description: "GPT-4o (latest)" + }, + { + id: "gpt-4o-mini", + description: "GPT-4o Mini" + }, + { + id: "gpt-4-turbo", + description: "GPT-4 Turbo" + }, + { + id: "gpt-4", + description: "GPT-4" + }, + { + id: "gpt-3.5-turbo", + description: "GPT-3.5 Turbo" + }, + ], + "required": true +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/temperature-param-types.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/temperature-param-types.jsonnet new file mode 100644 index 00000000..f5523e88 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/temperature-param-types.jsonnet @@ -0,0 +1,15 @@ +// LLM temperature definitions + +{ + "llm-temperature": { + "type": "float", + "description": "LLM temperature", + "placeholder": 0.3, + "helper": "A floating point number between 0 and 1", + "default": 0.3, + "min": 0.0, + "max": 10.0, + "required": true + } +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/vertexai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/vertexai.jsonnet new file mode 100644 index 00000000..6d90f8d3 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/vertexai.jsonnet @@ -0,0 +1,68 @@ +// VertexAI LLM Model Definitions +// Defines available models and their configurations for Google's VertexAI platform + +{ + "type": "string", + "description": "LLM model to use", + "default": "gemini-2.5-flash-lite", + "enum": [ + // Gemini 2.5 models (latest generation) + { + id: "gemini-2.5-pro", + description: "Gemini 2.5 Pro" + }, + { + id: "gemini-2.5-flash", + description: "Gemini 2.5 Flash" + }, + { + id: "gemini-2.5-flash-lite", + description: "Gemini 2.5 Flash Lite" + }, + + // Gemini 2.0 models + { + id: "gemini-2.0-flash-exp", + description: "Gemini 2.0 Flash (experimental)" + }, + + + // Claude models on VertexAI + { + id: "claude-3-5-sonnet@20241022", + description: "Claude 3.5 Sonnet (via VertexAI)" + }, + { + id: "claude-3-5-haiku@20241022", + description: "Claude 3.5 Haiku (via VertexAI)" + }, + { + id: "claude-3-opus@20240229", + description: "Claude 3 Opus (via VertexAI)" + }, + { + id: "claude-3-sonnet@20240229", + description: "Claude 3 Sonnet (via VertexAI)" + }, + { + id: "claude-3-haiku@20240307", + description: "Claude 3 Haiku (via VertexAI)" + }, + + // Llama models on VertexAI + { + id: "llama3-405b-instruct-maas", + description: "Llama 3 405B Instruct (via VertexAI)" + }, + { + id: "llama3-70b-instruct-maas", + description: "Llama 3 70B Instruct (via VertexAI)" + }, + { + id: "llama3-8b-instruct-maas", + description: "Llama 3 8B Instruct (via VertexAI)" + }, + ], + "required": true +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/vllm.jsonnet new file mode 100644 index 00000000..120342b2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/parameters/vllm.jsonnet @@ -0,0 +1,18 @@ +// vLLM Model Definitions +// Defines available models and their configurations for vLLM +// vLLM works with any HuggingFace model. We have to use the model +// vLLM was initialised with + +{ + "type": "string", + "description": "LLM model to use", + "default": "model", + "enum": [ + // Llama 3.1 models + { + id: "model", + description: "Pre-defined model" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/profiles/memory-profile-low.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/profiles/memory-profile-low.jsonnet new file mode 100644 index 00000000..e6341062 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/profiles/memory-profile-low.jsonnet @@ -0,0 +1,169 @@ +// Low memory profile - reduces memory allocation across components. +// Include this at the END of your configuration to override default memory +// settings with lower values suitable for memory-constrained environments. +// +// Usage: {"name": "memory-profile-low", "parameters": {}} +// +// Note: This trades some performance headroom for reduced memory usage. +// Monitor for OOM errors under heavy load. + +{ + + // Override Pulsar stack memory settings + "pulsar" +: { + + // Zookeeper: 512M -> 300M + "zk-memory-limit":: "300M", + "zk-memory-reservation":: "200M", + "zk-heap":: "128m", + "zk-direct-memory":: "64m", + + // Bookie: 1024M -> 600M + "bookie-memory-limit":: "600M", + "bookie-memory-reservation":: "400M", + "bookie-heap":: "128m", + "bookie-direct-memory":: "128m", + + // Broker: 800M -> 512M + "broker-memory-limit":: "512M", + "broker-memory-reservation":: "400M", + "broker-heap":: "192m", + "broker-direct-memory":: "192m", + + // Pulsar-init: 256M -> 128M + "init-memory-limit":: "128M", + "init-memory-reservation":: "128M", + "init-heap":: "64m", + "init-direct-memory":: "64m", + + }, + + // Override Cassandra memory settings: 1000M -> 600M + "cassandra" +: { + "memory-limit":: "600M", + "memory-reservation":: "500M", + "heap":: "200M", + }, + + // Override Qdrant memory settings: 1024M -> 600M + // Also enables mmap for vectors/payloads (trades latency for memory) + "qdrant" +: { + "memory-limit":: "600M", + "memory-reservation":: "500M", + "memmap-threshold-kb":: "1", + "on-disk-payload":: "true", + }, + + // TrustGraph core services - 50% memory reservations + "api-gateway" +: { + "memory-reservation":: "256M", // 512M -> 256M + }, + + "chunker" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "config-svc" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "pdf-decoder" +: { + "memory-reservation":: "256M", // 512M -> 256M + }, + + "mcp-tool" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "mcp-server" +: { + "memory-reservation":: "128M", // 256M -> 128M + }, + + "metering" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "metering-rag" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "kg-store" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "kg-manager" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "prompt" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "prompt-rag" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "document-rag" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "document-embeddings" +: { + "memory-reservation":: "256M", // 512M -> 256M + }, + + "librarian" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "agent-manager" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + // Graph RAG services + "kg-extract-definitions" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "kg-extract-relationships" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "kg-extract-agent" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "kg-extract-ontology" +: { + "memory-reservation":: "150M", // 300M -> 150M + }, + + "kg-extract-objects" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "graph-rag" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "graph-embeddings" +: { + "memory-reservation":: "256M", // 512M -> 256M + }, + + // Structured data services + "nlp-query" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "structured-query" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "structured-diag" +: { + "memory-reservation":: "48M", // 96M -> 48M + }, + + // Init service + "init-trustgraph" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/agent-kg-extract.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/agent-kg-extract.txt new file mode 100644 index 00000000..36dbd74f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/agent-kg-extract.txt @@ -0,0 +1,28 @@ +Analyze the following text and extract both entity definitions and relationships. + +For definitions, extract entities and their explanations or descriptions. +For relationships, extract subject-predicate-object triples where subjects and objects are entities, and predicates are relationship types. + +Text: {{text}} + +Output format: JSONL (one JSON object per line, no array wrapper) +Each line must include a "type" field to distinguish between definitions and relationships. + +For definitions, output: +{"type": "definition", "entity": "entity_name", "definition": "definition_text"} + +For relationships, output: +{"type": "relationship", "subject": "subject_entity", "predicate": "relationship_type", "object": "object_entity_or_literal", "object-entity": true} + +Requirements: +- Each line must be a complete, valid JSON object +- No commas between lines +- No [ ] array brackets +- No markdown formatting or prefixes +- Do not provide explanations, only output JSONL + +Example output: +{"type": "definition", "entity": "DNA", "definition": "Deoxyribonucleic acid, a molecule carrying genetic instructions"} +{"type": "definition", "entity": "RNA", "definition": "Ribonucleic acid, essential for coding and gene expression"} +{"type": "relationship", "subject": "DNA", "predicate": "transcribes_to", "object": "RNA", "object-entity": true} +{"type": "relationship", "subject": "DNA", "predicate": "located_in", "object": "cell nucleus", "object-entity": true} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/agent-prompt.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/agent-prompt.txt new file mode 100644 index 00000000..9155d20f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/agent-prompt.txt @@ -0,0 +1,104 @@ +# ReAct Agent System Prompt + +You are an AI assistant that uses the ReAct (Reasoning + Acting) framework to solve problems through systematic reasoning and tool use. + +## Core Instructions + +For each user query, work through the problem step-by-step using this cycle: +1. **Thought**: Reason about the current situation and determine what you need to do next +2. **Action**: Take ONE specific action using an available tool +3. Wait for **Observation**: The system will provide the result of your action +4. Continue with the next **Thought** based on the observation + +**CRITICAL**: Generate exactly ONE Thought followed by ONE Action, then STOP. Do not generate multiple Thought/Action pairs in a single response. Do not generate Observations yourself - the system will provide them. + +## Response Format + +Use this exact format for each step: + +``` +Thought: [Your reasoning about what to do next - be specific about why this action is needed] +Action: [tool_name] +Args: { + "parameter_name": "value", + "another_parameter": 123, + "list_parameter": ["item1", "item2"] +} +``` + +When you have finished provide the final answer: + +``` +Thought: [Your reasoning about why the process is complete] +Final Answer: [The final answer] +``` + +When providing a final answer, do not provide an Action or Args. + +## Action Format Rules + +1. **Tool Name**: Write "Action: " followed by the exact tool name on its own line +2. **Arguments**: Write "Args: " followed by a valid JSON object containing all parameters +3. **JSON Requirements**: + - Use double quotes for all string keys and values + - Numbers don't need quotes: `"count": 5` + - Booleans: `"enabled": true` or `"enabled": false` + - Arrays: `"items": ["a", "b", "c"]` + - Nested objects: `"config": {"setting": "value"}` + - Null values: `"optional_field": null` +4. **Required Parameters**: Include all required parameters for the tool +5. **No Extra Text**: Don't add explanations or comments within the Action block +1. **Final answer**: Write "Final Answer: " followed by the final answer + +## Available Tools + +{% for tool in tools %}- **{{ tool.name }}**: {{ tool.description }} +{% for arg in tool.arguments %} - Required: `"{{ arg.name }}"` ({{ arg.type }}): {{ arg.description }} +{% endfor %} +{% endfor %} + +## Behavior Rules + +1. **One Step at a Time**: Generate exactly one Thought and one Action, then wait for the system to provide an Observation +2. **Be Specific**: Your Thought should clearly explain why you're taking the specific action +3. **Use Context**: Build on previous Observations to inform your next steps +4. **Error Handling**: If an action fails, reason about the error and try a different approach +5. **Completion**: When you have enough information to fully answer the user's query, generate a final Thought explaining your conclusion, but do not take further actions + +## Error Responses + +If an action fails, you'll see: +``` +Observation: Error: [specific error message] +``` + +When this happens: +- Generate a Thought analyzing what went wrong +- Take a corrective Action with different parameters or a different tool +- If a tool is completely unavailable, explain this limitation in your next Thought + +## Termination + +The conversation ends when: +- You determine you have sufficient information to answer the user's query completely and provide a final answer. +- You encounter an unrecoverable error that prevents task completion +- The system reaches the maximum iteration limit + +## Important Notes + +- **Never generate Observations yourself** - only the system provides these +- **Always validate your JSON** - malformed JSON will cause action failures +- **Stay focused** - each Thought should directly relate to solving the user's query +- **Be efficient** - choose actions that gather the most relevant information for the task + +# Proceed + +Question: {{question}} + +{% for h in history %} +Action: "{{h.action}}" +Args: { +{% for k, v in h.arguments.items() %} "{{k}}": "{{v}}" +{% endfor %}} +Observation: "{{h.observation}}" +{% endfor %} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/cohere.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/cohere.jsonnet new file mode 100644 index 00000000..9541e4c2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/cohere.jsonnet @@ -0,0 +1,42 @@ +// For Cohere. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/default-prompts.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/default-prompts.jsonnet new file mode 100644 index 00000000..3b173a5a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/default-prompts.jsonnet @@ -0,0 +1,233 @@ + +// Prompt templates. For tidy JSONNET use, don't change these templates +// here, but use over-rides in the prompt directory + +{ + + "system-template":: "You are a helpful assistant.", + + "templates":: { + + "question":: { + "prompt": "{{question}}", + }, + + "extract-definitions":: { + "prompt": importstr "extract-definitions.txt", + "response-type": "jsonl", + "object-schema": { + "type": "object", + "properties": { + "entity": { + "type": "string" + }, + "definition": { + "type": "string" + } + }, + "required": [ + "entity", + "definition" + ] + } + }, + + "extract-relationships":: { + "prompt": importstr "extract-relationships.txt", + "response-type": "jsonl", + "object-schema": { + "type": "object", + "properties": { + "subject": { + "type": "string" + }, + "predicate": { + "type": "string" + }, + "object": { + "type": "string" + }, + "object-entity": { + "type": "boolean" + } + }, + "required": [ + "subject", + "predicate", + "object", + "object-entity" + ] + } + }, + + "extract-topics":: { + "prompt": importstr "extract-topics.txt", + "response-type": "jsonl", + "object-schema": { + "type": "object", + "properties": { + "topic": { + "type": "string" + }, + "definition": { + "type": "string" + } + }, + "required": [ + "topic", + "definition" + ] + } + }, + + "extract-rows":: { + "prompt": importstr "extract-rows.txt", + "response-type": "jsonl", + }, + + "kg-prompt":: { + "prompt": importstr "kg-prompt.txt", + "response-type": "text", + }, + + "document-prompt":: { + "prompt": importstr "document-prompt.txt", + "response-type": "text", + }, + + "agent-react":: { + "prompt": importstr "agent-prompt.txt", + "response-type": "text" + }, + + "agent-kg-extract":: { + "prompt": importstr "agent-kg-extract.txt", + "response-type": "jsonl", + "object-schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { "const": "definition" }, + "entity": { "type": "string" }, + "definition": { "type": "string" } + }, + "required": ["type", "entity", "definition"] + }, + { + "type": "object", + "properties": { + "type": { "const": "relationship" }, + "subject": { "type": "string" }, + "predicate": { "type": "string" }, + "object": { "type": "string" }, + "object-entity": { "type": "boolean" } + }, + "required": ["type", "subject", "predicate", "object"] + } + ] + } + }, + + "schema-selection":: { + "prompt": importstr "schema-selection.txt", + "response-type": "json", + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "description": "An array of schema names that are relevant to answering the given question" + } + }, + + "graphql-generation":: { + "prompt": importstr "graphql-generation.txt", + "response-type": "json", + "schema": { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "The GraphQL query string generated to answer the question" + }, + "variables": { + "type": "object", + "description": "Object containing any GraphQL variables needed for the query", + "additionalProperties": true + }, + "confidence": { + "type": "number", + "minimum": 0.0, + "maximum": 1.0, + "description": "Float between 0.0-1.0 indicating confidence in the generated query" + } + }, + "required": ["query", "variables", "confidence"], + "additionalProperties": false + } + }, + + "diagnose-structured-data":: { + "prompt": importstr "diagnose-structured-data.txt", + "response-type": "json", + }, + + "diagnose-xml":: { + "prompt": importstr "diagnose-xml.txt", + "response-type": "json", + }, + "diagnose-json":: { + "prompt": importstr "diagnose-json.txt", + "response-type": "json", + }, + "diagnose-csv":: { + "prompt": importstr "diagnose-csv.txt", + "response-type": "json", + }, + + "extract-with-ontologies":: { + "prompt": importstr "ontology-prompt.txt", + "response-type": "jsonl", + "object-schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { "const": "entity" }, + "entity": { "type": "string" }, + "entity_type": { "type": "string" } + }, + "required": ["type", "entity", "entity_type"] + }, + { + "type": "object", + "properties": { + "type": { "const": "relationship" }, + "subject": { "type": "string" }, + "subject_type": { "type": "string" }, + "relation": { "type": "string" }, + "object": { "type": "string" }, + "object_type": { "type": "string" } + }, + "required": ["type", "subject", "subject_type", "relation", "object", "object_type"] + }, + { + "type": "object", + "properties": { + "type": { "const": "attribute" }, + "entity": { "type": "string" }, + "entity_type": { "type": "string" }, + "attribute": { "type": "string" }, + "value": { "type": "string" } + }, + "required": ["type", "entity", "entity_type", "attribute", "value"] + } + ] + } + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/diagnose-csv.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/diagnose-csv.txt new file mode 100644 index 00000000..ad9725ba --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/diagnose-csv.txt @@ -0,0 +1,370 @@ +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for data import pipelines, with particular expertise in CSV processing and delimiter-separated value formats. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured CSV data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## CSV Processing Expertise + +When working with CSV data, you must: + +1. **Analyze CSV Structure** - Examine headers, delimiters, quoting, and data patterns +2. **Identify Column Mappings** - Map source column names to target fields +3. **Handle Complex CSV Patterns** - Support various CSV formats including: + - Standard comma-separated values: `name,age,city` + - Alternative delimiters: tab-separated (TSV), pipe-separated, semicolon-separated + - Quoted fields with embedded delimiters: `"Last, First",25,"New York, NY"` + - Headers with spaces or special characters: `"Customer Name","Order Date","Total Amount"` + - Files with or without headers + - Multi-line fields with embedded newlines + +## CSV Format Configuration Guidelines + +For CSV format configurations, use these patterns: + +**Basic CSV Configuration:** +```json +{ + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + "delimiter": ",", + "quote_char": "\"", + "has_header": true, + "skip_rows": 0 + } + } +} +``` + +**Advanced CSV Options:** +```json +{ + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + "delimiter": "\t", // Tab-separated + "quote_char": "\"", + "escape_char": "\\", + "has_header": true, + "skip_rows": 2, // Skip metadata rows + "null_values": ["", "NULL", "N/A"], + "trim_whitespace": true, + "skip_blank_lines": true + } + } +} +``` + +**CRITICAL: Source Field Names in Mappings** + +When processing CSV files, the parser uses column headers (if present) or generates column indices as field names. Your source field names in mappings must match exactly: + +**CORRECT Example with Headers:** +CSV file: +```csv +Customer Name,Order Date,Total Amount,Status +John Smith,2024-01-15,1000.50,Active +Jane Doe,2024-01-16,750.25,Pending +``` + +Becomes parsed data: +```json +{ + "Customer Name": "John Smith", + "Order Date": "2024-01-15", + "Total Amount": "1000.50", + "Status": "Active" +} +``` + +Your mappings should use: +```json +{ + "source_field": "Customer Name", // ✅ Correct - matches header exactly + "source_field": "Order Date", // ✅ Correct - matches header exactly + "source_field": "Total Amount", // ✅ Correct - matches header exactly + "source_field": "Status" // ✅ Correct - matches header exactly +} +``` + +**CORRECT Example without Headers:** +CSV file without headers uses column indices: +```csv +John Smith,2024-01-15,1000.50,Active +Jane Doe,2024-01-16,750.25,Pending +``` + +Becomes parsed data: +```json +{ + "0": "John Smith", + "1": "2024-01-15", + "2": "1000.50", + "3": "Active" +} +``` + +Your mappings should use: +```json +{ + "source_field": "0", // ✅ Correct - first column + "source_field": "1", // ✅ Correct - second column + "source_field": "2", // ✅ Correct - third column + "source_field": "3" // ✅ Correct - fourth column +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **Source Data Format** + - Delimiter character (comma, tab, pipe, semicolon, etc.) + - Quote character and escape character + - **For CSV**: Does the file have headers? Sample structure + - Text encoding (UTF-8, Windows-1252, etc.) + - Any rows to skip (metadata, blank lines) + - How null/empty values are represented + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source column → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + - Date/time format conversions + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or invalid data + - Duplicate handling strategy + - Row-level validation rules + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## CSV Structure Analysis + +When presented with CSV data, analyze: + +1. **Delimiter Detection**: What character separates the fields? +2. **Header Presence**: Does the first row contain column names? +3. **Quote Pattern**: Are fields quoted? What quote character is used? +4. **Data Types**: What types are present in each column? +5. **Null Representation**: How are empty/null values represented? +6. **Special Characters**: Are there embedded commas, quotes, or newlines? +7. **Encoding Issues**: Are there any character encoding problems? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + // CSV-specific parsing options + // delimiter, quote_char, has_header, skip_rows, etc. + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` +- `split`, `strip_quotes` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` +- `parse_number`, `parse_currency` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` +- `clean_whitespace`, `normalize_encoding` + +**Date/Time Operations:** +- `parse_date`, `format_date`, `date_component` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` +- `numeric_range`, `date_range` + +## CSV-Specific Best Practices + +1. **Detect delimiters accurately** - test with sample data to confirm delimiter +2. **Handle quoted fields properly** - account for embedded delimiters and quotes +3. **Trim whitespace consistently** - decide whether to preserve or remove extra spaces +4. **Validate data types early** - catch type conversion errors at the field level +5. **Handle empty values explicitly** - distinguish between empty strings and nulls +6. **Account for encoding issues** - especially with international characters +7. **Validate row structure** - ensure consistent column counts across rows + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes +9. **Handle CSV-specific edge cases** like malformed quotes, inconsistent delimiters +10. **Test with sample data** to validate parsing configuration + +## Complete CSV Example + +Given this CSV structure: +```csv +Customer Name,Order Date,Total Amount,Currency,Status +"Smith, John",2024-01-15,1000.50,USD,Active +"Doe, Jane",2024-01-16,750.25,EUR,Pending +"Johnson, Bob",2024-01-17,,USD,Cancelled +``` + +The parser will: +1. Use `delimiter: ","` and `quote_char: "\""` to parse fields correctly +2. Use `has_header: true` to treat first row as column names +3. Create this parsed data structure for each record: + ```json + { + "Customer Name": "Smith, John", + "Order Date": "2024-01-15", + "Total Amount": "1000.50", + "Currency": "USD", + "Status": "Active" + } + ``` + +Generate this COMPLETE configuration: +```json +{ + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + "delimiter": ",", + "quote_char": "\"", + "has_header": true, + "trim_whitespace": true, + "null_values": ["", "NULL"] + } + }, + "mappings": [ + { + "source_field": "Customer Name", // ✅ Matches header exactly + "target_field": "customer_name", + "transforms": [{"type": "trim"}] + }, + { + "source_field": "Order Date", // ✅ Matches header exactly + "target_field": "order_date", + "transforms": [{"type": "to_date", "format": "YYYY-MM-DD"}], + "validation": [{"type": "required"}] + }, + { + "source_field": "Total Amount", // ✅ Matches header exactly + "target_field": "amount", + "transforms": [ + {"type": "to_float"}, + {"type": "default", "value": 0.0} + ] + }, + { + "source_field": "Currency", // ✅ Matches header exactly + "target_field": "currency_code", + "transforms": [{"type": "upper"}], + "validation": [{"type": "in_list", "values": ["USD", "EUR", "GBP"]}] + }, + { + "source_field": "Status", // ✅ Matches header exactly + "target_field": "order_status", + "transforms": [{"type": "lower"}] + } + ] +} +``` + +**KEY RULE: source_field names must match the column headers exactly, or use column indices (0, 1, 2, etc.) for files without headers.** + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the CSV structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to delimiter detection, header identification, quoting patterns, and data type inference: + +{{sample}} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/diagnose-json.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/diagnose-json.txt new file mode 100644 index 00000000..2fe6341d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/diagnose-json.txt @@ -0,0 +1,327 @@ +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for data import pipelines, with particular expertise in JSON processing and JSONPath expressions. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured JSON data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## JSON Processing Expertise + +When working with JSON data, you must: + +1. **Analyze JSON Structure** - Examine the hierarchy, array patterns, and object nesting +2. **Generate Proper JSONPath Expressions** - Create efficient JSONPath selectors for record extraction +3. **Handle Complex JSON Patterns** - Support various JSON formats including: + - Array of objects: `[{"name": "John", "age": 30}, {...}]` + - Nested object arrays: `{"data": {"records": [{"id": 1}, {...}]}}` + - Mixed hierarchies with both arrays and nested objects + - Single object records: `{"record": {"field1": "value1"}}` + +## JSONPath Expression Guidelines + +For JSON format configurations, use these JSONPath patterns: + +**Record Path Examples:** +- Root array: `$[*]` (for arrays at the root level) +- Nested arrays: `$.data.records[*]` or `$.response.items[*]` +- Single object: `$.record` (when there's one record per file) +- Deep nesting: `$.data.results.items[*]` + +**Field Access Patterns:** +- Direct properties: Use property names directly in mappings +- Nested properties: Use dot notation like `address.street` or `contact.email` +- Array elements: Use bracket notation like `tags[0]` for first element + +**CRITICAL: Source Field Names in Mappings** + +When processing JSON, the parser creates a flat or nested dictionary based on the record structure. Your source field names in mappings must match the actual property names in the parsed records: + +**CORRECT Example:** +```json +{ + "Country or Area": "Albania", + "Trade (USD)": "1000.50", + "metadata": { + "source": "UN", + "year": 2024 + } +} +``` + +Your mappings should use: +```json +{ + "source_field": "Country or Area", // ✅ Correct - matches property name + "source_field": "Trade (USD)", // ✅ Correct - matches property name + "source_field": "metadata.source", // ✅ Correct - nested property access + "source_field": "metadata.year" // ✅ Correct - nested property access +} +``` + +**JSON Format Configuration Template:** +```json +{ + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + "record_path": "$[*]", // JSONPath to extract records + "flatten_nested": true, // Whether to flatten nested objects + "array_handling": "expand" // How to handle arrays: expand, first, concat + } + } +} +``` + +**Alternative JSON Options:** +```json +{ + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + "record_path": "$.data.items[*]", // For nested array structures + "flatten_nested": false, // Keep nested structure + "null_value_handling": "skip" // skip, empty_string, or preserve + } + } +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **Source Data Format** + - JSON structure type (array of objects, nested objects, single records) + - **For JSON**: Sample structure, nesting patterns, array locations + - Sample data or field descriptions + - Any format-specific details (encoding, special null handling, etc.) + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source field → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + - Nested object flattening requirements + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or null values + - Duplicate handling strategy + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## JSON Structure Analysis + +When presented with JSON data, analyze: + +1. **Root Structure**: Is it an array, object, or nested structure? +2. **Record Location**: Where are individual records located in the hierarchy? +3. **Field Pattern**: How are field names and values structured? + - Direct properties: `{"name": "John"}` + - Nested objects: `{"contact": {"email": "john@example.com"}}` + - Arrays: `{"tags": ["red", "blue"]}` + - Mixed types: `{"data": [{"id": 1, "details": {"name": "John"}}]}` +4. **Data Types**: What types are present (strings, numbers, booleans, nulls, arrays, objects)? +5. **Hierarchy Depth**: How deeply nested are the records and fields? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + // JSON-specific parsing options + // record_path (JSONPath), flatten_nested, array_handling, etc. + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` +- `flatten_object`, `extract_array_element`, `join_array` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` + +## JSON-Specific Best Practices + +1. **Use efficient JSONPath expressions** - Prefer specific paths over broad searches +2. **Handle nested objects appropriately** - decide whether to flatten or preserve structure +3. **Consider array handling strategies** - expand arrays to multiple records or extract specific elements +4. **Account for null vs undefined** values in field mappings +5. **Handle mixed data types** within the same field across records +6. **Use appropriate flattening** for deeply nested structures + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes +9. **Handle JSON-specific edge cases** like empty arrays, null objects, mixed types + +## Complete JSON Example + +Given this JSON structure: +```json +{ + "data": { + "records": [ + { + "Country": "USA", + "Year": 2024, + "Amount": 1000.50, + "metadata": { + "source": "World Bank", + "confidence": 0.95 + } + } + ] + } +} +``` + +The parser will: +1. Use `record_path: "$.data.records[*]"` to extract record objects from the array +2. Create this parsed data structure for each record: + ```json + { + "Country": "USA", + "Year": 2024, + "Amount": 1000.50, + "metadata.source": "World Bank", // If flattened + "metadata.confidence": 0.95 // If flattened + } + ``` + +Generate this COMPLETE configuration: +```json +{ + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + "record_path": "$.data.records[*]", + "flatten_nested": true, + "array_handling": "expand" + } + }, + "mappings": [ + { + "source_field": "Country", // ✅ Matches property name + "target_field": "country_name" + }, + { + "source_field": "Year", // ✅ Matches property name + "target_field": "year", + "transforms": [{"type": "to_int"}] + }, + { + "source_field": "Amount", // ✅ Matches property name + "target_field": "amount", + "transforms": [{"type": "to_float"}] + }, + { + "source_field": "metadata.source", // ✅ Flattened nested field + "target_field": "data_source" + } + ] +} +``` + +**KEY RULE: source_field names must match the actual property names in the parsed JSON records, using dot notation for nested properties when flattened.** + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the JSON structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to JSON hierarchy, object patterns, array structures, and generate appropriate JSONPath expressions: + +{{sample}} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/diagnose-structured-data.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/diagnose-structured-data.txt new file mode 100644 index 00000000..84c4b8be --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/diagnose-structured-data.txt @@ -0,0 +1,309 @@ + +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for data import pipelines, with particular expertise in XML processing and XPath expressions. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## XML Processing Expertise + +When working with XML data, you must: + +1. **Analyze XML Structure** - Examine the hierarchy, namespaces, and element patterns +2. **Generate Proper XPath Expressions** - Create efficient XPath selectors for record extraction +3. **Handle Complex XML Patterns** - Support various XML formats including: + - Standard element structures: `John` + - Attribute-based fields: `USA` + - Mixed content and nested hierarchies + - Namespaced XML documents + +## XPath Expression Guidelines + +For XML format configurations, use these XPath patterns: + +**Record Path Examples:** +- Simple records: `//record` or `//customer` +- Nested records: `//data/records/record` or `//customers/customer` +- Absolute paths: `/ROOT/data/record` (will be converted to relative paths automatically) +- With namespaces: `//ns:record` or `//soap:Body/data/record` + +**Field Attribute Patterns:** +- When fields use name attributes: set `field_attribute: "name"` for `value` +- For other attribute patterns: set appropriate attribute name + +**CRITICAL: Source Field Names in Mappings** + +When using `field_attribute`, the XML parser extracts field names from the attribute values and creates a flat dictionary. Your source field names in mappings must match these extracted names: + +**CORRECT Example:** +```xml +Albania +1000.50 +``` + +Becomes parsed data: +```json +{ + "Country or Area": "Albania", + "Trade (USD)": "1000.50" +} +``` + +So your mappings should use: +```json +{ + "source_field": "Country or Area", // ✅ Correct - matches parsed field name + "source_field": "Trade (USD)" // ✅ Correct - matches parsed field name +} +``` + +**INCORRECT Example:** +```json +{ + "source_field": "Field[@name='Country or Area']", // ❌ Wrong - XPath not needed here + "source_field": "field[@name='Trade (USD)']" // ❌ Wrong - XPath not needed here +} +``` + +**XML Format Configuration Template:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//data/record", // XPath to find record elements + "field_attribute": "name" // For value pattern + } + } +} +``` + +**Alternative XML Options:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//customer", // Direct element-based records + // No field_attribute needed for standard XML + } + } +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **Source Data Format** + - File type (CSV, JSON, XML, Excel, fixed-width, etc.) + - **For XML**: Sample structure, namespace prefixes, record element patterns + - Sample data or field descriptions + - Any format-specific details (delimiters, encoding, namespaces, etc.) + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source field → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or invalid data + - Duplicate handling strategy + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## XML Structure Analysis + +When presented with XML data, analyze: + +1. **Document Root**: What is the root element? +2. **Record Container**: Where are individual records located? +3. **Field Pattern**: How are field names and values structured? + - Direct child elements: `John` + - Attribute-based: `John` + - Mixed patterns +4. **Namespaces**: Are there any namespace prefixes? +5. **Hierarchy Depth**: How deeply nested are the records? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "[csv|json|xml|fixed-width|excel]", + "encoding": "utf-8", + "options": { + // Format-specific parsing options + // For XML: record_path (XPath), field_attribute (if applicable) + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` + +## XML-Specific Best Practices + +1. **Use efficient XPath expressions** - Prefer specific paths over broad searches +2. **Handle namespace prefixes** when present +3. **Identify field attribute patterns** correctly +4. **Test XPath expressions** mentally against the provided structure +5. **Consider XML element vs attribute data** in field mappings +6. **Account for mixed content** and nested structures + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes + +## Complete XML Example + +Given this XML structure: +```xml + + + + USA + 2024 + 1000.50 + + + +``` + +The parser will: +1. Use `record_path: "/ROOT/data/record"` to find record elements +2. Use `field_attribute: "name"` to extract field names from the name attribute +3. Create this parsed data structure: `{"Country": "USA", "Year": "2024", "Amount": "1000.50"}` + +Generate this COMPLETE configuration: +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "/ROOT/data/record", + "field_attribute": "name" + } + }, + "mappings": [ + { + "source_field": "Country", // ✅ Matches parsed field name + "target_field": "country_name" + }, + { + "source_field": "Year", // ✅ Matches parsed field name + "target_field": "year", + "transforms": [{"type": "to_int"}] + }, + { + "source_field": "Amount", // ✅ Matches parsed field name + "target_field": "amount", + "transforms": [{"type": "to_float"}] + } + ] +} +``` + +**KEY RULE: source_field names must match the extracted field names, NOT the XML element structure.** + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the XML structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to XML hierarchy, element patterns, and generate appropriate XPath expressions: + +{{sample}} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/diagnose-xml.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/diagnose-xml.txt new file mode 100644 index 00000000..a3d8efa4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/diagnose-xml.txt @@ -0,0 +1,453 @@ +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for XML data import pipelines, with particular expertise in XML processing and XPath expressions. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured XML data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## XML Processing Expertise + +When working with XML data, you must: + +1. **Analyze XML Structure** - Examine the hierarchy, namespaces, and element patterns +2. **Generate Proper XPath Expressions** - Create efficient XPath selectors for record extraction +3. **Handle Complex XML Patterns** - Support various XML formats including: + - Standard element structures: `John` + - Attribute-based fields: `USA` + - Mixed content and nested hierarchies + - Namespaced XML documents + - CDATA sections and text nodes + +## XPath Expression Guidelines + +For XML format configurations, use these XPath patterns: + +**Record Path Examples:** +- Simple records: `//record` or `//customer` +- Nested records: `//data/records/record` or `//customers/customer` +- Absolute paths: `/ROOT/data/record` +- With namespaces: `//ns:record` or `//soap:Body/data/record` +- Attribute-filtered: `//record[@type='customer']` + +**Field Attribute Patterns:** +- When fields use name attributes: set `field_attribute: "name"` for `value` +- For other attribute patterns: set appropriate attribute name like `field_attribute: "id"` or `field_attribute: "type"` + +## CRITICAL: Source Field Names in Mappings + +The behavior depends on whether you use `field_attribute`: + +### With field_attribute (Attribute-Based Fields) + +When using `field_attribute`, the XML parser extracts field names from the attribute values and creates a flat dictionary: + +**Example:** +```xml + + Albania + 1000.50 + Active + +``` + +Parser configuration: +```json +{ + "record_path": "//record", + "field_attribute": "name" +} +``` + +Becomes parsed data: +```json +{ + "Country or Area": "Albania", + "Trade (USD)": "1000.50", + "Status": "Active" +} +``` + +Your mappings should use: +```json +{ + "source_field": "Country or Area", // ✅ Correct - matches parsed field name + "source_field": "Trade (USD)", // ✅ Correct - matches parsed field name + "source_field": "Status" // ✅ Correct - matches parsed field name +} +``` + +### Without field_attribute (Element-Based Fields) + +When NOT using `field_attribute`, use direct element names or XPath expressions: + +**Example:** +```xml + + Albania + 1000.50 + Active + + UN + 2024 + + +``` + +Parser configuration: +```json +{ + "record_path": "//record" + // No field_attribute specified +} +``` + +Your mappings should use: +```json +{ + "source_field": "country", // ✅ Direct element name + "source_field": "trade_amount", // ✅ Direct element name + "source_field": "metadata/source", // ✅ Nested element path + "source_field": "metadata/year" // ✅ Nested element path +} +``` + +## XML Format Configuration Templates + +**For Attribute-Based Fields:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//data/record", // XPath to find record elements + "field_attribute": "name", // Extract field names from 'name' attribute + "namespace_prefixes": { // Optional: define namespaces + "ns": "http://example.com/namespace" + } + } + } +} +``` + +**For Element-Based Fields:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//customer", // XPath to find record elements + "preserve_namespaces": true, // Optional: keep namespace info + "ignore_attributes": false // Optional: include element attributes + } + } +} +``` + +**For Complex XML with Namespaces:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//soap:Body//data:record", + "namespace_prefixes": { + "soap": "http://schemas.xmlsoap.org/soap/envelope/", + "data": "http://example.com/data" + }, + "field_attribute": "name" + } + } +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **XML Structure Details** + - Sample XML structure or schema + - Root element and record location + - Field organization (elements vs attributes) + - Namespace declarations and prefixes + - Text encoding (UTF-8, ISO-8859-1, etc.) + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source field → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or invalid data + - Duplicate handling strategy + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## XML Structure Analysis + +When presented with XML data, analyze: + +1. **Document Root**: What is the root element? +2. **Namespaces**: Are there namespace declarations? What prefixes are used? +3. **Record Container**: Where are individual records located in the hierarchy? +4. **Field Pattern**: How are field names and values structured? + - Direct child elements: `John` + - Attribute-based: `John` + - Mixed patterns: `John` +5. **Data Location**: Are values in element text, attributes, or both? +6. **Hierarchy Depth**: How deeply nested are the records? +7. **Special Content**: Any CDATA sections, mixed content, or special characters? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + // XML-specific parsing options + // record_path (XPath), field_attribute (if applicable), namespace_prefixes + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` +- `strip_cdata`, `decode_html_entities` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` +- `parse_xml_date`, `parse_iso_date` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` +- `extract_attribute`, `get_text_content` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` +- `valid_xml_name`, `namespace_check` + +## XML-Specific Best Practices + +1. **Use efficient XPath expressions** - Prefer specific paths over broad searches like `//` when possible +2. **Handle namespace prefixes** when present - define them in `namespace_prefixes` +3. **Identify field attribute patterns** correctly - determine if using `field_attribute` +4. **Test XPath expressions** mentally against the provided structure +5. **Consider XML element vs attribute data** in field mappings +6. **Account for mixed content** and nested structures +7. **Handle CDATA sections** and special characters properly +8. **Preserve or normalize whitespace** as appropriate +9. **Consider XML schema validation** if XSD is available + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes +9. **Handle XML-specific edge cases** like empty elements, mixed content, processing instructions + +## Complete XML Examples + +### Example 1: Attribute-Based Fields + +Given this XML structure: +```xml + + + + USA + 2024 + 1000.50 + + + +``` + +The parser will: +1. Use `record_path: "//data/record"` to find record elements +2. Use `field_attribute: "name"` to extract field names from the name attribute +3. Create this parsed data structure: `{"Country": "USA", "Year": "2024", "Amount": "1000.50"}` + +Generate this configuration: +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//data/record", + "field_attribute": "name" + } + }, + "mappings": [ + { + "source_field": "Country", // ✅ Matches parsed field name + "target_field": "country_name" + }, + { + "source_field": "Year", // ✅ Matches parsed field name + "target_field": "year", + "transforms": [{"type": "to_int"}] + }, + { + "source_field": "Amount", // ✅ Matches parsed field name + "target_field": "amount", + "transforms": [{"type": "to_float"}] + } + ] +} +``` + +### Example 2: Element-Based Fields + +Given this XML structure: +```xml + + + John Smith + john@example.com +
+ 123 Main St + New York +
+ + + 456 + 100.50 + + +
+
+``` + +Generate this configuration: +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//customer" + } + }, + "mappings": [ + { + "source_field": "name", // ✅ Direct element name + "target_field": "customer_name" + }, + { + "source_field": "email", // ✅ Direct element name + "target_field": "email_address" + }, + { + "source_field": "address/street", // ✅ Nested element path + "target_field": "street_address" + }, + { + "source_field": "address/city", // ✅ Nested element path + "target_field": "city" + }, + { + "source_field": "@id", // ✅ Attribute reference + "target_field": "customer_id", + "transforms": [{"type": "to_int"}] + } + ] +} +``` + +**KEY RULES:** +- With `field_attribute`: source_field names must match the extracted attribute values +- Without `field_attribute`: use direct element names, nested paths, or XPath expressions +- Use `@attribute` notation for XML element attributes + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the XML structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to XML hierarchy, element patterns, namespace usage, and generate appropriate XPath expressions: + +{{sample}} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/document-prompt.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/document-prompt.txt new file mode 100644 index 00000000..5e5d65ba --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/document-prompt.txt @@ -0,0 +1,7 @@ +Study the following context. Use only the information provided in the context in your response. Do not speculate if the answer is not found in the provided set of knowledge statements. + +Here is the context: +{{documents}} + +Use only the provided knowledge statements to respond to the following: +{{query}} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/extract-definitions.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/extract-definitions.txt new file mode 100644 index 00000000..410ecddf --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/extract-definitions.txt @@ -0,0 +1,28 @@ + +Study the following text and derive definitions for any discovered entities. +Do not provide definitions for entities whose definitions are incomplete +or unknown. + +Output each definition as a separate JSON object on its own line (JSONL format). +Each object must have fields: +- entity: the name of the entity +- definition: English text which defines the entity + + + +{{text}} + + + +Output format: JSONL (one JSON object per line, no array wrapper) +- Each line must be a complete, valid JSON object +- No commas between lines +- No [ ] array brackets +- No markdown formatting or prefixes +- Do not use special characters in the definition text +- Do not include null or unknown definitions + +Example output format: +{"entity": "photosynthesis", "definition": "The process by which plants convert sunlight into energy"} +{"entity": "chlorophyll", "definition": "Green pigment in plants that absorbs light"} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/extract-relationships.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/extract-relationships.txt new file mode 100644 index 00000000..d10d8118 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/extract-relationships.txt @@ -0,0 +1,28 @@ + +Study the following text and derive entity relationships. For each +relationship, derive the subject, predicate and object of the relationship. + +Output each relationship as a separate JSON object on its own line (JSONL format). +Each object must have fields: +- subject: the subject of the relationship +- predicate: the predicate +- object: the object of the relationship +- object-entity: false if the object is a simple data type (name, value, date). true if it is an entity. + + + +{{text}} + + + +Output format: JSONL (one JSON object per line, no array wrapper) +- Each line must be a complete, valid JSON object +- No commas between lines +- No [ ] array brackets +- No markdown formatting or prefixes +- Do not use special characters in the text fields + +Example output format: +{"subject": "Earth", "predicate": "orbits", "object": "Sun", "object-entity": true} +{"subject": "Earth", "predicate": "has_diameter", "object": "12742 km", "object-entity": false} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/extract-rows.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/extract-rows.txt new file mode 100644 index 00000000..7cea8b7d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/extract-rows.txt @@ -0,0 +1,27 @@ + +Study the following text and derive objects which match the schema provided. + +Output each discovered object as a separate JSON object on its own line (JSONL format). +Each object's fields must carry the name field specified in the schema. + + + +{{schema}} + + + +{{text}} + + + +Output format: JSONL (one JSON object per line, no array wrapper) +- Each line must be a complete, valid JSON object matching the schema +- No commas between lines +- No [ ] array brackets +- No markdown formatting or prefixes +- Do not provide explanations + +Example output format (assuming schema has fields: name, age, city): +{"name": "Alice", "age": 30, "city": "London"} +{"name": "Bob", "age": 25, "city": "Paris"} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/extract-topics.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/extract-topics.txt new file mode 100644 index 00000000..bb4b1193 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/extract-topics.txt @@ -0,0 +1,26 @@ +You are a helpful assistant that performs information extraction tasks for a provided text. +Read the provided text. You will identify topics and their definitions. + +Reading Instructions: +- Ignore document formatting in the provided text. +- Study the provided text carefully. + +Here is the text: +{{text}} + +Response Instructions: +- Output each topic as a separate JSON object on its own line (JSONL format) +- Each object must have keys "topic" and "definition" +- Do not respond with special characters +- Return only topics that are concepts and unique to the provided text + +Output format: JSONL (one JSON object per line, no array wrapper) +- Each line must be a complete, valid JSON object +- No commas between lines +- No [ ] array brackets +- No markdown formatting or prefixes +- Do not write any additional text or explanations + +Example output format: +{"topic": "machine learning", "definition": "A subset of AI that enables systems to learn from data"} +{"topic": "neural network", "definition": "A computing system inspired by biological neural networks"} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/gemini.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/gemini.jsonnet new file mode 100644 index 00000000..b9a1e0c0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/gemini.jsonnet @@ -0,0 +1,42 @@ +// For VertexAI Gemini. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/graphql-generation.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/graphql-generation.txt new file mode 100644 index 00000000..58af61dc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/graphql-generation.txt @@ -0,0 +1,59 @@ +You are a GraphQL query generation expert. Given a natural language question and provided database schemas, generate a valid GraphQL query embedded in valid JSON. + +## Question: +{{ question }} + +## Provided Schemas: +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %}- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif %}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif %}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ field.enum_values|join(', ') }}]{% endif %} +{% endfor %} +{% endfor %} + +## GraphQL Query Rules: +1. Use the schema names as GraphQL query fields (e.g., `customers`, `orders`) +2. Apply filters using the `where` parameter with nested filter objects +3. Available filter operators per field type: + - String fields: `eq`, `contains`, `startsWith`, `endsWith`, `in`, `not`, `not_in` + - Integer/Float fields: `eq`, `gt`, `gte`, `lt`, `lte`, `in`, `not`, `not_in` +4. Use `order_by` for sorting (field name as string) +5. Use `direction` for sort direction: `ASC` or `DESC` +6. Use `limit` to restrict number of results +7. Select specific fields in the query body + +## Task Instructions: +1. Analyze the question to identify: + - What data to retrieve (which fields to select) + - What filters to apply (where conditions) + - What sorting is needed (order_by, direction) + - How many results (limit) + +2. Generate a GraphQL query that: + - Uses only the provided schema names and field names + - Applies appropriate filters based on the question + - Selects relevant fields for the response + - Includes reasonable limits (default 100 if not specified) + +3. If variables are needed, include them in the response + +## Output Format: +Return ONLY a valid JSON object (no markdown, no code blocks) with these fields: +- "query": the GraphQL query string +- "variables": object with any GraphQL variables (empty object if none) +- "confidence": float between 0.0-1.0 indicating confidence in the query + +Important: Return raw JSON only, with no markdown formatting, no code blocks, and no backticks. + +## Examples: + +Question: "Show me customers from California" +{"query": "query { customers(where: {state: {eq: \"California\"}}, limit: 100) { customer_id name email state } }", "variables": {}, "confidence": 0.92} + +Question: "Top 10 products by price" +{"query": "query { products(order_by: \"price\", direction: DESC, limit: 10) { product_id name price category } }", "variables": {}, "confidence": 0.88} + +Question: "Recent orders over $100" +{"query": "query { orders(where: {total_amount: {gt: 100}, order_date: {gte: \"2024-01-01\"}}, order_by: \"order_date\", direction: DESC, limit: 50) { order_id customer_id total_amount order_date status } }", "variables": {}, "confidence": 0.96} + +Now generate the GraphQL query for the question above. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/kg-prompt.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/kg-prompt.txt new file mode 100644 index 00000000..431c0d73 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/kg-prompt.txt @@ -0,0 +1,8 @@ +Study the following set of knowledge statements. The statements are written in Cypher format that has been extracted from a knowledge graph. Use only the provided set of knowledge statements in your response. Do not speculate if the answer is not found in the provided set of knowledge statements. + +Here's the knowledge statements: +{% for edge in knowledge %}({{edge.s}})-[{{edge.p}}]->({{edge.o}}) +{%endfor%} + +Use only the provided knowledge statements to respond to the following: +{{query}} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/mixtral.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/mixtral.jsonnet new file mode 100644 index 00000000..cd56e7ef --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/mixtral.jsonnet @@ -0,0 +1,42 @@ +// For Mixtral. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/ontology-prompt.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/ontology-prompt.txt new file mode 100644 index 00000000..1b451d9c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/ontology-prompt.txt @@ -0,0 +1,82 @@ +You are a knowledge extraction expert. Your task is to find entities, relationships, and attributes in text based on a provided schema. + +## Entity Types + +These are the types of entities you should look for: + +{% for class_id, class_def in classes.items() %} +- **{{class_id}}**{% if class_def.subclass_of %} (subclass of {{class_def.subclass_of}}){% endif %}{% if class_def.comment %}: {{class_def.comment}}{% endif %} +{% endfor %} + +## Relationships + +These relationships connect entities to other entities: + +{% for prop_id, prop_def in object_properties.items() %} +- **{{prop_id}}**{% if prop_def.domain and prop_def.range %} ({{prop_def.domain}} → {{prop_def.range}}){% endif %}{% if prop_def.comment %}: {{prop_def.comment}}{% endif %} +{% endfor %} + +## Attributes + +These attributes describe entity properties (text, numbers, etc.): + +{% for prop_id, prop_def in datatype_properties.items() %} +- **{{prop_id}}**{% if prop_def.domain and prop_def.range %} ({{prop_def.domain}} → {{prop_def.range}}){% endif %}{% if prop_def.comment %}: {{prop_def.comment}}{% endif %} +{% endfor %} + +## Text to Analyze + +{{text}} + +## Your Task + +Extract the following from the text above: + +1. **Entities**: Things mentioned in the text and their types +2. **Relationships**: How entities relate to each other +3. **Attributes**: Properties of entities (like quantities, descriptions, etc.) + +## Output Format + +Output format: JSONL (one JSON object per line, no array wrapper) +Each line must include a "type" field to distinguish between entities, relationships, and attributes. + +For entities, output: +{"type": "entity", "entity": "entity name as it appears in text", "entity_type": "EntityType"} + +For relationships, output: +{"type": "relationship", "subject": "subject entity name", "subject_type": "SubjectType", "relation": "relationship_name", "object": "object entity name", "object_type": "ObjectType"} + +For attributes, output: +{"type": "attribute", "entity": "entity name", "entity_type": "EntityType", "attribute": "attribute_name", "value": "literal value"} + +## Important Rules + +1. **Entity names**: Use the exact text as it appears (e.g., "Cornish pasty", "beef") +2. **Types**: Use the EXACT type identifiers from the schema above (e.g., "fo/Recipe", "fo/Food") +3. **Relationships**: Use the EXACT relationship names from the schema (e.g., "fo/has_ingredient") +4. **Attributes**: Use the EXACT attribute names from the schema (e.g., "fo/serves") +5. **No array brackets**: Output one JSON object per line, not an array +6. **No markdown**: Do not wrap output in code blocks + +## Requirements + +- Each line must be a complete, valid JSON object +- No commas between lines +- No [ ] array brackets +- No markdown formatting, code blocks, or prefixes +- Do not provide explanations, only output JSONL + +## Example + +Input text: "Cornish pasty is a savory pastry filled with beef and potatoes. This recipe serves 4 people." + +Expected output: +{"type": "entity", "entity": "Cornish pasty", "entity_type": "fo/Recipe"} +{"type": "entity", "entity": "beef", "entity_type": "fo/Food"} +{"type": "entity", "entity": "potatoes", "entity_type": "fo/Food"} +{"type": "relationship", "subject": "Cornish pasty", "subject_type": "fo/Recipe", "relation": "fo/has_ingredient", "object": "beef", "object_type": "fo/Food"} +{"type": "relationship", "subject": "Cornish pasty", "subject_type": "fo/Recipe", "relation": "fo/has_ingredient", "object": "potatoes", "object_type": "fo/Food"} +{"type": "attribute", "entity": "Cornish pasty", "entity_type": "fo/Recipe", "attribute": "fo/serves", "value": "4 people"} + +Now extract entities, relationships, and attributes from the text above. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/openai.jsonnet new file mode 100644 index 00000000..5d232337 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/openai.jsonnet @@ -0,0 +1,42 @@ +// For OpenAI LLMs. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/schema-selection.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/schema-selection.txt new file mode 100644 index 00000000..39b180e5 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/schema-selection.txt @@ -0,0 +1,25 @@ +You are a database schema selection expert. Given a natural language question and available +database schemas, your job is to identify which schemas are most relevant to answer the question. + +## Available Schemas: +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}): {{ field.description }} +{% endfor %} + +{% endfor %} + +## Question: +{{ question }} + +## Instructions: +1. Analyze the question to understand what data is being requested +2. Examine each schema to understand what data it contains +3. Select ONLY the schemas that are directly relevant to answering the question +4. Return your answer as a JSON array of schema names + +## Response Format: +Return ONLY a JSON array of schema names, nothing else. +Example: ["customers", "orders", "products"] diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/slm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/slm.jsonnet new file mode 100644 index 00000000..48eb96d0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/prompts/slm.jsonnet @@ -0,0 +1,44 @@ +// For SLM. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + + + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/pulsar/pulsar-manager.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/pulsar/pulsar-manager.jsonnet new file mode 100644 index 00000000..c1ca4515 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/pulsar/pulsar-manager.jsonnet @@ -0,0 +1,40 @@ +local images = import "values/images.jsonnet"; + +{ + + "pulsar" +: { + + create:: function(engine) + +// FIXME: Should persist something? +// local volume = engine.volume(...) + + local container = + engine.container("pulsar") + .with_image(images.pulsar_manager) + .with_environment({ + SPRING_CONFIGURATION_FILE: "/pulsar-manager/pulsar-manager/application.properties", + }) + .with_limits("0.5", "1.4G") + .with_reservations("0.1", "1.4G") + .with_port(9527, 9527, "api") + .with_port(7750, 7750, "api2"); + + local containerSet = engine.containers( + "pulsar", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9527, 9527, "api") + .with_port(7750, 7750, "api2); + + engine.resources([ + containerSet, + service, + ]) + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/pulsar/pulsar.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/pulsar/pulsar.jsonnet new file mode 100644 index 00000000..d417322d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/pulsar/pulsar.jsonnet @@ -0,0 +1,222 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +// This is a Pulsar configuration. Non-standalone mode so we deploy +// individual components: bookkeeper, broker and zookeeper. +// +// This also deploys the TrustGraph 'admin' container which initialises +// TrustGraph-specific namespaces etc. + +{ + + "pulsar" +: { + + // Zookeeper memory settings (can be overridden by memory-profile) + "zk-memory-limit":: "512M", + "zk-memory-reservation":: "512M", + "zk-heap":: "256m", + "zk-direct-memory":: "256m", + + // Bookie memory settings (can be overridden by memory-profile) + "bookie-memory-limit":: "1024M", + "bookie-memory-reservation":: "1024M", + "bookie-heap":: "256m", + "bookie-direct-memory":: "256m", + + // Broker memory settings (can be overridden by memory-profile) + "broker-memory-limit":: "800M", + "broker-memory-reservation":: "800M", + "broker-heap":: "384m", + "broker-direct-memory":: "384m", + + // Pulsar-init memory settings (can be overridden by memory-profile) + "init-memory-limit":: "256M", + "init-memory-reservation":: "256M", + "init-heap":: "128m", + "init-direct-memory":: "128m", + + create:: function(engine) + + // Capture memory settings into locals (self refers to pulsar object here) + local zkMemLimit = self["zk-memory-limit"]; + local zkMemReserv = self["zk-memory-reservation"]; + local zkHeap = self["zk-heap"]; + local zkDirect = self["zk-direct-memory"]; + + local bookieMemLimit = self["bookie-memory-limit"]; + local bookieMemReserv = self["bookie-memory-reservation"]; + local bookieHeap = self["bookie-heap"]; + local bookieDirect = self["bookie-direct-memory"]; + + local brokerMemLimit = self["broker-memory-limit"]; + local brokerMemReserv = self["broker-memory-reservation"]; + local brokerHeap = self["broker-heap"]; + local brokerDirect = self["broker-direct-memory"]; + + local initMemLimit = self["init-memory-limit"]; + local initMemReserv = self["init-memory-reservation"]; + local initHeap = self["init-heap"]; + local initDirect = self["init-direct-memory"]; + + // Zookeeper volume + local zkVolume = engine.volume("zookeeper").with_size("1G"); + + // Zookeeper container + local zkContainer = + engine.container("zookeeper") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "bin/apply-config-from-env.py conf/zookeeper.conf && bin/generate-zookeeper-config.sh conf/zookeeper.conf && exec bin/pulsar zookeeper" + ]) + .with_limits("1", zkMemLimit) + .with_reservations("0.05", zkMemReserv) + .with_user("0:1000") + .with_volume_mount(zkVolume, "/pulsar/data/zookeeper") + .with_environment({ + "metadataStoreUrl": "zk:zookeeper:2181", + "PULSAR_MEM": "-Xms%s -Xmx%s -XX:MaxDirectMemorySize=%s" % [ + zkHeap, zkHeap, zkDirect, + ], + }) + .with_port(2181, 2181, "zookeeper") + .with_port(2888, 2888, "zookeeper2") + .with_port(3888, 3888, "zookeeper3"); + + // Pulsar cluster init container + local initContainer = + engine.container("pulsar-init") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "sleep 10 && bin/pulsar initialize-cluster-metadata --cluster cluster-a --zookeeper zookeeper:2181 --configuration-store zookeeper:2181 --web-service-url http://pulsar:8080 --broker-service-url pulsar://pulsar:6650", + ]) + .with_limits("1", initMemLimit) + .with_reservations("0.05", initMemReserv) + .with_environment({ + "PULSAR_MEM": "-Xms%s -Xmx%s -XX:MaxDirectMemorySize=%s" % [ + initHeap, initHeap, initDirect, + ], + }); + + + // Bookkeeper volume + local bookieVolume = engine.volume("bookie").with_size("20G"); + + // Bookkeeper container + local bookieContainer = + engine.container("bookie") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "bin/apply-config-from-env.py conf/bookkeeper.conf && exec bin/pulsar bookie" + // false ^ causes this to be a 'failure' exit. + ]) + .with_limits("1", bookieMemLimit) + .with_reservations("0.1", bookieMemReserv) + .with_user("0:1000") + .with_volume_mount(bookieVolume, "/pulsar/data/bookkeeper") + .with_environment({ + "clusterName": "cluster-a", + "zkServers": "zookeeper:2181", + "bookieId": "bookie", + "metadataStoreUri": "metadata-store:zk:zookeeper:2181", + "advertisedAddress": "bookie", + "BOOKIE_MEM": "-Xms%s -Xmx%s -XX:MaxDirectMemorySize=%s" % [ + bookieHeap, bookieHeap, bookieDirect, + ], + }) + .with_port(3181, 3181, "bookie"); + + // Pulsar broker, stateless (uses ZK and Bookkeeper for state) + local brokerContainer = + engine.container("pulsar") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "bin/apply-config-from-env.py conf/broker.conf && exec bin/pulsar broker" + ]) + .with_limits("1", brokerMemLimit) + .with_reservations("0.1", brokerMemReserv) + .with_environment({ + "metadataStoreUrl": "zk:zookeeper:2181", + "zookeeperServers": "zookeeper:2181", + "clusterName": "cluster-a", + "managedLedgerDefaultEnsembleSize": "1", + "managedLedgerDefaultWriteQuorum": "1", + "managedLedgerDefaultAckQuorum": "1", + "advertisedAddress": "pulsar", + "advertisedListeners": "external:pulsar://pulsar:6650,localhost:pulsar://localhost:6650", + "PULSAR_MEM": "-Xms%s -Xmx%s -XX:MaxDirectMemorySize=%s" % [ + brokerHeap, brokerHeap, brokerDirect, + ], + }) + .with_port(6650, 6650, "pulsar") + .with_port(8080, 8080, "admin"); + + // Container sets + local zkContainerSet = engine.containers( + "zookeeper", + [ + zkContainer, + ] + ); + + local initContainerSet = engine.containers( + "init-pulsar", + [ + initContainer, + ] + ); + + local bookieContainerSet = engine.containers( + "bookie", + [ + bookieContainer, + ] + ); + + local brokerContainerSet = engine.containers( + "pulsar", + [ + brokerContainer, + ] + ); + + // Zookeeper service + local zkService = + engine.service(zkContainerSet) + .with_port(2181, 2181, "zookeeper") + .with_port(2888, 2888, "zookeeper2") + .with_port(3888, 3888, "zookeeper3"); + + // Bookkeeper service + local bookieService = + engine.service(bookieContainerSet) + .with_port(3181, 3181, "bookie"); + + // Pulsar broker service + local brokerService = + engine.service(brokerContainerSet) + .with_port(6650, 6650, "pulsar") + .with_port(8080, 8080, "admin"); + + engine.resources([ + zkVolume, + bookieVolume, + zkContainerSet, + initContainerSet, + bookieContainerSet, + brokerContainerSet, + zkService, + bookieService, + brokerService, + ]) + + } + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-additionals.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-additionals.jsonnet new file mode 100644 index 00000000..3e9f3481 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-additionals.jsonnet @@ -0,0 +1,134 @@ +local decode = import "decode-config.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Custom engine that collects configVolume parts +local engine = { + + // Collection of all configVolume parts + configVolumes:: [], + + // Implement all required engine methods as no-ops + container:: function(name) { + with_image:: function(x) self, + with_command:: function(x) self, + with_environment:: function(x) self, + with_limits:: function(c, m) self, + with_reservations:: function(c, m) self, + with_port:: function(src, dest, name) self, + with_volume_mount:: function(vol, mnt) self, + with_user:: function(x) self, + with_runtime:: function(x) self, + with_privileged:: function(x) self, + with_ipc:: function(x) self, + with_capability:: function(x) self, + with_device:: function(hdev, cdev) self, + with_env_var_secrets:: function(vars) self, + }, + + volume:: function(name) { + with_size:: function(size) self, + }, + + // The key method - collects configVolume parts + configVolume:: function(name, dir, parts) + local collector = self + { + configVolumes: super.configVolumes + [ + { + dir: dir, + parts: parts, + } + ] + }; + { + // Return a dummy volume that has the collector in it + name: name, + with_size:: function(size) collector, + // Provide a way to get back to the collector + getCollector:: function() collector, + }, + + secretVolume:: function(name, dir, parts) { + with_size:: function(size) self, + }, + + envSecrets:: function(name) { + with_env_var:: function(name, key) self, + }, + + containers:: function(name, containers) self, + + service:: function(containers) { + with_port:: function(src, dest, name) self, + }, + + internalService:: function(containers) { + with_port:: function(src, dest, name) self, + }, + + resources:: function(res) + // Fold over resources and collect any configVolume state + local collected = std.foldl( + function(state, r) + if std.objectHasAll(r, 'getCollector') then + // Merge the configVolumes from the volume's collector into our state + local volumeCollector = r.getCollector(); + state + { + configVolumes: state.configVolumes + volumeCollector.configVolumes + } + else + state, + res, + self + ); + collected, +}; + +// Execute all component create() functions with our collecting engine +// Note: create:: is a hidden field, so we must use objectHasAll not objectHas +local result = std.foldl( + function(state, p) + if std.objectHasAll(p, 'create') then + // Pattern has create directly - call it + p.create(state) + else + state, + std.objectValues(patterns), + engine +); + +// Debug: show what we collected +local debug = { + numPatterns: std.length(std.objectValues(patterns)), + numConfigVolumes: std.length(result.configVolumes), +}; + +// Transform collected data into output format +local allFiles = std.flattenArrays([ + [ + { + // Remove trailing slash from dir to avoid double slashes + path: std.join("/", [std.rstripChars(cv.dir, "/"), filename]), + content: cv.parts[filename] + } + for filename in std.objectFields(cv.parts) + ] + for cv in result.configVolumes +]); + +// Deduplicate by path - use a map to keep only unique paths +local uniqueMap = std.foldl( + function(acc, item) acc + { [item.path]: item }, + allFiles, + {} +); + +// Convert back to array +local additionals = std.objectValues(uniqueMap); + +// Output the array +additionals diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-aks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-aks-k8s.jsonnet new file mode 100644 index 00000000..bcec1cbb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-aks-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "../engine/aks-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-docker-compose.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-docker-compose.jsonnet new file mode 100644 index 00000000..7f0dbabe --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-docker-compose.jsonnet @@ -0,0 +1,20 @@ + +local engine = import "../engine/docker-compose.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resources = std.foldl( + function(state, p) state + p.create(engine), + std.objectValues(patterns), + {} +); + +resources + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-eks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-eks-k8s.jsonnet new file mode 100644 index 00000000..f9de59cb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-eks-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "../engine/eks-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-gcp-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-gcp-k8s.jsonnet new file mode 100644 index 00000000..50037a5c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-gcp-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "../engine/gcp-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-minikube-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-minikube-k8s.jsonnet new file mode 100644 index 00000000..6fa64706 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-minikube-k8s.jsonnet @@ -0,0 +1,26 @@ + +local engine = import "../engine/minikube-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +// Extract resources using the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-noop.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-noop.jsonnet new file mode 100644 index 00000000..3ad735d9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-noop.jsonnet @@ -0,0 +1,20 @@ + +local engine = import "../engine/noop.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resources = std.foldl( + function(state, p) state + p.create(engine), + std.objectValues(patterns), + {} +); + +resources + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-ovh-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-ovh-k8s.jsonnet new file mode 100644 index 00000000..92f61041 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-ovh-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "../engine/ovh-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-podman-compose.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-podman-compose.jsonnet new file mode 100644 index 00000000..7f0dbabe --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-podman-compose.jsonnet @@ -0,0 +1,20 @@ + +local engine = import "../engine/docker-compose.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resources = std.foldl( + function(state, p) state + p.create(engine), + std.objectValues(patterns), + {} +); + +resources + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-scw-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-scw-k8s.jsonnet new file mode 100644 index 00000000..4f6c1d7a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-scw-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "../engine/scw-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-tg-configuration.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-tg-configuration.jsonnet new file mode 100644 index 00000000..59b4732a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/config-to-tg-configuration.jsonnet @@ -0,0 +1,15 @@ + +local engine = import "../engine/noop.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract configuration directly from patterns +patterns.configuration.configuration + + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/decode-config.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/decode-config.jsonnet new file mode 100644 index 00000000..759513c4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/renderers/decode-config.jsonnet @@ -0,0 +1,31 @@ + +local components = import "../components.jsonnet"; + +local apply = function(p, components) + + local base = { + + with:: function(k, v) self + { + [k] +:: v + }, + + with_params:: function(pars) + self + std.foldl( + function(obj, par) obj.with(par.key, par.value), + std.objectKeysValues(pars), + self + ), + + }; + + local component = base + components[p.name]; + + component.with_params(p.parameters); + +local decode = function(config) + local add = function(state, c) state + apply(c, components); + local patterns = std.foldl(add, config, {}); + patterns; + +decode + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/row-store/cassandra.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/row-store/cassandra.jsonnet new file mode 100644 index 00000000..7fdf433e --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/row-store/cassandra.jsonnet @@ -0,0 +1,77 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local cassandra = import "backends/cassandra.jsonnet"; + +cassandra + { + + "store-objects" +: { + + create:: function(engine) + + local container = + engine.container("store-objects") + .with_image(images.trustgraph_flow) + .with_command([ + "objects-write-cassandra", + "-p", + url.pulsar, + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-objects", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-objects" +: { + + create:: function(engine) + + local container = + engine.container("query-objects") + .with_image(images.trustgraph_flow) + .with_command([ + "objects-query-cassandra", + "-p", + url.pulsar, + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "512M") + .with_reservations("0.1", "512M"); + + local containerSet = engine.containers( + "query-objects", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/runtime-config/config-composer.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/runtime-config/config-composer.jsonnet new file mode 100644 index 00000000..e93ff044 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/runtime-config/config-composer.jsonnet @@ -0,0 +1,97 @@ +// Configuration Composer Module +// Orchestrates the complete configuration building process +// Combines all components into the final TrustGraph configuration + +local flow_builder = import "flow-builder.jsonnet"; +local interface_builder = import "interface-builder.jsonnet"; + +{ + // Main function to build the complete configuration + build: function(config_spec) + // Extract configuration parameters + local flow_blueprints = config_spec.flow_blueprints; + local default_flow_blueprint = config_spec.default_flow_blueprint; + local default_flow_id = config_spec.default_flow_id; + local flow_init_parameters = config_spec.flow_init_parameters; + + // Build all processors for the default flow + local blueprint_processors = flow_builder.build_blueprint_processors( + flow_blueprints, + default_flow_blueprint, + flow_init_parameters + ); + + local flow_processors = flow_builder.build_flow_processors( + flow_blueprints, + default_flow_blueprint, + default_flow_id, + flow_init_parameters + ); + + // Combine processors into flow objects + local processor_array = blueprint_processors + flow_processors; + local flow_objects = flow_builder.build_flow_objects(processor_array); + local active_flows = flow_builder.merge_flow_objects(flow_objects); + + // Build interfaces for the default flow + local default_flow_interfaces = interface_builder.build_interfaces( + flow_blueprints, + default_flow_blueprint, + default_flow_id, + flow_init_parameters + ); + + // Return object with nested configuration (for backwards compatibility) + { + // Create function (for backwards compatibility) + create: function(engine) {}, + + // The actual configuration object + configuration: { + // Prompts configuration + prompt: { + "system": config_spec.prompts["system-template"], + "template-index": std.objectFieldsAll(config_spec.prompts.templates), + } + { + ["template." + template.key]: template.value + for template in std.objectKeysValuesAll(config_spec.prompts.templates) + }, + + // Tools configuration + tool: { + [tool.id]: tool + for tool in config_spec.tools + }, + + // MCP configuration + mcp: config_spec.mcp, + + // Flow blueprints reference + "flow-blueprint": flow_blueprints, + + // Interface descriptions + "interface-description": config_spec.interface_descriptions, + + // Flow instances + "flow": { + [default_flow_id]: { + "description": "Default processing flow", + "blueprint-name": default_flow_blueprint, + "interfaces": default_flow_interfaces, + "parameters": flow_init_parameters, + }, + }, + + // Active flow processors + "active-flow": active_flows, + + // Token costs and parameter types + "token-cost": config_spec.token_costs, + "parameter-type": config_spec.parameter_types, + + // Collections configuration + "collection": config_spec.collection, + + }, + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/runtime-config/flow-builder.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/runtime-config/flow-builder.jsonnet new file mode 100644 index 00000000..0b142750 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/runtime-config/flow-builder.jsonnet @@ -0,0 +1,72 @@ +// Flow Builder Module +// Processes flow blueprints and builds complete flow configurations +// Handles {blueprint}, {id}, and parameter substitutions + +local param_processor = import "parameter-processor.jsonnet"; + +{ + // Builds blueprint-level processors with parameter substitution + // Processes the 'blueprint' section of flow blueprints + build_blueprint_processors: function(flow_blueprints, blueprint_name, parameters) + [ + [ + // Replace {blueprint} in the processor key + local key = std.strReplace(processor.key, "{blueprint}", blueprint_name); + local parts = std.splitLimit(key, ":", 2); + parts, + { + // Process each field in the processor configuration + [field.key]: + // First replace {blueprint}, then substitute parameters + local blueprint_replaced = std.strReplace(field.value, "{blueprint}", blueprint_name); + param_processor.substitute_parameters(blueprint_replaced, parameters) + for field in std.objectKeysValuesAll(processor.value) + } + ] + for processor in std.objectKeysValuesAll(flow_blueprints[blueprint_name].blueprint) + ], + + // Builds flow-level processors with parameter substitution + // Processes the 'flow' section of flow blueprints + build_flow_processors: function(flow_blueprints, blueprint_name, flow_id, parameters) + [ + [ + // Replace both {blueprint} and {id} in the processor key + local key = std.strReplace( + std.strReplace(processor.key, "{blueprint}", blueprint_name), + "{id}", flow_id + ); + local parts = std.splitLimit(key, ":", 2); + parts, + { + // Process each field in the processor configuration + [field.key]: + // Replace {blueprint} and {id}, then substitute parameters + local blueprint_replaced = std.strReplace(field.value, "{blueprint}", blueprint_name); + local id_replaced = std.strReplace(blueprint_replaced, "{id}", flow_id); + param_processor.substitute_parameters(id_replaced, parameters) + for field in std.objectKeysValuesAll(processor.value) + } + ] + for processor in std.objectKeysValuesAll(flow_blueprints[blueprint_name].flow) + ], + + // Combines blueprint and flow processors into flow objects + build_flow_objects: function(processor_array) + std.map( + function(item) { + [item[0][0]] +: { + [item[0][1]]: item[1] + } + }, + processor_array + ), + + // Merges all flow objects into a single flows_active configuration + merge_flow_objects: function(flow_objects) + std.foldr( + function(a, b) a + b, + flow_objects, + {} + ), +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/runtime-config/interface-builder.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/runtime-config/interface-builder.jsonnet new file mode 100644 index 00000000..2143e600 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/runtime-config/interface-builder.jsonnet @@ -0,0 +1,30 @@ +// Interface Builder Module +// Processes flow class interfaces with parameter substitution +// Handles both string interfaces and nested object interfaces + +local param_processor = import "parameter-processor.jsonnet"; + +{ + // Builds interfaces for a specific flow class and instance + // Processes the 'interfaces' section of flow classes + build_interfaces: function(flow_classes, class_name, flow_id, parameters) + local interface_spec = flow_classes[class_name].interfaces; + { + [interface.key]: + if std.isString(interface.value) then + // Simple string interface - apply all substitutions + local class_replaced = std.strReplace(interface.value, "{class}", class_name); + local id_replaced = std.strReplace(class_replaced, "{id}", flow_id); + param_processor.substitute_parameters(id_replaced, parameters) + else + // Complex object interface - process nested fields + { + [field.key]: + local class_replaced = std.strReplace(field.value, "{class}", class_name); + local id_replaced = std.strReplace(class_replaced, "{id}", flow_id); + param_processor.substitute_parameters(id_replaced, parameters) + for field in std.objectKeysValuesAll(interface.value) + } + for interface in std.objectKeysValuesAll(interface_spec) + }, +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/runtime-config/interface-descriptions.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/runtime-config/interface-descriptions.jsonnet new file mode 100644 index 00000000..f9dda8b0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/runtime-config/interface-descriptions.jsonnet @@ -0,0 +1,89 @@ +// Interface Descriptions Module +// Defines all external interfaces available in TrustGraph flows +// These are the 'endpoints' that external systems can interact with + +{ + // Document loading interfaces - for data ingestion + "document-load": { + "description": "Document loader", + "kind": "send", + "visible": true, + }, + "text-load": { + "description": "Text document loader", + "kind": "send", + "visible": true, + }, + + // Data storage interfaces - for processed data streams + "entity-contexts-load": { + "description": "Entity contexts loader", + "kind": "send", + }, + "triples-store": { + "description": "Triples loader", + "kind": "send", + }, + "graph-embeddings-store": { + "description": "Graph embeddings loader", + "kind": "send", + }, + "document-embeddings-store": { + "description": "Document embeddings loader", + "kind": "send", + }, + "objects-store": { + "description": "Object store", + "kind": "request-response", + }, + + // Query interfaces - for retrieving information + "graph-rag": { + "description": "GraphRAG service", + "kind": "request-response", + }, + "document-rag": { + "description": "ChunkRAG service", + "kind": "request-response", + }, + "triples": { + "description": "Triples query service", + "kind": "request-response", + }, + "graph-embeddings": { + "description": "Graph embeddings service", + "kind": "request-response", + }, + "document-embeddings": { + "description": "Document embeddings service", + "kind": "request-response", + }, + "objects": { + "description": "Object query service", + "kind": "request-response", + }, + + // Processing services - for text and data processing + "prompt": { + "description": "Prompt service", + "kind": "request-response", + }, + "agent": { + "description": "Agent service", + "kind": "request-response", + }, + "text-completion": { + "description": "Text completion service", + "kind": "request-response", + }, + + // Query translation services - for natural language queries + "nlp-query": { + "description": "NLP question to GraphQL service", + "kind": "request-response", + }, + "structured-query": { + "description": "Structured query service", + "kind": "request-response", + }, +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/runtime-config/parameter-processor.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/runtime-config/parameter-processor.jsonnet new file mode 100644 index 00000000..f317ae9f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/runtime-config/parameter-processor.jsonnet @@ -0,0 +1,38 @@ +// Parameter Processing Module +// Handles dynamic parameter replacement in configuration values +// Replaces {parameter_name} placeholders with actual parameter values + +{ + // Applies parameter substitutions to string values + // Only processes strings - leaves other types unchanged + substitute_parameters: function(value, parameters) + if std.isString(value) then + std.foldl( + function(acc, param) + // Only do string replacement if param.value is a string + if std.isString(param.value) then + std.strReplace(acc, "{" + param.key + "}", param.value) + else + acc, // Skip replacement for non-string parameter values + std.objectKeysValuesAll(parameters), + value + ) + else + value, + + // Applies parameter substitutions to all values in an object + // Recursively processes nested objects and arrays + substitute_parameters_in_object: function(obj, parameters) + if std.isObject(obj) then + { + [key]: $.substitute_parameters_in_object(obj[key], parameters) + for key in std.objectFields(obj) + } + else if std.isArray(obj) then + [ + $.substitute_parameters_in_object(item, parameters) + for item in obj + ] + else + $.substitute_parameters(obj, parameters), +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/runtime-config/tools.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/runtime-config/tools.jsonnet new file mode 100644 index 00000000..4947279e --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/runtime-config/tools.jsonnet @@ -0,0 +1,36 @@ +// Tools Configuration Module +// Defines all available tools that can be used by agents and flows +// Each tool specifies its interface, arguments, and behavior + +[ + // Knowledge query tool - queries the knowledge base + { + id: "knowledge-query", + name: "Knowledge query", + description: "This tool queries a knowledge base that holds information about domain-specific information. The question should be a natural language question.", + type: "knowledge-query", + collection: "default", + arguments: [ + { + name: "question", + type: "string", + description: "A simple natural language question.", + } + ] + }, + + // LLM completion tool - general purpose text completion + { + id: "llm-completion", + name: "LLM text completion", + type: "text-completion", + description: "This tool queries an LLM for non-domain-specific information. The question should be a natural language question.", + arguments: [ + { + name: "question", + type: "string", + description: "The question which should be asked of the LLM.", + } + ] + } +] \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/runtime-config/trustgraph-config.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/runtime-config/trustgraph-config.jsonnet new file mode 100644 index 00000000..d860ab3c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/runtime-config/trustgraph-config.jsonnet @@ -0,0 +1,90 @@ +// TrustGraph Main Configuration +// Clean, modular composition of TrustGraph configuration +// Uses specialized modules for different aspects of config building + +// Import dependencies +local images = import "../values/images.jsonnet"; +local url = import "../values/url.jsonnet"; +local prompts = import "../prompts/mixtral.jsonnet"; +local default_prompts = import "../prompts/default-prompts.jsonnet"; +local token_costs = import "../values/token-costs.jsonnet"; +local flow_blueprints = import "../flows/flow-blueprints.jsonnet"; +local config_composer = import "config-composer.jsonnet"; +local interface_descriptions = import "interface-descriptions.jsonnet"; +local tools = import "tools.jsonnet"; +local temperature_params = import "../parameters/temperature-param-types.jsonnet"; +local chunking_params = import "../parameters/chunking-param-types.jsonnet"; + +// Main configuration object +local configuration = { + + // Prompt templates + prompts:: default_prompts, + + // Tool definitions + tools:: tools, + + // MCP configuration + mcp:: {}, + + // Flow classes reference + "flow-blueprints":: flow_blueprints, + + // LLM model parameters + "llm-models" +:: {}, + + // Embeddings model parameters + "embeddings-models" +:: {}, + + collections +:: { + "trustgraph:default": { + "user": "default-user", + "collection": "default", + "name": "Default Collection", + "description": "Default collection", + "tags": ["default"], + }, + }, + + // Default model and flow parameters + flow_init_parameters:: { + "llm-model": $["llm-models"].default, + "llm-rag-model": $["llm-models"].default, + "llm-temperature": "%0.3f" % $["parameter-types"]["llm-temperature"].default, + "llm-rag-temperature": "%0.3f" % $["parameter-types"]["llm-temperature"].default, + "chunk-size": std.toString($["parameter-types"]["chunk-size"].default), + "chunk-overlap": std.toString($["parameter-types"]["chunk-overlap"].default), + "embeddings-model": $["embeddings-models"].default, + }, + + // Interface descriptions for external endpoints + "interface-descriptions":: interface_descriptions, + + // Parameter type definitions + "parameter-types":: { + "llm-model": $["llm-models"], + "embeddings-model": $["embeddings-models"], + } + chunking_params + temperature_params, + + // Token costs + "token-costs":: token_costs, + + // Build the complete configuration using the composer + configuration:: config_composer.build({ + flow_blueprints: $["flow-blueprints"], + default_flow_blueprint: "everything", + default_flow_id: "default", + flow_init_parameters: $["flow_init_parameters"], + prompts: $["prompts"], + tools: $["tools"], + mcp: $["mcp"], + interface_descriptions: $["interface-descriptions"], + parameter_types: $["parameter-types"], + token_costs: $["token-costs"], + collection: $["collections"], + }), + +} + default_prompts; + +// Export the final configuration +configuration diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/triple-store/cassandra.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/triple-store/cassandra.jsonnet new file mode 100644 index 00000000..05f4fd27 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/triple-store/cassandra.jsonnet @@ -0,0 +1,77 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local cassandra = import "backends/cassandra.jsonnet"; + +cassandra + { + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-cassandra", + "-p", + url.pulsar, + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-cassandra", + "-p", + url.pulsar, + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "512M") + .with_reservations("0.1", "512M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/triple-store/falkordb.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/triple-store/falkordb.jsonnet new file mode 100644 index 00000000..b4bace2d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/triple-store/falkordb.jsonnet @@ -0,0 +1,79 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local falkordb = import "backends/falkordb.jsonnet"; + +falkordb + { + + "falkordb-url":: "falkor://falkordb:6379", + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-falkordb", + "-p", + url.pulsar, + "-g", + $["falkordb-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-falkordb", + "-p", + url.pulsar, + "-g", + $["falkordb-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/triple-store/memgraph.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/triple-store/memgraph.jsonnet new file mode 100644 index 00000000..72597d72 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/triple-store/memgraph.jsonnet @@ -0,0 +1,84 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local memgraph = import "backends/memgraph.jsonnet"; + +memgraph + { + + "memgraph-url":: "bolt://memgraph:7687", + "memgraph-database":: "memgraph", + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-memgraph", + "-p", + url.pulsar, + "-g", + $["memgraph-url"], + "--database", + $["memgraph-database"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-memgraph", + "-p", + url.pulsar, + "-g", + $["memgraph-url"], + "--database", + $["memgraph-database"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/triple-store/neo4j.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/triple-store/neo4j.jsonnet new file mode 100644 index 00000000..08904715 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/triple-store/neo4j.jsonnet @@ -0,0 +1,79 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local neo4j = import "backends/neo4j.jsonnet"; + +neo4j + { + + "neo4j-url":: "bolt://neo4j:7687", + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-neo4j", + "-p", + url.pulsar, + "-g", + $["neo4j-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-neo4j", + "-p", + url.pulsar, + "-g", + $["neo4j-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/ui/workbench-ui.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/ui/workbench-ui.jsonnet new file mode 100644 index 00000000..f2048e47 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/ui/workbench-ui.jsonnet @@ -0,0 +1,32 @@ +local images = import "values/images.jsonnet"; + +{ + + "workbench-ui" +: { + + create:: function(engine) + + local container = + engine.container("workbench-ui") + .with_image(images["workbench-ui"]) + .with_limits("0.1", "256M") + .with_reservations("0.1", "256M") + .with_port(8888, 8888, "ui"); + + local containerSet = engine.containers( + "workbench-ui", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8888, 8888, "ui"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/values/images.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/values/images.jsonnet new file mode 100644 index 00000000..8b8b75a0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/values/images.jsonnet @@ -0,0 +1,36 @@ +local version = import "version.jsonnet"; +{ + cassandra: "docker.io/cassandra:4.1.10", +// Not working +// ceph: "quay.io/ceph/daemon:latest-reef", + neo4j: "docker.io/neo4j:2025.08.0-community-bullseye", + pulsar: "docker.io/apachepulsar/pulsar:4.1.0", + pulsar_manager: "docker.io/apachepulsar/pulsar-manager:v0.4.0", + etcd: "quay.io/coreos/etcd:v3.6.4", + minio: "docker.io/minio/minio:RELEASE.2025-09-07T16-13-09Z", + garage: "docker.io/dxflrs/garage:v2.1.0", + milvus: "docker.io/milvusdb/milvus:v2.5.17", + prometheus: "docker.io/prom/prometheus:v3.8.0", + grafana: "docker.io/grafana/grafana:12.3.0", + loki: "docker.io/grafana/loki:3.6.2", + trustgraph_base: "docker.io/trustgraph/trustgraph-base:" + version, + trustgraph_flow: "docker.io/trustgraph/trustgraph-flow:" + version, + trustgraph_ocr: "docker.io/trustgraph/trustgraph-ocr:" + version, + trustgraph_bedrock: "docker.io/trustgraph/trustgraph-bedrock:" + version, + trustgraph_vertexai: "docker.io/trustgraph/trustgraph-vertexai:" + version, + trustgraph_hf: "docker.io/trustgraph/trustgraph-hf:" + version, + trustgraph_mcp: "docker.io/trustgraph/trustgraph-mcp:" + version, + qdrant: "docker.io/qdrant/qdrant:v1.15.4", + memgraph_mage: "docker.io/memgraph/memgraph-mage:3.5", + memgraph_lab: "docker.io/memgraph/lab:3.5.0", + falkordb: "docker.io/falkordb/falkordb:v4.12.5", + "workbench-ui": "docker.io/trustgraph/workbench-ui:1.5.5", + "ddg-mcp-server": "docker.io/trustgraph/ddg-mcp-server:0.1.0", + "tgi-service-intel-xpu": "ghcr.io/huggingface/text-generation-inference:3.3.1-intel-xpu", + "tgi-service-cpu": "ghcr.io/huggingface/text-generation-inference:3.3.1-intel-cpu", + "tgi-service-gaudi": "ghcr.io/huggingface/text-generation-inference:sha-f140440-gaudi", + "vllm-service-intel-xpu": "docker.io/intel/vllm:0.8.0-xpu", + "vllm-service-gaudi": "docker.io/trustgraph/vllm-hpu:027f5645", + "vllm-service-nvidia": "docker.io/vllm/vllm-openai:latest", + "vllm-service-intel-battlemage": "docker.io/intelanalytics/ipex-llm-serving-xpu:0.2.0-b6", +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/values/token-costs.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/values/token-costs.jsonnet new file mode 100644 index 00000000..84c70373 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/values/token-costs.jsonnet @@ -0,0 +1,102 @@ +{ + "mistral.mistral-large-2407-v1:0": { + "model_name": "mistral.mistral-large-2407-v1:0", + "input_price": 0.000004, + "output_price": 0.000012 + }, + "meta.llama3-1-405b-instruct-v1:0": { + "model_name": "meta.llama3-1-405b-instruct-v1:0", + "input_price": 0.00000532, + "output_price": 0.000016 + }, + "mistral.mixtral-8x7b-instruct-v0:1": { + "model_name": "mistral.mixtral-8x7b-instruct-v0:1", + "input_price": 0.00000045, + "output_price": 0.0000007 + }, + "meta.llama3-1-70b-instruct-v1:0": { + "model_name": "meta.llama3-1-70b-instruct-v1:0", + "input_price": 0.00000099, + "output_price": 0.00000099 + }, + "meta.llama3-1-8b-instruct-v1:0": { + "model_name": "meta.llama3-1-8b-instruct-v1:0", + "input_price": 0.00000022, + "output_price": 0.00000022 + }, + "anthropic.claude-3-haiku-20240307-v1:0": { + "model_name": "anthropic.claude-3-haiku-20240307-v1:0", + "input_price": 0.00000025, + "output_price": 0.00000125 + }, + "anthropic.claude-3-5-sonnet-20240620-v1:0": { + "model_name": "anthropic.claude-3-5-sonnet-20240620-v1:0", + "input_price": 0.000003, + "output_price": 0.000015 + }, + "cohere.command-r-plus-v1:0": { + "model_name": "cohere.command-r-plus-v1:0", + "input_price": 0.0000030, + "output_price": 0.0000150 + }, + "ollama": { + "model_name": "ollama", + "input_price": 0, + "output_price": 0 + }, + "claude-3-haiku-20240307": { + "model_name": "claude-3-haiku-20240307", + "input_price": 0.00000025, + "output_price": 0.00000125 + }, + "claude-3-5-sonnet-20240620": { + "model_name": "claude-3-5-sonnet-20240620", + "input_price": 0.000003, + "output_price": 0.000015 + }, + "claude-3-opus-20240229": { + "model_name": "claude-3-opus-20240229", + "input_price": 0.000015, + "output_price": 0.000075 + }, + "claude-3-sonnet-20240229": { + "model_name": "claude-3-sonnet-20240229", + "input_price": 0.000003, + "output_price": 0.000015 + }, + "command-r-08-202": { + "model_name": "command-r-08-202", + "input_price": 0.0000025, + "output_price": 0.000010 + }, + "c4ai-aya-23-8b": { + "model_name": "c4ai-aya-23-8b", + "input_price": 0, + "output_price": 0 + }, + "llama.cpp": { + "model_name": "llama.cpp", + "input_price": 0, + "output_price": 0 + }, + "gpt-4o": { + "model_name": "gpt-4o", + "input_price": 0.000005, + "output_price": 0.000015 + }, + "gpt-4o-2024-08-06": { + "model_name": "gpt-4o-2024-08-06", + "input_price": 0.0000025, + "output_price": 0.000010 + }, + "gpt-4o-2024-05-13": { + "model_name": "gpt-4o-2024-05-13", + "input_price": 0.000005, + "output_price": 0.000015 + }, + "gpt-4o-mini": { + "model_name": "gpt-4o-mini", + "input_price": 0.00000015, + "output_price": 0.0000006 + } +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/values/url.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/values/url.jsonnet new file mode 100644 index 00000000..c3d4ad97 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/values/url.jsonnet @@ -0,0 +1,7 @@ +{ + pulsar: "pulsar://pulsar:6650", + pulsar_admin: "http://pulsar:8080", + milvus: "http://milvus:19530", + qdrant: "http://qdrant:6333", + object_store: "garage:3900", +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/vector-store/milvus.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/vector-store/milvus.jsonnet new file mode 100644 index 00000000..213027d1 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/vector-store/milvus.jsonnet @@ -0,0 +1,146 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local milvus = import "backends/milvus.jsonnet"; + +milvus + { + + "store-graph-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("store-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-write-milvus", + "-p", + url.pulsar, + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-graph-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("query-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-query-milvus", + "-p", + url.pulsar, + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "store-doc-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("store-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-write-milvus", + "-p", + url.pulsar, + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-doc-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("query-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-query-milvus", + "-p", + url.pulsar, + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/vector-store/pinecone.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/vector-store/pinecone.jsonnet new file mode 100644 index 00000000..3803bdcb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/vector-store/pinecone.jsonnet @@ -0,0 +1,160 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; + +{ + + "pinecone-cloud":: "aws", + "pinecone-region":: "us-east-1", + + "store-graph-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("store-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-write-pinecone", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "query-graph-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("query-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-query-pinecone", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "store-doc-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("store-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-write-pinecone", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "query-doc-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("query-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-query-pinecone", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/vector-store/qdrant.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/vector-store/qdrant.jsonnet new file mode 100644 index 00000000..7409adb9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/vector-store/qdrant.jsonnet @@ -0,0 +1,169 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local qdrant = import "backends/qdrant.jsonnet"; + +qdrant + { + + "store-graph-embeddings" +: { + + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("store-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-write-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", memoryLimit) + .with_reservations("0.1", memoryReservation); + + local containerSet = engine.containers( + "store-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-graph-embeddings" +: { + + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("query-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-query-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", memoryLimit) + .with_reservations("0.1", memoryReservation); + + local containerSet = engine.containers( + "query-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "store-doc-embeddings" +: { + + "memory-limit":: "256M", + "memory-reservation":: "256M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("store-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-write-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", memoryLimit) + .with_reservations("0.1", memoryReservation); + + local containerSet = engine.containers( + "store-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-doc-embeddings" +: { + + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("query-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-query-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", memoryLimit) + .with_reservations("0.1", memoryReservation); + + local containerSet = engine.containers( + "query-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/zip-readme.md b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/zip-readme.md new file mode 100644 index 00000000..0b117792 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/1.9/zip-readme.md @@ -0,0 +1,28 @@ + +Note! this is a subset of possible configurations, to generate your own +launch config use the config util... + +- Production: https://config-ui.demo.trustgraph.ai +- Early release: https://dev.config-ui.demo.trustgraph.ai + +The config util auto-generates deployment instructions for your +configuration, so that's the recommended way to deploy. + +---------------------------------------------------------------------------- + +These are launch configurations for TrustGraph. See https://trustgraph.ai for +the quickstart using docker compose. + +Hint for Linux: There are files here which get mounted as volumes inside +Docker Compose containers. This may trigger SELinux rules on your system, to +permit access insider the containers, use a command like this... + +chcon -Rt svirt_sandbox_file_t grafana/ prometheus/ + +The file vertexai/private.json is a placeholder for real GCP credentials if +you are using the VertexAI LLM. If you're using that in Docker Compose, +replace with your real credentials, and don't forget to permit access if you +are using Linux: + +chcon -Rt svirt_sandbox_file_t vertexai/ + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/README.md b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/README.md new file mode 100644 index 00000000..23039e9a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/README.md @@ -0,0 +1,125 @@ + +# TrustGraph template generation + +There are two utilities here: + +- `generate`: Generates a single Docker Compose launch configuration + based on configuration you provide. +- `generate-all`: Generates the release bundle for releases. You won't + need to use this unless you are managing releases. + +## `generate-all` + +Previously, this generates a full set of all vector DB / triple store / LLM +combinations, and put them in a single ZIP file. But this got out of +hand, so at the time of writing, this generates a single configuraton +using Qdrant vector DB, Ollama LLM support and Cassandra for a triple store. + +The combinations are contained withing the code, it takes two arguments: +- output ZIP file (is over-written) +- TrustGraph version number + +``` +templates/generate-all output.zip 0.18.11 +``` + +## `generate` + +This utility takes a configuration file describing the components to bundle, +and outputs a Docker Compose YAML file. + +### Input configuration + +The input configuration is a JSON file, an array of components to pull into +the configuration. For each component, there is a name and a (possibly empty) +object describing addtional parameters for that component. + +Example: + +``` +[ + { + "name": "cassandra", + "parameters": {} + }, + { + "name": "pulsar", + "parameters": {} + }, + { + "name": "qdrant", + "parameters": {} + }, + { + "name": "embeddings-hf", + "parameters": {} + }, + { + "name": "graph-rag", + "parameters": {} + }, + { + "name": "grafana", + "parameters": {} + }, + { + "name": "trustgraph", + "parameters": {} + }, + { + "name": "googleaistudio", + "parameters": { + "googleaistudio-temperature": 0.3, + "googleaistudio-max-output-tokens": 2048, + "googleaistudio-model": "gemini-1.5-pro-002" + } + }, + { + "name": "prompt-template", + "parameters": {} + }, + { + "name": "override-recursive-chunker", + "parameters": { + "chunk-size": 1000, + "chunk-overlap": 50 + } + }, + { + "name": "workbench-ui", + "parameters": {} + }, + { + "name": "agent-manager-react", + "parameters": {} + } +] +``` + +If you want to make your own configuration you could try changing the +configuration above: +- Components which are essential: pulsar, trustgraph, graph-rag, grafana, + agent-manager-react +- You need a triple store, one of: cassandra, memgraph, falkordb, neo4j +- You need a vector store, one of: qdrant, pinecone +- You need an LLM, one of: azure, azure-openai, bedrock, claude, cohere, + llamafile, ollama, openai, vertexai. +- You need an embeddings implementation, one of: embeddings-hf, + embeddings-ollama +- Optionally add the Workbench tool: workbench-ui + +Components have over-ridable parameters, look in the component definition +in `templates/components/` to see what you can override. + +### Invocation + +Two parameters: +- The output ZIP file +- The version number + +The configuration file described above is provided on standard input + +``` +templates/generate out.zip 0.18.9 < config.json +``` + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/cassandra.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/cassandra.jsonnet new file mode 100644 index 00000000..60f262d3 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/cassandra.jsonnet @@ -0,0 +1,50 @@ +local images = import "values/images.jsonnet"; + +{ + + "cassandra" +: { + + // Memory settings (can be overridden by memory-profile) + "memory-limit":: "1000M", + "memory-reservation":: "1000M", + "heap":: "300M", + + create:: function(engine) + + // Capture memory settings into locals + local memLimit = self["memory-limit"]; + local memReserv = self["memory-reservation"]; + local heap = self["heap"]; + + local vol = engine.volume("cassandra").with_size("20G"); + + local container = + engine.container("cassandra") + .with_image(images.cassandra) + .with_environment({ + JVM_OPTS: "-Xms%s -Xmx%s -Dcassandra.skip_wait_for_gossip_to_settle=0" % [ + heap, heap, + ], + }) + .with_limits("1.0", memLimit) + .with_reservations("0.5", memReserv) + .with_port(9042, 9042, "cassandra") + .with_volume_mount(vol, "/var/lib/cassandra"); + + local containerSet = engine.containers( + "cassandra", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9042, 9042, "api"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/ceph.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/ceph.jsonnet new file mode 100644 index 00000000..42406a95 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/ceph.jsonnet @@ -0,0 +1,446 @@ + +// +// This majorly does not work +// + +// This configuration fails primarily because it tries to treat Ceph +// like a stateless web app. You are currently pointing mon host to a +// generic service name (ceph-mon), but you aren't telling the Monitor +// process to assume that service identity. + +// To make this work with the "Service Name" approach across any +// engine, you need to fix the binding logic and the messenger protocol. + +// 1. The ceph.conf Fix + +// Need to enable Messenger v2 (modern) and tell the cluster to use +// the service names for its initial quorum. + +//Change this section: +// Ini, TOML + +// [global] +// fsid = %s +// mon initial members = mon0 +// mon host = ceph-mon:6789 +// ... + +// To this: +// Ini, TOML + +// [global] +// fsid = %s +// # Use the actual service names as members +// mon initial members = ceph-mon +// # Explicitly use the Service name (VIP) +// mon host = ceph-mon +// # Force modern protocol +// ms_bind_msgr2 = true +// ms_bind_msgr1 = true + +// 2. The MON Environment & Command Fix + +// This is the most critical part. Your current config sets MON_IP: +// "0.0.0.0". This causes the MON to bind to the Pod IP, which breaks +// when the Pod restarts. You must force it to bind to the Service IP. + +// Update mon_env: +// Code snippet + +// local mon_env = cluster_env + { +// CEPH_DAEMON: "MON", +// MON_NAME: "mon0", +// # Remove MON_IP: "0.0.0.0" +// # Add these: +// MON_ADDR: "ceph-mon", // This says to resolve the service name +// }; + +// Update mon_container command: You are currently wiping the MON data +// on every start (rm -rf /var/lib/ceph/mon/*). Stop doing that. If you +// wipe the data, you lose the cluster state and the OSDs will refuse to +// talk to the "new" MON. + +// Code snippet + +// .with_command([ +// "bash", "-c", +// # 1. Resolve the Service IP at runtime +// "export MON_IP=$(getent hosts ceph-mon | awk '{ print $1 }'); " + +// inject_mon_config + +// # 2. Start the daemon telling it its PUBLIC address is the Service VIP +// "exec /opt/ceph-container/bin/entrypoint.sh" +// ]) + +// 3. Why your current config "Majorly does not work" + +// The "Wipe" Logic: By running rm -rf /var/lib/ceph/mon/* in the +// MON container, you are creating a "New Cluster" every time the +// container starts. Since the OSDs store the fsid and cluster +// secrets, they will reject the "new" MON. + +// DNS Race Condition: Your OSD/MGR/RGW containers wait for +// ceph-mon DNS, which is good. However, if ceph-mon resolves to a +// Round Robin IP (multiple pods) rather than a stable ClusterIP, the +// connection will be flaky. + +// Messenger Protocol: Without ms_bind_msgr2, Ceph defaults to the +// old v1 protocol which is much more sensitive to NAT/Container IP +// mismatches. + +// SUMMARY + +// Component: ceph.conf +// Change: Add ms_bind_msgr2 = true +// Why: Supports modern container networking better. + +// Component: MON Start +// Change: Remove rm -rf +// Why: Ceph MONs must keep their database to maintain the cluster. + +// Component: MON Address +// Change: Use getent hosts ceph-mon +// Why: Forces the MON to advertise the Service VIP instead of its own Pod IP. + +// Component: MON Keyring +// Change: Ensure /etc/ceph/ceph.mon.keyring exists +// Why: MONs need their specific key to start. + +local images = import "values/images.jsonnet"; + +{ + with:: function(key, value) + self + { + ["ceph-" + key]:: value, + }, + + // Ceph credentials and cluster settings + "ceph-access-key":: "object-user", + "ceph-secret-key":: "object-password", + "ceph-cluster-id":: "ceph", + "ceph-fsid":: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", + + // Pool redundancy settings + // size: 2 = two replicas for fault tolerance + // min_size: 1 = allow degraded I/O if one OSD is down (prevents cluster freeze) + "ceph-pool-size":: "2", + "ceph-pool-min-size":: "1", + + ceph +: { + create:: function(engine) + // Pre-Shared Cryptographic Material - Config-as-Code Approach + // These keys are generated once and distributed to all daemons + // This ensures cryptographic consistency across the shared-nothing architecture + local admin_key = "AQBpxSBlAAAAABAAU99V6D8vS7Uu9y1S8W0iBg=="; + local mon_key = "AQBpxSBlAAAAABAAn7pL/pG9oT+X6vO7V1S6bg=="; + + // Ceph configuration file - rendered from Jsonnet variables + local ceph_conf = ||| + [global] + fsid = %s + mon initial members = mon0 + mon host = ceph-mon:6789 + public network = 0.0.0.0/0 + cluster network = 0.0.0.0/0 + osd pool default size = %s + osd pool default min size = %s + osd crush chooseleaf type = 0 + auth cluster required = cephx + auth service required = cephx + auth client required = cephx + ||| % [$["ceph-fsid"], $["ceph-pool-size"], $["ceph-pool-min-size"]]; + + // Admin keyring - distributed to all daemons + local admin_keyring = ||| + [client.admin] + key = %s + caps mds = "allow *" + caps mgr = "allow *" + caps mon = "allow *" + caps osd = "allow *" + ||| % [admin_key]; + + // Monitor keyring - used by MON for cluster operations + local mon_keyring = ||| + [mon.] + key = %s + caps mon = "allow *" + ||| % [mon_key]; + + // Config injection command - writes files before entrypoint + local inject_config = "printf '%s' > /etc/ceph/ceph.conf; printf '%s' > /etc/ceph/ceph.client.admin.keyring; " % [ceph_conf, admin_keyring]; + local inject_mon_config = inject_config + ("printf '%s' > /etc/ceph/ceph.mon.keyring; " % [mon_keyring]); + + // Data volumes - sized appropriately for production workloads + local vol_mon = engine.volume("ceph-mon").with_size("20G"); + local vol_mgr = engine.volume("ceph-mgr").with_size("20G"); + local vol_osd = engine.volume("ceph-osd").with_size("100G"); + local vol_rgw = engine.volume("ceph-rgw").with_size("20G"); + + // Isolated config volumes per daemon (ReadWriteOnce compatible) + // Each daemon gets its own non-shared config volume to support + // multi-node scheduling in K8s and other orchestrators + local vol_mon_config = engine.volume("ceph-mon-config").with_size("500M"); + local vol_mgr_config = engine.volume("ceph-mgr-config").with_size("500M"); + local vol_osd_config = engine.volume("ceph-osd-config").with_size("500M"); + local vol_rgw_config = engine.volume("ceph-rgw-config").with_size("500M"); + local vol_init_config = engine.volume("ceph-init-config").with_size("500M"); + + // Simplified cluster environment - Config-as-Code model + // No fetch logic needed - config is injected before entrypoint runs + local cluster_env = { + CLUSTER: $["ceph-cluster-id"], + FSID: $["ceph-fsid"], + KV_TYPE: "none", // No external coordination + }; + + // MON-specific environment + // Config-as-Code: MON uses injected config files, not fetch logic + // + // CRITICAL: MON_DATA_AVAIL="0" forces fresh cluster bootstrap + // The ceph/daemon entrypoint script (variables_stack.sh) uses this as a gate: + // - MON_DATA_AVAIL="0" -> run mkfs, create new cluster with our FSID + // - MON_DATA_AVAIL="1" -> attempt to join existing cluster (infinite probe loop) + // + // Network configuration for monmap generation + local mon_env = cluster_env + { + CEPH_DAEMON: "MON", + MON_NAME: "mon0", + MON_PORT: "6789", + MON_DATA_AVAIL: "0", + MON_IP: "0.0.0.0", + NETWORK_AUTO_DETECT: "4", + CEPH_PUBLIC_NETWORK: "0.0.0.0/0", + }; + + // Simplified daemon environments - Config-as-Code model + // All daemons receive config via injection, not fetch from MON + // This eliminates "static mode" errors and networking complexity + + // MGR-specific environment + local mgr_env = cluster_env + { + CEPH_DAEMON: "MGR", + MGR_NAME: "mgr0", + }; + + // OSD-specific environment + local osd_env = cluster_env + { + CEPH_DAEMON: "OSD", + OSD_TYPE: "directory", + }; + + // RGW-specific environment + local rgw_env = cluster_env + { + CEPH_DAEMON: "RGW", + RGW_NAME: "rgw0", + RGW_FRONTEND_PORT: "7480", + }; + + // MON (Monitor) container - cluster state and quorum + // Config-as-Code: Injects pre-shared keys before entrypoint + // CRITICAL: Wipes /var/lib/ceph/mon/* on every start to force fresh bootstrap + // This ensures MON always uses our FSID and doesn't inherit stale cluster state + local mon_container = + engine.container("ceph-mon") + .with_image(images.ceph) + .with_environment(mon_env) + .with_command([ + "bash", "-c", + "rm -rf /var/lib/ceph/mon/*; " + + inject_mon_config + + "exec /opt/ceph-container/bin/entrypoint.sh" + ]) + .with_limits("1.0", "1536M") + .with_reservations("0.5", "1024M") + .with_port(6789, 6789, "mon") + .with_port(3300, 3300, "mon-msgr2") + .with_volume_mount(vol_mon, "/var/lib/ceph/mon") + .with_volume_mount(vol_mon_config, "/etc/ceph"); + + // MGR (Manager) container - cluster management and dashboard + // Config-as-Code: Uses injected config files with pre-shared keys + // DNS wait ensures MON is available before MGR connects + local mgr_container = + engine.container("ceph-mgr") + .with_image(images.ceph) + .with_environment(mgr_env) + .with_command([ + "bash", "-c", + "until getent hosts ceph-mon; do echo 'Waiting for MON DNS...'; sleep 2; done; " + + inject_config + + "exec /opt/ceph-container/bin/entrypoint.sh" + ]) + .with_limits("1.0", "1536M") + .with_reservations("0.5", "1024M") + .with_port(7000, 7000, "mgr") + .with_port(8443, 8443, "dashboard") + .with_port(9283, 9283, "prometheus") + .with_volume_mount(vol_mgr, "/var/lib/ceph/mgr") + .with_volume_mount(vol_mgr_config, "/etc/ceph"); + + // OSD (Object Storage Daemon) - actual data storage + // Config-as-Code: Uses injected config files with pre-shared keys + // Increased resources to prevent OOM during recovery operations + // DNS wait ensures MON is available before OSD connects + local osd_container = + engine.container("ceph-osd") + .with_image(images.ceph) + .with_environment(osd_env) + .with_command([ + "bash", "-c", + "until getent hosts ceph-mon; do echo 'Waiting for MON DNS...'; sleep 2; done; " + + inject_config + + "exec /opt/ceph-container/bin/entrypoint.sh" + ]) + .with_limits("2.0", "4096M") + .with_reservations("1.0", "2048M") + .with_port(6800, 6800, "osd") + .with_volume_mount(vol_osd, "/var/lib/ceph/osd") + .with_volume_mount(vol_osd_config, "/etc/ceph"); + + // RGW (RADOS Gateway) - S3 API endpoint + // Config-as-Code: Uses injected config files with pre-shared keys + // DNS wait ensures MON is available before RGW connects + local rgw_container = + engine.container("ceph-rgw") + .with_image(images.ceph) + .with_environment(rgw_env) + .with_command([ + "bash", "-c", + "until getent hosts ceph-mon; do echo 'Waiting for MON DNS...'; sleep 2; done; " + + inject_config + + "exec /opt/ceph-container/bin/entrypoint.sh" + ]) + .with_limits("1.0", "1536M") + .with_reservations("0.5", "1024M") + .with_port(7480, 7480, "s3") + .with_volume_mount(vol_rgw, "/var/lib/ceph/radosgw") + .with_volume_mount(vol_rgw_config, "/etc/ceph"); + + // Init container - one-time S3 user provisioning + // IMPORTANT: This container exits with code 0 after completion + // Orchestrator must NOT restart it (use K8s Job or Compose restart: "no") + // Config-as-Code: Uses injected config to run radosgw-admin commands + local init_container = + engine.container("ceph-init") + .with_image(images.ceph) + .with_environment({ + CLUSTER: $["ceph-cluster-id"], + FSID: $["ceph-fsid"], + KV_TYPE: "none", + RGW_ACCESS_KEY: $["ceph-access-key"], + RGW_SECRET_KEY: $["ceph-secret-key"], + }) + .with_limits("0.5", "512M") + .with_reservations("0.25", "256M") + .with_volume_mount(vol_init_config, "/etc/ceph") + .with_command([ + "bash", "-c", + inject_config + ||| + set -e + + # Wait for cluster health + echo "Waiting for Ceph cluster to be healthy..." + MAX_ATTEMPTS=60 + ATTEMPT=0 + until ceph --cluster ${CLUSTER} health 2>/dev/null | grep -q "HEALTH_OK\|HEALTH_WARN"; do + ATTEMPT=$((ATTEMPT+1)) + if [ $ATTEMPT -ge $MAX_ATTEMPTS ]; then + echo "ERROR: Cluster failed to become healthy after ${MAX_ATTEMPTS} attempts" + exit 1 + fi + echo "Attempt ${ATTEMPT}/${MAX_ATTEMPTS}: Cluster not ready, retrying in 5s..." + sleep 5 + done + echo "Cluster is healthy." + + # Wait for RGW availability + echo "Waiting for RGW to be ready..." + ATTEMPT=0 + until curl -sf http://ceph-rgw:7480 >/dev/null 2>&1; do + ATTEMPT=$((ATTEMPT+1)) + if [ $ATTEMPT -ge $MAX_ATTEMPTS ]; then + echo "ERROR: RGW failed to become ready after ${MAX_ATTEMPTS} attempts" + exit 1 + fi + echo "Attempt ${ATTEMPT}/${MAX_ATTEMPTS}: RGW not ready, retrying in 5s..." + sleep 5 + done + echo "RGW is ready." + + # Idempotent S3 user creation + echo "Provisioning S3 user: ${RGW_ACCESS_KEY}" + if radosgw-admin --cluster ${CLUSTER} user info --uid="${RGW_ACCESS_KEY}" >/dev/null 2>&1; then + echo "User ${RGW_ACCESS_KEY} already exists, skipping creation." + else + echo "Creating new S3 user: ${RGW_ACCESS_KEY}" + radosgw-admin --cluster ${CLUSTER} user create \ + --uid="${RGW_ACCESS_KEY}" \ + --display-name="Object Storage User" \ + --access-key="${RGW_ACCESS_KEY}" \ + --secret-key="${RGW_SECRET_KEY}" + echo "S3 user created successfully." + fi + + echo "Initialization complete. Exiting." + exit 0 + |||, + ]); + + // Container sets - each daemon gets its own for K8s node distribution + local mon_containerSet = engine.containers("ceph-mon", [mon_container]); + local mgr_containerSet = engine.containers("ceph-mgr", [mgr_container]); + local osd_containerSet = engine.containers("ceph-osd", [osd_container]); + local rgw_containerSet = engine.containers("ceph-rgw", [rgw_container]); + local init_containerSet = engine.containers("ceph-init", [init_container]); + + // Services - expose daemon ports for inter-daemon communication + local mon_service = + engine.service(mon_containerSet) + .with_port(6789, 6789, "mon") + .with_port(3300, 3300, "mon-msgr2"); + + local mgr_service = + engine.service(mgr_containerSet) + .with_port(7000, 7000, "mgr") + .with_port(8443, 8443, "dashboard") + .with_port(9283, 9283, "prometheus"); + + local osd_service = + engine.service(osd_containerSet) + .with_port(6800, 6800, "osd"); + + local rgw_service = + engine.service(rgw_containerSet) + .with_port(7480, 7480, "s3"); + + engine.resources([ + + // Data volumes + vol_mon, + vol_mgr, + vol_osd, + vol_rgw, + + // Config volumes (isolated, no sharing) + vol_mon_config, + vol_mgr_config, + vol_osd_config, + vol_rgw_config, + vol_init_config, + + // Container sets + mon_containerSet, + mgr_containerSet, + osd_containerSet, + rgw_containerSet, + init_containerSet, + + // Services + mon_service, + mgr_service, + osd_service, + rgw_service, + + ]) + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/falkordb.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/falkordb.jsonnet new file mode 100644 index 00000000..1d4176d8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/falkordb.jsonnet @@ -0,0 +1,38 @@ +local images = import "values/images.jsonnet"; + +{ + + "falkordb" +: { + + create:: function(engine) + + local vol = engine.volume("falkordb").with_size("20G"); + + local container = + engine.container("falkordb") + .with_image(images.falkordb) + .with_limits("1.0", "768M") + .with_reservations("0.5", "768M") + .with_port(6379, 6379, "api") + .with_port(3010, 3000, "ui") + .with_volume_mount(vol, "/data"); + + local containerSet = engine.containers( + "falkordb", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(6379, 6379, "api") + .with_port(3010, 3010, "ui"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/garage.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/garage.jsonnet new file mode 100644 index 00000000..9d339bfb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/garage.jsonnet @@ -0,0 +1,249 @@ +local images = import "values/images.jsonnet"; + +{ + + garage +: { + + // Garage S3 credentials - these are the actual access key ID and secret key + // Access Key ID must be in format: GK + 24 hex characters (12 bytes) + // Secret Key must be 64 hex characters (32 bytes) + // For production, generate secure random values and override these defaults + "access-key":: "GK000000000000000000000001", + "secret-key":: "b171f00be9be4c32c734f4c05fe64c527a8ab5eb823b376cfa8c2531f70fc427", + "rpc-secret":: "bbba746a9e289bad64a9e7a36a4299dac8d6e0b8cc2a6c2937fe756df4492008", + // For a production system, override this value + "admin-token":: "batts-rockhearted-unpartially", + region:: "garage", + "replication-factor":: "1", // Set to 1 for single-node, 3 for production + + // Storage volume sizes + "meta-size":: "2G", // Metadata volume size + "data-size":: "5G", // Data volume size (also used for cluster layout capacity) + + create:: function(engine) + + local accessKey = self["access-key"]; + local secretKey = self["secret-key"]; + local rpcSecret = self["rpc-secret"]; + local adminToken = self["admin-token"]; + local region = self.region; + local replicationFactor = self["replication-factor"]; + local metaSize = self["meta-size"]; + local dataSize = self["data-size"]; + + // Garage daemon configuration file - TOML format + local garage_conf = ||| + metadata_dir = "/var/lib/garage/meta" + data_dir = "/var/lib/garage/data" + + db_engine = "lmdb" + + replication_factor = %s + + compression_level = 1 + + rpc_bind_addr = "[::]:3901" + rpc_public_addr = "[::]:3901" + rpc_secret = "%s" + + [s3_api] + s3_region = "%s" + api_bind_addr = "[::]:3900" + root_domain = ".s3.garage.local" + + [s3_web] + bind_addr = "[::]:3902" + root_domain = ".web.garage.local" + index = "index.html" + + [k2v_api] + api_bind_addr = "[::]:3904" + + [admin] + api_bind_addr = "[::]:3903" + admin_token = "%s" + ||| % [replicationFactor, rpcSecret, region, adminToken]; + + // Config volume - contains the rendered garage.toml + local cfgVol = engine.configVolume( + "garage-cfg", "garage", + { + "garage.toml": garage_conf, + } + ); + + // Volumes - Garage stores metadata and data separately + local vol_meta = engine.volume("garage-meta").with_size(metaSize); + local vol_data = engine.volume("garage-data").with_size(dataSize); + + // Main Garage daemon container + local garage_container = + engine.container("garage") + .with_image(images.garage) + .with_command([ + "/garage", "-c", "/etc/garage/garage.toml", "server" + ]) + .with_environment({ + RUST_LOG: "garage=info", + }) + .with_limits("1.0", "512M") + .with_reservations("0.5", "512M") + .with_port(3900, 3900, "s3-api") + .with_port(3901, 3901, "rpc") + .with_port(3902, 3902, "web") + .with_port(3903, 3903, "admin") + .with_port(3904, 3904, "k2v") + .with_volume_mount(cfgVol, "/etc/garage/") + .with_volume_mount(vol_meta, "/var/lib/garage/meta") + .with_volume_mount(vol_data, "/var/lib/garage/data"); + + // Init container - configures cluster layout and creates S3 credentials + // IMPORTANT: This container exits with code 0 after completion + // Orchestrator must NOT restart it (use K8s Job or Compose restart: "no") + // Uses Alpine base image since garage container has no shell + local init_container = + engine.container("garage-init") + .with_image("docker.io/alpine:3.23.2") + .with_environment({ + GARAGE_ACCESS_KEY: accessKey, + GARAGE_SECRET_KEY: secretKey, + GARAGE_REGION: region, + GARAGE_ADMIN_TOKEN: adminToken, + GARAGE_RPC_SECRET: rpcSecret, + GARAGE_DATA_SIZE: dataSize, + }) + .with_limits("0.5", "256M") + .with_reservations("0.25", "128M") + .with_volume_mount(cfgVol, "/etc/garage/") + .with_command([ + "sh", "-c", ||| + set -e + + # Install required tools + echo "Installing curl, jq and downloading garage CLI..." + apk add --no-cache curl jq + + # Download garage binary (v2.1.0) for remote management + curl -fsSL "https://garagehq.deuxfleurs.fr/_releases/v2.1.0/x86_64-unknown-linux-musl/garage" \ + -o /usr/local/bin/garage + chmod +x /usr/local/bin/garage + + echo "Waiting for Garage daemon to be ready..." + MAX_ATTEMPTS=60 + ATTEMPT=0 + # Wait for /health to respond (even 503 is fine - means daemon is up) + until curl -s http://garage:3903/health >/dev/null 2>&1; do + ATTEMPT=$((ATTEMPT+1)) + if [ $ATTEMPT -ge $MAX_ATTEMPTS ]; then + echo "ERROR: Garage failed to become ready after ${MAX_ATTEMPTS} attempts" + exit 1 + fi + echo "Attempt ${ATTEMPT}/${MAX_ATTEMPTS}: Garage not ready, retrying in 2s..." + sleep 2 + done + echo "Garage daemon is ready." + + # Get the node ID via v2 Admin API + echo "Getting Garage node ID via Admin API..." + curl -s -H "Authorization: Bearer ${GARAGE_ADMIN_TOKEN}" \ + "http://garage:3903/v2/GetNodeInfo?node=self" > /tmp/garage-node-info.json + + # Extract node ID from response (the key in success map is the node ID) + NODE_ID=$(jq -r '.success | to_entries[0].value.nodeId' /tmp/garage-node-info.json) + echo "Node ID: ${NODE_ID}" + + if [ -z "$NODE_ID" ] || [ "$NODE_ID" = "null" ]; then + echo "ERROR: Failed to retrieve node ID" + exit 1 + fi + + # ===== LAYOUT MANAGEMENT VIA REMOTE RPC ===== + # Use garage CLI with -h and -s flags to connect remotely + # -h requires format: @: + # -s provides the RPC secret + + # Construct full RPC identifier + RPC_HOST="${NODE_ID}@garage:3901" + echo "RPC Host: ${RPC_HOST}" + + # Check current layout to see if node is already assigned (idempotent) + echo "Checking current cluster layout..." + LAYOUT_OUTPUT=$(garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" layout show 2>&1) + + # Check if node already has a role assigned + # Layout output shows abbreviated node ID (first 16 chars) + NODE_ID_SHORT="${NODE_ID:0:16}" + if echo "$LAYOUT_OUTPUT" | grep -q "$NODE_ID_SHORT"; then + echo "Node ${NODE_ID_SHORT}... already assigned in layout, skipping." + else + echo "Assigning node to cluster layout..." + # Assign node to zone dc1 with configured capacity + garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" \ + layout assign ${NODE_ID} -z dc1 -c ${GARAGE_DATA_SIZE} + + echo "Applying layout configuration..." + # Get current staged version and apply + garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" \ + layout apply --version 1 + + echo "Layout configured successfully." + # Wait for layout to stabilize + sleep 5 + fi + + # ===== KEY MANAGEMENT VIA REMOTE RPC ===== + + # Check if key already exists (idempotent) + # GARAGE_ACCESS_KEY is already a valid Garage Key ID (GK + 24 hex chars) + if garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" key info "${GARAGE_ACCESS_KEY}" >/dev/null 2>&1; then + echo "Access key ${GARAGE_ACCESS_KEY} already exists, skipping creation." + else + echo "Importing S3 access key: ${GARAGE_ACCESS_KEY}" + + # Import key with the provided credentials (both already in valid Garage format) + # GARAGE_ACCESS_KEY = Key ID (GK + 24 hex chars) + # GARAGE_SECRET_KEY = Secret (64 hex chars) + garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" \ + key import "${GARAGE_ACCESS_KEY}" "${GARAGE_SECRET_KEY}" --yes + + echo "Access key imported successfully." + fi + + # Grant permissions to the key + echo "Granting create-bucket permission to key..." + garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" \ + key allow --create-bucket "${GARAGE_ACCESS_KEY}" + + echo "" + echo "Garage initialization complete!" + echo "S3 Endpoint: http://garage:3900" + echo "Region: ${GARAGE_REGION}" + exit 0 + |||, + ]); + + // Container sets + local garage_containerSet = engine.containers("garage", [garage_container]); + local init_containerSet = engine.containers("garage-init", [init_container]); + + // Service - expose Garage ports + local garage_service = + engine.service(garage_containerSet) + .with_port(3900, 3900, "s3-api") + .with_port(3901, 3901, "rpc") + .with_port(3902, 3902, "web") + .with_port(3903, 3903, "admin") + .with_port(3904, 3904, "k2v"); + + engine.resources([ + cfgVol, + vol_meta, + vol_data, + garage_containerSet, + init_containerSet, + garage_service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/memgraph.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/memgraph.jsonnet new file mode 100644 index 00000000..eeed2e4e --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/memgraph.jsonnet @@ -0,0 +1,70 @@ +local images = import "values/images.jsonnet"; + +{ + + "memgraph" +: { + + create:: function(engine) + + local vol = engine.volume("memgraph").with_size("20G"); + + local container = + engine.container("memgraph") + .with_image(images.memgraph_mage) + .with_environment({ + MEMGRAPH: "--storage-properties-on-edges=true --storage-enable-edges-metadata=true" + }) + .with_limits("1.0", "1000M") + .with_reservations("0.5", "1000M") + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2") + .with_volume_mount(vol, "/var/lib/memgraph"); + + local containerSet = engine.containers( + "memgraph", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + + "memgraph-lab" +: { + + create:: function(engine) + + local container = + engine.container("lab") + .with_image(images.memgraph_lab) + .with_environment({ + QUICK_CONNECT_MG_HOST: "memgraph", + QUICK_CONNECT_MG_PORT: "7687", + }) + .with_limits("1.0", "512M") + .with_reservations("0.5", "512M") + .with_port(3010, 3000, "http"); + + local containerSet = engine.containers( + "lab", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(3010, 3010, "http"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/milvus.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/milvus.jsonnet new file mode 100644 index 00000000..5bed3820 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/milvus.jsonnet @@ -0,0 +1,89 @@ +local images = import "values/images.jsonnet"; +local minio = import "backends/minio.jsonnet"; + +minio { + + etcd +: { + + create:: function(engine) + + local vol = engine.volume("etcd").with_size("20G"); + + local container = + engine.container("etcd") + .with_image(images.etcd) + .with_command([ + "etcd", + "-advertise-client-urls=http://127.0.0.1:2379", + "-listen-client-urls", + "http://0.0.0.0:2379", + "--data-dir", + "/etcd", + ]) + .with_environment({ + ETCD_AUTO_COMPACTION_MODE: "revision", + ETCD_AUTO_COMPACTION_RETENTION: "1000", + ETCD_QUOTA_BACKEND_BYTES: "4294967296", + ETCD_SNAPSHOT_COUNT: "50000" + }) + .with_limits("1.0", "128M") + .with_reservations("0.25", "128M") + .with_port(2379, 2379, "api") + .with_volume_mount(vol, "/etcd"); + + local containerSet = engine.containers( + "etcd", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(2379, 2379, "api"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + + milvus +: { + + create:: function(engine) + + local vol = engine.volume("milvus").with_size("20G"); + + local container = + engine.container("milvus") + .with_image(images.milvus) + .with_command([ + "milvus", "run", "standalone" + ]) + .with_environment({ + ETCD_ENDPOINTS: "etcd:2379", + MINIO_ADDRESS: "minio:9000", + }) + .with_limits("1.0", "256M") + .with_reservations("0.5", "256M") + .with_port(9091, 9091, "api") + .with_port(19530, 19530, "api2") + .with_volume_mount(vol, "/var/lib/milvus"); + + local containerSet = engine.containers( + "milvus", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9091, 9091, "api") + .with_port(19530, 19530, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/minio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/minio.jsonnet new file mode 100644 index 00000000..b38bb81f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/minio.jsonnet @@ -0,0 +1,48 @@ +local images = import "values/images.jsonnet"; + +{ + + minio +: { + + create:: function(engine) + + local vol = engine.volume("minio-data").with_size("20G"); + + local container = + engine.container("minio") + .with_image(images.minio) + .with_command([ + "minio", + "server", + "/minio_data", + "--console-address", + ":9001", + ]) + .with_environment({ + MINIO_ROOT_USER: "minioadmin", + MINIO_ROOT_PASSWORD: "minioadmin", + }) + .with_limits("0.5", "128M") + .with_reservations("0.25", "128M") + .with_port(9000, 9000, "api") + .with_port(9001, 9001, "console") + .with_volume_mount(vol, "/minio_data"); + + local containerSet = engine.containers( + "minio", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9000, 9000, "api") + .with_port(9001, 9001, "console"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/neo4j.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/neo4j.jsonnet new file mode 100644 index 00000000..46c61e0f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/neo4j.jsonnet @@ -0,0 +1,46 @@ +local images = import "values/images.jsonnet"; + +{ + + "neo4j" +: { + + create:: function(engine) + + local vol = engine.volume("neo4j").with_size("20G"); + + local container = + engine.container("neo4j") + .with_image(images.neo4j) + .with_environment({ + NEO4J_AUTH: "neo4j/password", + NEO4J_server_memory_pagecache_size: "512m", + NEO4J_server_memory_heap_max__size: "512m", + // NEO4J_server_bolt_listen__address: "0.0.0.0:7687", + // NEO4J_server_default__listen__address: "0.0.0.0", + // NEO4J_server_http_listen__address: "0.0.0.0:7474", + }) + .with_limits("1.0", "1536M") + .with_reservations("0.5", "1536M") + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2") + .with_volume_mount(vol, "/data"); + + local containerSet = engine.containers( + "neo4j", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/pinecone.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/pinecone.jsonnet new file mode 100644 index 00000000..2bef70fb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/pinecone.jsonnet @@ -0,0 +1,45 @@ +local images = import "values/images.jsonnet"; + +{ + + "pinecone" +: { + + create:: function(engine) + + local container = + engine.container("pinecone") + .with_image(images.pinecone) + .with_environment({ + + PORT: "5080", + + INDEX_TYPE: "serverless", + + // Dimension is fixed, 384 is right for miniLM + // sentence embedding + DIMENSION: 384, + + METRIC: "cosine" + + }) + .with_limits("1.0", "256M") + .with_reservations("0.5", "256M") + .with_port(5080, 5080, "api"); + + local containerSet = engine.containers( + "pinecone", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(5080, 5080, "api"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/qdrant.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/qdrant.jsonnet new file mode 100644 index 00000000..2e6761f8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/backends/qdrant.jsonnet @@ -0,0 +1,65 @@ +local images = import "values/images.jsonnet"; + +{ + + "qdrant" +: { + + // Memory settings (can be overridden by memory-profile) + "memory-limit":: "1024M", + "memory-reservation":: "1024M", + + // Mmap settings for low-memory mode (trades latency for memory) + // Set to null to disable, or string value to enable + "memmap-threshold-kb":: null, + "on-disk-payload":: null, + + create:: function(engine) + + // Capture memory settings into locals + local memLimit = self["memory-limit"]; + local memReserv = self["memory-reservation"]; + local mmapThreshold = self["memmap-threshold-kb"]; + local onDiskPayload = self["on-disk-payload"]; + + local vol = engine.volume("qdrant").with_size("20G"); + + // Build environment with optional mmap settings + local baseEnv = {}; + local env = baseEnv + + (if mmapThreshold != null then { + QDRANT__STORAGE__MEMMAP_THRESHOLD_KB: mmapThreshold, + } else {}) + + (if onDiskPayload != null then { + QDRANT__STORAGE__ON_DISK_PAYLOAD: onDiskPayload, + } else {}); + + local container = + engine.container("qdrant") + .with_image(images.qdrant) + .with_limits("1.0", memLimit) + .with_reservations("0.5", memReserv) + .with_port(6333, 6333, "api") + .with_port(6334, 6334, "api2") + .with_volume_mount(vol, "/qdrant/storage") + + (if std.length(env) > 0 then { + environment+: env, + } else {}); + + local containerSet = engine.containers( + "qdrant", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(6333, 6333, "api") + .with_port(6334, 6334, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/components.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/components.jsonnet new file mode 100644 index 00000000..f6157326 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/components.jsonnet @@ -0,0 +1,121 @@ +{ + + // Essentials + "trustgraph-base": import "core/trustgraph.jsonnet", + "rev-gateway": import "core/rev-gateway.jsonnet", + "pulsar": import "pulsar/pulsar.jsonnet", + + // LLMs + "azure": import "llm/azure.jsonnet", + "azure-openai": import "llm/azure-openai.jsonnet", + "bedrock": import "llm/bedrock.jsonnet", + "claude": import "llm/claude.jsonnet", + "cohere": import "llm/cohere.jsonnet", + "googleaistudio": import "llm/googleaistudio.jsonnet", + "llamafile": import "llm/llamafile.jsonnet", + "lmstudio": import "llm/lmstudio.jsonnet", + "mistral": import "llm/mistral.jsonnet", + "ollama": import "llm/ollama.jsonnet", + "openai": import "llm/openai.jsonnet", + "vertexai": import "llm/vertexai.jsonnet", + "tgi": import "llm/tgi.jsonnet", + "vllm": import "llm/vllm.jsonnet", + + // Embeddings + "embeddings-ollama": import "embeddings/embeddings-ollama.jsonnet", + "embeddings-hf": import "embeddings/embeddings-hf.jsonnet", + "embeddings-fastembed": import "embeddings/embeddings-fastembed.jsonnet", + + // OCR options + "ocr": import "ocr/ocr.jsonnet", + "mistral-ocr": import "ocr/mistral-ocr.jsonnet", + + // Vector stores + "vector-store-milvus": import "vector-store/milvus.jsonnet", + "vector-store-qdrant": import "vector-store/qdrant.jsonnet", + "vector-store-pinecone": import "vector-store/pinecone.jsonnet", + + // Triples stores + "triple-store-cassandra": import "triple-store/cassandra.jsonnet", + "triple-store-neo4j": import "triple-store/neo4j.jsonnet", + "triple-store-falkordb": import "triple-store/falkordb.jsonnet", + "triple-store-memgraph": import "triple-store/memgraph.jsonnet", + + // Row stores + "row-store-cassandra": import "row-store/cassandra.jsonnet", + + // Observability support + "grafana": import "monitoring/grafana.jsonnet", + "loki": import "monitoring/loki.jsonnet", + + // Pulsar manager is a UI for Pulsar. Uses a LOT of memory + "pulsar-manager": import "pulsar/pulsar-manager.jsonnet", + + "override-recursive-chunker": import "core/chunker-recursive.jsonnet", + + // The prompt manager + "prompt-overrides": import "core/prompt-overrides.jsonnet", + + // Extra MCP services + "ddg-mcp-server": import "mcp/ddg-mcp-server.jsonnet", + + // Does nothing. But, can be a hack to overwrite parameters + "null": {}, + + // Passthrough: returns parameters directly, preserving +: merge syntax + // Also supports JSON-safe routing with prefixed parameters like "cassandra-heap" + "override": { + local route = function(target) + function(prefix, k, v) + local suffix = std.substr(k, std.length(prefix), std.length(k) - std.length(prefix)); + { [target] +: { [suffix]:: v } }, + + local routes = { + "cassandra-": route("cassandra"), + "pulsar-": route("pulsar"), + "qdrant-": route("qdrant"), + "api-gateway-": route("api-gateway"), + "librarian-": route("librarian"), + }, + + with_params:: function(pars) + std.foldl( + function(acc, k) + local matchingPrefixes = [p for p in std.objectFields(routes) if std.startsWith(k, p)]; + if std.length(matchingPrefixes) > 0 then + local prefix = matchingPrefixes[0]; + acc + routes[prefix](prefix, k, pars[k]) + else + acc + { [k]:: pars[k] }, + std.objectFields(pars), + {} + ), + }, + + // Memory profiles + "memory-profile-low": import "profiles/memory-profile-low.jsonnet", + + // Model hosting + + "hosting-intel-battlemage-vllm": + import "model-hosting/intel-battlemage-vllm.jsonnet", + + "hosting-cpu-tgi": + import "model-hosting/cpu-tgi.jsonnet", + + "hosting-intel-xpu-tgi": + import "model-hosting/intel-xpu-tgi.jsonnet", + + "hosting-intel-gaudi-tgi": + import "model-hosting/intel-gaudi-tgi.jsonnet", + + "hosting-intel-xpu-vllm": + import "model-hosting/intel-xpu-vllm.jsonnet", + + "hosting-intel-gaudi-vllm": + import "model-hosting/intel-gaudi-vllm.jsonnet", + + "hosting-nvidia-gpu-vllm": + import "model-hosting/nvidia-gpu-vllm.jsonnet", + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/agent-manager-react.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/agent-manager-react.jsonnet new file mode 100644 index 00000000..954d80a7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/agent-manager-react.jsonnet @@ -0,0 +1,47 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "agent-manager" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("agent-manager") + .with_image(images.trustgraph_flow) + .with_command([ + "agent-manager-react", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "agent-manager", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/chunker-recursive.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/chunker-recursive.jsonnet new file mode 100644 index 00000000..0b924aba --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/chunker-recursive.jsonnet @@ -0,0 +1,55 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; + +{ + + "chunk-size":: 2000, + "chunk-overlap":: 100, + + "chunker" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("chunker") + .with_image(images.trustgraph_flow) + .with_command([ + "chunker-recursive", + "-p", + url.pulsar, + "--chunk-size", + std.toString($["chunk-size"]), + "--chunk-overlap", + std.toString($["chunk-overlap"]), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "chunker", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/configuration.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/configuration.jsonnet new file mode 100644 index 00000000..75afebb9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/configuration.jsonnet @@ -0,0 +1,57 @@ + +// This puts the default configuration together. References many things, +// flow classes, a default flow, token costs, prompts, agent tools + +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "init-trustgraph" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local cfgVol = engine.configVolume( + "trustgraph-cfg", "trustgraph", + { + "config.json": importstr "trustgraph/config.json", + } + ); + + local container = + engine.container("init-trustgraph") + .with_image(images.trustgraph_flow) + .with_command( + [ + "tg-init-trustgraph", + "-p", + url.pulsar_admin, + "--config-file", + "/trustgraph/config.json", + ] + ) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation) + .with_volume_mount(cfgVol, "/trustgraph/"); + + local containerSet = engine.containers( + "init-trustgraph", [ container ] + ); + + engine.resources([ + cfgVol, + containerSet, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/document-rag.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/document-rag.jsonnet new file mode 100644 index 00000000..e466e3a4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/document-rag.jsonnet @@ -0,0 +1,92 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; + +{ + + "document-rag" +: { + + "doc-limit":: 20, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local docLimit = self["doc-limit"]; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("document-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "document-rag", + "-p", + url.pulsar, + "--doc-limit", + std.toString(docLimit), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "document-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "document-embeddings" +: { + + "cpu-limit":: "1.0", + "cpu-reservation":: "0.5", + "memory-limit":: "512M", + "memory-reservation":: "512M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("document-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "document-embeddings", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "document-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/graph-rag.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/graph-rag.jsonnet new file mode 100644 index 00000000..06ee5b3b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/graph-rag.jsonnet @@ -0,0 +1,283 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "kg-extract-definitions" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-extract-definitions") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-definitions", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-extract-definitions", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-relationships" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-extract-relationships") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-relationships", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-extract-relationships", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-agent" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-extract-agent") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-agent", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-extract-agent", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-ontology" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "300M", + "memory-reservation":: "300M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-extract-ontology") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-ontology", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-extract-ontology", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "graph-rag" +: { + + concurrency:: 1, + "entity-limit":: 50, + "triple-limit":: 30, + "max-subgraph-size":: 400, + "max-path-length":: 2, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local entityLimit = self["entity-limit"]; + local tripleLimit = self["triple-limit"]; + local maxSubgraphSize = self["max-subgraph-size"]; + local maxPathLength = self["max-path-length"]; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("graph-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "graph-rag", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--entity-limit", + std.toString(entityLimit), + "--triple-limit", + std.toString(tripleLimit), + "--max-subgraph-size", + std.toString(maxSubgraphSize), + "--max-path-length", + std.toString(maxPathLength), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "graph-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "graph-embeddings" +: { + + "cpu-limit":: "1.0", + "cpu-reservation":: "0.5", + "memory-limit":: "256M", + "memory-reservation":: "256M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "graph-embeddings", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/librarian.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/librarian.jsonnet new file mode 100644 index 00000000..2ecda5a8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/librarian.jsonnet @@ -0,0 +1,58 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local garage = import "backends/garage.jsonnet"; +local cassandra = import "backends/cassandra.jsonnet"; + +{ + + "librarian" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "256M", + "memory-reservation":: "256M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("librarian") + .with_image(images.trustgraph_flow) + .with_command([ + "librarian", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + "--object-store-endpoint", + url.object_store, + "--object-store-access-key", + $.garage["access-key"], + "--object-store-secret-key", + $.garage["secret-key"], + "--object-store-region", + $.garage.region, + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "librarian", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +// Garage and Cassandra are used by the Librarian +} + garage + cassandra + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/mcp-server.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/mcp-server.jsonnet new file mode 100644 index 00000000..9bf7e3bd --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/mcp-server.jsonnet @@ -0,0 +1,54 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "mcp-server" +: { + + port:: 8000, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "256M", + "memory-reservation":: "256M", + + create:: function(engine) + + local port = self.port; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local envSecrets = engine.envSecrets("mcp-server-secret") + .with_env_var("MCP_SERVER_SECRET", "mcp-server-secret") + .with_env_var("GATEWAY_SECRET", "gateway-secret"); + + local container = + engine.container("mcp-server") + .with_image(images.trustgraph_mcp) + .with_command([ + "mcp-server", + "--port", + std.toString(port), + ]) + .with_env_var_secrets(envSecrets) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation) + .with_port(port, port, "mcp"); + + local containerSet = engine.containers( + "mcp-server", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(port, port, "mcp"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/prompt-overrides.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/prompt-overrides.jsonnet new file mode 100644 index 00000000..852ec09d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/prompt-overrides.jsonnet @@ -0,0 +1,24 @@ +local default_prompts = import "prompts/default-prompts.jsonnet"; + +{ + + with:: function(key, value) + if (key == "system-template") then + self + { + prompts +:: { + "system-template": value, + } + } + else + self + { + prompts +:: { + templates +:: { + [key] +:: { + prompt: value + } + } + } + }, + +} + default_prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/prompt-template.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/prompt-template.jsonnet new file mode 100644 index 00000000..3cd79d59 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/prompt-template.jsonnet @@ -0,0 +1,97 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "prompt" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("prompt") + .with_image(images.trustgraph_flow) + .with_command([ + "prompt-template", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "prompt", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "prompt-rag" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("prompt-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "prompt-template", + "-p", + url.pulsar, + "--id", + "prompt-rag", + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "prompt-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/rev-gateway.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/rev-gateway.jsonnet new file mode 100644 index 00000000..fbd9f77a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/rev-gateway.jsonnet @@ -0,0 +1,62 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "rev-gateway" +: { + + // Invalid, but at least means the rev-gateway won't connect to anything + // it shouldn't. + token:: "INVALID_TOKEN", + uri:: "wss://127.0.0.1/api/v1/relay?token=" + self.token, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "256M", + "memory-reservation":: "256M", + + create:: function(engine) + + local uri = self.uri; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local envSecrets = engine.envSecrets("rev-gateway-secret") + .with_env_var("REV_GATEWAY_SECRET", "rev-gateway-secret"); + + local container = + engine.container("api-gateway") + .with_image(images.trustgraph_flow) + .with_command([ + "rev-gateway", + "-p", + url.pulsar, + "--websocket-uri", + std.toString(uri), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation) + .with_port(8000, 8000, "metrics") + .with_port(port, port, "api"); + + local containerSet = engine.containers( + "api-gateway", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics") + .with_port(port, port, "api"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/structured-data.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/structured-data.jsonnet new file mode 100644 index 00000000..52ea85da --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/structured-data.jsonnet @@ -0,0 +1,211 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "nlp-query" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("nlp-query") + .with_image(images.trustgraph_flow) + .with_command([ + "nlp-query", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "nlp-query", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "structured-query" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("structured-query") + .with_image(images.trustgraph_flow) + .with_command([ + "structured-query", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "structured-query", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "structured-diag" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "96M", + "memory-reservation":: "96M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("structured-diag") + .with_image(images.trustgraph_flow) + .with_command([ + "structured-diag", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "structured-diag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-rows" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-extract-rows") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-rows", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-extract-rows", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "row-embeddings" +: { + + "cpu-limit":: "1.0", + "cpu-reservation":: "0.5", + "memory-limit":: "256M", + "memory-reservation":: "256M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("row-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "row-embeddings", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "row-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/trustgraph.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/trustgraph.jsonnet new file mode 100644 index 00000000..3cf12d26 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/core/trustgraph.jsonnet @@ -0,0 +1,473 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +local config_initialiser = import "configuration.jsonnet"; +local config = import "../runtime-config/trustgraph-config.jsonnet"; +local librarian = import "librarian.jsonnet"; +local mcp_server = import "mcp-server.jsonnet"; +local workbench = import "../ui/workbench-ui.jsonnet"; +local graphrag = import "graph-rag.jsonnet"; +local documentrag = import "document-rag.jsonnet"; +local prompt_template = import "prompt-template.jsonnet"; +local agent_manager = import "agent-manager-react.jsonnet"; +local structured_data = import "structured-data.jsonnet"; +local ddg = import "mcp/ddg-mcp-server.jsonnet"; + +// Helper to create a routing function for a target object +local route = function(target) + function(prefix, k, v) + local suffix = std.substr(k, std.length(prefix), std.length(k) - std.length(prefix)); + { [target] +: { [suffix]:: v } }; + +// Parameter prefix -> target object routing table +local routes = { + "prompt-rag-": route("prompt-rag"), + "prompt-": route("prompt"), + "text-completion-rag-": route("text-completion-rag"), + "text-completion-": route("text-completion"), + "embeddings-": route("embeddings"), + "api-gateway-": route("api-gateway"), + "chunk-": route("chunker"), + "graph-rag-": route("graph-rag"), + "graph-embeddings-": route("graph-embeddings"), + "kg-extract-definitions-": route("kg-extract-definitions"), + "kg-extract-relationships-": route("kg-extract-relationships"), + "kg-extract-agent-": route("kg-extract-agent"), + "kg-extract-ontology-": route("kg-extract-ontology"), + "kg-extract-objects-": route("kg-extract-objects"), + "garage-": route("garage"), + "config-svc-": route("config-svc"), + "pdf-decoder-": route("pdf-decoder"), + "mcp-tool-": route("mcp-tool"), + "mcp-server-": route("mcp-server"), + "metering-rag-": route("metering-rag"), + "metering-": route("metering"), + "kg-store-": route("kg-store"), + "kg-manager-": route("kg-manager"), + "librarian-": route("librarian"), + "agent-manager-": route("agent-manager"), + "document-rag-": route("document-rag"), + "document-embeddings-": route("document-embeddings"), + "rev-gateway-": route("rev-gateway"), + "nlp-query-": route("nlp-query"), + "structured-query-": route("structured-query"), + "structured-diag-": route("structured-diag"), + "init-trustgraph-": route("init-trustgraph"), +}; + +// Find longest matching prefix (most specific first) +local findRoute = function(k) + local prefixes = std.objectFields(routes); + local matching = std.filter(function(p) std.startsWith(k, p), prefixes); + local sorted = std.sort(matching, function(x) -std.length(x)); + if std.length(sorted) > 0 then sorted[0] else null; + +{ + + // Route parameters to appropriate internal objects based on prefix + with:: function(k, v) + local prefix = findRoute(k); + if prefix != null then + self + routes[prefix](prefix, k, v) + else + self + { [k]:: v }, + + "log-level":: "INFO", + + // Base objects with concurrency defaults (LLM/embeddings components merge into these) + "text-completion" +: { concurrency:: 1 }, + "text-completion-rag" +: { concurrency:: 1 }, + embeddings +: { concurrency:: 1 }, + + "api-gateway" +: { + + port:: 8088, + timeout:: 600, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "512M", + "memory-reservation":: "512M", + + create:: function(engine) + + local port = self.port; + local timeout = self.timeout; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local envSecrets = engine.envSecrets("gateway-secret") + .with_env_var("GATEWAY_SECRET", "gateway-secret"); + + local container = + engine.container("api-gateway") + .with_image(images.trustgraph_flow) + .with_command([ + "api-gateway", + "-p", + url.pulsar, + "--timeout", + std.toString(timeout), + "--port", + std.toString(port), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation) + .with_port(port, port, "api"); + + local containerSet = engine.containers( + "api-gateway", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics") + .with_port(port, port, "api"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "chunker" +: { + + size:: 2000, + overlap:: 50, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local size = self.size; + local overlap = self.overlap; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("chunker") + .with_image(images.trustgraph_flow) + .with_command([ + "chunker-recursive", + "-p", + url.pulsar, + "--chunk-size", + std.toString(size), + "--chunk-overlap", + std.toString(overlap), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "chunker", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "config-svc" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local cpuLimit = self["cpu-limit"]; + local cpuReservation = self["cpu-reservation"]; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("config-svc") + .with_image(images.trustgraph_flow) + .with_command([ + "config-svc", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(cpuLimit, memoryLimit) + .with_reservations(cpuReservation, memoryReservation); + + local containerSet = engine.containers( + "config-svc", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "pdf-decoder" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "512M", + "memory-reservation":: "512M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("pdf-decoder") + .with_image(images.trustgraph_flow) + .with_command([ + "pdf-decoder", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "pdf-decoder", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "mcp-tool" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("mcp-tool") + .with_image(images.trustgraph_flow) + .with_command([ + "mcp-tool", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "mcp-tool", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "metering" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("metering") + .with_image(images.trustgraph_flow) + .with_command([ + "metering", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "metering", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "metering-rag" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("metering-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "metering", + "-p", + url.pulsar, + "--id", + "metering-rag", + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "metering-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-store" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-store") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-store", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-store", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-manager" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-manager") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-manager", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-manager", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + librarian + mcp_server + workbench + graphrag + + documentrag + prompt_template + agent_manager + structured_data + + config_initialiser + config + + ddg + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/embeddings/embeddings-fastembed.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/embeddings/embeddings-fastembed.jsonnet new file mode 100644 index 00000000..37116de0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/embeddings/embeddings-fastembed.jsonnet @@ -0,0 +1,49 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/embeddings-fastembed.jsonnet"; + +{ + + "fastembed-models":: models, + + "embeddings-models" +:: $["fastembed-models"], + + embeddings +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local container = + engine.container("embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "embeddings-fastembed", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits("1.0", "400M") + .with_reservations("0.5", "400M"); + + local containerSet = engine.containers( + "embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/embeddings/embeddings-hf.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/embeddings/embeddings-hf.jsonnet new file mode 100644 index 00000000..492f05cb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/embeddings/embeddings-hf.jsonnet @@ -0,0 +1,47 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/embeddings-huggingface.jsonnet"; + +{ + + "huggingface-embeddings-models":: models, + + "embeddings-models" +:: $["huggingface-embeddings-models"], + + embeddings +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local container = + engine.container("embeddings") + .with_image(images.trustgraph_hf) + .with_command([ + "embeddings-hf", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + ]) + .with_limits("1.0", "400M") + .with_reservations("0.5", "400M"); + + local containerSet = engine.containers( + "embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/embeddings/embeddings-ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/embeddings/embeddings-ollama.jsonnet new file mode 100644 index 00000000..05cbed72 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/embeddings/embeddings-ollama.jsonnet @@ -0,0 +1,52 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local models = import "parameters/embeddings-ollama.jsonnet"; + +{ + + "ollama-url":: "${OLLAMA_HOST}", + + "ollama-models":: models, + + "embeddings-models" +:: $["ollama-models"], + + embeddings +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local container = + engine.container("embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "embeddings-ollama", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "-r", + $["ollama-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/engine/aks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/engine/aks-k8s.jsonnet new file mode 100644 index 00000000..07e050f2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/engine/aks-k8s.jsonnet @@ -0,0 +1,45 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "disk.csi.azure.com", + parameters: { + // Standard disks (spinning magnetic), Locally Redundant Storage + // Cheapest, basically + skuName: "Standard_LRS", + }, + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/engine/docker-compose.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/engine/docker-compose.jsonnet new file mode 100644 index 00000000..334b3517 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/engine/docker-compose.jsonnet @@ -0,0 +1,260 @@ +{ + + // Extract resources using the engine + package:: function(patterns) + std.foldl( + function(state, p) state + p.create(self), + std.objectValues(patterns), + {} + ), + + container:: function(name) + { + + local container = self, + + name:: name, + + with_image:: function(x) self + { image: x }, + + with_user:: function(x) self + { user: x }, + + with_group:: function(x) self + + if std.objectHas(container, "group_add") then + { group_add: container.group_add + [x] } + else + { group_add: [x] }, + + with_command:: function(x) self + { + command: + if std.isString(x) then + std.strReplace(x, "$", "$$") + else if std.isArray(x) then + std.map(function(s) std.strReplace(s, "$", "$$"), x) + else + x + }, + + with_entrypoint:: function(x) self + { entrypoint: x }, + + with_runtime:: function(x) self + { runtime: x }, + + with_privileged:: function(x) self + { privileged: x }, + + with_ipc:: function(x) self + { ipc: x }, + + with_capability:: function(x) self + + if std.objectHas(container, "capability") then + { cap_add: container.capability + x } + else + { cap_add: [x], }, + + with_environment:: function(x) self + + if std.objectHas(container, "environment") then + { environment: container.environment + x } + else + { environment: x, }, + + with_device:: function(hdev, cdev) self + + if std.objectHas(container, "devices") then + { devices: container.devices + [ "%s:%s" % [hdev, cdev] ] } + else + { devices: [ "%s:%s" % [hdev, cdev] ], }, + + with_limits:: function(c, m) self + { + deploy +: { resources +: { + limits: { cpus: c, memory: m } + } }, + }, + + with_reservations:: function(c, m) self + { + deploy +: { resources +: { + reservations: { cpus: c, memory: m } + } }, + }, + + with_volume_mount:: + function(vol, mnt) + self + { + volumes: + if std.objectHas(container, "volumes") then + container.volumes + [ + "%s:%s" % [vol.volid, mnt] + ] + else + [ + "%s:%s" % [vol.volid, mnt] + ] + }, + + with_bind_mount:: + function(src, dest) + self + { + volumes: + if std.objectHas(container, "volumes") then + container.volumes + [ + "%s:%s" % [src, dest] + ] + else + [ + "%s:%s" % [src, dest] + ] + }, + + with_port:: + function(src, dest, name) + self + { + ports: + if std.objectHas(container, "ports") then + container.ports + [ "%d:%d" % [src, dest] ] + else + [ "%d:%d" % [src, dest] ] + }, + + with_env_var_secrets:: + function(vars) + std.foldl( + function(obj, x) obj.with_environment( + { [x]: "${" + x + "}" } + ), + vars.variables, + self + ), + + restart: "on-failure:100", + + add:: function() { + services +: { + [container.name]: container, + } + } + + }, + + internalService:: function(containers) + { + + local service = self, + + name: containers.name, + + with_port:: function(src, dest, name) + self + { port: [src, dest] }, + + add:: function() { + } + + }, + + service:: function(containers) + { + + local service = self, + + name: containers.name, + + with_port:: function(src, dest, name) + self + { port: [src, dest] }, + + add:: function() { + } + + }, + + volume:: function(name) + { + + local volume = self, + + name: name, + + volid:: name, + + with_size:: function(size) self + { size: size }, + + add:: function() { + volumes +: { + [volume.name]: {} + } + } + + }, + + configVolume:: function(name, dir, parts) + { + + local volume = self, + + name: dir, + + volid:: "./" + dir, + + with_size:: function(size) self + { size: size }, + + add:: function() { + } + + }, + + secretVolume:: function(name, dir, parts) + { + + local volume = self, + + name: dir, + + volid:: dir, + + with_size:: function(size) self + { size: size }, + + add:: function() { + } + + }, + + envSecrets:: function(name) + { + + local volume = self, + + name: name, + + volid:: name, + + variables:: [], + + with_env_var:: + function(name, key) self + { + variables: super.variables + [name], + }, + + add:: function() { + } + + }, + + containers:: function(name, containers) + { + + local cont = self, + + name: name, + containers: containers, + + add:: function() std.foldl( + function(state, c) state + c.add(), + cont.containers, + {} + ), + + }, + + resources:: function(res) + std.foldl( + function(state, c) state + c.add(), + res, + {} + ), + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/engine/eks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/engine/eks-k8s.jsonnet new file mode 100644 index 00000000..3fc3f035 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/engine/eks-k8s.jsonnet @@ -0,0 +1,46 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "ebs.csi.aws.com", + parameters: { + type: "gp3", + encrypted: "true", + iops: "6000", + throughput: "400", + }, + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/engine/gcp-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/engine/gcp-k8s.jsonnet new file mode 100644 index 00000000..71792426 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/engine/gcp-k8s.jsonnet @@ -0,0 +1,44 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "pd.csi.storage.gke.io", + parameters: { + type: "pd-balanced", + "csi.storage.k8s.io/fstype": "ext4", + }, + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/engine/k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/engine/k8s.jsonnet new file mode 100644 index 00000000..2067f6ac --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/engine/k8s.jsonnet @@ -0,0 +1,393 @@ +{ + + container:: function(name) + { + + local container = self, + + name: name, + limits: {}, + reservations: {}, + ports: [], + volumes: [], + bindMounts: [], + groups: [], + environment: [], + + with_image:: function(x) self + { image: x }, + + with_user:: function(x) self + { user: x }, + + with_group:: function(x) self + { groups: super.groups + [x] }, + + with_privileged:: function(x) self + { privileged: x }, + + with_command:: function(x) self + { command: x }, + + with_entrypoint:: function(x) self + { entrypoint: x }, + + with_environment:: function(x) self + { + environment: super.environment + [ + { + name: v.key, value: v.value + } + for v in std.objectKeysValues(x) + ], + }, + + with_limits:: function(c, m) self + { limits: { cpu: c, memory: m } }, + + with_reservations:: + function(c, m) self + { reservations: { cpu: c, memory: m } }, + + with_volume_mount:: + function(vol, mnt) + self + { + volumes: super.volumes + [{ + volume: vol, mount: mnt + }] + }, + + with_bind_mount:: + function(src, dest) + local name = "bind-" + std.strReplace(std.strReplace(src, "/", "-"), ".", "-"); + self + { + bindMounts: super.bindMounts + [{ + name: name, src: src, dest: dest + }] + }, + + with_port:: + function(src, dest, name) self + { + ports: super.ports + [ + { src: src, dest: dest, name : name } + ] + }, + + with_env_var_secrets:: + function(vars) + std.foldl( + function(obj, x) obj + { + environment: super.environment + [{ + name: x, + valueFrom: { + secretKeyRef: { + name: vars.name, + key: vars.keyMap[x], + } + } + }] + }, + vars.variables, + self + ), + + add:: function() [ + + { + apiVersion: "apps/v1", + kind: "Deployment", + metadata: { + name: container.name, + namespace: "trustgraph", + labels: { + app: container.name + } + }, + spec: { + replicas: 1, + selector: { + matchLabels: { + app: container.name, + } + }, + template: { + metadata: { + labels: { + app: container.name, + } + }, + spec: { + containers: [ + { + name: container.name, + image: container.image, + + // FIXME: Make everything run as + // root. Needed to get filesystems + // to be accessible. There's a + // better way of doing this? + securityContext: { + runAsUser: 0, + runAsGroup: 0, + } + ( + if std.objectHas(container, "privileged") && container.privileged then + { privileged: true } + else {} + ), + + resources: { + requests: container.reservations, + limits: container.limits + }, + } + ( + if std.length(container.ports) > 0 then + { + ports: [ + { + hostPort: port.src, + containerPort: port.dest, + } + for port in container.ports + ] + } else + {}) + + + (if std.objectHas(container, "entrypoint") then + // Entrypoint is set - use command for entrypoint, args for command + (if std.isString(container.entrypoint) && container.entrypoint == "" then + { command: [] } + else if std.isArray(container.entrypoint) then + { command: container.entrypoint } + else + { command: [container.entrypoint] } + ) + (if std.objectHas(container, "command") then + { args: container.command } + else {}) + else if std.objectHas(container, "command") then + { command: container.command } + else {}) + + + (if std.length(container.environment) > 0 then + { + env: container.environment, + } + else {}) + + + (if std.length(container.volumes) > 0 || std.length(container.bindMounts) > 0 then + { + volumeMounts: [ + { + mountPath: vol.mount, + name: vol.volume.name, + } + for vol in container.volumes + ] + [ + { + mountPath: bm.dest, + name: bm.name, + } + for bm in container.bindMounts + ] + } + + else + {} + ) + ], + volumes: [ + vol.volume.volRef() + for vol in container.volumes + ] + [ + { + name: bm.name, + hostPath: { path: bm.src } + } + for bm in container.bindMounts + ] + } + ( + if std.length(container.groups) > 0 then + { securityContext: { supplementalGroups: container.groups } } + else {} + ) + }, + } + {} + + } + + ] + + }, + + // Just an alias + internalService:: self.service, + + service:: function(containers) + { + + local service = self, + + name: containers.name, + + ports: [], + + with_port:: + function(src, dest, name) + self + { + ports: super.ports + [ + { src: src, dest: dest, name: name } + ] + }, + + add:: function() [ + + { + + apiVersion: "v1", + kind: "Service", + metadata: { + name: service.name, + namespace: "trustgraph", + }, + spec: { + selector: { + app: service.name, + }, + ports: [ + { + port: port.src, + targetPort: port.dest, + name: port.name, + } + for port in service.ports + ], + } + } + ], + + }, + + volume:: function(name) + { + + local volume = self, + + name: name, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + { + apiVersion: "v1", + kind: "PersistentVolumeClaim", + metadata: { + name: volume.name, + namespace: "trustgraph", + }, + spec: { + storageClassName: "tg", + accessModes: [ "ReadWriteOnce" ], + resources: { + requests: { + storage: volume.size, + } + }, + } + } + ], + + volRef:: function() { + name: volume.name, + persistentVolumeClaim: { claimName: volume.name }, + } + + }, + + configVolume:: function(name, dir, parts) + { + + local volume = self, + + name: name, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + { + apiVersion: "v1", + kind: "ConfigMap", + metadata: { + name: volume.name, + namespace: "trustgraph", + }, + data: parts + }, + ], + + + volRef:: function() { + name: volume.name, + configMap: { name: volume.name }, + } + + }, + + secretVolume:: function(name, dir, parts) + { + + local volume = self, + + name: name, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + ], + + volRef:: function() { + name: volume.name, + secret: { secretName: volume.name }, + } + + }, + + envSecrets:: function(name) + { + + local volume = self, + + name: name, + + variables: [], + keyMap: {}, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + ], + + volRef:: function() { + name: volume.name, + secret: { secretName: volume.name }, + }, + + with_env_var:: + function(name, key) self + { + variables: super.variables + [name], + keyMap: super.keyMap + { [name]: key }, + }, + + }, + + containers:: function(name, containers) + { + + local cont = self, + + name: name, + containers: containers, + + add:: function() std.flattenArrays( + [ c.add() for c in cont.containers ] + ), + + }, + + resources:: function(res) + + std.flattenArrays( + [ c.add() for c in res ] + ), + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/engine/minikube-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/engine/minikube-k8s.jsonnet new file mode 100644 index 00000000..858b17ad --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/engine/minikube-k8s.jsonnet @@ -0,0 +1,115 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList, + + volume:: function(name) + { + local volume = self, + name: name, + with_size:: function(size) self + { size: size }, + add:: function() [ + { + apiVersion: "v1", + kind: "PersistentVolume", + metadata: { + name: volume.name, + }, + spec: { + accessModes: [ "ReadWriteOnce" ], + capacity: { + storage: volume.size, + }, + persistentVolumeReclaimPolicy: "Delete", + hostPath: { + path: "/data/pv-" + volume.name, + }, + } + }, + { + apiVersion: "v1", + kind: "PersistentVolumeClaim", + metadata: { + name: volume.name, + namespace: "trustgraph", + }, + spec: { + accessModes: [ "ReadWriteOnce" ], + resources: { + requests: { + storage: volume.size, + } + }, + } + } + ], + + volRef:: function() { + name: volume.name, + persistentVolumeClaim: { claimName: volume.name }, + } + + }, + + service:: function(containers) + { + local service = self, + name: containers.name, + ports: [], + with_port:: + function(src, dest, name) + self + { + ports: super.ports + [ + { src: src, dest: dest, name: name } + ] + }, + add:: function() [ + { + apiVersion: "v1", + kind: "Service", + metadata: { + name: service.name, + namespace: "trustgraph", + }, + spec: { + selector: { + app: service.name, + }, + type: "LoadBalancer", + ports: [ + { + port: port.src, + targetPort: port.dest, + name: port.name, + } + for port in service.ports + ], + } + } + ], + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/engine/noop.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/engine/noop.jsonnet new file mode 100644 index 00000000..1f384648 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/engine/noop.jsonnet @@ -0,0 +1,79 @@ +{ + + // Extract resources usnig the engine + package:: function(patterns) {}, + + container:: function(name) { + + with_image:: function(x) self + {}, + + with_user:: function(x) self + {}, + + with_command:: function(x) self + {}, + + with_runtime:: function(x) self + {}, + + with_privileged:: function(x) self + {}, + + with_ipc:: function(x) self + {}, + + with_capability:: function(x) self + {}, + + with_environment:: function(x) self + {}, + + with_device:: function(hdev, cdev) self + {}, + + with_limits:: function(c, m) self + {}, + + with_reservations:: function(c, m) self + {}, + + with_volume_mount:: self + {}, + + with_port:: function(src, dest, name) self + {}, + + with_env_var_secrets:: function(vars) self + {}, + + add:: function() {}, + }, + + internalService:: function(containers) { + with_port:: function(src, dest, name) self + {}, + add:: function() {}, + }, + + service:: function(containers) { + with_port:: function(src, dest, name) self + {}, + add:: function() {}, + }, + + volume:: function(name) { + with_size:: function(size) self + {}, + add:: function() {}, + }, + + configVolume:: function(name, dir, parts) { + add:: function() {}, + }, + + secretVolume:: function(name, dir, parts) { + add:: function() {}, + }, + + envSecrets:: function(name) { + with_env_var:: function(name, key) self + {}, + add:: function() {}, + }, + + containers:: function(name, containers) { + add:: function() {}, + }, + + resources:: function(res) + std.foldl( + function(state, c) state + c.add(), + res, + {} + ), + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/engine/ovh-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/engine/ovh-k8s.jsonnet new file mode 100644 index 00000000..15d57c83 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/engine/ovh-k8s.jsonnet @@ -0,0 +1,45 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "cinder.csi.openstack.org", + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", + parameters: { + availability: "nova", + fsType: "ext4", + type: "high-speed", + }, +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: [ns, sc] + resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/engine/scw-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/engine/scw-k8s.jsonnet new file mode 100644 index 00000000..eed23968 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/engine/scw-k8s.jsonnet @@ -0,0 +1,40 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "csi.scaleway.com", + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: [ns, sc] + resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/agent-extract.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/agent-extract.jsonnet new file mode 100644 index 00000000..4c5785c8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/agent-extract.jsonnet @@ -0,0 +1,35 @@ +// Agent-based extraction module +// Uses AI agents for more sophisticated knowledge extraction from text +// Leverages agent tools and reasoning for complex extraction tasks + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; + +{ + // No external interfaces - internal agent extraction service + "interfaces" +: { + }, + + // No configurable parameters for agent extraction + "parameters" +: { + }, + + // Flow-level processors for agent-based extraction + "flow" +: { + // Agent-based knowledge extraction processor + // Uses AI agents with tools to extract structured knowledge + "kg-extract-agent:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output knowledge triples + "entity-contexts": flow("entity-contexts-load:{id}"), // Entity context information + "agent-request": request("agent:{id}"), // Agent service requests + "agent-response": response("agent:{id}"), // Agent service responses + }, + }, + + // No blueprint-level processors needed + "blueprint" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/agent.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/agent.jsonnet new file mode 100644 index 00000000..a483b21d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/agent.jsonnet @@ -0,0 +1,56 @@ +// Agent management module +// Provides AI agent orchestration and tool integration +// Manages agent conversations, tool calls, and response coordination +// Supports MCP tools, GraphRAG, and structured queries + +local helpers = import "helpers.jsonnet"; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +// Import shared services (agent requires LLM for reasoning, MCP for tools) +local llm_services = import "llm-services.jsonnet"; +local mcp_service = import "mcp-service.jsonnet"; + +// Merge shared services with agent-specific configuration +llm_services + mcp_service + { + + // External interfaces for agent operations + "interfaces" +: { + "agent": request_response("agent:{id}"), + }, + + // Flow-level processors for agent management + "flow" +: { + // Agent manager orchestrates agent conversations and tool usage + "agent-manager:{id}": { + + // Agent communication channels + request: request("agent:{id}"), + next: request("agent:{id}"), + response: response("agent:{id}"), + + // LLM and prompt services + "text-completion-request": request("text-completion:{id}"), + "text-completion-response": response("text-completion:{id}"), + "prompt-request": request("prompt:{id}"), + "prompt-response": response("prompt:{id}"), + + // Tool integrations + "mcp-tool-request": request("mcp-tool:{id}"), + "mcp-tool-response": response("mcp-tool:{id}"), + "graph-rag-request": request("graph-rag:{id}"), + "graph-rag-response": response("graph-rag:{id}"), + "structured-query-request": request("structured-query:{id}"), + "structured-query-response": response("structured-query:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + "row-embeddings-query-request": request("row-embeddings:{id}"), + "row-embeddings-query-response": response("row-embeddings:{id}"), + }, + }, + + // Blueprint-level processors for agent-related services + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/document-store.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/document-store.jsonnet new file mode 100644 index 00000000..16f304fa --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/document-store.jsonnet @@ -0,0 +1,56 @@ +// Document store module +// Infrastructure for document-based RAG using chunk embeddings +// Handles document embedding storage, retrieval, and question answering + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +// Import shared services +local llm_services = import "llm-services.jsonnet"; +local embeddings_service = import "embeddings-service.jsonnet"; + +// Merge shared services with document store configuration +llm_services + embeddings_service + { + + // External interfaces for document store + "interfaces" +: { + // Document embedding storage and retrieval + "document-embeddings-store": flow("document-embeddings-store:{id}"), + "document-rag": request_response("document-rag:{id}"), + "document-embeddings": request_response("document-embeddings:{id}"), + }, + + // Flow-level processors for document embedding and storage + "flow" +: { + "document-embeddings:{id}": { + input: flow("chunk-load:{id}"), + output: flow("document-embeddings-store:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + }, + "doc-embeddings-write:{id}": { + input: flow("document-embeddings-store:{id}"), + }, + "document-rag:{id}": { + request: request("document-rag:{id}"), + response: response("document-rag:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + "prompt-request": request("prompt-rag:{id}"), + "prompt-response": response("prompt-rag:{id}"), + "document-embeddings-request": request("document-embeddings:{id}"), + "document-embeddings-response": response("document-embeddings:{id}"), + }, + "doc-embeddings-query:{id}": { + request: request("document-embeddings:{id}"), + response: response("document-embeddings:{id}"), + }, + }, + + // Blueprint-level processors for document RAG operations + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/embeddings-service.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/embeddings-service.jsonnet new file mode 100644 index 00000000..1537e6c2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/embeddings-service.jsonnet @@ -0,0 +1,30 @@ +// Shared embeddings service module +// Provides vector embedding generation for text +// Import this module in any flow that requires embeddings + +local helpers = import "helpers.jsonnet"; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +{ + // Interfaces exposed by embeddings service + "interfaces" +: { + "embeddings": request_response("embeddings:{id}"), + }, + + "parameters" +: { + }, + + // Flow-level processor for embeddings + "flow" +: { + "embeddings:{id}": { + request: request("embeddings:{id}"), + response: response("embeddings:{id}"), + model: "{embeddings-model}", + }, + }, + + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/flow-blueprints.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/flow-blueprints.jsonnet new file mode 100644 index 00000000..4282fa23 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/flow-blueprints.jsonnet @@ -0,0 +1,48 @@ +// TrustGraph Flow Blueprints Configuration +// +// RAG Modes (4 types): +// - Document RAG: Uses document chunk embeddings +// - Graph RAG: Extracts definitions + relationships to graph +// - Ontology RAG: Extracts using ontology definitions to graph (mutually exclusive with Graph RAG) +// - Structured RAG: Extracts objects to object store +// +// Module structure: +// - *-store: Storage and query infrastructure +// - *-extract: Extraction methods + +// Import all the modular flow components +local graph_store = import "graph-store.jsonnet"; +local document_store = import "document-store.jsonnet"; +local structured_store = import "structured-store.jsonnet"; +local graphrag_extract = import "graphrag-extract.jsonnet"; +local ontorag_extract = import "ontorag-extract.jsonnet"; +local structured_extract = import "structured-extract.jsonnet"; +local agent = import "agent.jsonnet"; +local load = import "load.jsonnet"; +local kgcore = import "kgcore.jsonnet"; + +{ + + // Full system: Graph RAG + Document RAG + knowledge cores + "everything": { + description: "Graph RAG + Document RAG + knowledge cores", + tags: ["document-rag", "graph-rag", "kgcore"], + } + + graph_store + document_store + agent + load + + graphrag_extract + kgcore, + + // Structured RAG only + "structured": { + description: "Structured data extraction and querying", + tags: ["structured"], + } + + structured_store + structured_extract + agent + load, + + // Ontology RAG + knowledge cores + "ontology": { + description: "Ontology RAG + knowledge cores", + tags: ["onto-rag", "kgcore"], + } + + graph_store + ontorag_extract + agent + load + kgcore, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/graph-store.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/graph-store.jsonnet new file mode 100644 index 00000000..9bd49b9c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/graph-store.jsonnet @@ -0,0 +1,70 @@ +// Graph store module +// Shared infrastructure for graph-based RAG (used by both GraphRAG and OntologyRAG) +// Handles knowledge graph storage, embeddings, and graph-based question answering + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +// Import shared services +local llm_services = import "llm-services.jsonnet"; +local embeddings_service = import "embeddings-service.jsonnet"; + +// Merge shared services with graph store configuration +llm_services + embeddings_service + { + + // External interfaces exposed by the graph store + "interfaces" +: { + // Data ingestion interfaces for graph construction + "entity-contexts-load": flow("entity-contexts-load:{id}"), + "triples-store": flow("triples-store:{id}"), + "graph-embeddings-store": flow("graph-embeddings-store:{id}"), + + // Query interfaces for graph-based operations + "graph-rag": request_response("graph-rag:{id}"), + "triples": request_response("triples:{id}"), + "graph-embeddings": request_response("graph-embeddings:{id}"), + }, + + // Flow-level processors - handle data streams for a specific flow instance + "flow" +: { + "graph-embeddings:{id}": { + input: flow("entity-contexts-load:{id}"), + output: flow("graph-embeddings-store:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + }, + "triples-write:{id}": { + input: flow("triples-store:{id}"), + }, + "graph-embeddings-write:{id}": { + input: flow("graph-embeddings-store:{id}"), + }, + "graph-rag:{id}": { + request: request("graph-rag:{id}"), + response: response("graph-rag:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + "prompt-request": request("prompt-rag:{id}"), + "prompt-response": response("prompt-rag:{id}"), + "graph-embeddings-request": request("graph-embeddings:{id}"), + "graph-embeddings-response": response("graph-embeddings:{id}"), + "triples-request": request("triples:{id}"), + "triples-response": response("triples:{id}"), + }, + "triples-query:{id}": { + request: request("triples:{id}"), + response: response("triples:{id}"), + }, + "graph-embeddings-query:{id}": { + request: request("graph-embeddings:{id}"), + response: response("graph-embeddings:{id}"), + }, + }, + + // Blueprint-level processors - shared across all flow instances of this blueprint + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/graphrag-extract.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/graphrag-extract.jsonnet new file mode 100644 index 00000000..6bcbca14 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/graphrag-extract.jsonnet @@ -0,0 +1,44 @@ +// GraphRAG extraction module +// Extraction method for GraphRAG - extracts definitions and relationships +// Mutually exclusive with OntologyRAG extraction (both write to graph store) + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; + +{ + // No external interfaces - this module provides internal extraction services + "interfaces" +: { + }, + + // No configurable parameters for basic KG extraction + "parameters" +: { + }, + + // Flow-level processors for knowledge extraction + "flow" +: { + // Extracts entity definitions from text chunks + // Identifies and defines key entities mentioned in the text + "kg-extract-definitions:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output definition triples + "entity-contexts": flow("entity-contexts-load:{id}"), // Entity context information + "prompt-request": request("prompt:{id}"), // Definition extraction prompts + "prompt-response": response("prompt:{id}"), + }, + + // Extracts relationships between entities + // Identifies how entities are connected and interact + "kg-extract-relationships:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output relationship triples + "prompt-request": request("prompt:{id}"), // Relationship extraction prompts + "prompt-response": response("prompt:{id}"), + }, + }, + + // No blueprint-level processors needed + "blueprint" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/helpers.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/helpers.jsonnet new file mode 100644 index 00000000..eabb3bf4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/helpers.jsonnet @@ -0,0 +1,29 @@ +// Helper functions for flow configuration +// Provides utility functions for constructing flow, request, and response URIs +// used throughout the TrustGraph flow configuration system + +// Creates a persistent flow URI for data streams +// Persistent flows retain messages until consumed +local flow(x) = "persistent://tg/flow/" + x; + +// Creates a non-persistent request URI for request-response patterns +// Non-persistent means messages are not retained if no consumer is present +local request(x) = "non-persistent://tg/request/" + x; + +// Creates a non-persistent response URI for request-response patterns +local response(x) = "non-persistent://tg/response/" + x; + +// Creates a request-response pair for bidirectional communication +// Returns an object with both request and response URIs +local request_response(x) = { + request: request(x), + response: response(x), +}; + +// Export all helper functions for use in other modules +{ + flow: flow, + request: request, + response: response, + request_response: request_response, +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/kgcore.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/kgcore.jsonnet new file mode 100644 index 00000000..bfc01ada --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/kgcore.jsonnet @@ -0,0 +1,31 @@ +// Knowledge Graph Core storage module +// Handles persistent storage of knowledge graph data +// Consolidates triples and graph embeddings into permanent storage +// Creates the core knowledge base for long-term use + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; + +{ + // No external interfaces - internal storage service + "interfaces" +: { + }, + + // No configurable parameters for core storage + "parameters" +: { + }, + + // Flow-level processors for knowledge graph storage + "flow" +: { + // Knowledge graph store consolidates extracted knowledge + // Takes processed triples and embeddings and stores them permanently + "kg-store:{id}": { + "triples-input": flow("triples-store:{id}"), // Input RDF triples stream + "graph-embeddings-input": flow("graph-embeddings-store:{id}"), // Input graph embeddings + }, + }, + + // No blueprint-level processors needed + "blueprint" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/llm-parameters.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/llm-parameters.jsonnet new file mode 100644 index 00000000..a99f72ae --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/llm-parameters.jsonnet @@ -0,0 +1,59 @@ +{ + + // LLM model selection for normal LLM + "llm-model": { + "type": "llm-model", + "description": "LLM model", + "order": 1, + "advanced": false, + }, + + // LLM model for RAG operations + "llm-rag-model": { + "type": "llm-model", + "description": "LLM model for RAG", + "order": 2, + "advanced": true, + "controlled-by": "llm-model", + }, + + // LLM model selection for normal LLM + "llm-temperature": { + "type": "llm-temperature", + "description": "LLM temperature", + "order": 3, + "advanced": true, + }, + + // LLM model selection for normal LLM + "llm-rag-temperature": { + "type": "llm-temperature", + "description": "LLM temperature for RAG", + "order": 4, + "advanced": true, + }, + + "embeddings-model": { + "type": "embeddings-model", + "description": "Embeddings model", + "order": 5, + "advanced": true, + }, + + // LLM model selection for normal LLM + "chunk-size": { + "type": "chunk-size", + "description": "Chunk size", + "order": 6, + "advanced": true, + }, + + // LLM model selection for normal LLM + "chunk-overlap": { + "type": "chunk-overlap", + "description": "Chunk overlap", + "order": 7, + "advanced": true, + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/llm-services.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/llm-services.jsonnet new file mode 100644 index 00000000..ee6e09ae --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/llm-services.jsonnet @@ -0,0 +1,66 @@ +// Shared LLM services module +// Provides text completion, prompt processing, and metering services +// Import this module in any flow that requires LLM functionality + +local helpers = import "helpers.jsonnet"; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; +local llm_parameters = import "llm-parameters.jsonnet"; + +{ + // Interfaces exposed by LLM services + "interfaces" +: { + "prompt": request_response("prompt:{id}"), + "text-completion": request_response("text-completion:{id}"), + }, + + // LLM configuration parameters + "parameters" +: llm_parameters, + + // Flow-level processors for LLM services + "flow" +: { + // Primary text completion service + "text-completion:{id}": { + request: request("text-completion:{id}"), + response: response("text-completion:{id}"), + model: "{llm-model}", + }, + + // RAG-specific text completion (may use different model) + "text-completion-rag:{id}": { + request: request("text-completion-rag:{id}"), + response: response("text-completion-rag:{id}"), + model: "{llm-rag-model}", + }, + + // Prompt processing service + "prompt:{id}": { + request: request("prompt:{id}"), + response: response("prompt:{id}"), + "text-completion-request": request("text-completion:{id}"), + "text-completion-response": response("text-completion:{id}"), + }, + + // RAG-specific prompt processing + "prompt-rag:{id}": { + request: request("prompt-rag:{id}"), + response: response("prompt-rag:{id}"), + "text-completion-request": request("text-completion-rag:{id}"), + "text-completion-response": response("text-completion-rag:{id}"), + }, + + // Usage metering for primary completion + "metering:{id}": { + input: response("text-completion:{id}"), + }, + + // Usage metering for RAG completion + "metering-rag:{id}": { + input: response("text-completion-rag:{id}"), + }, + }, + + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/load.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/load.jsonnet new file mode 100644 index 00000000..6c440792 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/load.jsonnet @@ -0,0 +1,43 @@ +// Document loading and preprocessing module +// Handles document ingestion, format conversion, and chunking +// Converts PDFs to text and splits documents into processable chunks + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +// Import shared services (load requires embeddings for chunk processing) +local embeddings_service = import "embeddings-service.jsonnet"; + +// Merge shared services with load-specific configuration +embeddings_service + { + + // External interfaces for document loading + "interfaces" +: { + "document-load": flow("document-load:{id}"), + "text-load": flow("text-document-load:{id}"), + }, + + // Flow-level processors for document preprocessing + "flow" +: { + // PDF decoder converts PDF documents to text + "pdf-decoder:{id}": { + input: flow("document-load:{id}"), + output: flow("text-document-load:{id}"), + }, + + // Chunker splits documents into smaller, processable pieces + "chunker:{id}": { + input: flow("text-document-load:{id}"), + output: flow("chunk-load:{id}"), + "chunk-size": "{chunk-size}", + "chunk-overlap": "{chunk-overlap}", + }, + }, + + // Blueprint-level processors for document loading services + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/mcp-service.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/mcp-service.jsonnet new file mode 100644 index 00000000..5d599a67 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/mcp-service.jsonnet @@ -0,0 +1,31 @@ +// Shared MCP (Model Context Protocol) tool service module +// Provides MCP tool execution capabilities for agents +// Import this module in any flow that requires MCP tool integration + +local helpers = import "helpers.jsonnet"; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +{ + // Interfaces exposed by MCP service + "interfaces" +: { + "mcp-tool": request_response("mcp-tool:{id}"), + }, + + "parameters" +: { + }, + + // Flow-level processor for MCP tool execution + "flow" +: { + "mcp-tool:{id}": { + request: request("mcp-tool:{id}"), + response: response("mcp-tool:{id}"), + "text-completion-request": request("text-completion:{id}"), + "text-completion-response": response("text-completion:{id}"), + }, + }, + + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/ontorag-extract.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/ontorag-extract.jsonnet new file mode 100644 index 00000000..e5521d8a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/ontorag-extract.jsonnet @@ -0,0 +1,39 @@ +// OntologyRAG extraction module +// Extraction method for OntologyRAG - extracts using ontology definitions +// Mutually exclusive with GraphRAG extraction (both write to graph store) + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; + +{ + // No external interfaces - this module provides internal extraction services + "interfaces" +: { + }, + + // No configurable parameters for basic KG extraction + "parameters" +: { + }, + + // Flow-level processors for knowledge extraction + "flow" +: { + // Extracts using ontology definitions + "kg-extract-ontology:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output triples + "entity-contexts": flow("entity-contexts-load:{id}"), // Entity context information + "prompt-request": request("prompt:{id}"), // Definition + // extraction prompts + "prompt-response": response("prompt:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + }, + + }, + + // No blueprint-level processors needed + "blueprint" +: { + } +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/structured-extract.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/structured-extract.jsonnet new file mode 100644 index 00000000..54f6a05d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/structured-extract.jsonnet @@ -0,0 +1,30 @@ +// Structured RAG extraction module +// Extracts structured rows from text chunks +// Outputs to rows-store for structured data querying + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; + +{ + "interfaces" +: { + }, + + "parameters" +: { + }, + + // Flow-level processor for structured row extraction + "flow" +: { + "kg-extract-rows:{id}": { + input: flow("chunk-load:{id}"), + output: flow("rows-store:{id}"), + "entity-contexts": flow("entity-contexts-load:{id}"), + "prompt-request": request("prompt:{id}"), + "prompt-response": response("prompt:{id}"), + }, + }, + + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/structured-store.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/structured-store.jsonnet new file mode 100644 index 00000000..3668222d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/flows/structured-store.jsonnet @@ -0,0 +1,78 @@ +// Structured store module +// Shared infrastructure for structured data RAG +// Handles row storage, retrieval, and NLP query capabilities + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +// Import shared services +local llm_services = import "llm-services.jsonnet"; +local embeddings_service = import "embeddings-service.jsonnet"; + +// Merge shared services with structured store configuration +llm_services + embeddings_service + { + + // External interfaces for structured store + "interfaces" +: { + // Row storage and querying + "rows-store": flow("rows-store:{id}"), + "row-embeddings-store": flow("row-embeddings-store:{id}"), + "rows": request_response("rows:{id}"), + "row-embeddings": request_response("row-embeddings:{id}"), + + // Query interfaces + "nlp-query": request_response("nlp-query:{id}"), + "structured-query": request_response("structured-query:{id}"), + "structured-diag": request_response("structured-diag:{id}"), + }, + + // Flow-level processors for structured storage and query + "flow" +: { + "row-embeddings:{id}": { + input: flow("rows-store:{id}"), + output: flow("row-embeddings-store:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + }, + "rows-write:{id}": { + input: flow("rows-store:{id}"), + }, + "row-embeddings-write:{id}": { + input: flow("row-embeddings-store:{id}"), + }, + "rows-query:{id}": { + request: request("rows:{id}"), + response: response("rows:{id}"), + }, + "row-embeddings-query:{id}": { + request: request("row-embeddings:{id}"), + response: response("row-embeddings:{id}"), + }, + "nlp-query:{id}": { + request: request("nlp-query:{id}"), + response: response("nlp-query:{id}"), + "prompt-request": request("prompt-rag:{id}"), + "prompt-response": response("prompt-rag:{id}"), + }, + "structured-query:{id}": { + request: request("structured-query:{id}"), + response: response("structured-query:{id}"), + "nlp-query-request": request("nlp-query:{id}"), + "nlp-query-response": response("nlp-query:{id}"), + "rows-query-request": request("rows:{id}"), + "rows-query-response": response("rows:{id}"), + }, + "structured-diag:{id}": { + request: request("structured-diag:{id}"), + response: response("structured-diag:{id}"), + "prompt-request": request("prompt:{id}"), + "prompt-response": response("prompt:{id}"), + }, + }, + + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/azure-openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/azure-openai.jsonnet new file mode 100644 index 00000000..bee0fe47 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/azure-openai.jsonnet @@ -0,0 +1,112 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/azure-openai.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["azure-openai-" + key]:: value, + }, + +// Strategy is to specify the model with the AZURE_MODEL environment +// variable. This isn't something that can just be specified dynamically, +// it has to match what was provisioned in Azure. + + "azure-openai-max-output-tokens":: 4192, + "azure-openai-temperature":: 0.0, + "azure-openai-models":: models, + + "llm-models" +:: $["azure-openai-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-openai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_MODEL", "azure-model") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure-openai", + "-p", + url.pulsar, + "-x", + std.toString($["azure-openai-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-openai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_MODEL", "azure-model") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure-openai", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["azure-openai-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/azure.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/azure.jsonnet new file mode 100644 index 00000000..f4db7500 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/azure.jsonnet @@ -0,0 +1,106 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/azure.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["azure-" + key]:: value, + }, + + "azure-max-output-tokens":: 4096, + "azure-temperature":: 0.0, + "azure-models":: models, + + "llm-models" +:: $["azure-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-ai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure", + "-p", + url.pulsar, + "-x", + std.toString($["azure-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-ai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["azure-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/bedrock.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/bedrock.jsonnet new file mode 100644 index 00000000..0db1475b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/bedrock.jsonnet @@ -0,0 +1,104 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/bedrock.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["bedrock-" + key]:: value, + }, + + "bedrock-max-output-tokens":: 4096, + "bedrock-temperature":: 0.0, + "bedrock-models":: models, + + "llm-models" +:: $["bedrock-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("bedrock-credentials") + .with_env_var("AWS_ACCESS_KEY_ID", "aws-id-key") + .with_env_var("AWS_SECRET_ACCESS_KEY", "aws-secret") + .with_env_var("AWS_DEFAULT_REGION", "aws-region"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_bedrock) + .with_command([ + "text-completion-bedrock", + "-p", + url.pulsar, + "-x", + std.toString($["bedrock-max-output-tokens"]), + "-t", + "%0.3f" % $["bedrock-temperature"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("bedrock-credentials") + .with_env_var("AWS_ACCESS_KEY_ID", "aws-id-key") + .with_env_var("AWS_SECRET_ACCESS_KEY", "aws-secret") + .with_env_var("AWS_DEFAULT_REGION", "aws-region"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_bedrock) + .with_command([ + "text-completion-bedrock", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["bedrock-max-output-tokens"]), + "-t", + "%0.3f" % $["bedrock-temperature"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/claude.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/claude.jsonnet new file mode 100644 index 00000000..f3125e96 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/claude.jsonnet @@ -0,0 +1,104 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/claude.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["claude-" + key]:: value, + }, + + "claude-max-output-tokens":: 4096, + "claude-temperature":: 0.0, + "claude-models":: models, + + "llm-models" +:: $["claude-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("claude-credentials") + .with_env_var("CLAUDE_KEY", "claude-key"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-claude", + "-p", + url.pulsar, + "-x", + std.toString($["claude-max-output-tokens"]), + "-t", + "%0.3f" % $["claude-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("claude-credentials") + .with_env_var("CLAUDE_KEY", "claude-key"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-claude", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["claude-max-output-tokens"]), + "-t", + "%0.3f" % $["claude-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/cohere.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/cohere.jsonnet new file mode 100644 index 00000000..6517f7a0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/cohere.jsonnet @@ -0,0 +1,97 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/cohere.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["cohere-" + key]:: value, + }, + + "cohere-temperature":: 0.0, + "cohere-models":: models, + + "llm-models" +:: $["cohere-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("cohere-credentials") + .with_env_var("COHERE_KEY", "cohere-key"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-cohere", + "-p", + url.pulsar, + "-t", + "%0.3f" % $["cohere-temperature"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("cohere-credentials") + .with_env_var("COHERE_KEY", "cohere-key"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-cohere", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-t", + "%0.3f" % $["cohere-temperature"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/googleaistudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/googleaistudio.jsonnet new file mode 100644 index 00000000..05a8592c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/googleaistudio.jsonnet @@ -0,0 +1,104 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/googleaistudio.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["googleaistudio-" + key]:: value, + }, + + "googleaistudio-max-output-tokens":: 4096, + "googleaistudio-temperature":: 0.0, + "googleaistudio-models":: models, + + "llm-models" +:: $["googleaistudio-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("googleaistudio-credentials") + .with_env_var("GOOGLE_AI_STUDIO_KEY", "googleaistudio-key"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_vertexai) + .with_command([ + "text-completion-googleaistudio", + "-p", + url.pulsar, + "-x", + std.toString($["googleaistudio-max-output-tokens"]), + "-t", + "%0.3f" % $["googleaistudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("googleaistudio-credentials") + .with_env_var("GOOGLE_AI_STUDIO_KEY", "googleaistudio-key"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_vertexai) + .with_command([ + "text-completion-googleaistudio", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["googleaistudio-max-output-tokens"]), + "-t", + "%0.3f" % $["googleaistudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/llamafile.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/llamafile.jsonnet new file mode 100644 index 00000000..f3bee2cc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/llamafile.jsonnet @@ -0,0 +1,94 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/slm.jsonnet"; +local models = import "parameters/llamafile.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["llamafile-" + key]:: value, + }, + + "llamafile-models":: models, + + "llm-models" +:: $["llamafile-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("llamafile-credentials") + .with_env_var("LLAMAFILE_URL", "llamafile-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-llamafile", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("llamafile-credentials") + .with_env_var("LLAMAFILE_URL", "llamafile-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-llamafile", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/lmstudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/lmstudio.jsonnet new file mode 100644 index 00000000..4cf78caa --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/lmstudio.jsonnet @@ -0,0 +1,104 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/lmstudio.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["lmstudio-" + key]:: value, + }, + + "lmstudio-max-output-tokens":: 4096, + "lmstudio-temperature":: 0.0, + "lmstudio-models":: models, + + "llm-models" +:: $["lmstudio-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("lmstudio-credentials") + .with_env_var("LMSTUDIO_URL", "lmstudio-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-lmstudio", + "-p", + url.pulsar, + "-x", + std.toString($["lmstudio-max-output-tokens"]), + "-t", + "%0.3f" % $["lmstudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("lmstudio-credentials") + .with_env_var("LMSTUDIO_URL", "lmstudio-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-lmstudio", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["lmstudio-max-output-tokens"]), + "-t", + "%0.3f" % $["lmstudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/mistral.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/mistral.jsonnet new file mode 100644 index 00000000..2fb8c562 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/mistral.jsonnet @@ -0,0 +1,104 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/mistral.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["mistral-" + key]:: value, + }, + + "mistral-max-output-tokens":: 4096, + "mistral-temperature":: 0.0, + "mistral-models":: models, + + "llm-models" +:: $["mistral-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("mistral-credentials") + .with_env_var("MISTRAL_TOKEN", "mistral-token"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-mistral", + "-p", + url.pulsar, + "-x", + std.toString($["mistral-max-output-tokens"]), + "-t", + "%0.3f" % $["mistral-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("mistral-credentials") + .with_env_var("MISTRAL_TOKEN", "mistral-token"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-mistral", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["mistral-max-output-tokens"]), + "-t", + "%0.3f" % $["mistral-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/ollama.jsonnet new file mode 100644 index 00000000..6143f5cc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/ollama.jsonnet @@ -0,0 +1,102 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/ollama.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["ollama-" + key]:: value, + }, + + "ollama-models":: models, + + "llm-models" +:: $["ollama-models"], + + "text-completion" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("ollama-credentials") + .with_env_var("OLLAMA_HOST", "ollama-host"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-ollama", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("ollama-credentials") + .with_env_var("OLLAMA_HOST", "ollama-host"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-ollama", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/openai.jsonnet new file mode 100644 index 00000000..c3d482fd --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/openai.jsonnet @@ -0,0 +1,106 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/openai.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["openai-" + key]:: value, + }, + + "openai-max-output-tokens":: 4096, + "openai-temperature":: 0.0, + "openai-models":: models, + + "llm-models" +:: $["openai-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("openai-credentials") + .with_env_var("OPENAI_TOKEN", "openai-token") + .with_env_var("OPENAI_BASE_URL", "openai-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-openai", + "-p", + url.pulsar, + "-x", + std.toString($["openai-max-output-tokens"]), + "-t", + "%0.3f" % $["openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("openai-credentials") + .with_env_var("OPENAI_TOKEN", "openai-token") + .with_env_var("OPENAI_BASE_URL", "openai-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-openai", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["openai-max-output-tokens"]), + "-t", + "%0.3f" % $["openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/tgi.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/tgi.jsonnet new file mode 100644 index 00000000..91588dff --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/tgi.jsonnet @@ -0,0 +1,108 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-" + key]:: value, + }, + + "tgi-max-output-tokens":: 1024, + "tgi-temperature":: 0.0, + + "text-completion" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("tgi-credentials") + .with_env_var("TGI_BASE_URL", "tgi-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-tgi", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "-x", + std.toString($["tgi-max-output-tokens"]), + "-t", + "%0.3f" % $["tgi-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("tgi-credentials") + .with_env_var("TGI_BASE_URL", "tgi-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-tgi", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "--concurrency", + std.toString(concurrency), + "-x", + std.toString($["tgi-max-output-tokens"]), + "-t", + "%0.3f" % $["tgi-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/vertexai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/vertexai.jsonnet new file mode 100644 index 00000000..cb8c141f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/vertexai.jsonnet @@ -0,0 +1,120 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/vertexai.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vertexai-" + key]:: value, + }, + + "vertexai-private-key":: "/vertexai/private.json", + "vertexai-region":: "us-central1", + "vertexai-max-output-tokens":: 4096, + "vertexai-temperature":: 0.0, + "vertexai-models":: models, + + "llm-models" +:: $["vertexai-models"], + + "text-completion" +: { + + create:: function(engine) + + local cfgVol = engine.secretVolume( + "vertexai-creds", + "./vertexai", + { + "private.json": importstr "vertexai/private.json", + } + ); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_vertexai) + .with_command([ + "text-completion-vertexai", + "-p", + url.pulsar, + "-k", + $["vertexai-private-key"], + "-r", + $["vertexai-region"], + "-x", + std.toString($["vertexai-max-output-tokens"]), + "-t", + "%0.3f" % $["vertexai-temperature"], + ]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_volume_mount(cfgVol, "/vertexai"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + cfgVol, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local cfgVol = engine.secretVolume( + "vertexai-creds", + "./vertexai", + { + "private.json": importstr "vertexai/private.json", + } + ); + + local container = + engine.container("text-completion-rag") + .with_image(images.trustgraph_vertexai) + .with_command([ + "text-completion-vertexai", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-k", + $["vertexai-private-key"], + "-r", + $["vertexai-region"], + "-x", + std.toString($["vertexai-max-output-tokens"]), + "-t", + "%0.3f" % $["vertexai-temperature"], + ]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_volume_mount(cfgVol, "/vertexai"); + + local containerSet = engine.containers( + "text-completion-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + cfgVol, + containerSet, + service, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/vllm.jsonnet new file mode 100644 index 00000000..8b846bbf --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/llm/vllm.jsonnet @@ -0,0 +1,113 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/vllm.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-" + key]:: value, + }, + + "vllm-models":: models, + + "llm-models" +:: $["vllm-models"], + + "vllm-max-output-tokens":: 1024, + "vllm-temperature":: 0.0, + + "text-completion" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("vllm-credentials") + .with_env_var("VLLM_BASE_URL", "vllm-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-vllm", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "-x", + std.toString($["vllm-max-output-tokens"]), + "-t", + "%0.3f" % $["vllm-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("vllm-credentials") + .with_env_var("VLLM_BASE_URL", "vllm-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-vllm", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "--concurrency", + std.toString(concurrency), + "-x", + std.toString($["vllm-max-output-tokens"]), + "-t", + "%0.3f" % $["vllm-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/mcp/ddg-mcp-server.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/mcp/ddg-mcp-server.jsonnet new file mode 100644 index 00000000..04176f15 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/mcp/ddg-mcp-server.jsonnet @@ -0,0 +1,47 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "ddg-mcp-server-port":: 9870, + + "ddg-mcp-server" +: { + + create:: function(engine) + + local port = $["ddg-mcp-server-port"]; + + local container = + engine.container("ddg-mcp-server") + .with_image(images["ddg-mcp-server"]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_port(port, port, "mcp"); + + local containerSet = engine.containers( + "ddg-mcp-server", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(port, port, "mcp"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + mcp +:: { + "duckduckgo": { + "remote-name": "search", + local port = $["ddg-mcp-server-port"], + local url = "http://ddg-mcp-server:%s/mcp" % port, + "url": url, + } + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/model-hosting/cpu-tgi.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/model-hosting/cpu-tgi.jsonnet new file mode 100644 index 00000000..04b61566 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/model-hosting/cpu-tgi.jsonnet @@ -0,0 +1,68 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-service-" + key]:: value, + }, + + "tgi-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "tgi-service-cpus":: "8.0", + "tgi-service-memory":: "16G", + "tgi-service-storage":: "20G", + "tgi-service-hf-token":: null, + + "tgi-service" +: { + + create:: function(engine) + + local vol = engine.volume("tgi-storage") + .with_size($["tgi-service-storage"]); + + local container = + engine.container("tgi-service") + .with_image(images["tgi-service-cpu"]) + .with_command([ + "--model-id", + $["tgi-service-model"], + "--hostname", + "0.0.0.0", + "--port", + "7000", + "--cuda-graphs", + "0", + ]) + .with_environment({ + } + ( + if $["tgi-service-hf-token"] != null + then { HF_TOKEN: $["tgi-service-hf-token"] } + else {} + )) + .with_limits( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_reservations( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_port(7000, 7000, "tgi") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "tgi-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "tgi"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/model-hosting/intel-battlemage-vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/model-hosting/intel-battlemage-vllm.jsonnet new file mode 100644 index 00000000..7646398c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/model-hosting/intel-battlemage-vllm.jsonnet @@ -0,0 +1,98 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-service-" + key]:: value, + }, + + "vllm-service-model":: "mistralai/Mistral-Nemo-Instruct-2407", + "vllm-service-cpus":: "32.0", + "vllm-service-memory":: "48G", + "vllm-service-storage":: "48G", + "vllm-service-tokenizer-mode":: "mistral", + "vllm-service-datatype":: "float16", + "vllm-service-quantization":: "woq_int4", + "vllm-service-hf-token":: null, + "vllm-service-max-model-len":: 8192, + "vllm-service-max-num-seqs":: 16, + + "vllm-service" +: { + + create:: function(engine) + + local vol = engine.volume("vllm-storage") + .with_size($["vllm-service-storage"]); + + local container = + engine.container("vllm-service") + .with_image(images["vllm-service-intel-battlemage"]) + .with_entrypoint("") // Clear default entrypoint + .with_command([ + "python", + "-m", + "ipex_llm.vllm.xpu.entrypoints.openai.api_server", + "--model", + $["vllm-service-model"], + "--served-model-name", + "model", + "--host", + "0.0.0.0", + "--port", + "7000", + "--device", + "xpu", + "--dtype", + $["vllm-service-datatype"], + "--enforce-eager", + "--max-model-len", + std.toString($["vllm-service-max-model-len"]), + "--max-num-seqs", + std.toString($["vllm-service-max-num-seqs"]), + "--load-in-low-bit", + $["vllm-service-quantization"], + "--trust-remote-code", + "--tokenizer-mode", + $["vllm-service-tokenizer-mode"], + "--disable-sliding-window", + ]) + .with_environment({ + VLLM_WORKER_MULTIPROC_METHOD: "spawn", + SYCL_PI_LEVEL_ZERO_USE_IMMEDIATE_COMMANDLISTS: "1", + } + ( + if $["vllm-service-hf-token"] != null + then { HF_TOKEN: $["vllm-service-hf-token"] } + else {} + )) + .with_privileged(true) + .with_device("/dev/dri", "/dev/dri") + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_reservations( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_port(7000, 7000, "vllm") + .with_bind_mount("/dev/dri/by-path", "/dev/dri/by-path") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "vllm-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "vllm"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/model-hosting/intel-gaudi-tgi.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/model-hosting/intel-gaudi-tgi.jsonnet new file mode 100644 index 00000000..5af36b78 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/model-hosting/intel-gaudi-tgi.jsonnet @@ -0,0 +1,96 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-service-" + key]:: value, + }, + + "tgi-service-model":: "meta-llama/Llama-3.3-70B-Instruct", + "tgi-service-cpus":: "64.0", + "tgi-service-memory":: "64G", + "tgi-service-storage":: "50G", + "tgi-service-num-shard":: 8, + "tgi-service-max-input-tokens":: 4096, + "tgi-service-max-total-tokens":: 4096, + "tgi-service-max-batch-size":: 128, + "tgi-service-max-concurrent-requests":: 512, + "tgi-service-hf-token":: null, + + "tgi-service" +: { + + create:: function(engine) + + local vol = engine.volume("tgi-storage") + .with_size($["tgi-service-storage"]); + + local container = + engine.container("tgi-service") + .with_image(images["tgi-service-gaudi"]) + .with_command([ + "--model-id", + $["tgi-service-model"], + "--hostname", + "0.0.0.0", + "--port", + "7000", + "--sharded", + "true", + "--num-shard", + std.toString($["tgi-service-num-shard"]), + "--max-input-tokens", + std.toString($["tgi-service-max-input-tokens"]), + "--max-total-tokens", + std.toString($["tgi-service-max-total-tokens"]), + "--max-batch-size", + std.toString($["tgi-service-max-batch-size"]), + "--max-waiting-tokens", + "7", + "--max-concurrent-requests", + std.toString($["tgi-service-max-concurrent-requests"]), + "--cuda-graphs", + "0", + ]) + .with_runtime("habana") + .with_environment({ + HABANA_VISIBLE_DEVICES: "all", + OMPI_MCA_btl_vader_single_copy_mechanism: "none", + ENABLE_HPU_GRAPH: "true", + LIMIT_HPU_GRAPH: "true", + USE_FLASH_ATTENTION: "true", + FLASH_ATTENTION_RECOMPUTE: "true", + } + ( + if $["tgi-service-hf-token"] != null + then { HF_TOKEN: $["tgi-service-hf-token"] } + else {} + )) + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_reservations( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_port(7000, 7000, "tgi") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "tgi-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "tgi"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/model-hosting/intel-gaudi-vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/model-hosting/intel-gaudi-vllm.jsonnet new file mode 100644 index 00000000..81d31c14 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/model-hosting/intel-gaudi-vllm.jsonnet @@ -0,0 +1,76 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-service-" + key]:: value, + }, + + "vllm-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "vllm-service-cpus":: "64.0", + "vllm-service-memory":: "64G", + "vllm-service-storage":: "50G", + "vllm-service-tensor-parallel-size":: 8, + "vllm-service-hf-token":: null, + + "vllm-service" +: { + + create:: function(engine) + + local vol = engine.volume("vllm-storage") + .with_size($["vllm-service-storage"]); + + local container = + engine.container("vllm-service") + .with_image(images["vllm-service-gaudi"]) + .with_command([ + "--model", + $["vllm-service-model"], + "--served-model-name", + "model", + "--host", + "0.0.0.0", + "--port", + "7000", + "--tensor-parallel-size", + std.toString($["vllm-service-tensor-parallel-size"]), + ]) + .with_runtime("habana") + .with_environment({ + VLLM_SKIP_WARMUP: "true", + HABANA_VISIBLE_DEVICES: "all", + } + ( + if $["vllm-service-hf-token"] != null + then { HF_TOKEN: $["vllm-service-hf-token"] } + else {} + )) + .with_privileged(true) + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_reservations( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_port(7000, 7000, "vllm") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "vllm-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "vllm"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/model-hosting/intel-xpu-tgi.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/model-hosting/intel-xpu-tgi.jsonnet new file mode 100644 index 00000000..bc806874 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/model-hosting/intel-xpu-tgi.jsonnet @@ -0,0 +1,75 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-service-" + key]:: value, + }, + + "tgi-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "tgi-service-cpus":: "8.0", + "tgi-service-memory":: "16G", + "tgi-service-storage":: "20G", + "tgi-service-hf-token":: null, + + "tgi-service" +: { + + create:: function(engine) + + local vol = engine.volume("tgi-storage") + .with_size($["tgi-service-storage"]); + + local container = + engine.container("tgi-service") + .with_image(images["tgi-service-intel-xpu"]) + .with_command([ + "--model-id", + $["tgi-service-model"], + "--hostname", + "0.0.0.0", + "--port", + "7000", + "--cuda-graphs", + "0", + ]) + .with_environment({ + } + ( + if $["tgi-service-hf-token"] != null + then { HF_TOKEN: $["tgi-service-hf-token"] } + else {} + )) + .with_privileged(true) + .with_device("/dev/dri", "/dev/dri") + .with_ipc("host") + .with_group("video") + .with_group("render") + .with_capability("SYS_NICE") + .with_limits( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_reservations( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_port(7000, 7000, "tgi") + .with_bind_mount("/dev/dri/by-path", "/dev/dri/by-path") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "tgi-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "tgi"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/model-hosting/intel-xpu-vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/model-hosting/intel-xpu-vllm.jsonnet new file mode 100644 index 00000000..f6e82905 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/model-hosting/intel-xpu-vllm.jsonnet @@ -0,0 +1,97 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-service-" + key]:: value, + }, + + "vllm-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "vllm-service-cpus":: "8.0", + "vllm-service-memory":: "16G", + "vllm-service-storage":: "20G", + "vllm-service-datatype":: "float16", + "vllm-service-max-model-len":: 4096, + "vllm-service-max-num-seqs":: 16, + "vllm-service-hf-token":: null, + + "vllm-service" +: { + + create:: function(engine) + + local vol = engine.volume("vllm-storage") + .with_size($["vllm-service-storage"]); + + local container = + engine.container("vllm-service") + .with_image(images["vllm-service-intel-xpu"]) + .with_command([ + "python", + "-m", + "vllm.entrypoints.openai.api_server", + "--model", + $["vllm-service-model"], + "--served-model-name", + "model", + "--host", + "0.0.0.0", + "--port", + "7000", + "--device", + "xpu", + "--dtype", + $["vllm-service-datatype"], + "--enforce-eager", + "--max-model-len", + std.toString($["vllm-service-max-model-len"]), + "--max-num-seqs", + std.toString($["vllm-service-max-num-seqs"]), + "--block-size", + "64", + "--gpu-memory-util", + "0.85", + "--trust-remote-code", + "--disable-sliding-window", + ]) + .with_environment({ + VLLM_USE_V1: "1", + VLLM_WORKER_MULTIPROC_METHOD: "spawn", + } + ( + if $["vllm-service-hf-token"] != null + then { HF_TOKEN: $["vllm-service-hf-token"] } + else {} + )) + .with_privileged(true) + .with_device("/dev/dri", "/dev/dri") + .with_ipc("host") + .with_group("video") + .with_group("render") + .with_capability("SYS_NICE") + .with_limits( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_reservations( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_port(7000, 7000, "vllm") + .with_bind_mount("/dev/dri/by-path", "/dev/dri/by-path") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "vllm-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "vllm"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/model-hosting/nvidia-gpu-vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/model-hosting/nvidia-gpu-vllm.jsonnet new file mode 100644 index 00000000..d04876c4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/model-hosting/nvidia-gpu-vllm.jsonnet @@ -0,0 +1,72 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-service-" + key]:: value, + }, + + "vllm-service-model":: "mistralai/Mistral-7B-Instruct-v0.3", + "vllm-service-cpus":: "0.5", + "vllm-service-memory":: "1G", + "vllm-service-storage":: "50G", + "vllm-service-hf-token":: null, + + "vllm-service" +: { + + create:: function(engine) + + local vol = engine.volume("vllm-storage") + .with_size($["vllm-service-storage"]); + + local container = + engine.container("vllm-service") + .with_image(images["vllm-service-nvidia"]) + .with_command([ + "--model", + $["vllm-service-model"], + "--served-model-name", + "model", + "--host", + "0.0.0.0", + "--port", + "7000", + ]) + .with_runtime("nvidia") + .with_environment({ + VLLM_SKIP_WARMUP: "true", + } + ( + if $["vllm-service-hf-token"] != null + then { HF_TOKEN: $["vllm-service-hf-token"] } + else {} + )) + .with_privileged(true) + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_reservations( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_port(7000, 7000, "vllm") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "vllm-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "vllm"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/monitoring/grafana.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/monitoring/grafana.jsonnet new file mode 100644 index 00000000..dd0dddd7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/monitoring/grafana.jsonnet @@ -0,0 +1,124 @@ +local images = import "values/images.jsonnet"; +local loki = import "loki.jsonnet"; + +{ + + "prometheus" +: { + + create:: function(engine) + + local vol = engine.volume("prometheus-data").with_size("20G"); + + local cfgVol = engine.configVolume( + "prometheus-cfg", "prometheus", + { + "prometheus.yml": importstr "prometheus/prometheus.yml", + } + ); + + local container = + engine.container("prometheus") + .with_image(images.prometheus) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M") + .with_port(9090, 9090, "http") + .with_volume_mount(cfgVol, "/etc/prometheus/") + .with_volume_mount(vol, "/prometheus"); + + local containerSet = engine.containers( + "prometheus", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9090, 9090, "http"); + + engine.resources([ + cfgVol, + vol, + containerSet, + service, + ]) + + }, + + "grafana" +: { + + create:: function(engine) + + local vol = engine.volume("grafana-storage").with_size("20G"); + + local provDashVol = engine.configVolume( + "prov-dash", "grafana/provisioning/", + { + "dashboard.yml": + importstr "grafana/provisioning/dashboard.yml", + } + + ); + + local provDataVol = engine.configVolume( + "prov-data", "grafana/provisioning/", + { + "datasource.yml": + importstr "grafana/provisioning/datasource.yml", + } + + ); + + local dashVol = engine.configVolume( + "dashboards", "grafana/dashboards/", + { + "overview-dashboard.json": + importstr "grafana/dashboards/overview-dashboard.json", + "log-dashboard.json": + importstr "grafana/dashboards/log-dashboard.json", + } + + ); + + local container = + engine.container("grafana") + .with_image(images.grafana) + .with_environment({ + // GF_AUTH_ANONYMOUS_ORG_ROLE: "Admin", + // GF_AUTH_ANONYMOUS_ENABLED: "true", + // GF_ORG_ROLE: "Admin", + GF_ORG_NAME: "trustgraph.ai", + // GF_SERVER_ROOT_URL: "https://example.com", + }) + .with_limits("1.0", "256M") + .with_reservations("0.5", "256M") + .with_port(3000, 3000, "cassandra") + .with_volume_mount(vol, "/var/lib/grafana") + .with_volume_mount( + provDashVol, "/etc/grafana/provisioning/dashboards/" + ) + .with_volume_mount( + provDataVol, "/etc/grafana/provisioning/datasources/" + ) + .with_volume_mount( + dashVol, "/var/lib/grafana/dashboards/" + ); + + local containerSet = engine.containers( + "grafana", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(3000, 3000, "http"); + + engine.resources([ + vol, + provDashVol, + provDataVol, + dashVol, + containerSet, + service, + ]) + + }, + +} + loki + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/monitoring/loki.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/monitoring/loki.jsonnet new file mode 100644 index 00000000..0774f6cf --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/monitoring/loki.jsonnet @@ -0,0 +1,45 @@ +local images = import "values/images.jsonnet"; + +{ + + "loki" +: { + + create:: function(engine) + + local vol = engine.volume("loki-data").with_size("20G"); + + local cfgVol = engine.configVolume( + "loki-cfg", "loki", + { + "local-config.yaml": importstr "loki/local-config.yaml", + } + ); + + local container = + engine.container("loki") + .with_image(images.loki) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_port(3100, 3100, "http") + .with_volume_mount(cfgVol, "/etc/loki/") + .with_volume_mount(vol, "/loki"); + + local containerSet = engine.containers( + "loki", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(3100, 3100, "http"); + + engine.resources([ + cfgVol, + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/ocr/mistral-ocr.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/ocr/mistral-ocr.jsonnet new file mode 100644 index 00000000..f70e0cd6 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/ocr/mistral-ocr.jsonnet @@ -0,0 +1,49 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["mistral-" + key]:: value, + }, + + "pdf-decoder" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("mistral-credentials") + .with_env_var("MISTRAL_TOKEN", "mistral-token"); + + local container = + engine.container("mistral-ocr") + .with_image(images.trustgraph_flow) + .with_command([ + "pdf-ocr-mistral", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "mistral-ocr", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/ocr/ocr.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/ocr/ocr.jsonnet new file mode 100644 index 00000000..4f53aa5a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/ocr/ocr.jsonnet @@ -0,0 +1,37 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "pdf-decoder" +: { + + create:: function(engine) + + local container = + engine.container("pdf-ocr") + .with_image(images.trustgraph_ocr) + .with_command([ + "pdf-ocr", + "-p", + url.pulsar, + ]) + .with_limits("1.0", "512M") + .with_reservations("0.1", "512M"); + + local containerSet = engine.containers( + "pdf-ocr", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/azure-openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/azure-openai.jsonnet new file mode 100644 index 00000000..dcf706d4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/azure-openai.jsonnet @@ -0,0 +1,9 @@ +// Azure OpenAI LLM Model Definitions +// Model input is just text + +{ + "type": "string", + "description": "LLM model to use", + "default": "gpt-5.2", + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/azure.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/azure.jsonnet new file mode 100644 index 00000000..6ffa5fdf --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/azure.jsonnet @@ -0,0 +1,9 @@ +// Azure LLM Model Definitions +// Model input is just text + +{ + "type": "string", + "description": "LLM model to use", + "default": "phi4:14b", + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/bedrock.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/bedrock.jsonnet new file mode 100644 index 00000000..6c2c7e90 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/bedrock.jsonnet @@ -0,0 +1,84 @@ +// AWS Bedrock LLM Model Definitions +// Defines available models and their configurations for AWS Bedrock + +{ + "type": "string", + "description": "LLM model to use", + "default": "global.anthropic.claude-sonnet-4-5-20250929-v1:0", + "enum": [ + // ── Anthropic Claude ────────────────────────────────────── + { + id: "global.anthropic.claude-opus-4-6-v1", + description: "Claude Opus 4.6 (maximum intelligence)" + }, + { + id: "global.anthropic.claude-opus-4-5-20251101-v1:0", + description: "Claude Opus 4.5 (frontier coding + agents)" + }, + { + id: "global.anthropic.claude-sonnet-4-5-20250929-v1:0", + description: "Claude Sonnet 4.5 (complex agents + coding)" + }, + { + id: "global.anthropic.claude-haiku-4-5-20251001-v1:0", + description: "Claude Haiku 4.5 (fastest with near-frontier intelligence)" + }, + { + id: "global.anthropic.claude-opus-4-1-20250805-v1:0", + description: "Claude Opus 4.1 (agentic search + reasoning)" + }, + { + id: "global.anthropic.claude-sonnet-4-20250514-v1:0", + description: "Claude Sonnet 4.0" + }, + { + id: "anthropic.claude-3-5-haiku-20241022-v1:0", + description: "Claude 3.5 Haiku (legacy)" + }, + + // ── Meta Llama ──────────────────────────────────────────── + { + id: "us.meta.llama4-maverick-17b-instruct-v1:0", + description: "Llama 4 Maverick 17B (128 experts, 400B params, multimodal)" + }, + { + id: "us.meta.llama4-scout-17b-instruct-v1:0", + description: "Llama 4 Scout 17B (16 experts, 3.5M context)" + }, + { + id: "us.meta.llama3-3-70b-instruct-v1:0", + description: "Llama 3.3 70B Instruct" + }, + + // ── Mistral AI ──────────────────────────────────────────── + { + id: "us.mistral.mistral-large-2511-v1:0", + description: "Mistral Large 3 (flagship text, 128K context)" + }, + { + id: "us.mistral.magistral-small-2506-v1:0", + description: "Magistral Small 1.2 (reasoning, cost-effective)" + }, + + // ── DeepSeek ────────────────────────────────────────────── + { + id: "us.deepseek.r1-v1:0", + description: "DeepSeek-R1 (reasoning)" + }, + + // ── Amazon Nova ─────────────────────────────────────────── + { + id: "us.amazon.nova-pro-v1:0", + description: "Amazon Nova Pro (multimodal, balanced)" + }, + { + id: "us.amazon.nova-lite-v1:0", + description: "Amazon Nova Lite (fast, multimodal)" + }, + { + id: "us.amazon.nova-micro-v1:0", + description: "Amazon Nova Micro (text-only, cheapest)" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/chunking-param-types.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/chunking-param-types.jsonnet new file mode 100644 index 00000000..f82fa3f7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/chunking-param-types.jsonnet @@ -0,0 +1,25 @@ +// Chunk parameter type definitions + +{ + "chunk-size": { + "type": "integer", + "description": "Chunk size", + "placeholder": 2000, + "helper": "An integer, usually 2000 .. 8000", + "default": 2000, + "min": 0, + "max": 32768, + "required": true + }, + "chunk-overlap": { + "type": "integer", + "description": "Chunk overlap", + "placeholder": 50, + "helper": "An integer, usually 50 .. 100", + "default": 50, + "min": 0, + "max": 8000, + "required": true + }, +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/claude.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/claude.jsonnet new file mode 100644 index 00000000..b14a317c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/claude.jsonnet @@ -0,0 +1,39 @@ +// Claude LLM Model Definitions +// Defines available models and their configurations for Anthropic's Claude + +{ + "type": "string", + "description": "LLM model to use", + "default": "claude-sonnet-4-5-20250929", + "enum": [ + { + id: "claude-opus-4-6", + description: "Claude Opus 4.6 (maximum intelligence)" + }, + { + id: "claude-opus-4-5-20251101", + description: "Claude Opus 4.5 (frontier coding + agents)" + }, + { + id: "claude-sonnet-4-5-20250929", + description: "Claude Sonnet 4.5 (complex agents + coding)" + }, + { + id: "claude-haiku-4-5-20251001", + description: "Claude Haiku 4.5 (fast)" + }, + { + id: "claude-opus-4-1-20250805", + description: "Claude Opus 4.1 (agentic search + reasoning)" + }, + { + id: "claude-sonnet-4-20250514", + description: "Claude Sonnet 4.0" + }, + { + id: "claude-3-5-haiku-20241022", + description: "Claude 3.5 Haiku" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/cohere.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/cohere.jsonnet new file mode 100644 index 00000000..5a5865a3 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/cohere.jsonnet @@ -0,0 +1,35 @@ +// Cohere LLM Model Definitions +// Defines available models and their configurations for Cohere + +{ + "type": "string", + "description": "LLM model to use", + "default": "command-r-plus-08-2024", + "enum": [ + { + id: "command-r-plus-08-2024", + description: "Command R+ (August 2024)" + }, + { + id: "command-r-08-2024", + description: "Command R (August 2024)" + }, + { + id: "command-r-plus", + description: "Command R+ (legacy)" + }, + { + id: "command-r", + description: "Command R (legacy)" + }, + { + id: "command", + description: "Command" + }, + { + id: "command-light", + description: "Command Light" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/embeddings-fastembed.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/embeddings-fastembed.jsonnet new file mode 100644 index 00000000..477e25dc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/embeddings-fastembed.jsonnet @@ -0,0 +1,127 @@ +// Embeddings model definitions for fastembed +// Defines available models and their configurations for Fastembed + +{ + "type": "string", + "description": "Embeddings model to use", + "default": "sentence-transformers/all-MiniLM-L6-v2", + "enum": [ + { + "id": "sentence-transformers/all-MiniLM-L6-v2", + "description": "all-MiniLM-L6-v2" + }, + { + "id": "BAAI/bge-small-en-v1.5", + "description": "bge-small-en-v1.5" + }, + { + "id": "BAAI/bge-small-zh-v1.5", + "description": "bge-small-zh-v1.5" + }, + { + "id": "snowflake/snowflake-arctic-embed-xs", + "description": "snowflake-arctic-embed-xs" + }, + { + "id": "jinaai/jina-embeddings-v2-small-en", + "description": "jina-embeddings-v2-small-en" + }, + { + "id": "nomic-ai/nomic-embed-text-v1.5-Q", + "description": "nomic-embed-text-v1.5-Q" + }, + { + "id": "snowflake/snowflake-arctic-embed-s", + "description": "snowflake-arctic-embed-s" + }, + { + "id": "BAAI/bge-small-en", + "description": "bge-small-en" + }, + { + "id": "BAAI/bge-base-en-v1.5", + "description": "bge-base-en-v1.5" + }, + { + "id": "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2", + "description": "paraphrase-multilingual-MiniLM-L12-v2" + }, + { + "id": "Qdrant/clip-ViT-B-32-text", + "description": "clip-ViT-B-32-text" + }, + { + "id": "jinaai/jina-embeddings-v2-base-de", + "description": "jina-embeddings-v2-base-de" + }, + { + "id": "BAAI/bge-base-en", + "description": "bge-base-en" + }, + { + "id": "snowflake/snowflake-arctic-embed-m", + "description": "snowflake-arctic-embed-m" + }, + { + "id": "thenlper/gte-base", + "description": "gte-base" + }, + { + "id": "jinaai/jina-embeddings-v2-base-en", + "description": "jina-embeddings-v2-base-en" + }, + { + "id": "nomic-ai/nomic-embed-text-v1", + "description": "nomic-embed-text-v1" + }, + { + "id": "nomic-ai/nomic-embed-text-v1.5", + "description": "nomic-embed-text-v1.5" + }, + { + "id": "snowflake/snowflake-arctic-embed-m-long", + "description": "snowflake-arctic-embed-m-long" + }, + { + "id": "jinaai/jina-clip-v1", + "description": "jina-clip-v1" + }, + { + "id": "mixedbread-ai/mxbai-embed-large-v1", + "description": "mxbai-embed-large-v1" + }, + { + "id": "jinaai/jina-embeddings-v2-base-es", + "description": "jina-embeddings-v2-base-es" + }, + { + "id": "jinaai/jina-embeddings-v2-base-code", + "description": "jina-embeddings-v2-base-code" + }, + { + "id": "jinaai/jina-embeddings-v2-base-zh", + "description": "jina-embeddings-v2-base-zh" + }, + { + "id": "sentence-transformers/paraphrase-multilingual-mpnet-base-v2", + "description": "paraphrase-multilingual-mpnet-base-v2" + }, + { + "id": "snowflake/snowflake-arctic-embed-l", + "description": "snowflake-arctic-embed-l" + }, + { + "id": "BAAI/bge-large-en-v1.5", + "description": "bge-large-en-v1.5" + }, + { + "id": "thenlper/gte-large", + "description": "gte-large" + }, + { + "id": "intfloat/multilingual-e5-large", + "description": "multilingual-e5-large" + } + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/embeddings-huggingface.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/embeddings-huggingface.jsonnet new file mode 100644 index 00000000..32254a2b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/embeddings-huggingface.jsonnet @@ -0,0 +1,31 @@ +// Embeddings model definitions for fastembed +// Defines available models and their configurations for Fastembed + +{ + "type": "string", + "description": "Embeddings model to use", + "default": "sentence-transformers/all-MiniLM-L6-v2", + "enum": [ + { + "id": "all-MiniLM-L6-v2", + "description": "all-MiniLM-L6-v2" + }, + { + "id": "all-mpnet-base-v2", + "description": "all-mpnet-base-v2" + }, + { + "id": "all-distilroberta-v1", + "description": "all-distilroberta-v1" + }, + { + "id": "stsb-bert-large", + "description": "stsb-bert-large" + }, + { + "id": "sentence-camembert-large", + "description": "sentence-camembert-large" + } + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/embeddings-ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/embeddings-ollama.jsonnet new file mode 100644 index 00000000..c3f29fbb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/embeddings-ollama.jsonnet @@ -0,0 +1,23 @@ +// Embeddings model definitions for Ollama +// Defines available models and their configurations for Ollama + +{ + "type": "string", + "description": "Embeddings model to use", + "default": "all-minilm:latest", + "enum": [ + { + "id": "all-minilm:latest", + "description": "all-MiniLM-L6-v2" + }, + { + "id": "nomic-ai/nomic-embed-text-v1.5-Q", + "description": "nomic-embed-text-v1.5-Q" + }, + { + "id": "mixedbread-ai/mxbai-embed-large-v1", + "description": "mxbai-embed-large-v1" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/googleaistudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/googleaistudio.jsonnet new file mode 100644 index 00000000..6d454ea7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/googleaistudio.jsonnet @@ -0,0 +1,54 @@ +// Google AI Studio LLM Model Definitions +// Defines available models and their configurations for Google AI Studio + +{ + "type": "string", + "description": "LLM model to use", + "default": "gemini-2.5-flash-lite", + "enum": [ + // Gemini 3 models (preview) + { + id: "gemini-3-pro-preview", + description: "Gemini 3 Pro (preview)" + }, + { + id: "gemini-3-flash-preview", + description: "Gemini 3 Flash (preview)" + }, + + // Gemini 2.5 models + { + id: "gemini-2.5-pro", + description: "Gemini 2.5 Pro" + }, + { + id: "gemini-2.5-flash", + description: "Gemini 2.5 Flash" + }, + { + id: "gemini-2.5-flash-lite", + description: "Gemini 2.5 Flash Lite" + }, + + // Gemini 2.0 models + { + id: "gemini-2.0-flash-001", + description: "Gemini 2.0 Flash" + }, + { + id: "gemini-2.0-flash-lite-001", + description: "Gemini 2.0 Flash Lite" + }, + + // Gemma models + { + id: "gemma-3-27b", + description: "Gemma 3 27B" + }, + { + id: "gemma-3n-e4b", + description: "Gemma 3n E4B" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/llamafile.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/llamafile.jsonnet new file mode 100644 index 00000000..f6480aa9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/llamafile.jsonnet @@ -0,0 +1,9 @@ +// LlamaFile LLM Model Definitions +// Model input is just text + +{ + "type": "string", + "description": "LLM model to use", + "default": "phi4:14b", + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/lmstudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/lmstudio.jsonnet new file mode 100644 index 00000000..1c88da3c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/lmstudio.jsonnet @@ -0,0 +1,150 @@ +// LMStudio LLM Model Definitions +// Defines available models and their configurations for LMStudio + +{ + "type": "string", + "description": "LLM model to use", + "default": "llama3.1:70b", + "enum": [ + // Gemma3 models + { + id: "gemma3:1b", + description: "Gemma3 1B" + }, + { + id: "gemma3:4b", + description: "Gemma3 4B" + }, + { + id: "gemma3:12b", + description: "Gemma3 12B" + }, + { + id: "gemma3:27b", + description: "Gemma3 27B" + }, + + // Phi4 models + { + id: "phi4:mini:3.8b", + description: "Phi4 Mini 3.8B" + }, + { + id: "phi4:14b", + description: "Phi4 14B" + }, + + // DeepSeek-R1 models + { + id: "deepseek-r1:671b", + description: "DeepSeek-R1 671B" + }, + { + id: "deepseek-r1:70b", + description: "DeepSeek-R1 70B" + }, + { + id: "deepseek-r1:32b", + description: "DeepSeek-R1 32B" + }, + { + id: "deepseek-r1:14b", + description: "DeepSeek-R1 14B" + }, + { + id: "deepseek-r1:8b", + description: "DeepSeek-R1 8B" + }, + { + id: "deepseek-r1:7b", + description: "DeepSeek-R1 7B" + }, + { + id: "deepseek-r1:1.5b", + description: "DeepSeek-R1 1.5B" + }, + + // Llama3.1 models + { + id: "llama3.1:405b", + description: "Llama 3.1 405B" + }, + { + id: "llama3.1:70b", + description: "Llama 3.1 70B" + }, + { + id: "llama3.1:8b", + description: "Llama 3.1 8B" + }, + + // Gemma2 models + { + id: "gemma2:2b", + description: "Gemma2 2B" + }, + { + id: "gemma2:9b", + description: "Gemma2 9B" + }, + { + id: "gemma2:27b", + description: "Gemma2 27B" + }, + + // Qwen2.5 models + { + id: "qwen2.5:0.5b", + description: "Qwen2.5 0.5B" + }, + { + id: "qwen2.5:1.5b", + description: "Qwen2.5 1.5B" + }, + { + id: "qwen2.5:3b", + description: "Qwen2.5 3B" + }, + { + id: "qwen2.5:7b", + description: "Qwen2.5 7B" + }, + { + id: "qwen2.5:14b", + description: "Qwen2.5 14B" + }, + { + id: "qwen2.5:32b", + description: "Qwen2.5 32B" + }, + { + id: "qwen2.5:72b", + description: "Qwen2.5 72B" + }, + + // Mistral models + { + id: "mistral:7b", + description: "Mistral 7B" + }, + { + id: "mistral-nemo:12b", + description: "Mistral Nemo 12B" + }, + { + id: "mistral-large:123b", + description: "Mistral Large 123B" + }, + + // Command-R models + { + id: "command-r:35b", + description: "Command-R 35B" + }, + { + id: "command-r-plus:104b", + description: "Command-R Plus 104B" + }, + ], + "required": true +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/mistral.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/mistral.jsonnet new file mode 100644 index 00000000..07f75b81 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/mistral.jsonnet @@ -0,0 +1,61 @@ +// Mistral LLM Model Definitions +// Defines available models and their configurations for Mistral AI + +{ + "type": "string", + "description": "LLM model to use", + "default": "mistral-medium-2508", + "enum": [ + // Featured models + { + id: "mistral-medium-2508", + description: "Mistral Medium 3.1" + }, + { + id: "mistral-large-2512", + description: "Mistral Large 3" + }, + { + id: "mistral-small-2506", + description: "Mistral Small 3.2" + }, + { + id: "ministral-14b-2512", + description: "Ministral 3 14B" + }, + { + id: "ministral-8b-2512", + description: "Ministral 3 8B" + }, + { + id: "ministral-3b-2512", + description: "Ministral 3 3B" + }, + { + id: "magistral-medium-2509", + description: "Magistral Medium 1.2 (reasoning)" + }, + { + id: "magistral-small-2509", + description: "Magistral Small 1.2 (reasoning)" + }, + { + id: "devstral-2512", + description: "Devstral 2 (code)" + }, + // Other models + { + id: "codestral-2508", + description: "Codestral (code)" + }, + { + id: "pixtral-large-2411", + description: "Pixtral Large (vision)" + }, + { + id: "open-mistral-nemo", + description: "Open Mistral Nemo" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/ollama.jsonnet new file mode 100644 index 00000000..4ef98a6a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/ollama.jsonnet @@ -0,0 +1,150 @@ +// Ollama LLM Model Definitions +// Defines available models and their configurations for Ollama + +{ + "type": "string", + "description": "LLM model to use", + "default": "gemma3:12b", + "enum": [ + // Gemma3 models + { + id: "gemma3:1b", + description: "Gemma3 1B" + }, + { + id: "gemma3:4b", + description: "Gemma3 4B" + }, + { + id: "gemma3:12b", + description: "Gemma3 12B" + }, + { + id: "gemma3:27b", + description: "Gemma3 27B" + }, + + // Phi4 models + { + id: "phi4:mini:3.8b", + description: "Phi4 Mini 3.8B" + }, + { + id: "phi4:14b", + description: "Phi4 14B" + }, + + // DeepSeek-R1 models + { + id: "deepseek-r1:671b", + description: "DeepSeek-R1 671B" + }, + { + id: "deepseek-r1:70b", + description: "DeepSeek-R1 70B" + }, + { + id: "deepseek-r1:32b", + description: "DeepSeek-R1 32B" + }, + { + id: "deepseek-r1:14b", + description: "DeepSeek-R1 14B" + }, + { + id: "deepseek-r1:8b", + description: "DeepSeek-R1 8B" + }, + { + id: "deepseek-r1:7b", + description: "DeepSeek-R1 7B" + }, + { + id: "deepseek-r1:1.5b", + description: "DeepSeek-R1 1.5B" + }, + + // Llama3.1 models + { + id: "llama3.1:405b", + description: "Llama 3.1 405B" + }, + { + id: "llama3.1:70b", + description: "Llama 3.1 70B" + }, + { + id: "llama3.1:8b", + description: "Llama 3.1 8B" + }, + + // Gemma2 models + { + id: "gemma2:2b", + description: "Gemma2 2B" + }, + { + id: "gemma2:9b", + description: "Gemma2 9B" + }, + { + id: "gemma2:27b", + description: "Gemma2 27B" + }, + + // Qwen2.5 models + { + id: "qwen2.5:0.5b", + description: "Qwen2.5 0.5B" + }, + { + id: "qwen2.5:1.5b", + description: "Qwen2.5 1.5B" + }, + { + id: "qwen2.5:3b", + description: "Qwen2.5 3B" + }, + { + id: "qwen2.5:7b", + description: "Qwen2.5 7B" + }, + { + id: "qwen2.5:14b", + description: "Qwen2.5 14B" + }, + { + id: "qwen2.5:32b", + description: "Qwen2.5 32B" + }, + { + id: "qwen2.5:72b", + description: "Qwen2.5 72B" + }, + + // Mistral models + { + id: "mistral:7b", + description: "Mistral 7B" + }, + { + id: "mistral-nemo:12b", + description: "Mistral Nemo 12B" + }, + { + id: "mistral-large:123b", + description: "Mistral Large 123B" + }, + + // Command-R models + { + id: "command-r:35b", + description: "Command-R 35B" + }, + { + id: "command-r-plus:104b", + description: "Command-R Plus 104B" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/openai.jsonnet new file mode 100644 index 00000000..5039c982 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/openai.jsonnet @@ -0,0 +1,51 @@ +// OpenAI LLM Model Definitions +// Defines available models and their configurations for OpenAI's platform + +{ + "type": "string", + "description": "LLM model to use", + "default": "gpt-5.2", + "enum": [ + { + id: "gpt-5.2-pro", + description: "GPT-5.2 Pro (maximum intelligence, slow)" + }, + { + id: "gpt-5.2", + description: "GPT-5.2 (flagship, best general-purpose)" + }, + { + id: "gpt-5.2-codex", + description: "GPT-5.2 Codex (optimized for agentic coding)" + }, + { + id: "gpt-5.1", + description: "GPT-5.1 (previous flagship)" + }, + { + id: "gpt-5", + description: "GPT-5 (previous gen reasoning)" + }, + { + id: "gpt-4.1", + description: "GPT-4.1 (coding + long context, 1M tokens)" + }, + { + id: "gpt-4.1-mini", + description: "GPT-4.1 Mini (fast + affordable)" + }, + { + id: "gpt-4.1-nano", + description: "GPT-4.1 Nano (ultra-fast, cheapest)" + }, + { + id: "gpt-4o", + description: "GPT-4o (legacy multimodal)" + }, + { + id: "gpt-4o-mini", + description: "GPT-4o Mini (legacy, budget)" + }, + ], + "required": true +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/temperature-param-types.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/temperature-param-types.jsonnet new file mode 100644 index 00000000..f5523e88 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/temperature-param-types.jsonnet @@ -0,0 +1,15 @@ +// LLM temperature definitions + +{ + "llm-temperature": { + "type": "float", + "description": "LLM temperature", + "placeholder": 0.3, + "helper": "A floating point number between 0 and 1", + "default": 0.3, + "min": 0.0, + "max": 10.0, + "required": true + } +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/vertexai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/vertexai.jsonnet new file mode 100644 index 00000000..34318967 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/vertexai.jsonnet @@ -0,0 +1,81 @@ +// VertexAI LLM Model Definitions +// Defines available models and their configurations for Google's VertexAI platform + +{ + "type": "string", + "description": "LLM model to use", + "default": "gemini-2.5-flash-lite", + "enum": [ + // Gemini 3 models (preview) + { + id: "gemini-3-pro-preview", + description: "Gemini 3 Pro (preview)" + }, + { + id: "gemini-3-flash-preview", + description: "Gemini 3 Flash (preview)" + }, + + // Gemini 2.5 models + { + id: "gemini-2.5-pro", + description: "Gemini 2.5 Pro" + }, + { + id: "gemini-2.5-flash", + description: "Gemini 2.5 Flash" + }, + { + id: "gemini-2.5-flash-lite", + description: "Gemini 2.5 Flash Lite" + }, + + // Gemini 2.0 models + { + id: "gemini-2.0-flash-001", + description: "Gemini 2.0 Flash" + }, + { + id: "gemini-2.0-flash-lite-001", + description: "Gemini 2.0 Flash Lite" + }, + + // Gemma models + { + id: "gemma-3-27b", + description: "Gemma 3 27B" + }, + { + id: "gemma-3n-e4b", + description: "Gemma 3n E4B" + }, + + // Claude models on VertexAI + { + id: "claude-opus-4-6", + description: "Claude Opus 4.6" + }, + { + id: "claude-opus-4-5", + description: "Claude Opus 4.5" + }, + { + id: "claude-sonnet-4-5", + description: "Claude Sonnet 4.5" + }, + { + id: "claude-haiku-4-5", + description: "Claude Haiku 4.5" + }, + { + id: "claude-opus-4-1", + description: "Claude Opus 4.1" + }, + { + id: "claude-sonnet-4", + description: "Claude Sonnet 4" + }, + ], + "required": true +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/vllm.jsonnet new file mode 100644 index 00000000..120342b2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/parameters/vllm.jsonnet @@ -0,0 +1,18 @@ +// vLLM Model Definitions +// Defines available models and their configurations for vLLM +// vLLM works with any HuggingFace model. We have to use the model +// vLLM was initialised with + +{ + "type": "string", + "description": "LLM model to use", + "default": "model", + "enum": [ + // Llama 3.1 models + { + id: "model", + description: "Pre-defined model" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/profiles/memory-profile-low.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/profiles/memory-profile-low.jsonnet new file mode 100644 index 00000000..e6341062 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/profiles/memory-profile-low.jsonnet @@ -0,0 +1,169 @@ +// Low memory profile - reduces memory allocation across components. +// Include this at the END of your configuration to override default memory +// settings with lower values suitable for memory-constrained environments. +// +// Usage: {"name": "memory-profile-low", "parameters": {}} +// +// Note: This trades some performance headroom for reduced memory usage. +// Monitor for OOM errors under heavy load. + +{ + + // Override Pulsar stack memory settings + "pulsar" +: { + + // Zookeeper: 512M -> 300M + "zk-memory-limit":: "300M", + "zk-memory-reservation":: "200M", + "zk-heap":: "128m", + "zk-direct-memory":: "64m", + + // Bookie: 1024M -> 600M + "bookie-memory-limit":: "600M", + "bookie-memory-reservation":: "400M", + "bookie-heap":: "128m", + "bookie-direct-memory":: "128m", + + // Broker: 800M -> 512M + "broker-memory-limit":: "512M", + "broker-memory-reservation":: "400M", + "broker-heap":: "192m", + "broker-direct-memory":: "192m", + + // Pulsar-init: 256M -> 128M + "init-memory-limit":: "128M", + "init-memory-reservation":: "128M", + "init-heap":: "64m", + "init-direct-memory":: "64m", + + }, + + // Override Cassandra memory settings: 1000M -> 600M + "cassandra" +: { + "memory-limit":: "600M", + "memory-reservation":: "500M", + "heap":: "200M", + }, + + // Override Qdrant memory settings: 1024M -> 600M + // Also enables mmap for vectors/payloads (trades latency for memory) + "qdrant" +: { + "memory-limit":: "600M", + "memory-reservation":: "500M", + "memmap-threshold-kb":: "1", + "on-disk-payload":: "true", + }, + + // TrustGraph core services - 50% memory reservations + "api-gateway" +: { + "memory-reservation":: "256M", // 512M -> 256M + }, + + "chunker" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "config-svc" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "pdf-decoder" +: { + "memory-reservation":: "256M", // 512M -> 256M + }, + + "mcp-tool" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "mcp-server" +: { + "memory-reservation":: "128M", // 256M -> 128M + }, + + "metering" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "metering-rag" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "kg-store" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "kg-manager" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "prompt" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "prompt-rag" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "document-rag" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "document-embeddings" +: { + "memory-reservation":: "256M", // 512M -> 256M + }, + + "librarian" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "agent-manager" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + // Graph RAG services + "kg-extract-definitions" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "kg-extract-relationships" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "kg-extract-agent" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "kg-extract-ontology" +: { + "memory-reservation":: "150M", // 300M -> 150M + }, + + "kg-extract-objects" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "graph-rag" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "graph-embeddings" +: { + "memory-reservation":: "256M", // 512M -> 256M + }, + + // Structured data services + "nlp-query" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "structured-query" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "structured-diag" +: { + "memory-reservation":: "48M", // 96M -> 48M + }, + + // Init service + "init-trustgraph" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/agent-kg-extract.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/agent-kg-extract.txt new file mode 100644 index 00000000..36dbd74f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/agent-kg-extract.txt @@ -0,0 +1,28 @@ +Analyze the following text and extract both entity definitions and relationships. + +For definitions, extract entities and their explanations or descriptions. +For relationships, extract subject-predicate-object triples where subjects and objects are entities, and predicates are relationship types. + +Text: {{text}} + +Output format: JSONL (one JSON object per line, no array wrapper) +Each line must include a "type" field to distinguish between definitions and relationships. + +For definitions, output: +{"type": "definition", "entity": "entity_name", "definition": "definition_text"} + +For relationships, output: +{"type": "relationship", "subject": "subject_entity", "predicate": "relationship_type", "object": "object_entity_or_literal", "object-entity": true} + +Requirements: +- Each line must be a complete, valid JSON object +- No commas between lines +- No [ ] array brackets +- No markdown formatting or prefixes +- Do not provide explanations, only output JSONL + +Example output: +{"type": "definition", "entity": "DNA", "definition": "Deoxyribonucleic acid, a molecule carrying genetic instructions"} +{"type": "definition", "entity": "RNA", "definition": "Ribonucleic acid, essential for coding and gene expression"} +{"type": "relationship", "subject": "DNA", "predicate": "transcribes_to", "object": "RNA", "object-entity": true} +{"type": "relationship", "subject": "DNA", "predicate": "located_in", "object": "cell nucleus", "object-entity": true} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/agent-prompt.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/agent-prompt.txt new file mode 100644 index 00000000..9155d20f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/agent-prompt.txt @@ -0,0 +1,104 @@ +# ReAct Agent System Prompt + +You are an AI assistant that uses the ReAct (Reasoning + Acting) framework to solve problems through systematic reasoning and tool use. + +## Core Instructions + +For each user query, work through the problem step-by-step using this cycle: +1. **Thought**: Reason about the current situation and determine what you need to do next +2. **Action**: Take ONE specific action using an available tool +3. Wait for **Observation**: The system will provide the result of your action +4. Continue with the next **Thought** based on the observation + +**CRITICAL**: Generate exactly ONE Thought followed by ONE Action, then STOP. Do not generate multiple Thought/Action pairs in a single response. Do not generate Observations yourself - the system will provide them. + +## Response Format + +Use this exact format for each step: + +``` +Thought: [Your reasoning about what to do next - be specific about why this action is needed] +Action: [tool_name] +Args: { + "parameter_name": "value", + "another_parameter": 123, + "list_parameter": ["item1", "item2"] +} +``` + +When you have finished provide the final answer: + +``` +Thought: [Your reasoning about why the process is complete] +Final Answer: [The final answer] +``` + +When providing a final answer, do not provide an Action or Args. + +## Action Format Rules + +1. **Tool Name**: Write "Action: " followed by the exact tool name on its own line +2. **Arguments**: Write "Args: " followed by a valid JSON object containing all parameters +3. **JSON Requirements**: + - Use double quotes for all string keys and values + - Numbers don't need quotes: `"count": 5` + - Booleans: `"enabled": true` or `"enabled": false` + - Arrays: `"items": ["a", "b", "c"]` + - Nested objects: `"config": {"setting": "value"}` + - Null values: `"optional_field": null` +4. **Required Parameters**: Include all required parameters for the tool +5. **No Extra Text**: Don't add explanations or comments within the Action block +1. **Final answer**: Write "Final Answer: " followed by the final answer + +## Available Tools + +{% for tool in tools %}- **{{ tool.name }}**: {{ tool.description }} +{% for arg in tool.arguments %} - Required: `"{{ arg.name }}"` ({{ arg.type }}): {{ arg.description }} +{% endfor %} +{% endfor %} + +## Behavior Rules + +1. **One Step at a Time**: Generate exactly one Thought and one Action, then wait for the system to provide an Observation +2. **Be Specific**: Your Thought should clearly explain why you're taking the specific action +3. **Use Context**: Build on previous Observations to inform your next steps +4. **Error Handling**: If an action fails, reason about the error and try a different approach +5. **Completion**: When you have enough information to fully answer the user's query, generate a final Thought explaining your conclusion, but do not take further actions + +## Error Responses + +If an action fails, you'll see: +``` +Observation: Error: [specific error message] +``` + +When this happens: +- Generate a Thought analyzing what went wrong +- Take a corrective Action with different parameters or a different tool +- If a tool is completely unavailable, explain this limitation in your next Thought + +## Termination + +The conversation ends when: +- You determine you have sufficient information to answer the user's query completely and provide a final answer. +- You encounter an unrecoverable error that prevents task completion +- The system reaches the maximum iteration limit + +## Important Notes + +- **Never generate Observations yourself** - only the system provides these +- **Always validate your JSON** - malformed JSON will cause action failures +- **Stay focused** - each Thought should directly relate to solving the user's query +- **Be efficient** - choose actions that gather the most relevant information for the task + +# Proceed + +Question: {{question}} + +{% for h in history %} +Action: "{{h.action}}" +Args: { +{% for k, v in h.arguments.items() %} "{{k}}": "{{v}}" +{% endfor %}} +Observation: "{{h.observation}}" +{% endfor %} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/cohere.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/cohere.jsonnet new file mode 100644 index 00000000..9541e4c2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/cohere.jsonnet @@ -0,0 +1,42 @@ +// For Cohere. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/default-prompts.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/default-prompts.jsonnet new file mode 100644 index 00000000..3b173a5a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/default-prompts.jsonnet @@ -0,0 +1,233 @@ + +// Prompt templates. For tidy JSONNET use, don't change these templates +// here, but use over-rides in the prompt directory + +{ + + "system-template":: "You are a helpful assistant.", + + "templates":: { + + "question":: { + "prompt": "{{question}}", + }, + + "extract-definitions":: { + "prompt": importstr "extract-definitions.txt", + "response-type": "jsonl", + "object-schema": { + "type": "object", + "properties": { + "entity": { + "type": "string" + }, + "definition": { + "type": "string" + } + }, + "required": [ + "entity", + "definition" + ] + } + }, + + "extract-relationships":: { + "prompt": importstr "extract-relationships.txt", + "response-type": "jsonl", + "object-schema": { + "type": "object", + "properties": { + "subject": { + "type": "string" + }, + "predicate": { + "type": "string" + }, + "object": { + "type": "string" + }, + "object-entity": { + "type": "boolean" + } + }, + "required": [ + "subject", + "predicate", + "object", + "object-entity" + ] + } + }, + + "extract-topics":: { + "prompt": importstr "extract-topics.txt", + "response-type": "jsonl", + "object-schema": { + "type": "object", + "properties": { + "topic": { + "type": "string" + }, + "definition": { + "type": "string" + } + }, + "required": [ + "topic", + "definition" + ] + } + }, + + "extract-rows":: { + "prompt": importstr "extract-rows.txt", + "response-type": "jsonl", + }, + + "kg-prompt":: { + "prompt": importstr "kg-prompt.txt", + "response-type": "text", + }, + + "document-prompt":: { + "prompt": importstr "document-prompt.txt", + "response-type": "text", + }, + + "agent-react":: { + "prompt": importstr "agent-prompt.txt", + "response-type": "text" + }, + + "agent-kg-extract":: { + "prompt": importstr "agent-kg-extract.txt", + "response-type": "jsonl", + "object-schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { "const": "definition" }, + "entity": { "type": "string" }, + "definition": { "type": "string" } + }, + "required": ["type", "entity", "definition"] + }, + { + "type": "object", + "properties": { + "type": { "const": "relationship" }, + "subject": { "type": "string" }, + "predicate": { "type": "string" }, + "object": { "type": "string" }, + "object-entity": { "type": "boolean" } + }, + "required": ["type", "subject", "predicate", "object"] + } + ] + } + }, + + "schema-selection":: { + "prompt": importstr "schema-selection.txt", + "response-type": "json", + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "description": "An array of schema names that are relevant to answering the given question" + } + }, + + "graphql-generation":: { + "prompt": importstr "graphql-generation.txt", + "response-type": "json", + "schema": { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "The GraphQL query string generated to answer the question" + }, + "variables": { + "type": "object", + "description": "Object containing any GraphQL variables needed for the query", + "additionalProperties": true + }, + "confidence": { + "type": "number", + "minimum": 0.0, + "maximum": 1.0, + "description": "Float between 0.0-1.0 indicating confidence in the generated query" + } + }, + "required": ["query", "variables", "confidence"], + "additionalProperties": false + } + }, + + "diagnose-structured-data":: { + "prompt": importstr "diagnose-structured-data.txt", + "response-type": "json", + }, + + "diagnose-xml":: { + "prompt": importstr "diagnose-xml.txt", + "response-type": "json", + }, + "diagnose-json":: { + "prompt": importstr "diagnose-json.txt", + "response-type": "json", + }, + "diagnose-csv":: { + "prompt": importstr "diagnose-csv.txt", + "response-type": "json", + }, + + "extract-with-ontologies":: { + "prompt": importstr "ontology-prompt.txt", + "response-type": "jsonl", + "object-schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { "const": "entity" }, + "entity": { "type": "string" }, + "entity_type": { "type": "string" } + }, + "required": ["type", "entity", "entity_type"] + }, + { + "type": "object", + "properties": { + "type": { "const": "relationship" }, + "subject": { "type": "string" }, + "subject_type": { "type": "string" }, + "relation": { "type": "string" }, + "object": { "type": "string" }, + "object_type": { "type": "string" } + }, + "required": ["type", "subject", "subject_type", "relation", "object", "object_type"] + }, + { + "type": "object", + "properties": { + "type": { "const": "attribute" }, + "entity": { "type": "string" }, + "entity_type": { "type": "string" }, + "attribute": { "type": "string" }, + "value": { "type": "string" } + }, + "required": ["type", "entity", "entity_type", "attribute", "value"] + } + ] + } + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/diagnose-csv.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/diagnose-csv.txt new file mode 100644 index 00000000..ad9725ba --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/diagnose-csv.txt @@ -0,0 +1,370 @@ +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for data import pipelines, with particular expertise in CSV processing and delimiter-separated value formats. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured CSV data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## CSV Processing Expertise + +When working with CSV data, you must: + +1. **Analyze CSV Structure** - Examine headers, delimiters, quoting, and data patterns +2. **Identify Column Mappings** - Map source column names to target fields +3. **Handle Complex CSV Patterns** - Support various CSV formats including: + - Standard comma-separated values: `name,age,city` + - Alternative delimiters: tab-separated (TSV), pipe-separated, semicolon-separated + - Quoted fields with embedded delimiters: `"Last, First",25,"New York, NY"` + - Headers with spaces or special characters: `"Customer Name","Order Date","Total Amount"` + - Files with or without headers + - Multi-line fields with embedded newlines + +## CSV Format Configuration Guidelines + +For CSV format configurations, use these patterns: + +**Basic CSV Configuration:** +```json +{ + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + "delimiter": ",", + "quote_char": "\"", + "has_header": true, + "skip_rows": 0 + } + } +} +``` + +**Advanced CSV Options:** +```json +{ + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + "delimiter": "\t", // Tab-separated + "quote_char": "\"", + "escape_char": "\\", + "has_header": true, + "skip_rows": 2, // Skip metadata rows + "null_values": ["", "NULL", "N/A"], + "trim_whitespace": true, + "skip_blank_lines": true + } + } +} +``` + +**CRITICAL: Source Field Names in Mappings** + +When processing CSV files, the parser uses column headers (if present) or generates column indices as field names. Your source field names in mappings must match exactly: + +**CORRECT Example with Headers:** +CSV file: +```csv +Customer Name,Order Date,Total Amount,Status +John Smith,2024-01-15,1000.50,Active +Jane Doe,2024-01-16,750.25,Pending +``` + +Becomes parsed data: +```json +{ + "Customer Name": "John Smith", + "Order Date": "2024-01-15", + "Total Amount": "1000.50", + "Status": "Active" +} +``` + +Your mappings should use: +```json +{ + "source_field": "Customer Name", // ✅ Correct - matches header exactly + "source_field": "Order Date", // ✅ Correct - matches header exactly + "source_field": "Total Amount", // ✅ Correct - matches header exactly + "source_field": "Status" // ✅ Correct - matches header exactly +} +``` + +**CORRECT Example without Headers:** +CSV file without headers uses column indices: +```csv +John Smith,2024-01-15,1000.50,Active +Jane Doe,2024-01-16,750.25,Pending +``` + +Becomes parsed data: +```json +{ + "0": "John Smith", + "1": "2024-01-15", + "2": "1000.50", + "3": "Active" +} +``` + +Your mappings should use: +```json +{ + "source_field": "0", // ✅ Correct - first column + "source_field": "1", // ✅ Correct - second column + "source_field": "2", // ✅ Correct - third column + "source_field": "3" // ✅ Correct - fourth column +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **Source Data Format** + - Delimiter character (comma, tab, pipe, semicolon, etc.) + - Quote character and escape character + - **For CSV**: Does the file have headers? Sample structure + - Text encoding (UTF-8, Windows-1252, etc.) + - Any rows to skip (metadata, blank lines) + - How null/empty values are represented + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source column → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + - Date/time format conversions + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or invalid data + - Duplicate handling strategy + - Row-level validation rules + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## CSV Structure Analysis + +When presented with CSV data, analyze: + +1. **Delimiter Detection**: What character separates the fields? +2. **Header Presence**: Does the first row contain column names? +3. **Quote Pattern**: Are fields quoted? What quote character is used? +4. **Data Types**: What types are present in each column? +5. **Null Representation**: How are empty/null values represented? +6. **Special Characters**: Are there embedded commas, quotes, or newlines? +7. **Encoding Issues**: Are there any character encoding problems? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + // CSV-specific parsing options + // delimiter, quote_char, has_header, skip_rows, etc. + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` +- `split`, `strip_quotes` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` +- `parse_number`, `parse_currency` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` +- `clean_whitespace`, `normalize_encoding` + +**Date/Time Operations:** +- `parse_date`, `format_date`, `date_component` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` +- `numeric_range`, `date_range` + +## CSV-Specific Best Practices + +1. **Detect delimiters accurately** - test with sample data to confirm delimiter +2. **Handle quoted fields properly** - account for embedded delimiters and quotes +3. **Trim whitespace consistently** - decide whether to preserve or remove extra spaces +4. **Validate data types early** - catch type conversion errors at the field level +5. **Handle empty values explicitly** - distinguish between empty strings and nulls +6. **Account for encoding issues** - especially with international characters +7. **Validate row structure** - ensure consistent column counts across rows + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes +9. **Handle CSV-specific edge cases** like malformed quotes, inconsistent delimiters +10. **Test with sample data** to validate parsing configuration + +## Complete CSV Example + +Given this CSV structure: +```csv +Customer Name,Order Date,Total Amount,Currency,Status +"Smith, John",2024-01-15,1000.50,USD,Active +"Doe, Jane",2024-01-16,750.25,EUR,Pending +"Johnson, Bob",2024-01-17,,USD,Cancelled +``` + +The parser will: +1. Use `delimiter: ","` and `quote_char: "\""` to parse fields correctly +2. Use `has_header: true` to treat first row as column names +3. Create this parsed data structure for each record: + ```json + { + "Customer Name": "Smith, John", + "Order Date": "2024-01-15", + "Total Amount": "1000.50", + "Currency": "USD", + "Status": "Active" + } + ``` + +Generate this COMPLETE configuration: +```json +{ + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + "delimiter": ",", + "quote_char": "\"", + "has_header": true, + "trim_whitespace": true, + "null_values": ["", "NULL"] + } + }, + "mappings": [ + { + "source_field": "Customer Name", // ✅ Matches header exactly + "target_field": "customer_name", + "transforms": [{"type": "trim"}] + }, + { + "source_field": "Order Date", // ✅ Matches header exactly + "target_field": "order_date", + "transforms": [{"type": "to_date", "format": "YYYY-MM-DD"}], + "validation": [{"type": "required"}] + }, + { + "source_field": "Total Amount", // ✅ Matches header exactly + "target_field": "amount", + "transforms": [ + {"type": "to_float"}, + {"type": "default", "value": 0.0} + ] + }, + { + "source_field": "Currency", // ✅ Matches header exactly + "target_field": "currency_code", + "transforms": [{"type": "upper"}], + "validation": [{"type": "in_list", "values": ["USD", "EUR", "GBP"]}] + }, + { + "source_field": "Status", // ✅ Matches header exactly + "target_field": "order_status", + "transforms": [{"type": "lower"}] + } + ] +} +``` + +**KEY RULE: source_field names must match the column headers exactly, or use column indices (0, 1, 2, etc.) for files without headers.** + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the CSV structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to delimiter detection, header identification, quoting patterns, and data type inference: + +{{sample}} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/diagnose-json.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/diagnose-json.txt new file mode 100644 index 00000000..2fe6341d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/diagnose-json.txt @@ -0,0 +1,327 @@ +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for data import pipelines, with particular expertise in JSON processing and JSONPath expressions. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured JSON data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## JSON Processing Expertise + +When working with JSON data, you must: + +1. **Analyze JSON Structure** - Examine the hierarchy, array patterns, and object nesting +2. **Generate Proper JSONPath Expressions** - Create efficient JSONPath selectors for record extraction +3. **Handle Complex JSON Patterns** - Support various JSON formats including: + - Array of objects: `[{"name": "John", "age": 30}, {...}]` + - Nested object arrays: `{"data": {"records": [{"id": 1}, {...}]}}` + - Mixed hierarchies with both arrays and nested objects + - Single object records: `{"record": {"field1": "value1"}}` + +## JSONPath Expression Guidelines + +For JSON format configurations, use these JSONPath patterns: + +**Record Path Examples:** +- Root array: `$[*]` (for arrays at the root level) +- Nested arrays: `$.data.records[*]` or `$.response.items[*]` +- Single object: `$.record` (when there's one record per file) +- Deep nesting: `$.data.results.items[*]` + +**Field Access Patterns:** +- Direct properties: Use property names directly in mappings +- Nested properties: Use dot notation like `address.street` or `contact.email` +- Array elements: Use bracket notation like `tags[0]` for first element + +**CRITICAL: Source Field Names in Mappings** + +When processing JSON, the parser creates a flat or nested dictionary based on the record structure. Your source field names in mappings must match the actual property names in the parsed records: + +**CORRECT Example:** +```json +{ + "Country or Area": "Albania", + "Trade (USD)": "1000.50", + "metadata": { + "source": "UN", + "year": 2024 + } +} +``` + +Your mappings should use: +```json +{ + "source_field": "Country or Area", // ✅ Correct - matches property name + "source_field": "Trade (USD)", // ✅ Correct - matches property name + "source_field": "metadata.source", // ✅ Correct - nested property access + "source_field": "metadata.year" // ✅ Correct - nested property access +} +``` + +**JSON Format Configuration Template:** +```json +{ + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + "record_path": "$[*]", // JSONPath to extract records + "flatten_nested": true, // Whether to flatten nested objects + "array_handling": "expand" // How to handle arrays: expand, first, concat + } + } +} +``` + +**Alternative JSON Options:** +```json +{ + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + "record_path": "$.data.items[*]", // For nested array structures + "flatten_nested": false, // Keep nested structure + "null_value_handling": "skip" // skip, empty_string, or preserve + } + } +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **Source Data Format** + - JSON structure type (array of objects, nested objects, single records) + - **For JSON**: Sample structure, nesting patterns, array locations + - Sample data or field descriptions + - Any format-specific details (encoding, special null handling, etc.) + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source field → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + - Nested object flattening requirements + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or null values + - Duplicate handling strategy + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## JSON Structure Analysis + +When presented with JSON data, analyze: + +1. **Root Structure**: Is it an array, object, or nested structure? +2. **Record Location**: Where are individual records located in the hierarchy? +3. **Field Pattern**: How are field names and values structured? + - Direct properties: `{"name": "John"}` + - Nested objects: `{"contact": {"email": "john@example.com"}}` + - Arrays: `{"tags": ["red", "blue"]}` + - Mixed types: `{"data": [{"id": 1, "details": {"name": "John"}}]}` +4. **Data Types**: What types are present (strings, numbers, booleans, nulls, arrays, objects)? +5. **Hierarchy Depth**: How deeply nested are the records and fields? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + // JSON-specific parsing options + // record_path (JSONPath), flatten_nested, array_handling, etc. + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` +- `flatten_object`, `extract_array_element`, `join_array` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` + +## JSON-Specific Best Practices + +1. **Use efficient JSONPath expressions** - Prefer specific paths over broad searches +2. **Handle nested objects appropriately** - decide whether to flatten or preserve structure +3. **Consider array handling strategies** - expand arrays to multiple records or extract specific elements +4. **Account for null vs undefined** values in field mappings +5. **Handle mixed data types** within the same field across records +6. **Use appropriate flattening** for deeply nested structures + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes +9. **Handle JSON-specific edge cases** like empty arrays, null objects, mixed types + +## Complete JSON Example + +Given this JSON structure: +```json +{ + "data": { + "records": [ + { + "Country": "USA", + "Year": 2024, + "Amount": 1000.50, + "metadata": { + "source": "World Bank", + "confidence": 0.95 + } + } + ] + } +} +``` + +The parser will: +1. Use `record_path: "$.data.records[*]"` to extract record objects from the array +2. Create this parsed data structure for each record: + ```json + { + "Country": "USA", + "Year": 2024, + "Amount": 1000.50, + "metadata.source": "World Bank", // If flattened + "metadata.confidence": 0.95 // If flattened + } + ``` + +Generate this COMPLETE configuration: +```json +{ + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + "record_path": "$.data.records[*]", + "flatten_nested": true, + "array_handling": "expand" + } + }, + "mappings": [ + { + "source_field": "Country", // ✅ Matches property name + "target_field": "country_name" + }, + { + "source_field": "Year", // ✅ Matches property name + "target_field": "year", + "transforms": [{"type": "to_int"}] + }, + { + "source_field": "Amount", // ✅ Matches property name + "target_field": "amount", + "transforms": [{"type": "to_float"}] + }, + { + "source_field": "metadata.source", // ✅ Flattened nested field + "target_field": "data_source" + } + ] +} +``` + +**KEY RULE: source_field names must match the actual property names in the parsed JSON records, using dot notation for nested properties when flattened.** + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the JSON structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to JSON hierarchy, object patterns, array structures, and generate appropriate JSONPath expressions: + +{{sample}} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/diagnose-structured-data.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/diagnose-structured-data.txt new file mode 100644 index 00000000..84c4b8be --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/diagnose-structured-data.txt @@ -0,0 +1,309 @@ + +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for data import pipelines, with particular expertise in XML processing and XPath expressions. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## XML Processing Expertise + +When working with XML data, you must: + +1. **Analyze XML Structure** - Examine the hierarchy, namespaces, and element patterns +2. **Generate Proper XPath Expressions** - Create efficient XPath selectors for record extraction +3. **Handle Complex XML Patterns** - Support various XML formats including: + - Standard element structures: `John` + - Attribute-based fields: `USA` + - Mixed content and nested hierarchies + - Namespaced XML documents + +## XPath Expression Guidelines + +For XML format configurations, use these XPath patterns: + +**Record Path Examples:** +- Simple records: `//record` or `//customer` +- Nested records: `//data/records/record` or `//customers/customer` +- Absolute paths: `/ROOT/data/record` (will be converted to relative paths automatically) +- With namespaces: `//ns:record` or `//soap:Body/data/record` + +**Field Attribute Patterns:** +- When fields use name attributes: set `field_attribute: "name"` for `value` +- For other attribute patterns: set appropriate attribute name + +**CRITICAL: Source Field Names in Mappings** + +When using `field_attribute`, the XML parser extracts field names from the attribute values and creates a flat dictionary. Your source field names in mappings must match these extracted names: + +**CORRECT Example:** +```xml +Albania +1000.50 +``` + +Becomes parsed data: +```json +{ + "Country or Area": "Albania", + "Trade (USD)": "1000.50" +} +``` + +So your mappings should use: +```json +{ + "source_field": "Country or Area", // ✅ Correct - matches parsed field name + "source_field": "Trade (USD)" // ✅ Correct - matches parsed field name +} +``` + +**INCORRECT Example:** +```json +{ + "source_field": "Field[@name='Country or Area']", // ❌ Wrong - XPath not needed here + "source_field": "field[@name='Trade (USD)']" // ❌ Wrong - XPath not needed here +} +``` + +**XML Format Configuration Template:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//data/record", // XPath to find record elements + "field_attribute": "name" // For value pattern + } + } +} +``` + +**Alternative XML Options:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//customer", // Direct element-based records + // No field_attribute needed for standard XML + } + } +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **Source Data Format** + - File type (CSV, JSON, XML, Excel, fixed-width, etc.) + - **For XML**: Sample structure, namespace prefixes, record element patterns + - Sample data or field descriptions + - Any format-specific details (delimiters, encoding, namespaces, etc.) + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source field → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or invalid data + - Duplicate handling strategy + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## XML Structure Analysis + +When presented with XML data, analyze: + +1. **Document Root**: What is the root element? +2. **Record Container**: Where are individual records located? +3. **Field Pattern**: How are field names and values structured? + - Direct child elements: `John` + - Attribute-based: `John` + - Mixed patterns +4. **Namespaces**: Are there any namespace prefixes? +5. **Hierarchy Depth**: How deeply nested are the records? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "[csv|json|xml|fixed-width|excel]", + "encoding": "utf-8", + "options": { + // Format-specific parsing options + // For XML: record_path (XPath), field_attribute (if applicable) + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` + +## XML-Specific Best Practices + +1. **Use efficient XPath expressions** - Prefer specific paths over broad searches +2. **Handle namespace prefixes** when present +3. **Identify field attribute patterns** correctly +4. **Test XPath expressions** mentally against the provided structure +5. **Consider XML element vs attribute data** in field mappings +6. **Account for mixed content** and nested structures + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes + +## Complete XML Example + +Given this XML structure: +```xml + + + + USA + 2024 + 1000.50 + + + +``` + +The parser will: +1. Use `record_path: "/ROOT/data/record"` to find record elements +2. Use `field_attribute: "name"` to extract field names from the name attribute +3. Create this parsed data structure: `{"Country": "USA", "Year": "2024", "Amount": "1000.50"}` + +Generate this COMPLETE configuration: +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "/ROOT/data/record", + "field_attribute": "name" + } + }, + "mappings": [ + { + "source_field": "Country", // ✅ Matches parsed field name + "target_field": "country_name" + }, + { + "source_field": "Year", // ✅ Matches parsed field name + "target_field": "year", + "transforms": [{"type": "to_int"}] + }, + { + "source_field": "Amount", // ✅ Matches parsed field name + "target_field": "amount", + "transforms": [{"type": "to_float"}] + } + ] +} +``` + +**KEY RULE: source_field names must match the extracted field names, NOT the XML element structure.** + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the XML structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to XML hierarchy, element patterns, and generate appropriate XPath expressions: + +{{sample}} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/diagnose-xml.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/diagnose-xml.txt new file mode 100644 index 00000000..a3d8efa4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/diagnose-xml.txt @@ -0,0 +1,453 @@ +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for XML data import pipelines, with particular expertise in XML processing and XPath expressions. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured XML data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## XML Processing Expertise + +When working with XML data, you must: + +1. **Analyze XML Structure** - Examine the hierarchy, namespaces, and element patterns +2. **Generate Proper XPath Expressions** - Create efficient XPath selectors for record extraction +3. **Handle Complex XML Patterns** - Support various XML formats including: + - Standard element structures: `John` + - Attribute-based fields: `USA` + - Mixed content and nested hierarchies + - Namespaced XML documents + - CDATA sections and text nodes + +## XPath Expression Guidelines + +For XML format configurations, use these XPath patterns: + +**Record Path Examples:** +- Simple records: `//record` or `//customer` +- Nested records: `//data/records/record` or `//customers/customer` +- Absolute paths: `/ROOT/data/record` +- With namespaces: `//ns:record` or `//soap:Body/data/record` +- Attribute-filtered: `//record[@type='customer']` + +**Field Attribute Patterns:** +- When fields use name attributes: set `field_attribute: "name"` for `value` +- For other attribute patterns: set appropriate attribute name like `field_attribute: "id"` or `field_attribute: "type"` + +## CRITICAL: Source Field Names in Mappings + +The behavior depends on whether you use `field_attribute`: + +### With field_attribute (Attribute-Based Fields) + +When using `field_attribute`, the XML parser extracts field names from the attribute values and creates a flat dictionary: + +**Example:** +```xml + + Albania + 1000.50 + Active + +``` + +Parser configuration: +```json +{ + "record_path": "//record", + "field_attribute": "name" +} +``` + +Becomes parsed data: +```json +{ + "Country or Area": "Albania", + "Trade (USD)": "1000.50", + "Status": "Active" +} +``` + +Your mappings should use: +```json +{ + "source_field": "Country or Area", // ✅ Correct - matches parsed field name + "source_field": "Trade (USD)", // ✅ Correct - matches parsed field name + "source_field": "Status" // ✅ Correct - matches parsed field name +} +``` + +### Without field_attribute (Element-Based Fields) + +When NOT using `field_attribute`, use direct element names or XPath expressions: + +**Example:** +```xml + + Albania + 1000.50 + Active + + UN + 2024 + + +``` + +Parser configuration: +```json +{ + "record_path": "//record" + // No field_attribute specified +} +``` + +Your mappings should use: +```json +{ + "source_field": "country", // ✅ Direct element name + "source_field": "trade_amount", // ✅ Direct element name + "source_field": "metadata/source", // ✅ Nested element path + "source_field": "metadata/year" // ✅ Nested element path +} +``` + +## XML Format Configuration Templates + +**For Attribute-Based Fields:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//data/record", // XPath to find record elements + "field_attribute": "name", // Extract field names from 'name' attribute + "namespace_prefixes": { // Optional: define namespaces + "ns": "http://example.com/namespace" + } + } + } +} +``` + +**For Element-Based Fields:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//customer", // XPath to find record elements + "preserve_namespaces": true, // Optional: keep namespace info + "ignore_attributes": false // Optional: include element attributes + } + } +} +``` + +**For Complex XML with Namespaces:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//soap:Body//data:record", + "namespace_prefixes": { + "soap": "http://schemas.xmlsoap.org/soap/envelope/", + "data": "http://example.com/data" + }, + "field_attribute": "name" + } + } +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **XML Structure Details** + - Sample XML structure or schema + - Root element and record location + - Field organization (elements vs attributes) + - Namespace declarations and prefixes + - Text encoding (UTF-8, ISO-8859-1, etc.) + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source field → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or invalid data + - Duplicate handling strategy + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## XML Structure Analysis + +When presented with XML data, analyze: + +1. **Document Root**: What is the root element? +2. **Namespaces**: Are there namespace declarations? What prefixes are used? +3. **Record Container**: Where are individual records located in the hierarchy? +4. **Field Pattern**: How are field names and values structured? + - Direct child elements: `John` + - Attribute-based: `John` + - Mixed patterns: `John` +5. **Data Location**: Are values in element text, attributes, or both? +6. **Hierarchy Depth**: How deeply nested are the records? +7. **Special Content**: Any CDATA sections, mixed content, or special characters? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + // XML-specific parsing options + // record_path (XPath), field_attribute (if applicable), namespace_prefixes + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` +- `strip_cdata`, `decode_html_entities` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` +- `parse_xml_date`, `parse_iso_date` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` +- `extract_attribute`, `get_text_content` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` +- `valid_xml_name`, `namespace_check` + +## XML-Specific Best Practices + +1. **Use efficient XPath expressions** - Prefer specific paths over broad searches like `//` when possible +2. **Handle namespace prefixes** when present - define them in `namespace_prefixes` +3. **Identify field attribute patterns** correctly - determine if using `field_attribute` +4. **Test XPath expressions** mentally against the provided structure +5. **Consider XML element vs attribute data** in field mappings +6. **Account for mixed content** and nested structures +7. **Handle CDATA sections** and special characters properly +8. **Preserve or normalize whitespace** as appropriate +9. **Consider XML schema validation** if XSD is available + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes +9. **Handle XML-specific edge cases** like empty elements, mixed content, processing instructions + +## Complete XML Examples + +### Example 1: Attribute-Based Fields + +Given this XML structure: +```xml + + + + USA + 2024 + 1000.50 + + + +``` + +The parser will: +1. Use `record_path: "//data/record"` to find record elements +2. Use `field_attribute: "name"` to extract field names from the name attribute +3. Create this parsed data structure: `{"Country": "USA", "Year": "2024", "Amount": "1000.50"}` + +Generate this configuration: +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//data/record", + "field_attribute": "name" + } + }, + "mappings": [ + { + "source_field": "Country", // ✅ Matches parsed field name + "target_field": "country_name" + }, + { + "source_field": "Year", // ✅ Matches parsed field name + "target_field": "year", + "transforms": [{"type": "to_int"}] + }, + { + "source_field": "Amount", // ✅ Matches parsed field name + "target_field": "amount", + "transforms": [{"type": "to_float"}] + } + ] +} +``` + +### Example 2: Element-Based Fields + +Given this XML structure: +```xml + + + John Smith + john@example.com +
+ 123 Main St + New York +
+ + + 456 + 100.50 + + +
+
+``` + +Generate this configuration: +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//customer" + } + }, + "mappings": [ + { + "source_field": "name", // ✅ Direct element name + "target_field": "customer_name" + }, + { + "source_field": "email", // ✅ Direct element name + "target_field": "email_address" + }, + { + "source_field": "address/street", // ✅ Nested element path + "target_field": "street_address" + }, + { + "source_field": "address/city", // ✅ Nested element path + "target_field": "city" + }, + { + "source_field": "@id", // ✅ Attribute reference + "target_field": "customer_id", + "transforms": [{"type": "to_int"}] + } + ] +} +``` + +**KEY RULES:** +- With `field_attribute`: source_field names must match the extracted attribute values +- Without `field_attribute`: use direct element names, nested paths, or XPath expressions +- Use `@attribute` notation for XML element attributes + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the XML structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to XML hierarchy, element patterns, namespace usage, and generate appropriate XPath expressions: + +{{sample}} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/document-prompt.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/document-prompt.txt new file mode 100644 index 00000000..5e5d65ba --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/document-prompt.txt @@ -0,0 +1,7 @@ +Study the following context. Use only the information provided in the context in your response. Do not speculate if the answer is not found in the provided set of knowledge statements. + +Here is the context: +{{documents}} + +Use only the provided knowledge statements to respond to the following: +{{query}} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/extract-definitions.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/extract-definitions.txt new file mode 100644 index 00000000..410ecddf --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/extract-definitions.txt @@ -0,0 +1,28 @@ + +Study the following text and derive definitions for any discovered entities. +Do not provide definitions for entities whose definitions are incomplete +or unknown. + +Output each definition as a separate JSON object on its own line (JSONL format). +Each object must have fields: +- entity: the name of the entity +- definition: English text which defines the entity + + + +{{text}} + + + +Output format: JSONL (one JSON object per line, no array wrapper) +- Each line must be a complete, valid JSON object +- No commas between lines +- No [ ] array brackets +- No markdown formatting or prefixes +- Do not use special characters in the definition text +- Do not include null or unknown definitions + +Example output format: +{"entity": "photosynthesis", "definition": "The process by which plants convert sunlight into energy"} +{"entity": "chlorophyll", "definition": "Green pigment in plants that absorbs light"} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/extract-relationships.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/extract-relationships.txt new file mode 100644 index 00000000..31bcd2d4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/extract-relationships.txt @@ -0,0 +1,28 @@ + +Study the following text and derive entity relationships. For each +relationship, derive the subject, predicate and object of the relationship. + +Output each relationship as a separate JSON object on its own line (JSONL format). +Each object must have fields: +- subject: the subject of the relationship +- predicate: the predicate +- object: the object of the relationship +- object-entity: false if the object is a simple data type (name, value, date). true if it is an entity. + + + +{{text}} + + + +Output format: JSONL (one JSON object per line, no array wrapper) +- Each line must be a complete, valid JSON object +- No commas between lines +- No [ ] array brackets +- No markdown formatting or prefixes +- Do not use special characters in the text fields + +Example output format: +{"subject": "Earth", "predicate": "orbits", "object": "Sun", "object-entity": true} +{"subject": "Earth", "predicate": "has diameter", "object": "12742 km", "object-entity": false} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/extract-rows.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/extract-rows.txt new file mode 100644 index 00000000..7cea8b7d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/extract-rows.txt @@ -0,0 +1,27 @@ + +Study the following text and derive objects which match the schema provided. + +Output each discovered object as a separate JSON object on its own line (JSONL format). +Each object's fields must carry the name field specified in the schema. + + + +{{schema}} + + + +{{text}} + + + +Output format: JSONL (one JSON object per line, no array wrapper) +- Each line must be a complete, valid JSON object matching the schema +- No commas between lines +- No [ ] array brackets +- No markdown formatting or prefixes +- Do not provide explanations + +Example output format (assuming schema has fields: name, age, city): +{"name": "Alice", "age": 30, "city": "London"} +{"name": "Bob", "age": 25, "city": "Paris"} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/extract-topics.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/extract-topics.txt new file mode 100644 index 00000000..bb4b1193 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/extract-topics.txt @@ -0,0 +1,26 @@ +You are a helpful assistant that performs information extraction tasks for a provided text. +Read the provided text. You will identify topics and their definitions. + +Reading Instructions: +- Ignore document formatting in the provided text. +- Study the provided text carefully. + +Here is the text: +{{text}} + +Response Instructions: +- Output each topic as a separate JSON object on its own line (JSONL format) +- Each object must have keys "topic" and "definition" +- Do not respond with special characters +- Return only topics that are concepts and unique to the provided text + +Output format: JSONL (one JSON object per line, no array wrapper) +- Each line must be a complete, valid JSON object +- No commas between lines +- No [ ] array brackets +- No markdown formatting or prefixes +- Do not write any additional text or explanations + +Example output format: +{"topic": "machine learning", "definition": "A subset of AI that enables systems to learn from data"} +{"topic": "neural network", "definition": "A computing system inspired by biological neural networks"} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/gemini.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/gemini.jsonnet new file mode 100644 index 00000000..b9a1e0c0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/gemini.jsonnet @@ -0,0 +1,42 @@ +// For VertexAI Gemini. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/graphql-generation.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/graphql-generation.txt new file mode 100644 index 00000000..58af61dc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/graphql-generation.txt @@ -0,0 +1,59 @@ +You are a GraphQL query generation expert. Given a natural language question and provided database schemas, generate a valid GraphQL query embedded in valid JSON. + +## Question: +{{ question }} + +## Provided Schemas: +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %}- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif %}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif %}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ field.enum_values|join(', ') }}]{% endif %} +{% endfor %} +{% endfor %} + +## GraphQL Query Rules: +1. Use the schema names as GraphQL query fields (e.g., `customers`, `orders`) +2. Apply filters using the `where` parameter with nested filter objects +3. Available filter operators per field type: + - String fields: `eq`, `contains`, `startsWith`, `endsWith`, `in`, `not`, `not_in` + - Integer/Float fields: `eq`, `gt`, `gte`, `lt`, `lte`, `in`, `not`, `not_in` +4. Use `order_by` for sorting (field name as string) +5. Use `direction` for sort direction: `ASC` or `DESC` +6. Use `limit` to restrict number of results +7. Select specific fields in the query body + +## Task Instructions: +1. Analyze the question to identify: + - What data to retrieve (which fields to select) + - What filters to apply (where conditions) + - What sorting is needed (order_by, direction) + - How many results (limit) + +2. Generate a GraphQL query that: + - Uses only the provided schema names and field names + - Applies appropriate filters based on the question + - Selects relevant fields for the response + - Includes reasonable limits (default 100 if not specified) + +3. If variables are needed, include them in the response + +## Output Format: +Return ONLY a valid JSON object (no markdown, no code blocks) with these fields: +- "query": the GraphQL query string +- "variables": object with any GraphQL variables (empty object if none) +- "confidence": float between 0.0-1.0 indicating confidence in the query + +Important: Return raw JSON only, with no markdown formatting, no code blocks, and no backticks. + +## Examples: + +Question: "Show me customers from California" +{"query": "query { customers(where: {state: {eq: \"California\"}}, limit: 100) { customer_id name email state } }", "variables": {}, "confidence": 0.92} + +Question: "Top 10 products by price" +{"query": "query { products(order_by: \"price\", direction: DESC, limit: 10) { product_id name price category } }", "variables": {}, "confidence": 0.88} + +Question: "Recent orders over $100" +{"query": "query { orders(where: {total_amount: {gt: 100}, order_date: {gte: \"2024-01-01\"}}, order_by: \"order_date\", direction: DESC, limit: 50) { order_id customer_id total_amount order_date status } }", "variables": {}, "confidence": 0.96} + +Now generate the GraphQL query for the question above. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/kg-prompt.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/kg-prompt.txt new file mode 100644 index 00000000..431c0d73 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/kg-prompt.txt @@ -0,0 +1,8 @@ +Study the following set of knowledge statements. The statements are written in Cypher format that has been extracted from a knowledge graph. Use only the provided set of knowledge statements in your response. Do not speculate if the answer is not found in the provided set of knowledge statements. + +Here's the knowledge statements: +{% for edge in knowledge %}({{edge.s}})-[{{edge.p}}]->({{edge.o}}) +{%endfor%} + +Use only the provided knowledge statements to respond to the following: +{{query}} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/mixtral.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/mixtral.jsonnet new file mode 100644 index 00000000..cd56e7ef --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/mixtral.jsonnet @@ -0,0 +1,42 @@ +// For Mixtral. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/ontology-prompt.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/ontology-prompt.txt new file mode 100644 index 00000000..1b451d9c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/ontology-prompt.txt @@ -0,0 +1,82 @@ +You are a knowledge extraction expert. Your task is to find entities, relationships, and attributes in text based on a provided schema. + +## Entity Types + +These are the types of entities you should look for: + +{% for class_id, class_def in classes.items() %} +- **{{class_id}}**{% if class_def.subclass_of %} (subclass of {{class_def.subclass_of}}){% endif %}{% if class_def.comment %}: {{class_def.comment}}{% endif %} +{% endfor %} + +## Relationships + +These relationships connect entities to other entities: + +{% for prop_id, prop_def in object_properties.items() %} +- **{{prop_id}}**{% if prop_def.domain and prop_def.range %} ({{prop_def.domain}} → {{prop_def.range}}){% endif %}{% if prop_def.comment %}: {{prop_def.comment}}{% endif %} +{% endfor %} + +## Attributes + +These attributes describe entity properties (text, numbers, etc.): + +{% for prop_id, prop_def in datatype_properties.items() %} +- **{{prop_id}}**{% if prop_def.domain and prop_def.range %} ({{prop_def.domain}} → {{prop_def.range}}){% endif %}{% if prop_def.comment %}: {{prop_def.comment}}{% endif %} +{% endfor %} + +## Text to Analyze + +{{text}} + +## Your Task + +Extract the following from the text above: + +1. **Entities**: Things mentioned in the text and their types +2. **Relationships**: How entities relate to each other +3. **Attributes**: Properties of entities (like quantities, descriptions, etc.) + +## Output Format + +Output format: JSONL (one JSON object per line, no array wrapper) +Each line must include a "type" field to distinguish between entities, relationships, and attributes. + +For entities, output: +{"type": "entity", "entity": "entity name as it appears in text", "entity_type": "EntityType"} + +For relationships, output: +{"type": "relationship", "subject": "subject entity name", "subject_type": "SubjectType", "relation": "relationship_name", "object": "object entity name", "object_type": "ObjectType"} + +For attributes, output: +{"type": "attribute", "entity": "entity name", "entity_type": "EntityType", "attribute": "attribute_name", "value": "literal value"} + +## Important Rules + +1. **Entity names**: Use the exact text as it appears (e.g., "Cornish pasty", "beef") +2. **Types**: Use the EXACT type identifiers from the schema above (e.g., "fo/Recipe", "fo/Food") +3. **Relationships**: Use the EXACT relationship names from the schema (e.g., "fo/has_ingredient") +4. **Attributes**: Use the EXACT attribute names from the schema (e.g., "fo/serves") +5. **No array brackets**: Output one JSON object per line, not an array +6. **No markdown**: Do not wrap output in code blocks + +## Requirements + +- Each line must be a complete, valid JSON object +- No commas between lines +- No [ ] array brackets +- No markdown formatting, code blocks, or prefixes +- Do not provide explanations, only output JSONL + +## Example + +Input text: "Cornish pasty is a savory pastry filled with beef and potatoes. This recipe serves 4 people." + +Expected output: +{"type": "entity", "entity": "Cornish pasty", "entity_type": "fo/Recipe"} +{"type": "entity", "entity": "beef", "entity_type": "fo/Food"} +{"type": "entity", "entity": "potatoes", "entity_type": "fo/Food"} +{"type": "relationship", "subject": "Cornish pasty", "subject_type": "fo/Recipe", "relation": "fo/has_ingredient", "object": "beef", "object_type": "fo/Food"} +{"type": "relationship", "subject": "Cornish pasty", "subject_type": "fo/Recipe", "relation": "fo/has_ingredient", "object": "potatoes", "object_type": "fo/Food"} +{"type": "attribute", "entity": "Cornish pasty", "entity_type": "fo/Recipe", "attribute": "fo/serves", "value": "4 people"} + +Now extract entities, relationships, and attributes from the text above. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/openai.jsonnet new file mode 100644 index 00000000..5d232337 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/openai.jsonnet @@ -0,0 +1,42 @@ +// For OpenAI LLMs. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/schema-selection.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/schema-selection.txt new file mode 100644 index 00000000..39b180e5 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/schema-selection.txt @@ -0,0 +1,25 @@ +You are a database schema selection expert. Given a natural language question and available +database schemas, your job is to identify which schemas are most relevant to answer the question. + +## Available Schemas: +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}): {{ field.description }} +{% endfor %} + +{% endfor %} + +## Question: +{{ question }} + +## Instructions: +1. Analyze the question to understand what data is being requested +2. Examine each schema to understand what data it contains +3. Select ONLY the schemas that are directly relevant to answering the question +4. Return your answer as a JSON array of schema names + +## Response Format: +Return ONLY a JSON array of schema names, nothing else. +Example: ["customers", "orders", "products"] diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/slm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/slm.jsonnet new file mode 100644 index 00000000..48eb96d0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/prompts/slm.jsonnet @@ -0,0 +1,44 @@ +// For SLM. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + + + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/pulsar/pulsar-manager.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/pulsar/pulsar-manager.jsonnet new file mode 100644 index 00000000..c1ca4515 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/pulsar/pulsar-manager.jsonnet @@ -0,0 +1,40 @@ +local images = import "values/images.jsonnet"; + +{ + + "pulsar" +: { + + create:: function(engine) + +// FIXME: Should persist something? +// local volume = engine.volume(...) + + local container = + engine.container("pulsar") + .with_image(images.pulsar_manager) + .with_environment({ + SPRING_CONFIGURATION_FILE: "/pulsar-manager/pulsar-manager/application.properties", + }) + .with_limits("0.5", "1.4G") + .with_reservations("0.1", "1.4G") + .with_port(9527, 9527, "api") + .with_port(7750, 7750, "api2"); + + local containerSet = engine.containers( + "pulsar", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9527, 9527, "api") + .with_port(7750, 7750, "api2); + + engine.resources([ + containerSet, + service, + ]) + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/pulsar/pulsar.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/pulsar/pulsar.jsonnet new file mode 100644 index 00000000..d417322d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/pulsar/pulsar.jsonnet @@ -0,0 +1,222 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +// This is a Pulsar configuration. Non-standalone mode so we deploy +// individual components: bookkeeper, broker and zookeeper. +// +// This also deploys the TrustGraph 'admin' container which initialises +// TrustGraph-specific namespaces etc. + +{ + + "pulsar" +: { + + // Zookeeper memory settings (can be overridden by memory-profile) + "zk-memory-limit":: "512M", + "zk-memory-reservation":: "512M", + "zk-heap":: "256m", + "zk-direct-memory":: "256m", + + // Bookie memory settings (can be overridden by memory-profile) + "bookie-memory-limit":: "1024M", + "bookie-memory-reservation":: "1024M", + "bookie-heap":: "256m", + "bookie-direct-memory":: "256m", + + // Broker memory settings (can be overridden by memory-profile) + "broker-memory-limit":: "800M", + "broker-memory-reservation":: "800M", + "broker-heap":: "384m", + "broker-direct-memory":: "384m", + + // Pulsar-init memory settings (can be overridden by memory-profile) + "init-memory-limit":: "256M", + "init-memory-reservation":: "256M", + "init-heap":: "128m", + "init-direct-memory":: "128m", + + create:: function(engine) + + // Capture memory settings into locals (self refers to pulsar object here) + local zkMemLimit = self["zk-memory-limit"]; + local zkMemReserv = self["zk-memory-reservation"]; + local zkHeap = self["zk-heap"]; + local zkDirect = self["zk-direct-memory"]; + + local bookieMemLimit = self["bookie-memory-limit"]; + local bookieMemReserv = self["bookie-memory-reservation"]; + local bookieHeap = self["bookie-heap"]; + local bookieDirect = self["bookie-direct-memory"]; + + local brokerMemLimit = self["broker-memory-limit"]; + local brokerMemReserv = self["broker-memory-reservation"]; + local brokerHeap = self["broker-heap"]; + local brokerDirect = self["broker-direct-memory"]; + + local initMemLimit = self["init-memory-limit"]; + local initMemReserv = self["init-memory-reservation"]; + local initHeap = self["init-heap"]; + local initDirect = self["init-direct-memory"]; + + // Zookeeper volume + local zkVolume = engine.volume("zookeeper").with_size("1G"); + + // Zookeeper container + local zkContainer = + engine.container("zookeeper") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "bin/apply-config-from-env.py conf/zookeeper.conf && bin/generate-zookeeper-config.sh conf/zookeeper.conf && exec bin/pulsar zookeeper" + ]) + .with_limits("1", zkMemLimit) + .with_reservations("0.05", zkMemReserv) + .with_user("0:1000") + .with_volume_mount(zkVolume, "/pulsar/data/zookeeper") + .with_environment({ + "metadataStoreUrl": "zk:zookeeper:2181", + "PULSAR_MEM": "-Xms%s -Xmx%s -XX:MaxDirectMemorySize=%s" % [ + zkHeap, zkHeap, zkDirect, + ], + }) + .with_port(2181, 2181, "zookeeper") + .with_port(2888, 2888, "zookeeper2") + .with_port(3888, 3888, "zookeeper3"); + + // Pulsar cluster init container + local initContainer = + engine.container("pulsar-init") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "sleep 10 && bin/pulsar initialize-cluster-metadata --cluster cluster-a --zookeeper zookeeper:2181 --configuration-store zookeeper:2181 --web-service-url http://pulsar:8080 --broker-service-url pulsar://pulsar:6650", + ]) + .with_limits("1", initMemLimit) + .with_reservations("0.05", initMemReserv) + .with_environment({ + "PULSAR_MEM": "-Xms%s -Xmx%s -XX:MaxDirectMemorySize=%s" % [ + initHeap, initHeap, initDirect, + ], + }); + + + // Bookkeeper volume + local bookieVolume = engine.volume("bookie").with_size("20G"); + + // Bookkeeper container + local bookieContainer = + engine.container("bookie") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "bin/apply-config-from-env.py conf/bookkeeper.conf && exec bin/pulsar bookie" + // false ^ causes this to be a 'failure' exit. + ]) + .with_limits("1", bookieMemLimit) + .with_reservations("0.1", bookieMemReserv) + .with_user("0:1000") + .with_volume_mount(bookieVolume, "/pulsar/data/bookkeeper") + .with_environment({ + "clusterName": "cluster-a", + "zkServers": "zookeeper:2181", + "bookieId": "bookie", + "metadataStoreUri": "metadata-store:zk:zookeeper:2181", + "advertisedAddress": "bookie", + "BOOKIE_MEM": "-Xms%s -Xmx%s -XX:MaxDirectMemorySize=%s" % [ + bookieHeap, bookieHeap, bookieDirect, + ], + }) + .with_port(3181, 3181, "bookie"); + + // Pulsar broker, stateless (uses ZK and Bookkeeper for state) + local brokerContainer = + engine.container("pulsar") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "bin/apply-config-from-env.py conf/broker.conf && exec bin/pulsar broker" + ]) + .with_limits("1", brokerMemLimit) + .with_reservations("0.1", brokerMemReserv) + .with_environment({ + "metadataStoreUrl": "zk:zookeeper:2181", + "zookeeperServers": "zookeeper:2181", + "clusterName": "cluster-a", + "managedLedgerDefaultEnsembleSize": "1", + "managedLedgerDefaultWriteQuorum": "1", + "managedLedgerDefaultAckQuorum": "1", + "advertisedAddress": "pulsar", + "advertisedListeners": "external:pulsar://pulsar:6650,localhost:pulsar://localhost:6650", + "PULSAR_MEM": "-Xms%s -Xmx%s -XX:MaxDirectMemorySize=%s" % [ + brokerHeap, brokerHeap, brokerDirect, + ], + }) + .with_port(6650, 6650, "pulsar") + .with_port(8080, 8080, "admin"); + + // Container sets + local zkContainerSet = engine.containers( + "zookeeper", + [ + zkContainer, + ] + ); + + local initContainerSet = engine.containers( + "init-pulsar", + [ + initContainer, + ] + ); + + local bookieContainerSet = engine.containers( + "bookie", + [ + bookieContainer, + ] + ); + + local brokerContainerSet = engine.containers( + "pulsar", + [ + brokerContainer, + ] + ); + + // Zookeeper service + local zkService = + engine.service(zkContainerSet) + .with_port(2181, 2181, "zookeeper") + .with_port(2888, 2888, "zookeeper2") + .with_port(3888, 3888, "zookeeper3"); + + // Bookkeeper service + local bookieService = + engine.service(bookieContainerSet) + .with_port(3181, 3181, "bookie"); + + // Pulsar broker service + local brokerService = + engine.service(brokerContainerSet) + .with_port(6650, 6650, "pulsar") + .with_port(8080, 8080, "admin"); + + engine.resources([ + zkVolume, + bookieVolume, + zkContainerSet, + initContainerSet, + bookieContainerSet, + brokerContainerSet, + zkService, + bookieService, + brokerService, + ]) + + } + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-additionals.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-additionals.jsonnet new file mode 100644 index 00000000..3e9f3481 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-additionals.jsonnet @@ -0,0 +1,134 @@ +local decode = import "decode-config.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Custom engine that collects configVolume parts +local engine = { + + // Collection of all configVolume parts + configVolumes:: [], + + // Implement all required engine methods as no-ops + container:: function(name) { + with_image:: function(x) self, + with_command:: function(x) self, + with_environment:: function(x) self, + with_limits:: function(c, m) self, + with_reservations:: function(c, m) self, + with_port:: function(src, dest, name) self, + with_volume_mount:: function(vol, mnt) self, + with_user:: function(x) self, + with_runtime:: function(x) self, + with_privileged:: function(x) self, + with_ipc:: function(x) self, + with_capability:: function(x) self, + with_device:: function(hdev, cdev) self, + with_env_var_secrets:: function(vars) self, + }, + + volume:: function(name) { + with_size:: function(size) self, + }, + + // The key method - collects configVolume parts + configVolume:: function(name, dir, parts) + local collector = self + { + configVolumes: super.configVolumes + [ + { + dir: dir, + parts: parts, + } + ] + }; + { + // Return a dummy volume that has the collector in it + name: name, + with_size:: function(size) collector, + // Provide a way to get back to the collector + getCollector:: function() collector, + }, + + secretVolume:: function(name, dir, parts) { + with_size:: function(size) self, + }, + + envSecrets:: function(name) { + with_env_var:: function(name, key) self, + }, + + containers:: function(name, containers) self, + + service:: function(containers) { + with_port:: function(src, dest, name) self, + }, + + internalService:: function(containers) { + with_port:: function(src, dest, name) self, + }, + + resources:: function(res) + // Fold over resources and collect any configVolume state + local collected = std.foldl( + function(state, r) + if std.objectHasAll(r, 'getCollector') then + // Merge the configVolumes from the volume's collector into our state + local volumeCollector = r.getCollector(); + state + { + configVolumes: state.configVolumes + volumeCollector.configVolumes + } + else + state, + res, + self + ); + collected, +}; + +// Execute all component create() functions with our collecting engine +// Note: create:: is a hidden field, so we must use objectHasAll not objectHas +local result = std.foldl( + function(state, p) + if std.objectHasAll(p, 'create') then + // Pattern has create directly - call it + p.create(state) + else + state, + std.objectValues(patterns), + engine +); + +// Debug: show what we collected +local debug = { + numPatterns: std.length(std.objectValues(patterns)), + numConfigVolumes: std.length(result.configVolumes), +}; + +// Transform collected data into output format +local allFiles = std.flattenArrays([ + [ + { + // Remove trailing slash from dir to avoid double slashes + path: std.join("/", [std.rstripChars(cv.dir, "/"), filename]), + content: cv.parts[filename] + } + for filename in std.objectFields(cv.parts) + ] + for cv in result.configVolumes +]); + +// Deduplicate by path - use a map to keep only unique paths +local uniqueMap = std.foldl( + function(acc, item) acc + { [item.path]: item }, + allFiles, + {} +); + +// Convert back to array +local additionals = std.objectValues(uniqueMap); + +// Output the array +additionals diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-aks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-aks-k8s.jsonnet new file mode 100644 index 00000000..bcec1cbb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-aks-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "../engine/aks-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-docker-compose.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-docker-compose.jsonnet new file mode 100644 index 00000000..7f0dbabe --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-docker-compose.jsonnet @@ -0,0 +1,20 @@ + +local engine = import "../engine/docker-compose.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resources = std.foldl( + function(state, p) state + p.create(engine), + std.objectValues(patterns), + {} +); + +resources + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-eks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-eks-k8s.jsonnet new file mode 100644 index 00000000..f9de59cb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-eks-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "../engine/eks-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-gcp-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-gcp-k8s.jsonnet new file mode 100644 index 00000000..50037a5c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-gcp-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "../engine/gcp-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-minikube-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-minikube-k8s.jsonnet new file mode 100644 index 00000000..6fa64706 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-minikube-k8s.jsonnet @@ -0,0 +1,26 @@ + +local engine = import "../engine/minikube-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +// Extract resources using the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-noop.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-noop.jsonnet new file mode 100644 index 00000000..3ad735d9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-noop.jsonnet @@ -0,0 +1,20 @@ + +local engine = import "../engine/noop.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resources = std.foldl( + function(state, p) state + p.create(engine), + std.objectValues(patterns), + {} +); + +resources + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-ovh-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-ovh-k8s.jsonnet new file mode 100644 index 00000000..92f61041 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-ovh-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "../engine/ovh-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-podman-compose.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-podman-compose.jsonnet new file mode 100644 index 00000000..7f0dbabe --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-podman-compose.jsonnet @@ -0,0 +1,20 @@ + +local engine = import "../engine/docker-compose.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resources = std.foldl( + function(state, p) state + p.create(engine), + std.objectValues(patterns), + {} +); + +resources + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-scw-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-scw-k8s.jsonnet new file mode 100644 index 00000000..4f6c1d7a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-scw-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "../engine/scw-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-tg-configuration.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-tg-configuration.jsonnet new file mode 100644 index 00000000..59b4732a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/config-to-tg-configuration.jsonnet @@ -0,0 +1,15 @@ + +local engine = import "../engine/noop.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract configuration directly from patterns +patterns.configuration.configuration + + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/decode-config.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/decode-config.jsonnet new file mode 100644 index 00000000..759513c4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/renderers/decode-config.jsonnet @@ -0,0 +1,31 @@ + +local components = import "../components.jsonnet"; + +local apply = function(p, components) + + local base = { + + with:: function(k, v) self + { + [k] +:: v + }, + + with_params:: function(pars) + self + std.foldl( + function(obj, par) obj.with(par.key, par.value), + std.objectKeysValues(pars), + self + ), + + }; + + local component = base + components[p.name]; + + component.with_params(p.parameters); + +local decode = function(config) + local add = function(state, c) state + apply(c, components); + local patterns = std.foldl(add, config, {}); + patterns; + +decode + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/row-store/cassandra.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/row-store/cassandra.jsonnet new file mode 100644 index 00000000..f90526bc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/row-store/cassandra.jsonnet @@ -0,0 +1,77 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local cassandra = import "backends/cassandra.jsonnet"; + +cassandra + { + + "store-rows" +: { + + create:: function(engine) + + local container = + engine.container("store-rows") + .with_image(images.trustgraph_flow) + .with_command([ + "rows-write-cassandra", + "-p", + url.pulsar, + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-rows", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-rows" +: { + + create:: function(engine) + + local container = + engine.container("query-rows") + .with_image(images.trustgraph_flow) + .with_command([ + "rows-query-cassandra", + "-p", + url.pulsar, + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "512M") + .with_reservations("0.1", "512M"); + + local containerSet = engine.containers( + "query-rows", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/runtime-config/config-composer.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/runtime-config/config-composer.jsonnet new file mode 100644 index 00000000..e93ff044 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/runtime-config/config-composer.jsonnet @@ -0,0 +1,97 @@ +// Configuration Composer Module +// Orchestrates the complete configuration building process +// Combines all components into the final TrustGraph configuration + +local flow_builder = import "flow-builder.jsonnet"; +local interface_builder = import "interface-builder.jsonnet"; + +{ + // Main function to build the complete configuration + build: function(config_spec) + // Extract configuration parameters + local flow_blueprints = config_spec.flow_blueprints; + local default_flow_blueprint = config_spec.default_flow_blueprint; + local default_flow_id = config_spec.default_flow_id; + local flow_init_parameters = config_spec.flow_init_parameters; + + // Build all processors for the default flow + local blueprint_processors = flow_builder.build_blueprint_processors( + flow_blueprints, + default_flow_blueprint, + flow_init_parameters + ); + + local flow_processors = flow_builder.build_flow_processors( + flow_blueprints, + default_flow_blueprint, + default_flow_id, + flow_init_parameters + ); + + // Combine processors into flow objects + local processor_array = blueprint_processors + flow_processors; + local flow_objects = flow_builder.build_flow_objects(processor_array); + local active_flows = flow_builder.merge_flow_objects(flow_objects); + + // Build interfaces for the default flow + local default_flow_interfaces = interface_builder.build_interfaces( + flow_blueprints, + default_flow_blueprint, + default_flow_id, + flow_init_parameters + ); + + // Return object with nested configuration (for backwards compatibility) + { + // Create function (for backwards compatibility) + create: function(engine) {}, + + // The actual configuration object + configuration: { + // Prompts configuration + prompt: { + "system": config_spec.prompts["system-template"], + "template-index": std.objectFieldsAll(config_spec.prompts.templates), + } + { + ["template." + template.key]: template.value + for template in std.objectKeysValuesAll(config_spec.prompts.templates) + }, + + // Tools configuration + tool: { + [tool.id]: tool + for tool in config_spec.tools + }, + + // MCP configuration + mcp: config_spec.mcp, + + // Flow blueprints reference + "flow-blueprint": flow_blueprints, + + // Interface descriptions + "interface-description": config_spec.interface_descriptions, + + // Flow instances + "flow": { + [default_flow_id]: { + "description": "Default processing flow", + "blueprint-name": default_flow_blueprint, + "interfaces": default_flow_interfaces, + "parameters": flow_init_parameters, + }, + }, + + // Active flow processors + "active-flow": active_flows, + + // Token costs and parameter types + "token-cost": config_spec.token_costs, + "parameter-type": config_spec.parameter_types, + + // Collections configuration + "collection": config_spec.collection, + + }, + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/runtime-config/flow-builder.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/runtime-config/flow-builder.jsonnet new file mode 100644 index 00000000..0b142750 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/runtime-config/flow-builder.jsonnet @@ -0,0 +1,72 @@ +// Flow Builder Module +// Processes flow blueprints and builds complete flow configurations +// Handles {blueprint}, {id}, and parameter substitutions + +local param_processor = import "parameter-processor.jsonnet"; + +{ + // Builds blueprint-level processors with parameter substitution + // Processes the 'blueprint' section of flow blueprints + build_blueprint_processors: function(flow_blueprints, blueprint_name, parameters) + [ + [ + // Replace {blueprint} in the processor key + local key = std.strReplace(processor.key, "{blueprint}", blueprint_name); + local parts = std.splitLimit(key, ":", 2); + parts, + { + // Process each field in the processor configuration + [field.key]: + // First replace {blueprint}, then substitute parameters + local blueprint_replaced = std.strReplace(field.value, "{blueprint}", blueprint_name); + param_processor.substitute_parameters(blueprint_replaced, parameters) + for field in std.objectKeysValuesAll(processor.value) + } + ] + for processor in std.objectKeysValuesAll(flow_blueprints[blueprint_name].blueprint) + ], + + // Builds flow-level processors with parameter substitution + // Processes the 'flow' section of flow blueprints + build_flow_processors: function(flow_blueprints, blueprint_name, flow_id, parameters) + [ + [ + // Replace both {blueprint} and {id} in the processor key + local key = std.strReplace( + std.strReplace(processor.key, "{blueprint}", blueprint_name), + "{id}", flow_id + ); + local parts = std.splitLimit(key, ":", 2); + parts, + { + // Process each field in the processor configuration + [field.key]: + // Replace {blueprint} and {id}, then substitute parameters + local blueprint_replaced = std.strReplace(field.value, "{blueprint}", blueprint_name); + local id_replaced = std.strReplace(blueprint_replaced, "{id}", flow_id); + param_processor.substitute_parameters(id_replaced, parameters) + for field in std.objectKeysValuesAll(processor.value) + } + ] + for processor in std.objectKeysValuesAll(flow_blueprints[blueprint_name].flow) + ], + + // Combines blueprint and flow processors into flow objects + build_flow_objects: function(processor_array) + std.map( + function(item) { + [item[0][0]] +: { + [item[0][1]]: item[1] + } + }, + processor_array + ), + + // Merges all flow objects into a single flows_active configuration + merge_flow_objects: function(flow_objects) + std.foldr( + function(a, b) a + b, + flow_objects, + {} + ), +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/runtime-config/interface-builder.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/runtime-config/interface-builder.jsonnet new file mode 100644 index 00000000..2143e600 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/runtime-config/interface-builder.jsonnet @@ -0,0 +1,30 @@ +// Interface Builder Module +// Processes flow class interfaces with parameter substitution +// Handles both string interfaces and nested object interfaces + +local param_processor = import "parameter-processor.jsonnet"; + +{ + // Builds interfaces for a specific flow class and instance + // Processes the 'interfaces' section of flow classes + build_interfaces: function(flow_classes, class_name, flow_id, parameters) + local interface_spec = flow_classes[class_name].interfaces; + { + [interface.key]: + if std.isString(interface.value) then + // Simple string interface - apply all substitutions + local class_replaced = std.strReplace(interface.value, "{class}", class_name); + local id_replaced = std.strReplace(class_replaced, "{id}", flow_id); + param_processor.substitute_parameters(id_replaced, parameters) + else + // Complex object interface - process nested fields + { + [field.key]: + local class_replaced = std.strReplace(field.value, "{class}", class_name); + local id_replaced = std.strReplace(class_replaced, "{id}", flow_id); + param_processor.substitute_parameters(id_replaced, parameters) + for field in std.objectKeysValuesAll(interface.value) + } + for interface in std.objectKeysValuesAll(interface_spec) + }, +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/runtime-config/interface-descriptions.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/runtime-config/interface-descriptions.jsonnet new file mode 100644 index 00000000..f9dda8b0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/runtime-config/interface-descriptions.jsonnet @@ -0,0 +1,89 @@ +// Interface Descriptions Module +// Defines all external interfaces available in TrustGraph flows +// These are the 'endpoints' that external systems can interact with + +{ + // Document loading interfaces - for data ingestion + "document-load": { + "description": "Document loader", + "kind": "send", + "visible": true, + }, + "text-load": { + "description": "Text document loader", + "kind": "send", + "visible": true, + }, + + // Data storage interfaces - for processed data streams + "entity-contexts-load": { + "description": "Entity contexts loader", + "kind": "send", + }, + "triples-store": { + "description": "Triples loader", + "kind": "send", + }, + "graph-embeddings-store": { + "description": "Graph embeddings loader", + "kind": "send", + }, + "document-embeddings-store": { + "description": "Document embeddings loader", + "kind": "send", + }, + "objects-store": { + "description": "Object store", + "kind": "request-response", + }, + + // Query interfaces - for retrieving information + "graph-rag": { + "description": "GraphRAG service", + "kind": "request-response", + }, + "document-rag": { + "description": "ChunkRAG service", + "kind": "request-response", + }, + "triples": { + "description": "Triples query service", + "kind": "request-response", + }, + "graph-embeddings": { + "description": "Graph embeddings service", + "kind": "request-response", + }, + "document-embeddings": { + "description": "Document embeddings service", + "kind": "request-response", + }, + "objects": { + "description": "Object query service", + "kind": "request-response", + }, + + // Processing services - for text and data processing + "prompt": { + "description": "Prompt service", + "kind": "request-response", + }, + "agent": { + "description": "Agent service", + "kind": "request-response", + }, + "text-completion": { + "description": "Text completion service", + "kind": "request-response", + }, + + // Query translation services - for natural language queries + "nlp-query": { + "description": "NLP question to GraphQL service", + "kind": "request-response", + }, + "structured-query": { + "description": "Structured query service", + "kind": "request-response", + }, +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/runtime-config/parameter-processor.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/runtime-config/parameter-processor.jsonnet new file mode 100644 index 00000000..f317ae9f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/runtime-config/parameter-processor.jsonnet @@ -0,0 +1,38 @@ +// Parameter Processing Module +// Handles dynamic parameter replacement in configuration values +// Replaces {parameter_name} placeholders with actual parameter values + +{ + // Applies parameter substitutions to string values + // Only processes strings - leaves other types unchanged + substitute_parameters: function(value, parameters) + if std.isString(value) then + std.foldl( + function(acc, param) + // Only do string replacement if param.value is a string + if std.isString(param.value) then + std.strReplace(acc, "{" + param.key + "}", param.value) + else + acc, // Skip replacement for non-string parameter values + std.objectKeysValuesAll(parameters), + value + ) + else + value, + + // Applies parameter substitutions to all values in an object + // Recursively processes nested objects and arrays + substitute_parameters_in_object: function(obj, parameters) + if std.isObject(obj) then + { + [key]: $.substitute_parameters_in_object(obj[key], parameters) + for key in std.objectFields(obj) + } + else if std.isArray(obj) then + [ + $.substitute_parameters_in_object(item, parameters) + for item in obj + ] + else + $.substitute_parameters(obj, parameters), +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/runtime-config/tools.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/runtime-config/tools.jsonnet new file mode 100644 index 00000000..4947279e --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/runtime-config/tools.jsonnet @@ -0,0 +1,36 @@ +// Tools Configuration Module +// Defines all available tools that can be used by agents and flows +// Each tool specifies its interface, arguments, and behavior + +[ + // Knowledge query tool - queries the knowledge base + { + id: "knowledge-query", + name: "Knowledge query", + description: "This tool queries a knowledge base that holds information about domain-specific information. The question should be a natural language question.", + type: "knowledge-query", + collection: "default", + arguments: [ + { + name: "question", + type: "string", + description: "A simple natural language question.", + } + ] + }, + + // LLM completion tool - general purpose text completion + { + id: "llm-completion", + name: "LLM text completion", + type: "text-completion", + description: "This tool queries an LLM for non-domain-specific information. The question should be a natural language question.", + arguments: [ + { + name: "question", + type: "string", + description: "The question which should be asked of the LLM.", + } + ] + } +] \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/runtime-config/trustgraph-config.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/runtime-config/trustgraph-config.jsonnet new file mode 100644 index 00000000..d860ab3c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/runtime-config/trustgraph-config.jsonnet @@ -0,0 +1,90 @@ +// TrustGraph Main Configuration +// Clean, modular composition of TrustGraph configuration +// Uses specialized modules for different aspects of config building + +// Import dependencies +local images = import "../values/images.jsonnet"; +local url = import "../values/url.jsonnet"; +local prompts = import "../prompts/mixtral.jsonnet"; +local default_prompts = import "../prompts/default-prompts.jsonnet"; +local token_costs = import "../values/token-costs.jsonnet"; +local flow_blueprints = import "../flows/flow-blueprints.jsonnet"; +local config_composer = import "config-composer.jsonnet"; +local interface_descriptions = import "interface-descriptions.jsonnet"; +local tools = import "tools.jsonnet"; +local temperature_params = import "../parameters/temperature-param-types.jsonnet"; +local chunking_params = import "../parameters/chunking-param-types.jsonnet"; + +// Main configuration object +local configuration = { + + // Prompt templates + prompts:: default_prompts, + + // Tool definitions + tools:: tools, + + // MCP configuration + mcp:: {}, + + // Flow classes reference + "flow-blueprints":: flow_blueprints, + + // LLM model parameters + "llm-models" +:: {}, + + // Embeddings model parameters + "embeddings-models" +:: {}, + + collections +:: { + "trustgraph:default": { + "user": "default-user", + "collection": "default", + "name": "Default Collection", + "description": "Default collection", + "tags": ["default"], + }, + }, + + // Default model and flow parameters + flow_init_parameters:: { + "llm-model": $["llm-models"].default, + "llm-rag-model": $["llm-models"].default, + "llm-temperature": "%0.3f" % $["parameter-types"]["llm-temperature"].default, + "llm-rag-temperature": "%0.3f" % $["parameter-types"]["llm-temperature"].default, + "chunk-size": std.toString($["parameter-types"]["chunk-size"].default), + "chunk-overlap": std.toString($["parameter-types"]["chunk-overlap"].default), + "embeddings-model": $["embeddings-models"].default, + }, + + // Interface descriptions for external endpoints + "interface-descriptions":: interface_descriptions, + + // Parameter type definitions + "parameter-types":: { + "llm-model": $["llm-models"], + "embeddings-model": $["embeddings-models"], + } + chunking_params + temperature_params, + + // Token costs + "token-costs":: token_costs, + + // Build the complete configuration using the composer + configuration:: config_composer.build({ + flow_blueprints: $["flow-blueprints"], + default_flow_blueprint: "everything", + default_flow_id: "default", + flow_init_parameters: $["flow_init_parameters"], + prompts: $["prompts"], + tools: $["tools"], + mcp: $["mcp"], + interface_descriptions: $["interface-descriptions"], + parameter_types: $["parameter-types"], + token_costs: $["token-costs"], + collection: $["collections"], + }), + +} + default_prompts; + +// Export the final configuration +configuration diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/triple-store/cassandra.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/triple-store/cassandra.jsonnet new file mode 100644 index 00000000..05f4fd27 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/triple-store/cassandra.jsonnet @@ -0,0 +1,77 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local cassandra = import "backends/cassandra.jsonnet"; + +cassandra + { + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-cassandra", + "-p", + url.pulsar, + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-cassandra", + "-p", + url.pulsar, + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "512M") + .with_reservations("0.1", "512M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/triple-store/falkordb.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/triple-store/falkordb.jsonnet new file mode 100644 index 00000000..b4bace2d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/triple-store/falkordb.jsonnet @@ -0,0 +1,79 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local falkordb = import "backends/falkordb.jsonnet"; + +falkordb + { + + "falkordb-url":: "falkor://falkordb:6379", + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-falkordb", + "-p", + url.pulsar, + "-g", + $["falkordb-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-falkordb", + "-p", + url.pulsar, + "-g", + $["falkordb-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/triple-store/memgraph.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/triple-store/memgraph.jsonnet new file mode 100644 index 00000000..72597d72 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/triple-store/memgraph.jsonnet @@ -0,0 +1,84 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local memgraph = import "backends/memgraph.jsonnet"; + +memgraph + { + + "memgraph-url":: "bolt://memgraph:7687", + "memgraph-database":: "memgraph", + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-memgraph", + "-p", + url.pulsar, + "-g", + $["memgraph-url"], + "--database", + $["memgraph-database"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-memgraph", + "-p", + url.pulsar, + "-g", + $["memgraph-url"], + "--database", + $["memgraph-database"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/triple-store/neo4j.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/triple-store/neo4j.jsonnet new file mode 100644 index 00000000..08904715 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/triple-store/neo4j.jsonnet @@ -0,0 +1,79 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local neo4j = import "backends/neo4j.jsonnet"; + +neo4j + { + + "neo4j-url":: "bolt://neo4j:7687", + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-neo4j", + "-p", + url.pulsar, + "-g", + $["neo4j-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-neo4j", + "-p", + url.pulsar, + "-g", + $["neo4j-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/ui/workbench-ui.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/ui/workbench-ui.jsonnet new file mode 100644 index 00000000..f2048e47 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/ui/workbench-ui.jsonnet @@ -0,0 +1,32 @@ +local images = import "values/images.jsonnet"; + +{ + + "workbench-ui" +: { + + create:: function(engine) + + local container = + engine.container("workbench-ui") + .with_image(images["workbench-ui"]) + .with_limits("0.1", "256M") + .with_reservations("0.1", "256M") + .with_port(8888, 8888, "ui"); + + local containerSet = engine.containers( + "workbench-ui", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8888, 8888, "ui"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/values/images.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/values/images.jsonnet new file mode 100644 index 00000000..232aeaf3 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/values/images.jsonnet @@ -0,0 +1,36 @@ +local version = import "version.jsonnet"; +{ + cassandra: "docker.io/cassandra:5.0.6", +// Not working +// ceph: "quay.io/ceph/daemon:latest-reef", + neo4j: "docker.io/neo4j:2025.08.0-community-bullseye", + pulsar: "docker.io/apachepulsar/pulsar:4.1.0", + pulsar_manager: "docker.io/apachepulsar/pulsar-manager:v0.4.0", + etcd: "quay.io/coreos/etcd:v3.6.4", + minio: "docker.io/minio/minio:RELEASE.2025-09-07T16-13-09Z", + garage: "docker.io/dxflrs/garage:v2.1.0", + milvus: "docker.io/milvusdb/milvus:v2.5.17", + prometheus: "docker.io/prom/prometheus:v3.8.0", + grafana: "docker.io/grafana/grafana:12.3.0", + loki: "docker.io/grafana/loki:3.6.2", + trustgraph_base: "docker.io/trustgraph/trustgraph-base:" + version, + trustgraph_flow: "docker.io/trustgraph/trustgraph-flow:" + version, + trustgraph_ocr: "docker.io/trustgraph/trustgraph-ocr:" + version, + trustgraph_bedrock: "docker.io/trustgraph/trustgraph-bedrock:" + version, + trustgraph_vertexai: "docker.io/trustgraph/trustgraph-vertexai:" + version, + trustgraph_hf: "docker.io/trustgraph/trustgraph-hf:" + version, + trustgraph_mcp: "docker.io/trustgraph/trustgraph-mcp:" + version, + qdrant: "docker.io/qdrant/qdrant:v1.15.4", + memgraph_mage: "docker.io/memgraph/memgraph-mage:3.5", + memgraph_lab: "docker.io/memgraph/lab:3.5.0", + falkordb: "docker.io/falkordb/falkordb:v4.12.5", + "workbench-ui": "docker.io/trustgraph/workbench-ui:1.6.4", + "ddg-mcp-server": "docker.io/trustgraph/ddg-mcp-server:0.1.0", + "tgi-service-intel-xpu": "ghcr.io/huggingface/text-generation-inference:3.3.1-intel-xpu", + "tgi-service-cpu": "ghcr.io/huggingface/text-generation-inference:3.3.1-intel-cpu", + "tgi-service-gaudi": "ghcr.io/huggingface/text-generation-inference:sha-f140440-gaudi", + "vllm-service-intel-xpu": "docker.io/intel/vllm:0.8.0-xpu", + "vllm-service-gaudi": "docker.io/trustgraph/vllm-hpu:027f5645", + "vllm-service-nvidia": "docker.io/vllm/vllm-openai:latest", + "vllm-service-intel-battlemage": "docker.io/intelanalytics/ipex-llm-serving-xpu:0.2.0-b6", +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/values/token-costs.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/values/token-costs.jsonnet new file mode 100644 index 00000000..84c70373 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/values/token-costs.jsonnet @@ -0,0 +1,102 @@ +{ + "mistral.mistral-large-2407-v1:0": { + "model_name": "mistral.mistral-large-2407-v1:0", + "input_price": 0.000004, + "output_price": 0.000012 + }, + "meta.llama3-1-405b-instruct-v1:0": { + "model_name": "meta.llama3-1-405b-instruct-v1:0", + "input_price": 0.00000532, + "output_price": 0.000016 + }, + "mistral.mixtral-8x7b-instruct-v0:1": { + "model_name": "mistral.mixtral-8x7b-instruct-v0:1", + "input_price": 0.00000045, + "output_price": 0.0000007 + }, + "meta.llama3-1-70b-instruct-v1:0": { + "model_name": "meta.llama3-1-70b-instruct-v1:0", + "input_price": 0.00000099, + "output_price": 0.00000099 + }, + "meta.llama3-1-8b-instruct-v1:0": { + "model_name": "meta.llama3-1-8b-instruct-v1:0", + "input_price": 0.00000022, + "output_price": 0.00000022 + }, + "anthropic.claude-3-haiku-20240307-v1:0": { + "model_name": "anthropic.claude-3-haiku-20240307-v1:0", + "input_price": 0.00000025, + "output_price": 0.00000125 + }, + "anthropic.claude-3-5-sonnet-20240620-v1:0": { + "model_name": "anthropic.claude-3-5-sonnet-20240620-v1:0", + "input_price": 0.000003, + "output_price": 0.000015 + }, + "cohere.command-r-plus-v1:0": { + "model_name": "cohere.command-r-plus-v1:0", + "input_price": 0.0000030, + "output_price": 0.0000150 + }, + "ollama": { + "model_name": "ollama", + "input_price": 0, + "output_price": 0 + }, + "claude-3-haiku-20240307": { + "model_name": "claude-3-haiku-20240307", + "input_price": 0.00000025, + "output_price": 0.00000125 + }, + "claude-3-5-sonnet-20240620": { + "model_name": "claude-3-5-sonnet-20240620", + "input_price": 0.000003, + "output_price": 0.000015 + }, + "claude-3-opus-20240229": { + "model_name": "claude-3-opus-20240229", + "input_price": 0.000015, + "output_price": 0.000075 + }, + "claude-3-sonnet-20240229": { + "model_name": "claude-3-sonnet-20240229", + "input_price": 0.000003, + "output_price": 0.000015 + }, + "command-r-08-202": { + "model_name": "command-r-08-202", + "input_price": 0.0000025, + "output_price": 0.000010 + }, + "c4ai-aya-23-8b": { + "model_name": "c4ai-aya-23-8b", + "input_price": 0, + "output_price": 0 + }, + "llama.cpp": { + "model_name": "llama.cpp", + "input_price": 0, + "output_price": 0 + }, + "gpt-4o": { + "model_name": "gpt-4o", + "input_price": 0.000005, + "output_price": 0.000015 + }, + "gpt-4o-2024-08-06": { + "model_name": "gpt-4o-2024-08-06", + "input_price": 0.0000025, + "output_price": 0.000010 + }, + "gpt-4o-2024-05-13": { + "model_name": "gpt-4o-2024-05-13", + "input_price": 0.000005, + "output_price": 0.000015 + }, + "gpt-4o-mini": { + "model_name": "gpt-4o-mini", + "input_price": 0.00000015, + "output_price": 0.0000006 + } +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/values/url.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/values/url.jsonnet new file mode 100644 index 00000000..c3d4ad97 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/values/url.jsonnet @@ -0,0 +1,7 @@ +{ + pulsar: "pulsar://pulsar:6650", + pulsar_admin: "http://pulsar:8080", + milvus: "http://milvus:19530", + qdrant: "http://qdrant:6333", + object_store: "garage:3900", +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/vector-store/milvus.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/vector-store/milvus.jsonnet new file mode 100644 index 00000000..213027d1 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/vector-store/milvus.jsonnet @@ -0,0 +1,146 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local milvus = import "backends/milvus.jsonnet"; + +milvus + { + + "store-graph-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("store-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-write-milvus", + "-p", + url.pulsar, + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-graph-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("query-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-query-milvus", + "-p", + url.pulsar, + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "store-doc-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("store-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-write-milvus", + "-p", + url.pulsar, + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-doc-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("query-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-query-milvus", + "-p", + url.pulsar, + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/vector-store/pinecone.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/vector-store/pinecone.jsonnet new file mode 100644 index 00000000..3803bdcb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/vector-store/pinecone.jsonnet @@ -0,0 +1,160 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; + +{ + + "pinecone-cloud":: "aws", + "pinecone-region":: "us-east-1", + + "store-graph-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("store-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-write-pinecone", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "query-graph-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("query-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-query-pinecone", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "store-doc-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("store-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-write-pinecone", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "query-doc-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("query-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-query-pinecone", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/vector-store/qdrant.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/vector-store/qdrant.jsonnet new file mode 100644 index 00000000..06c9f0e1 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/vector-store/qdrant.jsonnet @@ -0,0 +1,250 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local qdrant = import "backends/qdrant.jsonnet"; + +qdrant + { + + "store-graph-embeddings" +: { + + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("store-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "graph-embeddings-write-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", memoryLimit) + .with_reservations("0.1", memoryReservation); + + local containerSet = engine.containers( + "store-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-graph-embeddings" +: { + + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("query-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "graph-embeddings-query-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", memoryLimit) + .with_reservations("0.1", memoryReservation); + + local containerSet = engine.containers( + "query-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "store-doc-embeddings" +: { + + "memory-limit":: "256M", + "memory-reservation":: "256M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("store-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "doc-embeddings-write-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", memoryLimit) + .with_reservations("0.1", memoryReservation); + + local containerSet = engine.containers( + "store-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-doc-embeddings" +: { + + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("query-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "doc-embeddings-query-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", memoryLimit) + .with_reservations("0.1", memoryReservation); + + local containerSet = engine.containers( + "query-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + }, + + "store-row-embeddings" +: { + + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("store-row-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "row-embeddings-write-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", memoryLimit) + .with_reservations("0.1", memoryReservation); + + local containerSet = engine.containers( + "store-row-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-row-embeddings" +: { + + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("query-row-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "row-embeddings-query-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", memoryLimit) + .with_reservations("0.1", memoryReservation); + + local containerSet = engine.containers( + "query-row-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/zip-readme.md b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/zip-readme.md new file mode 100644 index 00000000..0b117792 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.0/zip-readme.md @@ -0,0 +1,28 @@ + +Note! this is a subset of possible configurations, to generate your own +launch config use the config util... + +- Production: https://config-ui.demo.trustgraph.ai +- Early release: https://dev.config-ui.demo.trustgraph.ai + +The config util auto-generates deployment instructions for your +configuration, so that's the recommended way to deploy. + +---------------------------------------------------------------------------- + +These are launch configurations for TrustGraph. See https://trustgraph.ai for +the quickstart using docker compose. + +Hint for Linux: There are files here which get mounted as volumes inside +Docker Compose containers. This may trigger SELinux rules on your system, to +permit access insider the containers, use a command like this... + +chcon -Rt svirt_sandbox_file_t grafana/ prometheus/ + +The file vertexai/private.json is a placeholder for real GCP credentials if +you are using the VertexAI LLM. If you're using that in Docker Compose, +replace with your real credentials, and don't forget to permit access if you +are using Linux: + +chcon -Rt svirt_sandbox_file_t vertexai/ + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/README.md b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/README.md new file mode 100644 index 00000000..23039e9a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/README.md @@ -0,0 +1,125 @@ + +# TrustGraph template generation + +There are two utilities here: + +- `generate`: Generates a single Docker Compose launch configuration + based on configuration you provide. +- `generate-all`: Generates the release bundle for releases. You won't + need to use this unless you are managing releases. + +## `generate-all` + +Previously, this generates a full set of all vector DB / triple store / LLM +combinations, and put them in a single ZIP file. But this got out of +hand, so at the time of writing, this generates a single configuraton +using Qdrant vector DB, Ollama LLM support and Cassandra for a triple store. + +The combinations are contained withing the code, it takes two arguments: +- output ZIP file (is over-written) +- TrustGraph version number + +``` +templates/generate-all output.zip 0.18.11 +``` + +## `generate` + +This utility takes a configuration file describing the components to bundle, +and outputs a Docker Compose YAML file. + +### Input configuration + +The input configuration is a JSON file, an array of components to pull into +the configuration. For each component, there is a name and a (possibly empty) +object describing addtional parameters for that component. + +Example: + +``` +[ + { + "name": "cassandra", + "parameters": {} + }, + { + "name": "pulsar", + "parameters": {} + }, + { + "name": "qdrant", + "parameters": {} + }, + { + "name": "embeddings-hf", + "parameters": {} + }, + { + "name": "graph-rag", + "parameters": {} + }, + { + "name": "grafana", + "parameters": {} + }, + { + "name": "trustgraph", + "parameters": {} + }, + { + "name": "googleaistudio", + "parameters": { + "googleaistudio-temperature": 0.3, + "googleaistudio-max-output-tokens": 2048, + "googleaistudio-model": "gemini-1.5-pro-002" + } + }, + { + "name": "prompt-template", + "parameters": {} + }, + { + "name": "override-recursive-chunker", + "parameters": { + "chunk-size": 1000, + "chunk-overlap": 50 + } + }, + { + "name": "workbench-ui", + "parameters": {} + }, + { + "name": "agent-manager-react", + "parameters": {} + } +] +``` + +If you want to make your own configuration you could try changing the +configuration above: +- Components which are essential: pulsar, trustgraph, graph-rag, grafana, + agent-manager-react +- You need a triple store, one of: cassandra, memgraph, falkordb, neo4j +- You need a vector store, one of: qdrant, pinecone +- You need an LLM, one of: azure, azure-openai, bedrock, claude, cohere, + llamafile, ollama, openai, vertexai. +- You need an embeddings implementation, one of: embeddings-hf, + embeddings-ollama +- Optionally add the Workbench tool: workbench-ui + +Components have over-ridable parameters, look in the component definition +in `templates/components/` to see what you can override. + +### Invocation + +Two parameters: +- The output ZIP file +- The version number + +The configuration file described above is provided on standard input + +``` +templates/generate out.zip 0.18.9 < config.json +``` + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/cassandra.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/cassandra.jsonnet new file mode 100644 index 00000000..60f262d3 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/cassandra.jsonnet @@ -0,0 +1,50 @@ +local images = import "values/images.jsonnet"; + +{ + + "cassandra" +: { + + // Memory settings (can be overridden by memory-profile) + "memory-limit":: "1000M", + "memory-reservation":: "1000M", + "heap":: "300M", + + create:: function(engine) + + // Capture memory settings into locals + local memLimit = self["memory-limit"]; + local memReserv = self["memory-reservation"]; + local heap = self["heap"]; + + local vol = engine.volume("cassandra").with_size("20G"); + + local container = + engine.container("cassandra") + .with_image(images.cassandra) + .with_environment({ + JVM_OPTS: "-Xms%s -Xmx%s -Dcassandra.skip_wait_for_gossip_to_settle=0" % [ + heap, heap, + ], + }) + .with_limits("1.0", memLimit) + .with_reservations("0.5", memReserv) + .with_port(9042, 9042, "cassandra") + .with_volume_mount(vol, "/var/lib/cassandra"); + + local containerSet = engine.containers( + "cassandra", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9042, 9042, "api"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/ceph.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/ceph.jsonnet new file mode 100644 index 00000000..42406a95 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/ceph.jsonnet @@ -0,0 +1,446 @@ + +// +// This majorly does not work +// + +// This configuration fails primarily because it tries to treat Ceph +// like a stateless web app. You are currently pointing mon host to a +// generic service name (ceph-mon), but you aren't telling the Monitor +// process to assume that service identity. + +// To make this work with the "Service Name" approach across any +// engine, you need to fix the binding logic and the messenger protocol. + +// 1. The ceph.conf Fix + +// Need to enable Messenger v2 (modern) and tell the cluster to use +// the service names for its initial quorum. + +//Change this section: +// Ini, TOML + +// [global] +// fsid = %s +// mon initial members = mon0 +// mon host = ceph-mon:6789 +// ... + +// To this: +// Ini, TOML + +// [global] +// fsid = %s +// # Use the actual service names as members +// mon initial members = ceph-mon +// # Explicitly use the Service name (VIP) +// mon host = ceph-mon +// # Force modern protocol +// ms_bind_msgr2 = true +// ms_bind_msgr1 = true + +// 2. The MON Environment & Command Fix + +// This is the most critical part. Your current config sets MON_IP: +// "0.0.0.0". This causes the MON to bind to the Pod IP, which breaks +// when the Pod restarts. You must force it to bind to the Service IP. + +// Update mon_env: +// Code snippet + +// local mon_env = cluster_env + { +// CEPH_DAEMON: "MON", +// MON_NAME: "mon0", +// # Remove MON_IP: "0.0.0.0" +// # Add these: +// MON_ADDR: "ceph-mon", // This says to resolve the service name +// }; + +// Update mon_container command: You are currently wiping the MON data +// on every start (rm -rf /var/lib/ceph/mon/*). Stop doing that. If you +// wipe the data, you lose the cluster state and the OSDs will refuse to +// talk to the "new" MON. + +// Code snippet + +// .with_command([ +// "bash", "-c", +// # 1. Resolve the Service IP at runtime +// "export MON_IP=$(getent hosts ceph-mon | awk '{ print $1 }'); " + +// inject_mon_config + +// # 2. Start the daemon telling it its PUBLIC address is the Service VIP +// "exec /opt/ceph-container/bin/entrypoint.sh" +// ]) + +// 3. Why your current config "Majorly does not work" + +// The "Wipe" Logic: By running rm -rf /var/lib/ceph/mon/* in the +// MON container, you are creating a "New Cluster" every time the +// container starts. Since the OSDs store the fsid and cluster +// secrets, they will reject the "new" MON. + +// DNS Race Condition: Your OSD/MGR/RGW containers wait for +// ceph-mon DNS, which is good. However, if ceph-mon resolves to a +// Round Robin IP (multiple pods) rather than a stable ClusterIP, the +// connection will be flaky. + +// Messenger Protocol: Without ms_bind_msgr2, Ceph defaults to the +// old v1 protocol which is much more sensitive to NAT/Container IP +// mismatches. + +// SUMMARY + +// Component: ceph.conf +// Change: Add ms_bind_msgr2 = true +// Why: Supports modern container networking better. + +// Component: MON Start +// Change: Remove rm -rf +// Why: Ceph MONs must keep their database to maintain the cluster. + +// Component: MON Address +// Change: Use getent hosts ceph-mon +// Why: Forces the MON to advertise the Service VIP instead of its own Pod IP. + +// Component: MON Keyring +// Change: Ensure /etc/ceph/ceph.mon.keyring exists +// Why: MONs need their specific key to start. + +local images = import "values/images.jsonnet"; + +{ + with:: function(key, value) + self + { + ["ceph-" + key]:: value, + }, + + // Ceph credentials and cluster settings + "ceph-access-key":: "object-user", + "ceph-secret-key":: "object-password", + "ceph-cluster-id":: "ceph", + "ceph-fsid":: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", + + // Pool redundancy settings + // size: 2 = two replicas for fault tolerance + // min_size: 1 = allow degraded I/O if one OSD is down (prevents cluster freeze) + "ceph-pool-size":: "2", + "ceph-pool-min-size":: "1", + + ceph +: { + create:: function(engine) + // Pre-Shared Cryptographic Material - Config-as-Code Approach + // These keys are generated once and distributed to all daemons + // This ensures cryptographic consistency across the shared-nothing architecture + local admin_key = "AQBpxSBlAAAAABAAU99V6D8vS7Uu9y1S8W0iBg=="; + local mon_key = "AQBpxSBlAAAAABAAn7pL/pG9oT+X6vO7V1S6bg=="; + + // Ceph configuration file - rendered from Jsonnet variables + local ceph_conf = ||| + [global] + fsid = %s + mon initial members = mon0 + mon host = ceph-mon:6789 + public network = 0.0.0.0/0 + cluster network = 0.0.0.0/0 + osd pool default size = %s + osd pool default min size = %s + osd crush chooseleaf type = 0 + auth cluster required = cephx + auth service required = cephx + auth client required = cephx + ||| % [$["ceph-fsid"], $["ceph-pool-size"], $["ceph-pool-min-size"]]; + + // Admin keyring - distributed to all daemons + local admin_keyring = ||| + [client.admin] + key = %s + caps mds = "allow *" + caps mgr = "allow *" + caps mon = "allow *" + caps osd = "allow *" + ||| % [admin_key]; + + // Monitor keyring - used by MON for cluster operations + local mon_keyring = ||| + [mon.] + key = %s + caps mon = "allow *" + ||| % [mon_key]; + + // Config injection command - writes files before entrypoint + local inject_config = "printf '%s' > /etc/ceph/ceph.conf; printf '%s' > /etc/ceph/ceph.client.admin.keyring; " % [ceph_conf, admin_keyring]; + local inject_mon_config = inject_config + ("printf '%s' > /etc/ceph/ceph.mon.keyring; " % [mon_keyring]); + + // Data volumes - sized appropriately for production workloads + local vol_mon = engine.volume("ceph-mon").with_size("20G"); + local vol_mgr = engine.volume("ceph-mgr").with_size("20G"); + local vol_osd = engine.volume("ceph-osd").with_size("100G"); + local vol_rgw = engine.volume("ceph-rgw").with_size("20G"); + + // Isolated config volumes per daemon (ReadWriteOnce compatible) + // Each daemon gets its own non-shared config volume to support + // multi-node scheduling in K8s and other orchestrators + local vol_mon_config = engine.volume("ceph-mon-config").with_size("500M"); + local vol_mgr_config = engine.volume("ceph-mgr-config").with_size("500M"); + local vol_osd_config = engine.volume("ceph-osd-config").with_size("500M"); + local vol_rgw_config = engine.volume("ceph-rgw-config").with_size("500M"); + local vol_init_config = engine.volume("ceph-init-config").with_size("500M"); + + // Simplified cluster environment - Config-as-Code model + // No fetch logic needed - config is injected before entrypoint runs + local cluster_env = { + CLUSTER: $["ceph-cluster-id"], + FSID: $["ceph-fsid"], + KV_TYPE: "none", // No external coordination + }; + + // MON-specific environment + // Config-as-Code: MON uses injected config files, not fetch logic + // + // CRITICAL: MON_DATA_AVAIL="0" forces fresh cluster bootstrap + // The ceph/daemon entrypoint script (variables_stack.sh) uses this as a gate: + // - MON_DATA_AVAIL="0" -> run mkfs, create new cluster with our FSID + // - MON_DATA_AVAIL="1" -> attempt to join existing cluster (infinite probe loop) + // + // Network configuration for monmap generation + local mon_env = cluster_env + { + CEPH_DAEMON: "MON", + MON_NAME: "mon0", + MON_PORT: "6789", + MON_DATA_AVAIL: "0", + MON_IP: "0.0.0.0", + NETWORK_AUTO_DETECT: "4", + CEPH_PUBLIC_NETWORK: "0.0.0.0/0", + }; + + // Simplified daemon environments - Config-as-Code model + // All daemons receive config via injection, not fetch from MON + // This eliminates "static mode" errors and networking complexity + + // MGR-specific environment + local mgr_env = cluster_env + { + CEPH_DAEMON: "MGR", + MGR_NAME: "mgr0", + }; + + // OSD-specific environment + local osd_env = cluster_env + { + CEPH_DAEMON: "OSD", + OSD_TYPE: "directory", + }; + + // RGW-specific environment + local rgw_env = cluster_env + { + CEPH_DAEMON: "RGW", + RGW_NAME: "rgw0", + RGW_FRONTEND_PORT: "7480", + }; + + // MON (Monitor) container - cluster state and quorum + // Config-as-Code: Injects pre-shared keys before entrypoint + // CRITICAL: Wipes /var/lib/ceph/mon/* on every start to force fresh bootstrap + // This ensures MON always uses our FSID and doesn't inherit stale cluster state + local mon_container = + engine.container("ceph-mon") + .with_image(images.ceph) + .with_environment(mon_env) + .with_command([ + "bash", "-c", + "rm -rf /var/lib/ceph/mon/*; " + + inject_mon_config + + "exec /opt/ceph-container/bin/entrypoint.sh" + ]) + .with_limits("1.0", "1536M") + .with_reservations("0.5", "1024M") + .with_port(6789, 6789, "mon") + .with_port(3300, 3300, "mon-msgr2") + .with_volume_mount(vol_mon, "/var/lib/ceph/mon") + .with_volume_mount(vol_mon_config, "/etc/ceph"); + + // MGR (Manager) container - cluster management and dashboard + // Config-as-Code: Uses injected config files with pre-shared keys + // DNS wait ensures MON is available before MGR connects + local mgr_container = + engine.container("ceph-mgr") + .with_image(images.ceph) + .with_environment(mgr_env) + .with_command([ + "bash", "-c", + "until getent hosts ceph-mon; do echo 'Waiting for MON DNS...'; sleep 2; done; " + + inject_config + + "exec /opt/ceph-container/bin/entrypoint.sh" + ]) + .with_limits("1.0", "1536M") + .with_reservations("0.5", "1024M") + .with_port(7000, 7000, "mgr") + .with_port(8443, 8443, "dashboard") + .with_port(9283, 9283, "prometheus") + .with_volume_mount(vol_mgr, "/var/lib/ceph/mgr") + .with_volume_mount(vol_mgr_config, "/etc/ceph"); + + // OSD (Object Storage Daemon) - actual data storage + // Config-as-Code: Uses injected config files with pre-shared keys + // Increased resources to prevent OOM during recovery operations + // DNS wait ensures MON is available before OSD connects + local osd_container = + engine.container("ceph-osd") + .with_image(images.ceph) + .with_environment(osd_env) + .with_command([ + "bash", "-c", + "until getent hosts ceph-mon; do echo 'Waiting for MON DNS...'; sleep 2; done; " + + inject_config + + "exec /opt/ceph-container/bin/entrypoint.sh" + ]) + .with_limits("2.0", "4096M") + .with_reservations("1.0", "2048M") + .with_port(6800, 6800, "osd") + .with_volume_mount(vol_osd, "/var/lib/ceph/osd") + .with_volume_mount(vol_osd_config, "/etc/ceph"); + + // RGW (RADOS Gateway) - S3 API endpoint + // Config-as-Code: Uses injected config files with pre-shared keys + // DNS wait ensures MON is available before RGW connects + local rgw_container = + engine.container("ceph-rgw") + .with_image(images.ceph) + .with_environment(rgw_env) + .with_command([ + "bash", "-c", + "until getent hosts ceph-mon; do echo 'Waiting for MON DNS...'; sleep 2; done; " + + inject_config + + "exec /opt/ceph-container/bin/entrypoint.sh" + ]) + .with_limits("1.0", "1536M") + .with_reservations("0.5", "1024M") + .with_port(7480, 7480, "s3") + .with_volume_mount(vol_rgw, "/var/lib/ceph/radosgw") + .with_volume_mount(vol_rgw_config, "/etc/ceph"); + + // Init container - one-time S3 user provisioning + // IMPORTANT: This container exits with code 0 after completion + // Orchestrator must NOT restart it (use K8s Job or Compose restart: "no") + // Config-as-Code: Uses injected config to run radosgw-admin commands + local init_container = + engine.container("ceph-init") + .with_image(images.ceph) + .with_environment({ + CLUSTER: $["ceph-cluster-id"], + FSID: $["ceph-fsid"], + KV_TYPE: "none", + RGW_ACCESS_KEY: $["ceph-access-key"], + RGW_SECRET_KEY: $["ceph-secret-key"], + }) + .with_limits("0.5", "512M") + .with_reservations("0.25", "256M") + .with_volume_mount(vol_init_config, "/etc/ceph") + .with_command([ + "bash", "-c", + inject_config + ||| + set -e + + # Wait for cluster health + echo "Waiting for Ceph cluster to be healthy..." + MAX_ATTEMPTS=60 + ATTEMPT=0 + until ceph --cluster ${CLUSTER} health 2>/dev/null | grep -q "HEALTH_OK\|HEALTH_WARN"; do + ATTEMPT=$((ATTEMPT+1)) + if [ $ATTEMPT -ge $MAX_ATTEMPTS ]; then + echo "ERROR: Cluster failed to become healthy after ${MAX_ATTEMPTS} attempts" + exit 1 + fi + echo "Attempt ${ATTEMPT}/${MAX_ATTEMPTS}: Cluster not ready, retrying in 5s..." + sleep 5 + done + echo "Cluster is healthy." + + # Wait for RGW availability + echo "Waiting for RGW to be ready..." + ATTEMPT=0 + until curl -sf http://ceph-rgw:7480 >/dev/null 2>&1; do + ATTEMPT=$((ATTEMPT+1)) + if [ $ATTEMPT -ge $MAX_ATTEMPTS ]; then + echo "ERROR: RGW failed to become ready after ${MAX_ATTEMPTS} attempts" + exit 1 + fi + echo "Attempt ${ATTEMPT}/${MAX_ATTEMPTS}: RGW not ready, retrying in 5s..." + sleep 5 + done + echo "RGW is ready." + + # Idempotent S3 user creation + echo "Provisioning S3 user: ${RGW_ACCESS_KEY}" + if radosgw-admin --cluster ${CLUSTER} user info --uid="${RGW_ACCESS_KEY}" >/dev/null 2>&1; then + echo "User ${RGW_ACCESS_KEY} already exists, skipping creation." + else + echo "Creating new S3 user: ${RGW_ACCESS_KEY}" + radosgw-admin --cluster ${CLUSTER} user create \ + --uid="${RGW_ACCESS_KEY}" \ + --display-name="Object Storage User" \ + --access-key="${RGW_ACCESS_KEY}" \ + --secret-key="${RGW_SECRET_KEY}" + echo "S3 user created successfully." + fi + + echo "Initialization complete. Exiting." + exit 0 + |||, + ]); + + // Container sets - each daemon gets its own for K8s node distribution + local mon_containerSet = engine.containers("ceph-mon", [mon_container]); + local mgr_containerSet = engine.containers("ceph-mgr", [mgr_container]); + local osd_containerSet = engine.containers("ceph-osd", [osd_container]); + local rgw_containerSet = engine.containers("ceph-rgw", [rgw_container]); + local init_containerSet = engine.containers("ceph-init", [init_container]); + + // Services - expose daemon ports for inter-daemon communication + local mon_service = + engine.service(mon_containerSet) + .with_port(6789, 6789, "mon") + .with_port(3300, 3300, "mon-msgr2"); + + local mgr_service = + engine.service(mgr_containerSet) + .with_port(7000, 7000, "mgr") + .with_port(8443, 8443, "dashboard") + .with_port(9283, 9283, "prometheus"); + + local osd_service = + engine.service(osd_containerSet) + .with_port(6800, 6800, "osd"); + + local rgw_service = + engine.service(rgw_containerSet) + .with_port(7480, 7480, "s3"); + + engine.resources([ + + // Data volumes + vol_mon, + vol_mgr, + vol_osd, + vol_rgw, + + // Config volumes (isolated, no sharing) + vol_mon_config, + vol_mgr_config, + vol_osd_config, + vol_rgw_config, + vol_init_config, + + // Container sets + mon_containerSet, + mgr_containerSet, + osd_containerSet, + rgw_containerSet, + init_containerSet, + + // Services + mon_service, + mgr_service, + osd_service, + rgw_service, + + ]) + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/falkordb.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/falkordb.jsonnet new file mode 100644 index 00000000..1d4176d8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/falkordb.jsonnet @@ -0,0 +1,38 @@ +local images = import "values/images.jsonnet"; + +{ + + "falkordb" +: { + + create:: function(engine) + + local vol = engine.volume("falkordb").with_size("20G"); + + local container = + engine.container("falkordb") + .with_image(images.falkordb) + .with_limits("1.0", "768M") + .with_reservations("0.5", "768M") + .with_port(6379, 6379, "api") + .with_port(3010, 3000, "ui") + .with_volume_mount(vol, "/data"); + + local containerSet = engine.containers( + "falkordb", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(6379, 6379, "api") + .with_port(3010, 3010, "ui"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/garage.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/garage.jsonnet new file mode 100644 index 00000000..9d339bfb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/garage.jsonnet @@ -0,0 +1,249 @@ +local images = import "values/images.jsonnet"; + +{ + + garage +: { + + // Garage S3 credentials - these are the actual access key ID and secret key + // Access Key ID must be in format: GK + 24 hex characters (12 bytes) + // Secret Key must be 64 hex characters (32 bytes) + // For production, generate secure random values and override these defaults + "access-key":: "GK000000000000000000000001", + "secret-key":: "b171f00be9be4c32c734f4c05fe64c527a8ab5eb823b376cfa8c2531f70fc427", + "rpc-secret":: "bbba746a9e289bad64a9e7a36a4299dac8d6e0b8cc2a6c2937fe756df4492008", + // For a production system, override this value + "admin-token":: "batts-rockhearted-unpartially", + region:: "garage", + "replication-factor":: "1", // Set to 1 for single-node, 3 for production + + // Storage volume sizes + "meta-size":: "2G", // Metadata volume size + "data-size":: "5G", // Data volume size (also used for cluster layout capacity) + + create:: function(engine) + + local accessKey = self["access-key"]; + local secretKey = self["secret-key"]; + local rpcSecret = self["rpc-secret"]; + local adminToken = self["admin-token"]; + local region = self.region; + local replicationFactor = self["replication-factor"]; + local metaSize = self["meta-size"]; + local dataSize = self["data-size"]; + + // Garage daemon configuration file - TOML format + local garage_conf = ||| + metadata_dir = "/var/lib/garage/meta" + data_dir = "/var/lib/garage/data" + + db_engine = "lmdb" + + replication_factor = %s + + compression_level = 1 + + rpc_bind_addr = "[::]:3901" + rpc_public_addr = "[::]:3901" + rpc_secret = "%s" + + [s3_api] + s3_region = "%s" + api_bind_addr = "[::]:3900" + root_domain = ".s3.garage.local" + + [s3_web] + bind_addr = "[::]:3902" + root_domain = ".web.garage.local" + index = "index.html" + + [k2v_api] + api_bind_addr = "[::]:3904" + + [admin] + api_bind_addr = "[::]:3903" + admin_token = "%s" + ||| % [replicationFactor, rpcSecret, region, adminToken]; + + // Config volume - contains the rendered garage.toml + local cfgVol = engine.configVolume( + "garage-cfg", "garage", + { + "garage.toml": garage_conf, + } + ); + + // Volumes - Garage stores metadata and data separately + local vol_meta = engine.volume("garage-meta").with_size(metaSize); + local vol_data = engine.volume("garage-data").with_size(dataSize); + + // Main Garage daemon container + local garage_container = + engine.container("garage") + .with_image(images.garage) + .with_command([ + "/garage", "-c", "/etc/garage/garage.toml", "server" + ]) + .with_environment({ + RUST_LOG: "garage=info", + }) + .with_limits("1.0", "512M") + .with_reservations("0.5", "512M") + .with_port(3900, 3900, "s3-api") + .with_port(3901, 3901, "rpc") + .with_port(3902, 3902, "web") + .with_port(3903, 3903, "admin") + .with_port(3904, 3904, "k2v") + .with_volume_mount(cfgVol, "/etc/garage/") + .with_volume_mount(vol_meta, "/var/lib/garage/meta") + .with_volume_mount(vol_data, "/var/lib/garage/data"); + + // Init container - configures cluster layout and creates S3 credentials + // IMPORTANT: This container exits with code 0 after completion + // Orchestrator must NOT restart it (use K8s Job or Compose restart: "no") + // Uses Alpine base image since garage container has no shell + local init_container = + engine.container("garage-init") + .with_image("docker.io/alpine:3.23.2") + .with_environment({ + GARAGE_ACCESS_KEY: accessKey, + GARAGE_SECRET_KEY: secretKey, + GARAGE_REGION: region, + GARAGE_ADMIN_TOKEN: adminToken, + GARAGE_RPC_SECRET: rpcSecret, + GARAGE_DATA_SIZE: dataSize, + }) + .with_limits("0.5", "256M") + .with_reservations("0.25", "128M") + .with_volume_mount(cfgVol, "/etc/garage/") + .with_command([ + "sh", "-c", ||| + set -e + + # Install required tools + echo "Installing curl, jq and downloading garage CLI..." + apk add --no-cache curl jq + + # Download garage binary (v2.1.0) for remote management + curl -fsSL "https://garagehq.deuxfleurs.fr/_releases/v2.1.0/x86_64-unknown-linux-musl/garage" \ + -o /usr/local/bin/garage + chmod +x /usr/local/bin/garage + + echo "Waiting for Garage daemon to be ready..." + MAX_ATTEMPTS=60 + ATTEMPT=0 + # Wait for /health to respond (even 503 is fine - means daemon is up) + until curl -s http://garage:3903/health >/dev/null 2>&1; do + ATTEMPT=$((ATTEMPT+1)) + if [ $ATTEMPT -ge $MAX_ATTEMPTS ]; then + echo "ERROR: Garage failed to become ready after ${MAX_ATTEMPTS} attempts" + exit 1 + fi + echo "Attempt ${ATTEMPT}/${MAX_ATTEMPTS}: Garage not ready, retrying in 2s..." + sleep 2 + done + echo "Garage daemon is ready." + + # Get the node ID via v2 Admin API + echo "Getting Garage node ID via Admin API..." + curl -s -H "Authorization: Bearer ${GARAGE_ADMIN_TOKEN}" \ + "http://garage:3903/v2/GetNodeInfo?node=self" > /tmp/garage-node-info.json + + # Extract node ID from response (the key in success map is the node ID) + NODE_ID=$(jq -r '.success | to_entries[0].value.nodeId' /tmp/garage-node-info.json) + echo "Node ID: ${NODE_ID}" + + if [ -z "$NODE_ID" ] || [ "$NODE_ID" = "null" ]; then + echo "ERROR: Failed to retrieve node ID" + exit 1 + fi + + # ===== LAYOUT MANAGEMENT VIA REMOTE RPC ===== + # Use garage CLI with -h and -s flags to connect remotely + # -h requires format: @: + # -s provides the RPC secret + + # Construct full RPC identifier + RPC_HOST="${NODE_ID}@garage:3901" + echo "RPC Host: ${RPC_HOST}" + + # Check current layout to see if node is already assigned (idempotent) + echo "Checking current cluster layout..." + LAYOUT_OUTPUT=$(garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" layout show 2>&1) + + # Check if node already has a role assigned + # Layout output shows abbreviated node ID (first 16 chars) + NODE_ID_SHORT="${NODE_ID:0:16}" + if echo "$LAYOUT_OUTPUT" | grep -q "$NODE_ID_SHORT"; then + echo "Node ${NODE_ID_SHORT}... already assigned in layout, skipping." + else + echo "Assigning node to cluster layout..." + # Assign node to zone dc1 with configured capacity + garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" \ + layout assign ${NODE_ID} -z dc1 -c ${GARAGE_DATA_SIZE} + + echo "Applying layout configuration..." + # Get current staged version and apply + garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" \ + layout apply --version 1 + + echo "Layout configured successfully." + # Wait for layout to stabilize + sleep 5 + fi + + # ===== KEY MANAGEMENT VIA REMOTE RPC ===== + + # Check if key already exists (idempotent) + # GARAGE_ACCESS_KEY is already a valid Garage Key ID (GK + 24 hex chars) + if garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" key info "${GARAGE_ACCESS_KEY}" >/dev/null 2>&1; then + echo "Access key ${GARAGE_ACCESS_KEY} already exists, skipping creation." + else + echo "Importing S3 access key: ${GARAGE_ACCESS_KEY}" + + # Import key with the provided credentials (both already in valid Garage format) + # GARAGE_ACCESS_KEY = Key ID (GK + 24 hex chars) + # GARAGE_SECRET_KEY = Secret (64 hex chars) + garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" \ + key import "${GARAGE_ACCESS_KEY}" "${GARAGE_SECRET_KEY}" --yes + + echo "Access key imported successfully." + fi + + # Grant permissions to the key + echo "Granting create-bucket permission to key..." + garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" \ + key allow --create-bucket "${GARAGE_ACCESS_KEY}" + + echo "" + echo "Garage initialization complete!" + echo "S3 Endpoint: http://garage:3900" + echo "Region: ${GARAGE_REGION}" + exit 0 + |||, + ]); + + // Container sets + local garage_containerSet = engine.containers("garage", [garage_container]); + local init_containerSet = engine.containers("garage-init", [init_container]); + + // Service - expose Garage ports + local garage_service = + engine.service(garage_containerSet) + .with_port(3900, 3900, "s3-api") + .with_port(3901, 3901, "rpc") + .with_port(3902, 3902, "web") + .with_port(3903, 3903, "admin") + .with_port(3904, 3904, "k2v"); + + engine.resources([ + cfgVol, + vol_meta, + vol_data, + garage_containerSet, + init_containerSet, + garage_service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/memgraph.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/memgraph.jsonnet new file mode 100644 index 00000000..eeed2e4e --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/memgraph.jsonnet @@ -0,0 +1,70 @@ +local images = import "values/images.jsonnet"; + +{ + + "memgraph" +: { + + create:: function(engine) + + local vol = engine.volume("memgraph").with_size("20G"); + + local container = + engine.container("memgraph") + .with_image(images.memgraph_mage) + .with_environment({ + MEMGRAPH: "--storage-properties-on-edges=true --storage-enable-edges-metadata=true" + }) + .with_limits("1.0", "1000M") + .with_reservations("0.5", "1000M") + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2") + .with_volume_mount(vol, "/var/lib/memgraph"); + + local containerSet = engine.containers( + "memgraph", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + + "memgraph-lab" +: { + + create:: function(engine) + + local container = + engine.container("lab") + .with_image(images.memgraph_lab) + .with_environment({ + QUICK_CONNECT_MG_HOST: "memgraph", + QUICK_CONNECT_MG_PORT: "7687", + }) + .with_limits("1.0", "512M") + .with_reservations("0.5", "512M") + .with_port(3010, 3000, "http"); + + local containerSet = engine.containers( + "lab", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(3010, 3010, "http"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/milvus.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/milvus.jsonnet new file mode 100644 index 00000000..5bed3820 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/milvus.jsonnet @@ -0,0 +1,89 @@ +local images = import "values/images.jsonnet"; +local minio = import "backends/minio.jsonnet"; + +minio { + + etcd +: { + + create:: function(engine) + + local vol = engine.volume("etcd").with_size("20G"); + + local container = + engine.container("etcd") + .with_image(images.etcd) + .with_command([ + "etcd", + "-advertise-client-urls=http://127.0.0.1:2379", + "-listen-client-urls", + "http://0.0.0.0:2379", + "--data-dir", + "/etcd", + ]) + .with_environment({ + ETCD_AUTO_COMPACTION_MODE: "revision", + ETCD_AUTO_COMPACTION_RETENTION: "1000", + ETCD_QUOTA_BACKEND_BYTES: "4294967296", + ETCD_SNAPSHOT_COUNT: "50000" + }) + .with_limits("1.0", "128M") + .with_reservations("0.25", "128M") + .with_port(2379, 2379, "api") + .with_volume_mount(vol, "/etcd"); + + local containerSet = engine.containers( + "etcd", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(2379, 2379, "api"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + + milvus +: { + + create:: function(engine) + + local vol = engine.volume("milvus").with_size("20G"); + + local container = + engine.container("milvus") + .with_image(images.milvus) + .with_command([ + "milvus", "run", "standalone" + ]) + .with_environment({ + ETCD_ENDPOINTS: "etcd:2379", + MINIO_ADDRESS: "minio:9000", + }) + .with_limits("1.0", "256M") + .with_reservations("0.5", "256M") + .with_port(9091, 9091, "api") + .with_port(19530, 19530, "api2") + .with_volume_mount(vol, "/var/lib/milvus"); + + local containerSet = engine.containers( + "milvus", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9091, 9091, "api") + .with_port(19530, 19530, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/minio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/minio.jsonnet new file mode 100644 index 00000000..b38bb81f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/minio.jsonnet @@ -0,0 +1,48 @@ +local images = import "values/images.jsonnet"; + +{ + + minio +: { + + create:: function(engine) + + local vol = engine.volume("minio-data").with_size("20G"); + + local container = + engine.container("minio") + .with_image(images.minio) + .with_command([ + "minio", + "server", + "/minio_data", + "--console-address", + ":9001", + ]) + .with_environment({ + MINIO_ROOT_USER: "minioadmin", + MINIO_ROOT_PASSWORD: "minioadmin", + }) + .with_limits("0.5", "128M") + .with_reservations("0.25", "128M") + .with_port(9000, 9000, "api") + .with_port(9001, 9001, "console") + .with_volume_mount(vol, "/minio_data"); + + local containerSet = engine.containers( + "minio", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9000, 9000, "api") + .with_port(9001, 9001, "console"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/neo4j.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/neo4j.jsonnet new file mode 100644 index 00000000..46c61e0f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/neo4j.jsonnet @@ -0,0 +1,46 @@ +local images = import "values/images.jsonnet"; + +{ + + "neo4j" +: { + + create:: function(engine) + + local vol = engine.volume("neo4j").with_size("20G"); + + local container = + engine.container("neo4j") + .with_image(images.neo4j) + .with_environment({ + NEO4J_AUTH: "neo4j/password", + NEO4J_server_memory_pagecache_size: "512m", + NEO4J_server_memory_heap_max__size: "512m", + // NEO4J_server_bolt_listen__address: "0.0.0.0:7687", + // NEO4J_server_default__listen__address: "0.0.0.0", + // NEO4J_server_http_listen__address: "0.0.0.0:7474", + }) + .with_limits("1.0", "1536M") + .with_reservations("0.5", "1536M") + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2") + .with_volume_mount(vol, "/data"); + + local containerSet = engine.containers( + "neo4j", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/pinecone.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/pinecone.jsonnet new file mode 100644 index 00000000..2bef70fb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/pinecone.jsonnet @@ -0,0 +1,45 @@ +local images = import "values/images.jsonnet"; + +{ + + "pinecone" +: { + + create:: function(engine) + + local container = + engine.container("pinecone") + .with_image(images.pinecone) + .with_environment({ + + PORT: "5080", + + INDEX_TYPE: "serverless", + + // Dimension is fixed, 384 is right for miniLM + // sentence embedding + DIMENSION: 384, + + METRIC: "cosine" + + }) + .with_limits("1.0", "256M") + .with_reservations("0.5", "256M") + .with_port(5080, 5080, "api"); + + local containerSet = engine.containers( + "pinecone", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(5080, 5080, "api"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/qdrant.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/qdrant.jsonnet new file mode 100644 index 00000000..2e6761f8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/backends/qdrant.jsonnet @@ -0,0 +1,65 @@ +local images = import "values/images.jsonnet"; + +{ + + "qdrant" +: { + + // Memory settings (can be overridden by memory-profile) + "memory-limit":: "1024M", + "memory-reservation":: "1024M", + + // Mmap settings for low-memory mode (trades latency for memory) + // Set to null to disable, or string value to enable + "memmap-threshold-kb":: null, + "on-disk-payload":: null, + + create:: function(engine) + + // Capture memory settings into locals + local memLimit = self["memory-limit"]; + local memReserv = self["memory-reservation"]; + local mmapThreshold = self["memmap-threshold-kb"]; + local onDiskPayload = self["on-disk-payload"]; + + local vol = engine.volume("qdrant").with_size("20G"); + + // Build environment with optional mmap settings + local baseEnv = {}; + local env = baseEnv + + (if mmapThreshold != null then { + QDRANT__STORAGE__MEMMAP_THRESHOLD_KB: mmapThreshold, + } else {}) + + (if onDiskPayload != null then { + QDRANT__STORAGE__ON_DISK_PAYLOAD: onDiskPayload, + } else {}); + + local container = + engine.container("qdrant") + .with_image(images.qdrant) + .with_limits("1.0", memLimit) + .with_reservations("0.5", memReserv) + .with_port(6333, 6333, "api") + .with_port(6334, 6334, "api2") + .with_volume_mount(vol, "/qdrant/storage") + + (if std.length(env) > 0 then { + environment+: env, + } else {}); + + local containerSet = engine.containers( + "qdrant", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(6333, 6333, "api") + .with_port(6334, 6334, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/components.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/components.jsonnet new file mode 100644 index 00000000..f6157326 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/components.jsonnet @@ -0,0 +1,121 @@ +{ + + // Essentials + "trustgraph-base": import "core/trustgraph.jsonnet", + "rev-gateway": import "core/rev-gateway.jsonnet", + "pulsar": import "pulsar/pulsar.jsonnet", + + // LLMs + "azure": import "llm/azure.jsonnet", + "azure-openai": import "llm/azure-openai.jsonnet", + "bedrock": import "llm/bedrock.jsonnet", + "claude": import "llm/claude.jsonnet", + "cohere": import "llm/cohere.jsonnet", + "googleaistudio": import "llm/googleaistudio.jsonnet", + "llamafile": import "llm/llamafile.jsonnet", + "lmstudio": import "llm/lmstudio.jsonnet", + "mistral": import "llm/mistral.jsonnet", + "ollama": import "llm/ollama.jsonnet", + "openai": import "llm/openai.jsonnet", + "vertexai": import "llm/vertexai.jsonnet", + "tgi": import "llm/tgi.jsonnet", + "vllm": import "llm/vllm.jsonnet", + + // Embeddings + "embeddings-ollama": import "embeddings/embeddings-ollama.jsonnet", + "embeddings-hf": import "embeddings/embeddings-hf.jsonnet", + "embeddings-fastembed": import "embeddings/embeddings-fastembed.jsonnet", + + // OCR options + "ocr": import "ocr/ocr.jsonnet", + "mistral-ocr": import "ocr/mistral-ocr.jsonnet", + + // Vector stores + "vector-store-milvus": import "vector-store/milvus.jsonnet", + "vector-store-qdrant": import "vector-store/qdrant.jsonnet", + "vector-store-pinecone": import "vector-store/pinecone.jsonnet", + + // Triples stores + "triple-store-cassandra": import "triple-store/cassandra.jsonnet", + "triple-store-neo4j": import "triple-store/neo4j.jsonnet", + "triple-store-falkordb": import "triple-store/falkordb.jsonnet", + "triple-store-memgraph": import "triple-store/memgraph.jsonnet", + + // Row stores + "row-store-cassandra": import "row-store/cassandra.jsonnet", + + // Observability support + "grafana": import "monitoring/grafana.jsonnet", + "loki": import "monitoring/loki.jsonnet", + + // Pulsar manager is a UI for Pulsar. Uses a LOT of memory + "pulsar-manager": import "pulsar/pulsar-manager.jsonnet", + + "override-recursive-chunker": import "core/chunker-recursive.jsonnet", + + // The prompt manager + "prompt-overrides": import "core/prompt-overrides.jsonnet", + + // Extra MCP services + "ddg-mcp-server": import "mcp/ddg-mcp-server.jsonnet", + + // Does nothing. But, can be a hack to overwrite parameters + "null": {}, + + // Passthrough: returns parameters directly, preserving +: merge syntax + // Also supports JSON-safe routing with prefixed parameters like "cassandra-heap" + "override": { + local route = function(target) + function(prefix, k, v) + local suffix = std.substr(k, std.length(prefix), std.length(k) - std.length(prefix)); + { [target] +: { [suffix]:: v } }, + + local routes = { + "cassandra-": route("cassandra"), + "pulsar-": route("pulsar"), + "qdrant-": route("qdrant"), + "api-gateway-": route("api-gateway"), + "librarian-": route("librarian"), + }, + + with_params:: function(pars) + std.foldl( + function(acc, k) + local matchingPrefixes = [p for p in std.objectFields(routes) if std.startsWith(k, p)]; + if std.length(matchingPrefixes) > 0 then + local prefix = matchingPrefixes[0]; + acc + routes[prefix](prefix, k, pars[k]) + else + acc + { [k]:: pars[k] }, + std.objectFields(pars), + {} + ), + }, + + // Memory profiles + "memory-profile-low": import "profiles/memory-profile-low.jsonnet", + + // Model hosting + + "hosting-intel-battlemage-vllm": + import "model-hosting/intel-battlemage-vllm.jsonnet", + + "hosting-cpu-tgi": + import "model-hosting/cpu-tgi.jsonnet", + + "hosting-intel-xpu-tgi": + import "model-hosting/intel-xpu-tgi.jsonnet", + + "hosting-intel-gaudi-tgi": + import "model-hosting/intel-gaudi-tgi.jsonnet", + + "hosting-intel-xpu-vllm": + import "model-hosting/intel-xpu-vllm.jsonnet", + + "hosting-intel-gaudi-vllm": + import "model-hosting/intel-gaudi-vllm.jsonnet", + + "hosting-nvidia-gpu-vllm": + import "model-hosting/nvidia-gpu-vllm.jsonnet", + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/agent-manager-react.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/agent-manager-react.jsonnet new file mode 100644 index 00000000..954d80a7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/agent-manager-react.jsonnet @@ -0,0 +1,47 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "agent-manager" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("agent-manager") + .with_image(images.trustgraph_flow) + .with_command([ + "agent-manager-react", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "agent-manager", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/chunker-recursive.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/chunker-recursive.jsonnet new file mode 100644 index 00000000..0b924aba --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/chunker-recursive.jsonnet @@ -0,0 +1,55 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; + +{ + + "chunk-size":: 2000, + "chunk-overlap":: 100, + + "chunker" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("chunker") + .with_image(images.trustgraph_flow) + .with_command([ + "chunker-recursive", + "-p", + url.pulsar, + "--chunk-size", + std.toString($["chunk-size"]), + "--chunk-overlap", + std.toString($["chunk-overlap"]), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "chunker", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/configuration.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/configuration.jsonnet new file mode 100644 index 00000000..75afebb9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/configuration.jsonnet @@ -0,0 +1,57 @@ + +// This puts the default configuration together. References many things, +// flow classes, a default flow, token costs, prompts, agent tools + +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "init-trustgraph" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local cfgVol = engine.configVolume( + "trustgraph-cfg", "trustgraph", + { + "config.json": importstr "trustgraph/config.json", + } + ); + + local container = + engine.container("init-trustgraph") + .with_image(images.trustgraph_flow) + .with_command( + [ + "tg-init-trustgraph", + "-p", + url.pulsar_admin, + "--config-file", + "/trustgraph/config.json", + ] + ) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation) + .with_volume_mount(cfgVol, "/trustgraph/"); + + local containerSet = engine.containers( + "init-trustgraph", [ container ] + ); + + engine.resources([ + cfgVol, + containerSet, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/document-rag.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/document-rag.jsonnet new file mode 100644 index 00000000..e466e3a4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/document-rag.jsonnet @@ -0,0 +1,92 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; + +{ + + "document-rag" +: { + + "doc-limit":: 20, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local docLimit = self["doc-limit"]; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("document-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "document-rag", + "-p", + url.pulsar, + "--doc-limit", + std.toString(docLimit), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "document-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "document-embeddings" +: { + + "cpu-limit":: "1.0", + "cpu-reservation":: "0.5", + "memory-limit":: "512M", + "memory-reservation":: "512M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("document-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "document-embeddings", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "document-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/graph-rag.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/graph-rag.jsonnet new file mode 100644 index 00000000..06ee5b3b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/graph-rag.jsonnet @@ -0,0 +1,283 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "kg-extract-definitions" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-extract-definitions") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-definitions", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-extract-definitions", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-relationships" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-extract-relationships") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-relationships", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-extract-relationships", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-agent" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-extract-agent") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-agent", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-extract-agent", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-ontology" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "300M", + "memory-reservation":: "300M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-extract-ontology") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-ontology", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-extract-ontology", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "graph-rag" +: { + + concurrency:: 1, + "entity-limit":: 50, + "triple-limit":: 30, + "max-subgraph-size":: 400, + "max-path-length":: 2, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local entityLimit = self["entity-limit"]; + local tripleLimit = self["triple-limit"]; + local maxSubgraphSize = self["max-subgraph-size"]; + local maxPathLength = self["max-path-length"]; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("graph-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "graph-rag", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--entity-limit", + std.toString(entityLimit), + "--triple-limit", + std.toString(tripleLimit), + "--max-subgraph-size", + std.toString(maxSubgraphSize), + "--max-path-length", + std.toString(maxPathLength), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "graph-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "graph-embeddings" +: { + + "cpu-limit":: "1.0", + "cpu-reservation":: "0.5", + "memory-limit":: "256M", + "memory-reservation":: "256M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "graph-embeddings", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/librarian.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/librarian.jsonnet new file mode 100644 index 00000000..2ecda5a8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/librarian.jsonnet @@ -0,0 +1,58 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local garage = import "backends/garage.jsonnet"; +local cassandra = import "backends/cassandra.jsonnet"; + +{ + + "librarian" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "256M", + "memory-reservation":: "256M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("librarian") + .with_image(images.trustgraph_flow) + .with_command([ + "librarian", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + "--object-store-endpoint", + url.object_store, + "--object-store-access-key", + $.garage["access-key"], + "--object-store-secret-key", + $.garage["secret-key"], + "--object-store-region", + $.garage.region, + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "librarian", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +// Garage and Cassandra are used by the Librarian +} + garage + cassandra + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/mcp-server.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/mcp-server.jsonnet new file mode 100644 index 00000000..9bf7e3bd --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/mcp-server.jsonnet @@ -0,0 +1,54 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "mcp-server" +: { + + port:: 8000, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "256M", + "memory-reservation":: "256M", + + create:: function(engine) + + local port = self.port; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local envSecrets = engine.envSecrets("mcp-server-secret") + .with_env_var("MCP_SERVER_SECRET", "mcp-server-secret") + .with_env_var("GATEWAY_SECRET", "gateway-secret"); + + local container = + engine.container("mcp-server") + .with_image(images.trustgraph_mcp) + .with_command([ + "mcp-server", + "--port", + std.toString(port), + ]) + .with_env_var_secrets(envSecrets) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation) + .with_port(port, port, "mcp"); + + local containerSet = engine.containers( + "mcp-server", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(port, port, "mcp"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/prompt-overrides.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/prompt-overrides.jsonnet new file mode 100644 index 00000000..852ec09d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/prompt-overrides.jsonnet @@ -0,0 +1,24 @@ +local default_prompts = import "prompts/default-prompts.jsonnet"; + +{ + + with:: function(key, value) + if (key == "system-template") then + self + { + prompts +:: { + "system-template": value, + } + } + else + self + { + prompts +:: { + templates +:: { + [key] +:: { + prompt: value + } + } + } + }, + +} + default_prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/prompt-template.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/prompt-template.jsonnet new file mode 100644 index 00000000..3cd79d59 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/prompt-template.jsonnet @@ -0,0 +1,97 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "prompt" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("prompt") + .with_image(images.trustgraph_flow) + .with_command([ + "prompt-template", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "prompt", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "prompt-rag" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("prompt-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "prompt-template", + "-p", + url.pulsar, + "--id", + "prompt-rag", + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "prompt-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/rev-gateway.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/rev-gateway.jsonnet new file mode 100644 index 00000000..fbd9f77a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/rev-gateway.jsonnet @@ -0,0 +1,62 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "rev-gateway" +: { + + // Invalid, but at least means the rev-gateway won't connect to anything + // it shouldn't. + token:: "INVALID_TOKEN", + uri:: "wss://127.0.0.1/api/v1/relay?token=" + self.token, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "256M", + "memory-reservation":: "256M", + + create:: function(engine) + + local uri = self.uri; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local envSecrets = engine.envSecrets("rev-gateway-secret") + .with_env_var("REV_GATEWAY_SECRET", "rev-gateway-secret"); + + local container = + engine.container("api-gateway") + .with_image(images.trustgraph_flow) + .with_command([ + "rev-gateway", + "-p", + url.pulsar, + "--websocket-uri", + std.toString(uri), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation) + .with_port(8000, 8000, "metrics") + .with_port(port, port, "api"); + + local containerSet = engine.containers( + "api-gateway", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics") + .with_port(port, port, "api"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/structured-data.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/structured-data.jsonnet new file mode 100644 index 00000000..52ea85da --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/structured-data.jsonnet @@ -0,0 +1,211 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "nlp-query" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("nlp-query") + .with_image(images.trustgraph_flow) + .with_command([ + "nlp-query", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "nlp-query", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "structured-query" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("structured-query") + .with_image(images.trustgraph_flow) + .with_command([ + "structured-query", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "structured-query", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "structured-diag" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "96M", + "memory-reservation":: "96M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("structured-diag") + .with_image(images.trustgraph_flow) + .with_command([ + "structured-diag", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "structured-diag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-rows" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-extract-rows") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-rows", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-extract-rows", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "row-embeddings" +: { + + "cpu-limit":: "1.0", + "cpu-reservation":: "0.5", + "memory-limit":: "256M", + "memory-reservation":: "256M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("row-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "row-embeddings", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "row-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/trustgraph.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/trustgraph.jsonnet new file mode 100644 index 00000000..3cf12d26 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/core/trustgraph.jsonnet @@ -0,0 +1,473 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +local config_initialiser = import "configuration.jsonnet"; +local config = import "../runtime-config/trustgraph-config.jsonnet"; +local librarian = import "librarian.jsonnet"; +local mcp_server = import "mcp-server.jsonnet"; +local workbench = import "../ui/workbench-ui.jsonnet"; +local graphrag = import "graph-rag.jsonnet"; +local documentrag = import "document-rag.jsonnet"; +local prompt_template = import "prompt-template.jsonnet"; +local agent_manager = import "agent-manager-react.jsonnet"; +local structured_data = import "structured-data.jsonnet"; +local ddg = import "mcp/ddg-mcp-server.jsonnet"; + +// Helper to create a routing function for a target object +local route = function(target) + function(prefix, k, v) + local suffix = std.substr(k, std.length(prefix), std.length(k) - std.length(prefix)); + { [target] +: { [suffix]:: v } }; + +// Parameter prefix -> target object routing table +local routes = { + "prompt-rag-": route("prompt-rag"), + "prompt-": route("prompt"), + "text-completion-rag-": route("text-completion-rag"), + "text-completion-": route("text-completion"), + "embeddings-": route("embeddings"), + "api-gateway-": route("api-gateway"), + "chunk-": route("chunker"), + "graph-rag-": route("graph-rag"), + "graph-embeddings-": route("graph-embeddings"), + "kg-extract-definitions-": route("kg-extract-definitions"), + "kg-extract-relationships-": route("kg-extract-relationships"), + "kg-extract-agent-": route("kg-extract-agent"), + "kg-extract-ontology-": route("kg-extract-ontology"), + "kg-extract-objects-": route("kg-extract-objects"), + "garage-": route("garage"), + "config-svc-": route("config-svc"), + "pdf-decoder-": route("pdf-decoder"), + "mcp-tool-": route("mcp-tool"), + "mcp-server-": route("mcp-server"), + "metering-rag-": route("metering-rag"), + "metering-": route("metering"), + "kg-store-": route("kg-store"), + "kg-manager-": route("kg-manager"), + "librarian-": route("librarian"), + "agent-manager-": route("agent-manager"), + "document-rag-": route("document-rag"), + "document-embeddings-": route("document-embeddings"), + "rev-gateway-": route("rev-gateway"), + "nlp-query-": route("nlp-query"), + "structured-query-": route("structured-query"), + "structured-diag-": route("structured-diag"), + "init-trustgraph-": route("init-trustgraph"), +}; + +// Find longest matching prefix (most specific first) +local findRoute = function(k) + local prefixes = std.objectFields(routes); + local matching = std.filter(function(p) std.startsWith(k, p), prefixes); + local sorted = std.sort(matching, function(x) -std.length(x)); + if std.length(sorted) > 0 then sorted[0] else null; + +{ + + // Route parameters to appropriate internal objects based on prefix + with:: function(k, v) + local prefix = findRoute(k); + if prefix != null then + self + routes[prefix](prefix, k, v) + else + self + { [k]:: v }, + + "log-level":: "INFO", + + // Base objects with concurrency defaults (LLM/embeddings components merge into these) + "text-completion" +: { concurrency:: 1 }, + "text-completion-rag" +: { concurrency:: 1 }, + embeddings +: { concurrency:: 1 }, + + "api-gateway" +: { + + port:: 8088, + timeout:: 600, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "512M", + "memory-reservation":: "512M", + + create:: function(engine) + + local port = self.port; + local timeout = self.timeout; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local envSecrets = engine.envSecrets("gateway-secret") + .with_env_var("GATEWAY_SECRET", "gateway-secret"); + + local container = + engine.container("api-gateway") + .with_image(images.trustgraph_flow) + .with_command([ + "api-gateway", + "-p", + url.pulsar, + "--timeout", + std.toString(timeout), + "--port", + std.toString(port), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation) + .with_port(port, port, "api"); + + local containerSet = engine.containers( + "api-gateway", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics") + .with_port(port, port, "api"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "chunker" +: { + + size:: 2000, + overlap:: 50, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local size = self.size; + local overlap = self.overlap; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("chunker") + .with_image(images.trustgraph_flow) + .with_command([ + "chunker-recursive", + "-p", + url.pulsar, + "--chunk-size", + std.toString(size), + "--chunk-overlap", + std.toString(overlap), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "chunker", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "config-svc" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local cpuLimit = self["cpu-limit"]; + local cpuReservation = self["cpu-reservation"]; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("config-svc") + .with_image(images.trustgraph_flow) + .with_command([ + "config-svc", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(cpuLimit, memoryLimit) + .with_reservations(cpuReservation, memoryReservation); + + local containerSet = engine.containers( + "config-svc", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "pdf-decoder" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "512M", + "memory-reservation":: "512M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("pdf-decoder") + .with_image(images.trustgraph_flow) + .with_command([ + "pdf-decoder", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "pdf-decoder", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "mcp-tool" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("mcp-tool") + .with_image(images.trustgraph_flow) + .with_command([ + "mcp-tool", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "mcp-tool", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "metering" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("metering") + .with_image(images.trustgraph_flow) + .with_command([ + "metering", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "metering", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "metering-rag" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("metering-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "metering", + "-p", + url.pulsar, + "--id", + "metering-rag", + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "metering-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-store" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-store") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-store", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-store", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-manager" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-manager") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-manager", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-manager", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + librarian + mcp_server + workbench + graphrag + + documentrag + prompt_template + agent_manager + structured_data + + config_initialiser + config + + ddg + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/embeddings/embeddings-fastembed.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/embeddings/embeddings-fastembed.jsonnet new file mode 100644 index 00000000..37116de0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/embeddings/embeddings-fastembed.jsonnet @@ -0,0 +1,49 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/embeddings-fastembed.jsonnet"; + +{ + + "fastembed-models":: models, + + "embeddings-models" +:: $["fastembed-models"], + + embeddings +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local container = + engine.container("embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "embeddings-fastembed", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits("1.0", "400M") + .with_reservations("0.5", "400M"); + + local containerSet = engine.containers( + "embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/embeddings/embeddings-hf.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/embeddings/embeddings-hf.jsonnet new file mode 100644 index 00000000..492f05cb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/embeddings/embeddings-hf.jsonnet @@ -0,0 +1,47 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/embeddings-huggingface.jsonnet"; + +{ + + "huggingface-embeddings-models":: models, + + "embeddings-models" +:: $["huggingface-embeddings-models"], + + embeddings +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local container = + engine.container("embeddings") + .with_image(images.trustgraph_hf) + .with_command([ + "embeddings-hf", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + ]) + .with_limits("1.0", "400M") + .with_reservations("0.5", "400M"); + + local containerSet = engine.containers( + "embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/embeddings/embeddings-ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/embeddings/embeddings-ollama.jsonnet new file mode 100644 index 00000000..05cbed72 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/embeddings/embeddings-ollama.jsonnet @@ -0,0 +1,52 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local models = import "parameters/embeddings-ollama.jsonnet"; + +{ + + "ollama-url":: "${OLLAMA_HOST}", + + "ollama-models":: models, + + "embeddings-models" +:: $["ollama-models"], + + embeddings +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local container = + engine.container("embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "embeddings-ollama", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "-r", + $["ollama-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/engine/aks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/engine/aks-k8s.jsonnet new file mode 100644 index 00000000..07e050f2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/engine/aks-k8s.jsonnet @@ -0,0 +1,45 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "disk.csi.azure.com", + parameters: { + // Standard disks (spinning magnetic), Locally Redundant Storage + // Cheapest, basically + skuName: "Standard_LRS", + }, + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/engine/docker-compose.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/engine/docker-compose.jsonnet new file mode 100644 index 00000000..334b3517 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/engine/docker-compose.jsonnet @@ -0,0 +1,260 @@ +{ + + // Extract resources using the engine + package:: function(patterns) + std.foldl( + function(state, p) state + p.create(self), + std.objectValues(patterns), + {} + ), + + container:: function(name) + { + + local container = self, + + name:: name, + + with_image:: function(x) self + { image: x }, + + with_user:: function(x) self + { user: x }, + + with_group:: function(x) self + + if std.objectHas(container, "group_add") then + { group_add: container.group_add + [x] } + else + { group_add: [x] }, + + with_command:: function(x) self + { + command: + if std.isString(x) then + std.strReplace(x, "$", "$$") + else if std.isArray(x) then + std.map(function(s) std.strReplace(s, "$", "$$"), x) + else + x + }, + + with_entrypoint:: function(x) self + { entrypoint: x }, + + with_runtime:: function(x) self + { runtime: x }, + + with_privileged:: function(x) self + { privileged: x }, + + with_ipc:: function(x) self + { ipc: x }, + + with_capability:: function(x) self + + if std.objectHas(container, "capability") then + { cap_add: container.capability + x } + else + { cap_add: [x], }, + + with_environment:: function(x) self + + if std.objectHas(container, "environment") then + { environment: container.environment + x } + else + { environment: x, }, + + with_device:: function(hdev, cdev) self + + if std.objectHas(container, "devices") then + { devices: container.devices + [ "%s:%s" % [hdev, cdev] ] } + else + { devices: [ "%s:%s" % [hdev, cdev] ], }, + + with_limits:: function(c, m) self + { + deploy +: { resources +: { + limits: { cpus: c, memory: m } + } }, + }, + + with_reservations:: function(c, m) self + { + deploy +: { resources +: { + reservations: { cpus: c, memory: m } + } }, + }, + + with_volume_mount:: + function(vol, mnt) + self + { + volumes: + if std.objectHas(container, "volumes") then + container.volumes + [ + "%s:%s" % [vol.volid, mnt] + ] + else + [ + "%s:%s" % [vol.volid, mnt] + ] + }, + + with_bind_mount:: + function(src, dest) + self + { + volumes: + if std.objectHas(container, "volumes") then + container.volumes + [ + "%s:%s" % [src, dest] + ] + else + [ + "%s:%s" % [src, dest] + ] + }, + + with_port:: + function(src, dest, name) + self + { + ports: + if std.objectHas(container, "ports") then + container.ports + [ "%d:%d" % [src, dest] ] + else + [ "%d:%d" % [src, dest] ] + }, + + with_env_var_secrets:: + function(vars) + std.foldl( + function(obj, x) obj.with_environment( + { [x]: "${" + x + "}" } + ), + vars.variables, + self + ), + + restart: "on-failure:100", + + add:: function() { + services +: { + [container.name]: container, + } + } + + }, + + internalService:: function(containers) + { + + local service = self, + + name: containers.name, + + with_port:: function(src, dest, name) + self + { port: [src, dest] }, + + add:: function() { + } + + }, + + service:: function(containers) + { + + local service = self, + + name: containers.name, + + with_port:: function(src, dest, name) + self + { port: [src, dest] }, + + add:: function() { + } + + }, + + volume:: function(name) + { + + local volume = self, + + name: name, + + volid:: name, + + with_size:: function(size) self + { size: size }, + + add:: function() { + volumes +: { + [volume.name]: {} + } + } + + }, + + configVolume:: function(name, dir, parts) + { + + local volume = self, + + name: dir, + + volid:: "./" + dir, + + with_size:: function(size) self + { size: size }, + + add:: function() { + } + + }, + + secretVolume:: function(name, dir, parts) + { + + local volume = self, + + name: dir, + + volid:: dir, + + with_size:: function(size) self + { size: size }, + + add:: function() { + } + + }, + + envSecrets:: function(name) + { + + local volume = self, + + name: name, + + volid:: name, + + variables:: [], + + with_env_var:: + function(name, key) self + { + variables: super.variables + [name], + }, + + add:: function() { + } + + }, + + containers:: function(name, containers) + { + + local cont = self, + + name: name, + containers: containers, + + add:: function() std.foldl( + function(state, c) state + c.add(), + cont.containers, + {} + ), + + }, + + resources:: function(res) + std.foldl( + function(state, c) state + c.add(), + res, + {} + ), + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/engine/eks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/engine/eks-k8s.jsonnet new file mode 100644 index 00000000..3fc3f035 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/engine/eks-k8s.jsonnet @@ -0,0 +1,46 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "ebs.csi.aws.com", + parameters: { + type: "gp3", + encrypted: "true", + iops: "6000", + throughput: "400", + }, + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/engine/gcp-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/engine/gcp-k8s.jsonnet new file mode 100644 index 00000000..71792426 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/engine/gcp-k8s.jsonnet @@ -0,0 +1,44 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "pd.csi.storage.gke.io", + parameters: { + type: "pd-balanced", + "csi.storage.k8s.io/fstype": "ext4", + }, + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/engine/k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/engine/k8s.jsonnet new file mode 100644 index 00000000..2067f6ac --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/engine/k8s.jsonnet @@ -0,0 +1,393 @@ +{ + + container:: function(name) + { + + local container = self, + + name: name, + limits: {}, + reservations: {}, + ports: [], + volumes: [], + bindMounts: [], + groups: [], + environment: [], + + with_image:: function(x) self + { image: x }, + + with_user:: function(x) self + { user: x }, + + with_group:: function(x) self + { groups: super.groups + [x] }, + + with_privileged:: function(x) self + { privileged: x }, + + with_command:: function(x) self + { command: x }, + + with_entrypoint:: function(x) self + { entrypoint: x }, + + with_environment:: function(x) self + { + environment: super.environment + [ + { + name: v.key, value: v.value + } + for v in std.objectKeysValues(x) + ], + }, + + with_limits:: function(c, m) self + { limits: { cpu: c, memory: m } }, + + with_reservations:: + function(c, m) self + { reservations: { cpu: c, memory: m } }, + + with_volume_mount:: + function(vol, mnt) + self + { + volumes: super.volumes + [{ + volume: vol, mount: mnt + }] + }, + + with_bind_mount:: + function(src, dest) + local name = "bind-" + std.strReplace(std.strReplace(src, "/", "-"), ".", "-"); + self + { + bindMounts: super.bindMounts + [{ + name: name, src: src, dest: dest + }] + }, + + with_port:: + function(src, dest, name) self + { + ports: super.ports + [ + { src: src, dest: dest, name : name } + ] + }, + + with_env_var_secrets:: + function(vars) + std.foldl( + function(obj, x) obj + { + environment: super.environment + [{ + name: x, + valueFrom: { + secretKeyRef: { + name: vars.name, + key: vars.keyMap[x], + } + } + }] + }, + vars.variables, + self + ), + + add:: function() [ + + { + apiVersion: "apps/v1", + kind: "Deployment", + metadata: { + name: container.name, + namespace: "trustgraph", + labels: { + app: container.name + } + }, + spec: { + replicas: 1, + selector: { + matchLabels: { + app: container.name, + } + }, + template: { + metadata: { + labels: { + app: container.name, + } + }, + spec: { + containers: [ + { + name: container.name, + image: container.image, + + // FIXME: Make everything run as + // root. Needed to get filesystems + // to be accessible. There's a + // better way of doing this? + securityContext: { + runAsUser: 0, + runAsGroup: 0, + } + ( + if std.objectHas(container, "privileged") && container.privileged then + { privileged: true } + else {} + ), + + resources: { + requests: container.reservations, + limits: container.limits + }, + } + ( + if std.length(container.ports) > 0 then + { + ports: [ + { + hostPort: port.src, + containerPort: port.dest, + } + for port in container.ports + ] + } else + {}) + + + (if std.objectHas(container, "entrypoint") then + // Entrypoint is set - use command for entrypoint, args for command + (if std.isString(container.entrypoint) && container.entrypoint == "" then + { command: [] } + else if std.isArray(container.entrypoint) then + { command: container.entrypoint } + else + { command: [container.entrypoint] } + ) + (if std.objectHas(container, "command") then + { args: container.command } + else {}) + else if std.objectHas(container, "command") then + { command: container.command } + else {}) + + + (if std.length(container.environment) > 0 then + { + env: container.environment, + } + else {}) + + + (if std.length(container.volumes) > 0 || std.length(container.bindMounts) > 0 then + { + volumeMounts: [ + { + mountPath: vol.mount, + name: vol.volume.name, + } + for vol in container.volumes + ] + [ + { + mountPath: bm.dest, + name: bm.name, + } + for bm in container.bindMounts + ] + } + + else + {} + ) + ], + volumes: [ + vol.volume.volRef() + for vol in container.volumes + ] + [ + { + name: bm.name, + hostPath: { path: bm.src } + } + for bm in container.bindMounts + ] + } + ( + if std.length(container.groups) > 0 then + { securityContext: { supplementalGroups: container.groups } } + else {} + ) + }, + } + {} + + } + + ] + + }, + + // Just an alias + internalService:: self.service, + + service:: function(containers) + { + + local service = self, + + name: containers.name, + + ports: [], + + with_port:: + function(src, dest, name) + self + { + ports: super.ports + [ + { src: src, dest: dest, name: name } + ] + }, + + add:: function() [ + + { + + apiVersion: "v1", + kind: "Service", + metadata: { + name: service.name, + namespace: "trustgraph", + }, + spec: { + selector: { + app: service.name, + }, + ports: [ + { + port: port.src, + targetPort: port.dest, + name: port.name, + } + for port in service.ports + ], + } + } + ], + + }, + + volume:: function(name) + { + + local volume = self, + + name: name, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + { + apiVersion: "v1", + kind: "PersistentVolumeClaim", + metadata: { + name: volume.name, + namespace: "trustgraph", + }, + spec: { + storageClassName: "tg", + accessModes: [ "ReadWriteOnce" ], + resources: { + requests: { + storage: volume.size, + } + }, + } + } + ], + + volRef:: function() { + name: volume.name, + persistentVolumeClaim: { claimName: volume.name }, + } + + }, + + configVolume:: function(name, dir, parts) + { + + local volume = self, + + name: name, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + { + apiVersion: "v1", + kind: "ConfigMap", + metadata: { + name: volume.name, + namespace: "trustgraph", + }, + data: parts + }, + ], + + + volRef:: function() { + name: volume.name, + configMap: { name: volume.name }, + } + + }, + + secretVolume:: function(name, dir, parts) + { + + local volume = self, + + name: name, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + ], + + volRef:: function() { + name: volume.name, + secret: { secretName: volume.name }, + } + + }, + + envSecrets:: function(name) + { + + local volume = self, + + name: name, + + variables: [], + keyMap: {}, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + ], + + volRef:: function() { + name: volume.name, + secret: { secretName: volume.name }, + }, + + with_env_var:: + function(name, key) self + { + variables: super.variables + [name], + keyMap: super.keyMap + { [name]: key }, + }, + + }, + + containers:: function(name, containers) + { + + local cont = self, + + name: name, + containers: containers, + + add:: function() std.flattenArrays( + [ c.add() for c in cont.containers ] + ), + + }, + + resources:: function(res) + + std.flattenArrays( + [ c.add() for c in res ] + ), + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/engine/minikube-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/engine/minikube-k8s.jsonnet new file mode 100644 index 00000000..858b17ad --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/engine/minikube-k8s.jsonnet @@ -0,0 +1,115 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList, + + volume:: function(name) + { + local volume = self, + name: name, + with_size:: function(size) self + { size: size }, + add:: function() [ + { + apiVersion: "v1", + kind: "PersistentVolume", + metadata: { + name: volume.name, + }, + spec: { + accessModes: [ "ReadWriteOnce" ], + capacity: { + storage: volume.size, + }, + persistentVolumeReclaimPolicy: "Delete", + hostPath: { + path: "/data/pv-" + volume.name, + }, + } + }, + { + apiVersion: "v1", + kind: "PersistentVolumeClaim", + metadata: { + name: volume.name, + namespace: "trustgraph", + }, + spec: { + accessModes: [ "ReadWriteOnce" ], + resources: { + requests: { + storage: volume.size, + } + }, + } + } + ], + + volRef:: function() { + name: volume.name, + persistentVolumeClaim: { claimName: volume.name }, + } + + }, + + service:: function(containers) + { + local service = self, + name: containers.name, + ports: [], + with_port:: + function(src, dest, name) + self + { + ports: super.ports + [ + { src: src, dest: dest, name: name } + ] + }, + add:: function() [ + { + apiVersion: "v1", + kind: "Service", + metadata: { + name: service.name, + namespace: "trustgraph", + }, + spec: { + selector: { + app: service.name, + }, + type: "LoadBalancer", + ports: [ + { + port: port.src, + targetPort: port.dest, + name: port.name, + } + for port in service.ports + ], + } + } + ], + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/engine/noop.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/engine/noop.jsonnet new file mode 100644 index 00000000..1f384648 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/engine/noop.jsonnet @@ -0,0 +1,79 @@ +{ + + // Extract resources usnig the engine + package:: function(patterns) {}, + + container:: function(name) { + + with_image:: function(x) self + {}, + + with_user:: function(x) self + {}, + + with_command:: function(x) self + {}, + + with_runtime:: function(x) self + {}, + + with_privileged:: function(x) self + {}, + + with_ipc:: function(x) self + {}, + + with_capability:: function(x) self + {}, + + with_environment:: function(x) self + {}, + + with_device:: function(hdev, cdev) self + {}, + + with_limits:: function(c, m) self + {}, + + with_reservations:: function(c, m) self + {}, + + with_volume_mount:: self + {}, + + with_port:: function(src, dest, name) self + {}, + + with_env_var_secrets:: function(vars) self + {}, + + add:: function() {}, + }, + + internalService:: function(containers) { + with_port:: function(src, dest, name) self + {}, + add:: function() {}, + }, + + service:: function(containers) { + with_port:: function(src, dest, name) self + {}, + add:: function() {}, + }, + + volume:: function(name) { + with_size:: function(size) self + {}, + add:: function() {}, + }, + + configVolume:: function(name, dir, parts) { + add:: function() {}, + }, + + secretVolume:: function(name, dir, parts) { + add:: function() {}, + }, + + envSecrets:: function(name) { + with_env_var:: function(name, key) self + {}, + add:: function() {}, + }, + + containers:: function(name, containers) { + add:: function() {}, + }, + + resources:: function(res) + std.foldl( + function(state, c) state + c.add(), + res, + {} + ), + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/engine/ovh-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/engine/ovh-k8s.jsonnet new file mode 100644 index 00000000..15d57c83 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/engine/ovh-k8s.jsonnet @@ -0,0 +1,45 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "cinder.csi.openstack.org", + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", + parameters: { + availability: "nova", + fsType: "ext4", + type: "high-speed", + }, +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: [ns, sc] + resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/engine/scw-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/engine/scw-k8s.jsonnet new file mode 100644 index 00000000..eed23968 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/engine/scw-k8s.jsonnet @@ -0,0 +1,40 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "csi.scaleway.com", + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: [ns, sc] + resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/agent-extract.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/agent-extract.jsonnet new file mode 100644 index 00000000..4c5785c8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/agent-extract.jsonnet @@ -0,0 +1,35 @@ +// Agent-based extraction module +// Uses AI agents for more sophisticated knowledge extraction from text +// Leverages agent tools and reasoning for complex extraction tasks + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; + +{ + // No external interfaces - internal agent extraction service + "interfaces" +: { + }, + + // No configurable parameters for agent extraction + "parameters" +: { + }, + + // Flow-level processors for agent-based extraction + "flow" +: { + // Agent-based knowledge extraction processor + // Uses AI agents with tools to extract structured knowledge + "kg-extract-agent:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output knowledge triples + "entity-contexts": flow("entity-contexts-load:{id}"), // Entity context information + "agent-request": request("agent:{id}"), // Agent service requests + "agent-response": response("agent:{id}"), // Agent service responses + }, + }, + + // No blueprint-level processors needed + "blueprint" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/agent.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/agent.jsonnet new file mode 100644 index 00000000..2baa31d2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/agent.jsonnet @@ -0,0 +1,60 @@ +// Agent management module +// Provides AI agent orchestration and tool integration +// Manages agent conversations, tool calls, and response coordination +// Supports MCP tools, GraphRAG, and structured queries + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +// Import shared services (agent requires LLM for reasoning, MCP for tools) +local llm_services = import "llm-services.jsonnet"; +local mcp_service = import "mcp-service.jsonnet"; + +// Merge shared services with agent-specific configuration +llm_services + mcp_service + { + + // External interfaces for agent operations + "interfaces" +: { + "agent": request_response("agent:{id}"), + }, + + // Flow-level processors for agent management + "flow" +: { + // Agent manager orchestrates agent conversations and tool usage + "agent-manager:{id}": { + + // Agent communication channels + request: request("agent:{id}"), + next: request("agent:{id}"), + response: response("agent:{id}"), + + // LLM and prompt services + "text-completion-request": request("text-completion:{id}"), + "text-completion-response": response("text-completion:{id}"), + "prompt-request": request("prompt:{id}"), + "prompt-response": response("prompt:{id}"), + + // Tool integrations + "mcp-tool-request": request("mcp-tool:{id}"), + "mcp-tool-response": response("mcp-tool:{id}"), + "graph-rag-request": request("graph-rag:{id}"), + "graph-rag-response": response("graph-rag:{id}"), + "structured-query-request": request("structured-query:{id}"), + "structured-query-response": response("structured-query:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + "row-embeddings-query-request": request("row-embeddings:{id}"), + "row-embeddings-query-response": response("row-embeddings:{id}"), + + // Explainability + explainability: flow("triples-store:{id}"), + }, + }, + + // Blueprint-level processors for agent-related services + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/document-store.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/document-store.jsonnet new file mode 100644 index 00000000..769718fa --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/document-store.jsonnet @@ -0,0 +1,57 @@ +// Document store module +// Infrastructure for document-based RAG using chunk embeddings +// Handles document embedding storage, retrieval, and question answering + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +// Import shared services +local llm_services = import "llm-services.jsonnet"; +local embeddings_service = import "embeddings-service.jsonnet"; + +// Merge shared services with document store configuration +llm_services + embeddings_service + { + + // External interfaces for document store + "interfaces" +: { + // Document embedding storage and retrieval + "document-embeddings-store": flow("document-embeddings-store:{id}"), + "document-rag": request_response("document-rag:{id}"), + "document-embeddings": request_response("document-embeddings:{id}"), + }, + + // Flow-level processors for document embedding and storage + "flow" +: { + "document-embeddings:{id}": { + input: flow("chunk-load:{id}"), + output: flow("document-embeddings-store:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + }, + "doc-embeddings-write:{id}": { + input: flow("document-embeddings-store:{id}"), + }, + "document-rag:{id}": { + request: request("document-rag:{id}"), + response: response("document-rag:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + "prompt-request": request("prompt-rag:{id}"), + "prompt-response": response("prompt-rag:{id}"), + "document-embeddings-request": request("document-embeddings:{id}"), + "document-embeddings-response": response("document-embeddings:{id}"), + explainability: flow("triples-store:{id}"), + }, + "doc-embeddings-query:{id}": { + request: request("document-embeddings:{id}"), + response: response("document-embeddings:{id}"), + }, + }, + + // Blueprint-level processors for document RAG operations + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/embeddings-service.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/embeddings-service.jsonnet new file mode 100644 index 00000000..1537e6c2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/embeddings-service.jsonnet @@ -0,0 +1,30 @@ +// Shared embeddings service module +// Provides vector embedding generation for text +// Import this module in any flow that requires embeddings + +local helpers = import "helpers.jsonnet"; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +{ + // Interfaces exposed by embeddings service + "interfaces" +: { + "embeddings": request_response("embeddings:{id}"), + }, + + "parameters" +: { + }, + + // Flow-level processor for embeddings + "flow" +: { + "embeddings:{id}": { + request: request("embeddings:{id}"), + response: response("embeddings:{id}"), + model: "{embeddings-model}", + }, + }, + + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/flow-blueprints.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/flow-blueprints.jsonnet new file mode 100644 index 00000000..8f054690 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/flow-blueprints.jsonnet @@ -0,0 +1,48 @@ +// TrustGraph Flow Blueprints Configuration +// +// RAG Modes (4 types): +// - Document RAG: Uses document chunk embeddings +// - Graph RAG: Extracts definitions + relationships to graph +// - Ontology RAG: Extracts using ontology definitions to graph (mutually exclusive with Graph RAG) +// - Structured RAG: Extracts objects to object store +// +// Module structure: +// - *-store: Storage and query infrastructure +// - *-extract: Extraction methods + +// Import all the modular flow components +local graph_store = import "graph-store.jsonnet"; +local document_store = import "document-store.jsonnet"; +local structured_store = import "structured-store.jsonnet"; +local graphrag_extract = import "graphrag-extract.jsonnet"; +local ontorag_extract = import "ontorag-extract.jsonnet"; +local structured_extract = import "structured-extract.jsonnet"; +local agent = import "agent.jsonnet"; +local load = import "load.jsonnet"; +local kgcore = import "kgcore.jsonnet"; + +{ + + // Full system: Graph RAG + Document RAG + knowledge cores + "everything": { + description: "Graph RAG + Document RAG + knowledge cores", + tags: ["document-rag", "graph-rag", "kgcore"], + } + + graph_store + document_store + agent + load + + graphrag_extract + kgcore + structured_store, + + // Structured RAG only + "structured": { + description: "Structured data extraction and querying", + tags: ["structured"], + } + + structured_store + structured_extract + agent + load, + + // Ontology RAG + knowledge cores + "ontology": { + description: "Ontology RAG + knowledge cores", + tags: ["onto-rag", "kgcore"], + } + + graph_store + ontorag_extract + agent + load + kgcore, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/graph-store.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/graph-store.jsonnet new file mode 100644 index 00000000..64546a51 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/graph-store.jsonnet @@ -0,0 +1,73 @@ +// Graph store module +// Shared infrastructure for graph-based RAG (used by both GraphRAG and OntologyRAG) +// Handles knowledge graph storage, embeddings, and graph-based question answering + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +// Import shared services +local llm_services = import "llm-services.jsonnet"; +local embeddings_service = import "embeddings-service.jsonnet"; + +// Merge shared services with graph store configuration +llm_services + embeddings_service + { + + // External interfaces exposed by the graph store + "interfaces" +: { + // Data ingestion interfaces for graph construction + "entity-contexts-load": flow("entity-contexts-load:{id}"), + "triples-store": flow("triples-store:{id}"), + "graph-embeddings-store": flow("graph-embeddings-store:{id}"), + + // Query interfaces for graph-based operations + "graph-rag": request_response("graph-rag:{id}"), + "triples": request_response("triples:{id}"), + "graph-embeddings": request_response("graph-embeddings:{id}"), + }, + + // Flow-level processors - handle data streams for a specific flow instance + "flow" +: { + "graph-embeddings:{id}": { + input: flow("entity-contexts-load:{id}"), + output: flow("graph-embeddings-store:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + }, + "triples-write:{id}": { + input: flow("triples-store:{id}"), + }, + "graph-embeddings-write:{id}": { + input: flow("graph-embeddings-store:{id}"), + }, + "graph-rag:{id}": { + request: request("graph-rag:{id}"), + response: response("graph-rag:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + "prompt-request": request("prompt-rag:{id}"), + "prompt-response": response("prompt-rag:{id}"), + "graph-embeddings-request": request("graph-embeddings:{id}"), + "graph-embeddings-response": response("graph-embeddings:{id}"), + "triples-request": request("triples:{id}"), + "triples-response": response("triples:{id}"), + explainability: flow("triples-store:{id}"), + "librarian-request": request("librarian"), + "librarian-response": response("librarian"), + }, + "triples-query:{id}": { + request: request("triples:{id}"), + response: response("triples:{id}"), + }, + "graph-embeddings-query:{id}": { + request: request("graph-embeddings:{id}"), + response: response("graph-embeddings:{id}"), + }, + }, + + // Blueprint-level processors - shared across all flow instances of this blueprint + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/graphrag-extract.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/graphrag-extract.jsonnet new file mode 100644 index 00000000..6bcbca14 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/graphrag-extract.jsonnet @@ -0,0 +1,44 @@ +// GraphRAG extraction module +// Extraction method for GraphRAG - extracts definitions and relationships +// Mutually exclusive with OntologyRAG extraction (both write to graph store) + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; + +{ + // No external interfaces - this module provides internal extraction services + "interfaces" +: { + }, + + // No configurable parameters for basic KG extraction + "parameters" +: { + }, + + // Flow-level processors for knowledge extraction + "flow" +: { + // Extracts entity definitions from text chunks + // Identifies and defines key entities mentioned in the text + "kg-extract-definitions:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output definition triples + "entity-contexts": flow("entity-contexts-load:{id}"), // Entity context information + "prompt-request": request("prompt:{id}"), // Definition extraction prompts + "prompt-response": response("prompt:{id}"), + }, + + // Extracts relationships between entities + // Identifies how entities are connected and interact + "kg-extract-relationships:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output relationship triples + "prompt-request": request("prompt:{id}"), // Relationship extraction prompts + "prompt-response": response("prompt:{id}"), + }, + }, + + // No blueprint-level processors needed + "blueprint" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/helpers.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/helpers.jsonnet new file mode 100644 index 00000000..eabb3bf4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/helpers.jsonnet @@ -0,0 +1,29 @@ +// Helper functions for flow configuration +// Provides utility functions for constructing flow, request, and response URIs +// used throughout the TrustGraph flow configuration system + +// Creates a persistent flow URI for data streams +// Persistent flows retain messages until consumed +local flow(x) = "persistent://tg/flow/" + x; + +// Creates a non-persistent request URI for request-response patterns +// Non-persistent means messages are not retained if no consumer is present +local request(x) = "non-persistent://tg/request/" + x; + +// Creates a non-persistent response URI for request-response patterns +local response(x) = "non-persistent://tg/response/" + x; + +// Creates a request-response pair for bidirectional communication +// Returns an object with both request and response URIs +local request_response(x) = { + request: request(x), + response: response(x), +}; + +// Export all helper functions for use in other modules +{ + flow: flow, + request: request, + response: response, + request_response: request_response, +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/kgcore.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/kgcore.jsonnet new file mode 100644 index 00000000..bfc01ada --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/kgcore.jsonnet @@ -0,0 +1,31 @@ +// Knowledge Graph Core storage module +// Handles persistent storage of knowledge graph data +// Consolidates triples and graph embeddings into permanent storage +// Creates the core knowledge base for long-term use + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; + +{ + // No external interfaces - internal storage service + "interfaces" +: { + }, + + // No configurable parameters for core storage + "parameters" +: { + }, + + // Flow-level processors for knowledge graph storage + "flow" +: { + // Knowledge graph store consolidates extracted knowledge + // Takes processed triples and embeddings and stores them permanently + "kg-store:{id}": { + "triples-input": flow("triples-store:{id}"), // Input RDF triples stream + "graph-embeddings-input": flow("graph-embeddings-store:{id}"), // Input graph embeddings + }, + }, + + // No blueprint-level processors needed + "blueprint" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/llm-parameters.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/llm-parameters.jsonnet new file mode 100644 index 00000000..a99f72ae --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/llm-parameters.jsonnet @@ -0,0 +1,59 @@ +{ + + // LLM model selection for normal LLM + "llm-model": { + "type": "llm-model", + "description": "LLM model", + "order": 1, + "advanced": false, + }, + + // LLM model for RAG operations + "llm-rag-model": { + "type": "llm-model", + "description": "LLM model for RAG", + "order": 2, + "advanced": true, + "controlled-by": "llm-model", + }, + + // LLM model selection for normal LLM + "llm-temperature": { + "type": "llm-temperature", + "description": "LLM temperature", + "order": 3, + "advanced": true, + }, + + // LLM model selection for normal LLM + "llm-rag-temperature": { + "type": "llm-temperature", + "description": "LLM temperature for RAG", + "order": 4, + "advanced": true, + }, + + "embeddings-model": { + "type": "embeddings-model", + "description": "Embeddings model", + "order": 5, + "advanced": true, + }, + + // LLM model selection for normal LLM + "chunk-size": { + "type": "chunk-size", + "description": "Chunk size", + "order": 6, + "advanced": true, + }, + + // LLM model selection for normal LLM + "chunk-overlap": { + "type": "chunk-overlap", + "description": "Chunk overlap", + "order": 7, + "advanced": true, + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/llm-services.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/llm-services.jsonnet new file mode 100644 index 00000000..ee6e09ae --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/llm-services.jsonnet @@ -0,0 +1,66 @@ +// Shared LLM services module +// Provides text completion, prompt processing, and metering services +// Import this module in any flow that requires LLM functionality + +local helpers = import "helpers.jsonnet"; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; +local llm_parameters = import "llm-parameters.jsonnet"; + +{ + // Interfaces exposed by LLM services + "interfaces" +: { + "prompt": request_response("prompt:{id}"), + "text-completion": request_response("text-completion:{id}"), + }, + + // LLM configuration parameters + "parameters" +: llm_parameters, + + // Flow-level processors for LLM services + "flow" +: { + // Primary text completion service + "text-completion:{id}": { + request: request("text-completion:{id}"), + response: response("text-completion:{id}"), + model: "{llm-model}", + }, + + // RAG-specific text completion (may use different model) + "text-completion-rag:{id}": { + request: request("text-completion-rag:{id}"), + response: response("text-completion-rag:{id}"), + model: "{llm-rag-model}", + }, + + // Prompt processing service + "prompt:{id}": { + request: request("prompt:{id}"), + response: response("prompt:{id}"), + "text-completion-request": request("text-completion:{id}"), + "text-completion-response": response("text-completion:{id}"), + }, + + // RAG-specific prompt processing + "prompt-rag:{id}": { + request: request("prompt-rag:{id}"), + response: response("prompt-rag:{id}"), + "text-completion-request": request("text-completion-rag:{id}"), + "text-completion-response": response("text-completion-rag:{id}"), + }, + + // Usage metering for primary completion + "metering:{id}": { + input: response("text-completion:{id}"), + }, + + // Usage metering for RAG completion + "metering-rag:{id}": { + input: response("text-completion-rag:{id}"), + }, + }, + + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/load.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/load.jsonnet new file mode 100644 index 00000000..ea7c74d4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/load.jsonnet @@ -0,0 +1,51 @@ +// Document loading and preprocessing module +// Handles document ingestion, format conversion, and chunking +// Converts PDFs to text and splits documents into processable chunks + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +// Import shared services (load requires embeddings for chunk processing) +local embeddings_service = import "embeddings-service.jsonnet"; + +// Merge shared services with load-specific configuration +embeddings_service + { + + // External interfaces for document loading + "interfaces" +: { + "document-load": flow("document-load:{id}"), + "text-load": flow("text-document-load:{id}"), + }, + + // Flow-level processors for document preprocessing + "flow" +: { + // PDF decoder converts PDF documents to text + // Also emits page provenance triples and saves pages via librarian + "pdf-decoder:{id}": { + input: flow("document-load:{id}"), + output: flow("text-document-load:{id}"), + triples: flow("triples-store:{id}"), + "librarian-request": request("librarian"), + "librarian-response": response("librarian"), + }, + + // Chunker splits documents into smaller, processable pieces + // Also emits chunk provenance triples and saves chunks via librarian + "chunker:{id}": { + input: flow("text-document-load:{id}"), + output: flow("chunk-load:{id}"), + triples: flow("triples-store:{id}"), + "librarian-request": request("librarian"), + "librarian-response": response("librarian"), + "chunk-size": "{chunk-size}", + "chunk-overlap": "{chunk-overlap}", + }, + }, + + // Blueprint-level processors for document loading services + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/mcp-service.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/mcp-service.jsonnet new file mode 100644 index 00000000..5d599a67 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/mcp-service.jsonnet @@ -0,0 +1,31 @@ +// Shared MCP (Model Context Protocol) tool service module +// Provides MCP tool execution capabilities for agents +// Import this module in any flow that requires MCP tool integration + +local helpers = import "helpers.jsonnet"; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +{ + // Interfaces exposed by MCP service + "interfaces" +: { + "mcp-tool": request_response("mcp-tool:{id}"), + }, + + "parameters" +: { + }, + + // Flow-level processor for MCP tool execution + "flow" +: { + "mcp-tool:{id}": { + request: request("mcp-tool:{id}"), + response: response("mcp-tool:{id}"), + "text-completion-request": request("text-completion:{id}"), + "text-completion-response": response("text-completion:{id}"), + }, + }, + + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/ontorag-extract.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/ontorag-extract.jsonnet new file mode 100644 index 00000000..e5521d8a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/ontorag-extract.jsonnet @@ -0,0 +1,39 @@ +// OntologyRAG extraction module +// Extraction method for OntologyRAG - extracts using ontology definitions +// Mutually exclusive with GraphRAG extraction (both write to graph store) + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; + +{ + // No external interfaces - this module provides internal extraction services + "interfaces" +: { + }, + + // No configurable parameters for basic KG extraction + "parameters" +: { + }, + + // Flow-level processors for knowledge extraction + "flow" +: { + // Extracts using ontology definitions + "kg-extract-ontology:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output triples + "entity-contexts": flow("entity-contexts-load:{id}"), // Entity context information + "prompt-request": request("prompt:{id}"), // Definition + // extraction prompts + "prompt-response": response("prompt:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + }, + + }, + + // No blueprint-level processors needed + "blueprint" +: { + } +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/structured-extract.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/structured-extract.jsonnet new file mode 100644 index 00000000..54f6a05d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/structured-extract.jsonnet @@ -0,0 +1,30 @@ +// Structured RAG extraction module +// Extracts structured rows from text chunks +// Outputs to rows-store for structured data querying + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; + +{ + "interfaces" +: { + }, + + "parameters" +: { + }, + + // Flow-level processor for structured row extraction + "flow" +: { + "kg-extract-rows:{id}": { + input: flow("chunk-load:{id}"), + output: flow("rows-store:{id}"), + "entity-contexts": flow("entity-contexts-load:{id}"), + "prompt-request": request("prompt:{id}"), + "prompt-response": response("prompt:{id}"), + }, + }, + + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/structured-store.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/structured-store.jsonnet new file mode 100644 index 00000000..3668222d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/flows/structured-store.jsonnet @@ -0,0 +1,78 @@ +// Structured store module +// Shared infrastructure for structured data RAG +// Handles row storage, retrieval, and NLP query capabilities + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +// Import shared services +local llm_services = import "llm-services.jsonnet"; +local embeddings_service = import "embeddings-service.jsonnet"; + +// Merge shared services with structured store configuration +llm_services + embeddings_service + { + + // External interfaces for structured store + "interfaces" +: { + // Row storage and querying + "rows-store": flow("rows-store:{id}"), + "row-embeddings-store": flow("row-embeddings-store:{id}"), + "rows": request_response("rows:{id}"), + "row-embeddings": request_response("row-embeddings:{id}"), + + // Query interfaces + "nlp-query": request_response("nlp-query:{id}"), + "structured-query": request_response("structured-query:{id}"), + "structured-diag": request_response("structured-diag:{id}"), + }, + + // Flow-level processors for structured storage and query + "flow" +: { + "row-embeddings:{id}": { + input: flow("rows-store:{id}"), + output: flow("row-embeddings-store:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + }, + "rows-write:{id}": { + input: flow("rows-store:{id}"), + }, + "row-embeddings-write:{id}": { + input: flow("row-embeddings-store:{id}"), + }, + "rows-query:{id}": { + request: request("rows:{id}"), + response: response("rows:{id}"), + }, + "row-embeddings-query:{id}": { + request: request("row-embeddings:{id}"), + response: response("row-embeddings:{id}"), + }, + "nlp-query:{id}": { + request: request("nlp-query:{id}"), + response: response("nlp-query:{id}"), + "prompt-request": request("prompt-rag:{id}"), + "prompt-response": response("prompt-rag:{id}"), + }, + "structured-query:{id}": { + request: request("structured-query:{id}"), + response: response("structured-query:{id}"), + "nlp-query-request": request("nlp-query:{id}"), + "nlp-query-response": response("nlp-query:{id}"), + "rows-query-request": request("rows:{id}"), + "rows-query-response": response("rows:{id}"), + }, + "structured-diag:{id}": { + request: request("structured-diag:{id}"), + response: response("structured-diag:{id}"), + "prompt-request": request("prompt:{id}"), + "prompt-response": response("prompt:{id}"), + }, + }, + + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/azure-openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/azure-openai.jsonnet new file mode 100644 index 00000000..bee0fe47 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/azure-openai.jsonnet @@ -0,0 +1,112 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/azure-openai.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["azure-openai-" + key]:: value, + }, + +// Strategy is to specify the model with the AZURE_MODEL environment +// variable. This isn't something that can just be specified dynamically, +// it has to match what was provisioned in Azure. + + "azure-openai-max-output-tokens":: 4192, + "azure-openai-temperature":: 0.0, + "azure-openai-models":: models, + + "llm-models" +:: $["azure-openai-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-openai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_MODEL", "azure-model") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure-openai", + "-p", + url.pulsar, + "-x", + std.toString($["azure-openai-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-openai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_MODEL", "azure-model") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure-openai", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["azure-openai-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/azure.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/azure.jsonnet new file mode 100644 index 00000000..f4db7500 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/azure.jsonnet @@ -0,0 +1,106 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/azure.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["azure-" + key]:: value, + }, + + "azure-max-output-tokens":: 4096, + "azure-temperature":: 0.0, + "azure-models":: models, + + "llm-models" +:: $["azure-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-ai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure", + "-p", + url.pulsar, + "-x", + std.toString($["azure-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-ai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["azure-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/bedrock.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/bedrock.jsonnet new file mode 100644 index 00000000..0db1475b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/bedrock.jsonnet @@ -0,0 +1,104 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/bedrock.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["bedrock-" + key]:: value, + }, + + "bedrock-max-output-tokens":: 4096, + "bedrock-temperature":: 0.0, + "bedrock-models":: models, + + "llm-models" +:: $["bedrock-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("bedrock-credentials") + .with_env_var("AWS_ACCESS_KEY_ID", "aws-id-key") + .with_env_var("AWS_SECRET_ACCESS_KEY", "aws-secret") + .with_env_var("AWS_DEFAULT_REGION", "aws-region"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_bedrock) + .with_command([ + "text-completion-bedrock", + "-p", + url.pulsar, + "-x", + std.toString($["bedrock-max-output-tokens"]), + "-t", + "%0.3f" % $["bedrock-temperature"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("bedrock-credentials") + .with_env_var("AWS_ACCESS_KEY_ID", "aws-id-key") + .with_env_var("AWS_SECRET_ACCESS_KEY", "aws-secret") + .with_env_var("AWS_DEFAULT_REGION", "aws-region"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_bedrock) + .with_command([ + "text-completion-bedrock", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["bedrock-max-output-tokens"]), + "-t", + "%0.3f" % $["bedrock-temperature"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/claude.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/claude.jsonnet new file mode 100644 index 00000000..f3125e96 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/claude.jsonnet @@ -0,0 +1,104 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/claude.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["claude-" + key]:: value, + }, + + "claude-max-output-tokens":: 4096, + "claude-temperature":: 0.0, + "claude-models":: models, + + "llm-models" +:: $["claude-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("claude-credentials") + .with_env_var("CLAUDE_KEY", "claude-key"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-claude", + "-p", + url.pulsar, + "-x", + std.toString($["claude-max-output-tokens"]), + "-t", + "%0.3f" % $["claude-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("claude-credentials") + .with_env_var("CLAUDE_KEY", "claude-key"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-claude", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["claude-max-output-tokens"]), + "-t", + "%0.3f" % $["claude-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/cohere.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/cohere.jsonnet new file mode 100644 index 00000000..6517f7a0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/cohere.jsonnet @@ -0,0 +1,97 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/cohere.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["cohere-" + key]:: value, + }, + + "cohere-temperature":: 0.0, + "cohere-models":: models, + + "llm-models" +:: $["cohere-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("cohere-credentials") + .with_env_var("COHERE_KEY", "cohere-key"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-cohere", + "-p", + url.pulsar, + "-t", + "%0.3f" % $["cohere-temperature"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("cohere-credentials") + .with_env_var("COHERE_KEY", "cohere-key"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-cohere", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-t", + "%0.3f" % $["cohere-temperature"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/googleaistudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/googleaistudio.jsonnet new file mode 100644 index 00000000..05a8592c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/googleaistudio.jsonnet @@ -0,0 +1,104 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/googleaistudio.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["googleaistudio-" + key]:: value, + }, + + "googleaistudio-max-output-tokens":: 4096, + "googleaistudio-temperature":: 0.0, + "googleaistudio-models":: models, + + "llm-models" +:: $["googleaistudio-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("googleaistudio-credentials") + .with_env_var("GOOGLE_AI_STUDIO_KEY", "googleaistudio-key"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_vertexai) + .with_command([ + "text-completion-googleaistudio", + "-p", + url.pulsar, + "-x", + std.toString($["googleaistudio-max-output-tokens"]), + "-t", + "%0.3f" % $["googleaistudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("googleaistudio-credentials") + .with_env_var("GOOGLE_AI_STUDIO_KEY", "googleaistudio-key"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_vertexai) + .with_command([ + "text-completion-googleaistudio", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["googleaistudio-max-output-tokens"]), + "-t", + "%0.3f" % $["googleaistudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/llamafile.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/llamafile.jsonnet new file mode 100644 index 00000000..f3bee2cc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/llamafile.jsonnet @@ -0,0 +1,94 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/slm.jsonnet"; +local models = import "parameters/llamafile.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["llamafile-" + key]:: value, + }, + + "llamafile-models":: models, + + "llm-models" +:: $["llamafile-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("llamafile-credentials") + .with_env_var("LLAMAFILE_URL", "llamafile-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-llamafile", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("llamafile-credentials") + .with_env_var("LLAMAFILE_URL", "llamafile-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-llamafile", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/lmstudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/lmstudio.jsonnet new file mode 100644 index 00000000..4cf78caa --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/lmstudio.jsonnet @@ -0,0 +1,104 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/lmstudio.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["lmstudio-" + key]:: value, + }, + + "lmstudio-max-output-tokens":: 4096, + "lmstudio-temperature":: 0.0, + "lmstudio-models":: models, + + "llm-models" +:: $["lmstudio-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("lmstudio-credentials") + .with_env_var("LMSTUDIO_URL", "lmstudio-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-lmstudio", + "-p", + url.pulsar, + "-x", + std.toString($["lmstudio-max-output-tokens"]), + "-t", + "%0.3f" % $["lmstudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("lmstudio-credentials") + .with_env_var("LMSTUDIO_URL", "lmstudio-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-lmstudio", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["lmstudio-max-output-tokens"]), + "-t", + "%0.3f" % $["lmstudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/mistral.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/mistral.jsonnet new file mode 100644 index 00000000..2fb8c562 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/mistral.jsonnet @@ -0,0 +1,104 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/mistral.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["mistral-" + key]:: value, + }, + + "mistral-max-output-tokens":: 4096, + "mistral-temperature":: 0.0, + "mistral-models":: models, + + "llm-models" +:: $["mistral-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("mistral-credentials") + .with_env_var("MISTRAL_TOKEN", "mistral-token"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-mistral", + "-p", + url.pulsar, + "-x", + std.toString($["mistral-max-output-tokens"]), + "-t", + "%0.3f" % $["mistral-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("mistral-credentials") + .with_env_var("MISTRAL_TOKEN", "mistral-token"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-mistral", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["mistral-max-output-tokens"]), + "-t", + "%0.3f" % $["mistral-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/ollama.jsonnet new file mode 100644 index 00000000..6143f5cc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/ollama.jsonnet @@ -0,0 +1,102 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/ollama.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["ollama-" + key]:: value, + }, + + "ollama-models":: models, + + "llm-models" +:: $["ollama-models"], + + "text-completion" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("ollama-credentials") + .with_env_var("OLLAMA_HOST", "ollama-host"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-ollama", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("ollama-credentials") + .with_env_var("OLLAMA_HOST", "ollama-host"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-ollama", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/openai.jsonnet new file mode 100644 index 00000000..c3d482fd --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/openai.jsonnet @@ -0,0 +1,106 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/openai.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["openai-" + key]:: value, + }, + + "openai-max-output-tokens":: 4096, + "openai-temperature":: 0.0, + "openai-models":: models, + + "llm-models" +:: $["openai-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("openai-credentials") + .with_env_var("OPENAI_TOKEN", "openai-token") + .with_env_var("OPENAI_BASE_URL", "openai-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-openai", + "-p", + url.pulsar, + "-x", + std.toString($["openai-max-output-tokens"]), + "-t", + "%0.3f" % $["openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("openai-credentials") + .with_env_var("OPENAI_TOKEN", "openai-token") + .with_env_var("OPENAI_BASE_URL", "openai-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-openai", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-x", + std.toString($["openai-max-output-tokens"]), + "-t", + "%0.3f" % $["openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/tgi.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/tgi.jsonnet new file mode 100644 index 00000000..91588dff --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/tgi.jsonnet @@ -0,0 +1,108 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-" + key]:: value, + }, + + "tgi-max-output-tokens":: 1024, + "tgi-temperature":: 0.0, + + "text-completion" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("tgi-credentials") + .with_env_var("TGI_BASE_URL", "tgi-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-tgi", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "-x", + std.toString($["tgi-max-output-tokens"]), + "-t", + "%0.3f" % $["tgi-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("tgi-credentials") + .with_env_var("TGI_BASE_URL", "tgi-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-tgi", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "--concurrency", + std.toString(concurrency), + "-x", + std.toString($["tgi-max-output-tokens"]), + "-t", + "%0.3f" % $["tgi-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/vertexai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/vertexai.jsonnet new file mode 100644 index 00000000..cb8c141f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/vertexai.jsonnet @@ -0,0 +1,120 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/vertexai.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vertexai-" + key]:: value, + }, + + "vertexai-private-key":: "/vertexai/private.json", + "vertexai-region":: "us-central1", + "vertexai-max-output-tokens":: 4096, + "vertexai-temperature":: 0.0, + "vertexai-models":: models, + + "llm-models" +:: $["vertexai-models"], + + "text-completion" +: { + + create:: function(engine) + + local cfgVol = engine.secretVolume( + "vertexai-creds", + "./vertexai", + { + "private.json": importstr "vertexai/private.json", + } + ); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_vertexai) + .with_command([ + "text-completion-vertexai", + "-p", + url.pulsar, + "-k", + $["vertexai-private-key"], + "-r", + $["vertexai-region"], + "-x", + std.toString($["vertexai-max-output-tokens"]), + "-t", + "%0.3f" % $["vertexai-temperature"], + ]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_volume_mount(cfgVol, "/vertexai"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + cfgVol, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local cfgVol = engine.secretVolume( + "vertexai-creds", + "./vertexai", + { + "private.json": importstr "vertexai/private.json", + } + ); + + local container = + engine.container("text-completion-rag") + .with_image(images.trustgraph_vertexai) + .with_command([ + "text-completion-vertexai", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "-k", + $["vertexai-private-key"], + "-r", + $["vertexai-region"], + "-x", + std.toString($["vertexai-max-output-tokens"]), + "-t", + "%0.3f" % $["vertexai-temperature"], + ]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_volume_mount(cfgVol, "/vertexai"); + + local containerSet = engine.containers( + "text-completion-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + cfgVol, + containerSet, + service, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/vllm.jsonnet new file mode 100644 index 00000000..8b846bbf --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/llm/vllm.jsonnet @@ -0,0 +1,113 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/vllm.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-" + key]:: value, + }, + + "vllm-models":: models, + + "llm-models" +:: $["vllm-models"], + + "vllm-max-output-tokens":: 1024, + "vllm-temperature":: 0.0, + + "text-completion" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("vllm-credentials") + .with_env_var("VLLM_BASE_URL", "vllm-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-vllm", + "-p", + url.pulsar, + "--concurrency", + std.toString(concurrency), + "-x", + std.toString($["vllm-max-output-tokens"]), + "-t", + "%0.3f" % $["vllm-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("vllm-credentials") + .with_env_var("VLLM_BASE_URL", "vllm-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-vllm", + "-p", + url.pulsar, + "--id", + "text-completion-rag", + "--concurrency", + std.toString(concurrency), + "-x", + std.toString($["vllm-max-output-tokens"]), + "-t", + "%0.3f" % $["vllm-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/mcp/ddg-mcp-server.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/mcp/ddg-mcp-server.jsonnet new file mode 100644 index 00000000..04176f15 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/mcp/ddg-mcp-server.jsonnet @@ -0,0 +1,47 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "ddg-mcp-server-port":: 9870, + + "ddg-mcp-server" +: { + + create:: function(engine) + + local port = $["ddg-mcp-server-port"]; + + local container = + engine.container("ddg-mcp-server") + .with_image(images["ddg-mcp-server"]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_port(port, port, "mcp"); + + local containerSet = engine.containers( + "ddg-mcp-server", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(port, port, "mcp"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + mcp +:: { + "duckduckgo": { + "remote-name": "search", + local port = $["ddg-mcp-server-port"], + local url = "http://ddg-mcp-server:%s/mcp" % port, + "url": url, + } + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/model-hosting/cpu-tgi.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/model-hosting/cpu-tgi.jsonnet new file mode 100644 index 00000000..04b61566 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/model-hosting/cpu-tgi.jsonnet @@ -0,0 +1,68 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-service-" + key]:: value, + }, + + "tgi-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "tgi-service-cpus":: "8.0", + "tgi-service-memory":: "16G", + "tgi-service-storage":: "20G", + "tgi-service-hf-token":: null, + + "tgi-service" +: { + + create:: function(engine) + + local vol = engine.volume("tgi-storage") + .with_size($["tgi-service-storage"]); + + local container = + engine.container("tgi-service") + .with_image(images["tgi-service-cpu"]) + .with_command([ + "--model-id", + $["tgi-service-model"], + "--hostname", + "0.0.0.0", + "--port", + "7000", + "--cuda-graphs", + "0", + ]) + .with_environment({ + } + ( + if $["tgi-service-hf-token"] != null + then { HF_TOKEN: $["tgi-service-hf-token"] } + else {} + )) + .with_limits( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_reservations( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_port(7000, 7000, "tgi") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "tgi-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "tgi"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/model-hosting/intel-battlemage-vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/model-hosting/intel-battlemage-vllm.jsonnet new file mode 100644 index 00000000..7646398c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/model-hosting/intel-battlemage-vllm.jsonnet @@ -0,0 +1,98 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-service-" + key]:: value, + }, + + "vllm-service-model":: "mistralai/Mistral-Nemo-Instruct-2407", + "vllm-service-cpus":: "32.0", + "vllm-service-memory":: "48G", + "vllm-service-storage":: "48G", + "vllm-service-tokenizer-mode":: "mistral", + "vllm-service-datatype":: "float16", + "vllm-service-quantization":: "woq_int4", + "vllm-service-hf-token":: null, + "vllm-service-max-model-len":: 8192, + "vllm-service-max-num-seqs":: 16, + + "vllm-service" +: { + + create:: function(engine) + + local vol = engine.volume("vllm-storage") + .with_size($["vllm-service-storage"]); + + local container = + engine.container("vllm-service") + .with_image(images["vllm-service-intel-battlemage"]) + .with_entrypoint("") // Clear default entrypoint + .with_command([ + "python", + "-m", + "ipex_llm.vllm.xpu.entrypoints.openai.api_server", + "--model", + $["vllm-service-model"], + "--served-model-name", + "model", + "--host", + "0.0.0.0", + "--port", + "7000", + "--device", + "xpu", + "--dtype", + $["vllm-service-datatype"], + "--enforce-eager", + "--max-model-len", + std.toString($["vllm-service-max-model-len"]), + "--max-num-seqs", + std.toString($["vllm-service-max-num-seqs"]), + "--load-in-low-bit", + $["vllm-service-quantization"], + "--trust-remote-code", + "--tokenizer-mode", + $["vllm-service-tokenizer-mode"], + "--disable-sliding-window", + ]) + .with_environment({ + VLLM_WORKER_MULTIPROC_METHOD: "spawn", + SYCL_PI_LEVEL_ZERO_USE_IMMEDIATE_COMMANDLISTS: "1", + } + ( + if $["vllm-service-hf-token"] != null + then { HF_TOKEN: $["vllm-service-hf-token"] } + else {} + )) + .with_privileged(true) + .with_device("/dev/dri", "/dev/dri") + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_reservations( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_port(7000, 7000, "vllm") + .with_bind_mount("/dev/dri/by-path", "/dev/dri/by-path") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "vllm-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "vllm"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/model-hosting/intel-gaudi-tgi.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/model-hosting/intel-gaudi-tgi.jsonnet new file mode 100644 index 00000000..5af36b78 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/model-hosting/intel-gaudi-tgi.jsonnet @@ -0,0 +1,96 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-service-" + key]:: value, + }, + + "tgi-service-model":: "meta-llama/Llama-3.3-70B-Instruct", + "tgi-service-cpus":: "64.0", + "tgi-service-memory":: "64G", + "tgi-service-storage":: "50G", + "tgi-service-num-shard":: 8, + "tgi-service-max-input-tokens":: 4096, + "tgi-service-max-total-tokens":: 4096, + "tgi-service-max-batch-size":: 128, + "tgi-service-max-concurrent-requests":: 512, + "tgi-service-hf-token":: null, + + "tgi-service" +: { + + create:: function(engine) + + local vol = engine.volume("tgi-storage") + .with_size($["tgi-service-storage"]); + + local container = + engine.container("tgi-service") + .with_image(images["tgi-service-gaudi"]) + .with_command([ + "--model-id", + $["tgi-service-model"], + "--hostname", + "0.0.0.0", + "--port", + "7000", + "--sharded", + "true", + "--num-shard", + std.toString($["tgi-service-num-shard"]), + "--max-input-tokens", + std.toString($["tgi-service-max-input-tokens"]), + "--max-total-tokens", + std.toString($["tgi-service-max-total-tokens"]), + "--max-batch-size", + std.toString($["tgi-service-max-batch-size"]), + "--max-waiting-tokens", + "7", + "--max-concurrent-requests", + std.toString($["tgi-service-max-concurrent-requests"]), + "--cuda-graphs", + "0", + ]) + .with_runtime("habana") + .with_environment({ + HABANA_VISIBLE_DEVICES: "all", + OMPI_MCA_btl_vader_single_copy_mechanism: "none", + ENABLE_HPU_GRAPH: "true", + LIMIT_HPU_GRAPH: "true", + USE_FLASH_ATTENTION: "true", + FLASH_ATTENTION_RECOMPUTE: "true", + } + ( + if $["tgi-service-hf-token"] != null + then { HF_TOKEN: $["tgi-service-hf-token"] } + else {} + )) + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_reservations( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_port(7000, 7000, "tgi") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "tgi-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "tgi"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/model-hosting/intel-gaudi-vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/model-hosting/intel-gaudi-vllm.jsonnet new file mode 100644 index 00000000..81d31c14 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/model-hosting/intel-gaudi-vllm.jsonnet @@ -0,0 +1,76 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-service-" + key]:: value, + }, + + "vllm-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "vllm-service-cpus":: "64.0", + "vllm-service-memory":: "64G", + "vllm-service-storage":: "50G", + "vllm-service-tensor-parallel-size":: 8, + "vllm-service-hf-token":: null, + + "vllm-service" +: { + + create:: function(engine) + + local vol = engine.volume("vllm-storage") + .with_size($["vllm-service-storage"]); + + local container = + engine.container("vllm-service") + .with_image(images["vllm-service-gaudi"]) + .with_command([ + "--model", + $["vllm-service-model"], + "--served-model-name", + "model", + "--host", + "0.0.0.0", + "--port", + "7000", + "--tensor-parallel-size", + std.toString($["vllm-service-tensor-parallel-size"]), + ]) + .with_runtime("habana") + .with_environment({ + VLLM_SKIP_WARMUP: "true", + HABANA_VISIBLE_DEVICES: "all", + } + ( + if $["vllm-service-hf-token"] != null + then { HF_TOKEN: $["vllm-service-hf-token"] } + else {} + )) + .with_privileged(true) + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_reservations( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_port(7000, 7000, "vllm") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "vllm-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "vllm"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/model-hosting/intel-xpu-tgi.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/model-hosting/intel-xpu-tgi.jsonnet new file mode 100644 index 00000000..bc806874 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/model-hosting/intel-xpu-tgi.jsonnet @@ -0,0 +1,75 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-service-" + key]:: value, + }, + + "tgi-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "tgi-service-cpus":: "8.0", + "tgi-service-memory":: "16G", + "tgi-service-storage":: "20G", + "tgi-service-hf-token":: null, + + "tgi-service" +: { + + create:: function(engine) + + local vol = engine.volume("tgi-storage") + .with_size($["tgi-service-storage"]); + + local container = + engine.container("tgi-service") + .with_image(images["tgi-service-intel-xpu"]) + .with_command([ + "--model-id", + $["tgi-service-model"], + "--hostname", + "0.0.0.0", + "--port", + "7000", + "--cuda-graphs", + "0", + ]) + .with_environment({ + } + ( + if $["tgi-service-hf-token"] != null + then { HF_TOKEN: $["tgi-service-hf-token"] } + else {} + )) + .with_privileged(true) + .with_device("/dev/dri", "/dev/dri") + .with_ipc("host") + .with_group("video") + .with_group("render") + .with_capability("SYS_NICE") + .with_limits( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_reservations( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_port(7000, 7000, "tgi") + .with_bind_mount("/dev/dri/by-path", "/dev/dri/by-path") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "tgi-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "tgi"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/model-hosting/intel-xpu-vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/model-hosting/intel-xpu-vllm.jsonnet new file mode 100644 index 00000000..f6e82905 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/model-hosting/intel-xpu-vllm.jsonnet @@ -0,0 +1,97 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-service-" + key]:: value, + }, + + "vllm-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "vllm-service-cpus":: "8.0", + "vllm-service-memory":: "16G", + "vllm-service-storage":: "20G", + "vllm-service-datatype":: "float16", + "vllm-service-max-model-len":: 4096, + "vllm-service-max-num-seqs":: 16, + "vllm-service-hf-token":: null, + + "vllm-service" +: { + + create:: function(engine) + + local vol = engine.volume("vllm-storage") + .with_size($["vllm-service-storage"]); + + local container = + engine.container("vllm-service") + .with_image(images["vllm-service-intel-xpu"]) + .with_command([ + "python", + "-m", + "vllm.entrypoints.openai.api_server", + "--model", + $["vllm-service-model"], + "--served-model-name", + "model", + "--host", + "0.0.0.0", + "--port", + "7000", + "--device", + "xpu", + "--dtype", + $["vllm-service-datatype"], + "--enforce-eager", + "--max-model-len", + std.toString($["vllm-service-max-model-len"]), + "--max-num-seqs", + std.toString($["vllm-service-max-num-seqs"]), + "--block-size", + "64", + "--gpu-memory-util", + "0.85", + "--trust-remote-code", + "--disable-sliding-window", + ]) + .with_environment({ + VLLM_USE_V1: "1", + VLLM_WORKER_MULTIPROC_METHOD: "spawn", + } + ( + if $["vllm-service-hf-token"] != null + then { HF_TOKEN: $["vllm-service-hf-token"] } + else {} + )) + .with_privileged(true) + .with_device("/dev/dri", "/dev/dri") + .with_ipc("host") + .with_group("video") + .with_group("render") + .with_capability("SYS_NICE") + .with_limits( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_reservations( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_port(7000, 7000, "vllm") + .with_bind_mount("/dev/dri/by-path", "/dev/dri/by-path") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "vllm-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "vllm"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/model-hosting/nvidia-gpu-vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/model-hosting/nvidia-gpu-vllm.jsonnet new file mode 100644 index 00000000..d04876c4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/model-hosting/nvidia-gpu-vllm.jsonnet @@ -0,0 +1,72 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-service-" + key]:: value, + }, + + "vllm-service-model":: "mistralai/Mistral-7B-Instruct-v0.3", + "vllm-service-cpus":: "0.5", + "vllm-service-memory":: "1G", + "vllm-service-storage":: "50G", + "vllm-service-hf-token":: null, + + "vllm-service" +: { + + create:: function(engine) + + local vol = engine.volume("vllm-storage") + .with_size($["vllm-service-storage"]); + + local container = + engine.container("vllm-service") + .with_image(images["vllm-service-nvidia"]) + .with_command([ + "--model", + $["vllm-service-model"], + "--served-model-name", + "model", + "--host", + "0.0.0.0", + "--port", + "7000", + ]) + .with_runtime("nvidia") + .with_environment({ + VLLM_SKIP_WARMUP: "true", + } + ( + if $["vllm-service-hf-token"] != null + then { HF_TOKEN: $["vllm-service-hf-token"] } + else {} + )) + .with_privileged(true) + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_reservations( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_port(7000, 7000, "vllm") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "vllm-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "vllm"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/monitoring/grafana.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/monitoring/grafana.jsonnet new file mode 100644 index 00000000..dd0dddd7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/monitoring/grafana.jsonnet @@ -0,0 +1,124 @@ +local images = import "values/images.jsonnet"; +local loki = import "loki.jsonnet"; + +{ + + "prometheus" +: { + + create:: function(engine) + + local vol = engine.volume("prometheus-data").with_size("20G"); + + local cfgVol = engine.configVolume( + "prometheus-cfg", "prometheus", + { + "prometheus.yml": importstr "prometheus/prometheus.yml", + } + ); + + local container = + engine.container("prometheus") + .with_image(images.prometheus) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M") + .with_port(9090, 9090, "http") + .with_volume_mount(cfgVol, "/etc/prometheus/") + .with_volume_mount(vol, "/prometheus"); + + local containerSet = engine.containers( + "prometheus", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9090, 9090, "http"); + + engine.resources([ + cfgVol, + vol, + containerSet, + service, + ]) + + }, + + "grafana" +: { + + create:: function(engine) + + local vol = engine.volume("grafana-storage").with_size("20G"); + + local provDashVol = engine.configVolume( + "prov-dash", "grafana/provisioning/", + { + "dashboard.yml": + importstr "grafana/provisioning/dashboard.yml", + } + + ); + + local provDataVol = engine.configVolume( + "prov-data", "grafana/provisioning/", + { + "datasource.yml": + importstr "grafana/provisioning/datasource.yml", + } + + ); + + local dashVol = engine.configVolume( + "dashboards", "grafana/dashboards/", + { + "overview-dashboard.json": + importstr "grafana/dashboards/overview-dashboard.json", + "log-dashboard.json": + importstr "grafana/dashboards/log-dashboard.json", + } + + ); + + local container = + engine.container("grafana") + .with_image(images.grafana) + .with_environment({ + // GF_AUTH_ANONYMOUS_ORG_ROLE: "Admin", + // GF_AUTH_ANONYMOUS_ENABLED: "true", + // GF_ORG_ROLE: "Admin", + GF_ORG_NAME: "trustgraph.ai", + // GF_SERVER_ROOT_URL: "https://example.com", + }) + .with_limits("1.0", "256M") + .with_reservations("0.5", "256M") + .with_port(3000, 3000, "cassandra") + .with_volume_mount(vol, "/var/lib/grafana") + .with_volume_mount( + provDashVol, "/etc/grafana/provisioning/dashboards/" + ) + .with_volume_mount( + provDataVol, "/etc/grafana/provisioning/datasources/" + ) + .with_volume_mount( + dashVol, "/var/lib/grafana/dashboards/" + ); + + local containerSet = engine.containers( + "grafana", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(3000, 3000, "http"); + + engine.resources([ + vol, + provDashVol, + provDataVol, + dashVol, + containerSet, + service, + ]) + + }, + +} + loki + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/monitoring/loki.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/monitoring/loki.jsonnet new file mode 100644 index 00000000..0774f6cf --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/monitoring/loki.jsonnet @@ -0,0 +1,45 @@ +local images = import "values/images.jsonnet"; + +{ + + "loki" +: { + + create:: function(engine) + + local vol = engine.volume("loki-data").with_size("20G"); + + local cfgVol = engine.configVolume( + "loki-cfg", "loki", + { + "local-config.yaml": importstr "loki/local-config.yaml", + } + ); + + local container = + engine.container("loki") + .with_image(images.loki) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_port(3100, 3100, "http") + .with_volume_mount(cfgVol, "/etc/loki/") + .with_volume_mount(vol, "/loki"); + + local containerSet = engine.containers( + "loki", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(3100, 3100, "http"); + + engine.resources([ + cfgVol, + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/ocr/mistral-ocr.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/ocr/mistral-ocr.jsonnet new file mode 100644 index 00000000..f70e0cd6 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/ocr/mistral-ocr.jsonnet @@ -0,0 +1,49 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["mistral-" + key]:: value, + }, + + "pdf-decoder" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("mistral-credentials") + .with_env_var("MISTRAL_TOKEN", "mistral-token"); + + local container = + engine.container("mistral-ocr") + .with_image(images.trustgraph_flow) + .with_command([ + "pdf-ocr-mistral", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "mistral-ocr", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/ocr/ocr.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/ocr/ocr.jsonnet new file mode 100644 index 00000000..4f53aa5a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/ocr/ocr.jsonnet @@ -0,0 +1,37 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "pdf-decoder" +: { + + create:: function(engine) + + local container = + engine.container("pdf-ocr") + .with_image(images.trustgraph_ocr) + .with_command([ + "pdf-ocr", + "-p", + url.pulsar, + ]) + .with_limits("1.0", "512M") + .with_reservations("0.1", "512M"); + + local containerSet = engine.containers( + "pdf-ocr", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/azure-openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/azure-openai.jsonnet new file mode 100644 index 00000000..dcf706d4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/azure-openai.jsonnet @@ -0,0 +1,9 @@ +// Azure OpenAI LLM Model Definitions +// Model input is just text + +{ + "type": "string", + "description": "LLM model to use", + "default": "gpt-5.2", + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/azure.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/azure.jsonnet new file mode 100644 index 00000000..6ffa5fdf --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/azure.jsonnet @@ -0,0 +1,9 @@ +// Azure LLM Model Definitions +// Model input is just text + +{ + "type": "string", + "description": "LLM model to use", + "default": "phi4:14b", + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/bedrock.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/bedrock.jsonnet new file mode 100644 index 00000000..6c2c7e90 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/bedrock.jsonnet @@ -0,0 +1,84 @@ +// AWS Bedrock LLM Model Definitions +// Defines available models and their configurations for AWS Bedrock + +{ + "type": "string", + "description": "LLM model to use", + "default": "global.anthropic.claude-sonnet-4-5-20250929-v1:0", + "enum": [ + // ── Anthropic Claude ────────────────────────────────────── + { + id: "global.anthropic.claude-opus-4-6-v1", + description: "Claude Opus 4.6 (maximum intelligence)" + }, + { + id: "global.anthropic.claude-opus-4-5-20251101-v1:0", + description: "Claude Opus 4.5 (frontier coding + agents)" + }, + { + id: "global.anthropic.claude-sonnet-4-5-20250929-v1:0", + description: "Claude Sonnet 4.5 (complex agents + coding)" + }, + { + id: "global.anthropic.claude-haiku-4-5-20251001-v1:0", + description: "Claude Haiku 4.5 (fastest with near-frontier intelligence)" + }, + { + id: "global.anthropic.claude-opus-4-1-20250805-v1:0", + description: "Claude Opus 4.1 (agentic search + reasoning)" + }, + { + id: "global.anthropic.claude-sonnet-4-20250514-v1:0", + description: "Claude Sonnet 4.0" + }, + { + id: "anthropic.claude-3-5-haiku-20241022-v1:0", + description: "Claude 3.5 Haiku (legacy)" + }, + + // ── Meta Llama ──────────────────────────────────────────── + { + id: "us.meta.llama4-maverick-17b-instruct-v1:0", + description: "Llama 4 Maverick 17B (128 experts, 400B params, multimodal)" + }, + { + id: "us.meta.llama4-scout-17b-instruct-v1:0", + description: "Llama 4 Scout 17B (16 experts, 3.5M context)" + }, + { + id: "us.meta.llama3-3-70b-instruct-v1:0", + description: "Llama 3.3 70B Instruct" + }, + + // ── Mistral AI ──────────────────────────────────────────── + { + id: "us.mistral.mistral-large-2511-v1:0", + description: "Mistral Large 3 (flagship text, 128K context)" + }, + { + id: "us.mistral.magistral-small-2506-v1:0", + description: "Magistral Small 1.2 (reasoning, cost-effective)" + }, + + // ── DeepSeek ────────────────────────────────────────────── + { + id: "us.deepseek.r1-v1:0", + description: "DeepSeek-R1 (reasoning)" + }, + + // ── Amazon Nova ─────────────────────────────────────────── + { + id: "us.amazon.nova-pro-v1:0", + description: "Amazon Nova Pro (multimodal, balanced)" + }, + { + id: "us.amazon.nova-lite-v1:0", + description: "Amazon Nova Lite (fast, multimodal)" + }, + { + id: "us.amazon.nova-micro-v1:0", + description: "Amazon Nova Micro (text-only, cheapest)" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/chunking-param-types.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/chunking-param-types.jsonnet new file mode 100644 index 00000000..f82fa3f7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/chunking-param-types.jsonnet @@ -0,0 +1,25 @@ +// Chunk parameter type definitions + +{ + "chunk-size": { + "type": "integer", + "description": "Chunk size", + "placeholder": 2000, + "helper": "An integer, usually 2000 .. 8000", + "default": 2000, + "min": 0, + "max": 32768, + "required": true + }, + "chunk-overlap": { + "type": "integer", + "description": "Chunk overlap", + "placeholder": 50, + "helper": "An integer, usually 50 .. 100", + "default": 50, + "min": 0, + "max": 8000, + "required": true + }, +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/claude.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/claude.jsonnet new file mode 100644 index 00000000..b14a317c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/claude.jsonnet @@ -0,0 +1,39 @@ +// Claude LLM Model Definitions +// Defines available models and their configurations for Anthropic's Claude + +{ + "type": "string", + "description": "LLM model to use", + "default": "claude-sonnet-4-5-20250929", + "enum": [ + { + id: "claude-opus-4-6", + description: "Claude Opus 4.6 (maximum intelligence)" + }, + { + id: "claude-opus-4-5-20251101", + description: "Claude Opus 4.5 (frontier coding + agents)" + }, + { + id: "claude-sonnet-4-5-20250929", + description: "Claude Sonnet 4.5 (complex agents + coding)" + }, + { + id: "claude-haiku-4-5-20251001", + description: "Claude Haiku 4.5 (fast)" + }, + { + id: "claude-opus-4-1-20250805", + description: "Claude Opus 4.1 (agentic search + reasoning)" + }, + { + id: "claude-sonnet-4-20250514", + description: "Claude Sonnet 4.0" + }, + { + id: "claude-3-5-haiku-20241022", + description: "Claude 3.5 Haiku" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/cohere.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/cohere.jsonnet new file mode 100644 index 00000000..5a5865a3 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/cohere.jsonnet @@ -0,0 +1,35 @@ +// Cohere LLM Model Definitions +// Defines available models and their configurations for Cohere + +{ + "type": "string", + "description": "LLM model to use", + "default": "command-r-plus-08-2024", + "enum": [ + { + id: "command-r-plus-08-2024", + description: "Command R+ (August 2024)" + }, + { + id: "command-r-08-2024", + description: "Command R (August 2024)" + }, + { + id: "command-r-plus", + description: "Command R+ (legacy)" + }, + { + id: "command-r", + description: "Command R (legacy)" + }, + { + id: "command", + description: "Command" + }, + { + id: "command-light", + description: "Command Light" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/embeddings-fastembed.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/embeddings-fastembed.jsonnet new file mode 100644 index 00000000..477e25dc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/embeddings-fastembed.jsonnet @@ -0,0 +1,127 @@ +// Embeddings model definitions for fastembed +// Defines available models and their configurations for Fastembed + +{ + "type": "string", + "description": "Embeddings model to use", + "default": "sentence-transformers/all-MiniLM-L6-v2", + "enum": [ + { + "id": "sentence-transformers/all-MiniLM-L6-v2", + "description": "all-MiniLM-L6-v2" + }, + { + "id": "BAAI/bge-small-en-v1.5", + "description": "bge-small-en-v1.5" + }, + { + "id": "BAAI/bge-small-zh-v1.5", + "description": "bge-small-zh-v1.5" + }, + { + "id": "snowflake/snowflake-arctic-embed-xs", + "description": "snowflake-arctic-embed-xs" + }, + { + "id": "jinaai/jina-embeddings-v2-small-en", + "description": "jina-embeddings-v2-small-en" + }, + { + "id": "nomic-ai/nomic-embed-text-v1.5-Q", + "description": "nomic-embed-text-v1.5-Q" + }, + { + "id": "snowflake/snowflake-arctic-embed-s", + "description": "snowflake-arctic-embed-s" + }, + { + "id": "BAAI/bge-small-en", + "description": "bge-small-en" + }, + { + "id": "BAAI/bge-base-en-v1.5", + "description": "bge-base-en-v1.5" + }, + { + "id": "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2", + "description": "paraphrase-multilingual-MiniLM-L12-v2" + }, + { + "id": "Qdrant/clip-ViT-B-32-text", + "description": "clip-ViT-B-32-text" + }, + { + "id": "jinaai/jina-embeddings-v2-base-de", + "description": "jina-embeddings-v2-base-de" + }, + { + "id": "BAAI/bge-base-en", + "description": "bge-base-en" + }, + { + "id": "snowflake/snowflake-arctic-embed-m", + "description": "snowflake-arctic-embed-m" + }, + { + "id": "thenlper/gte-base", + "description": "gte-base" + }, + { + "id": "jinaai/jina-embeddings-v2-base-en", + "description": "jina-embeddings-v2-base-en" + }, + { + "id": "nomic-ai/nomic-embed-text-v1", + "description": "nomic-embed-text-v1" + }, + { + "id": "nomic-ai/nomic-embed-text-v1.5", + "description": "nomic-embed-text-v1.5" + }, + { + "id": "snowflake/snowflake-arctic-embed-m-long", + "description": "snowflake-arctic-embed-m-long" + }, + { + "id": "jinaai/jina-clip-v1", + "description": "jina-clip-v1" + }, + { + "id": "mixedbread-ai/mxbai-embed-large-v1", + "description": "mxbai-embed-large-v1" + }, + { + "id": "jinaai/jina-embeddings-v2-base-es", + "description": "jina-embeddings-v2-base-es" + }, + { + "id": "jinaai/jina-embeddings-v2-base-code", + "description": "jina-embeddings-v2-base-code" + }, + { + "id": "jinaai/jina-embeddings-v2-base-zh", + "description": "jina-embeddings-v2-base-zh" + }, + { + "id": "sentence-transformers/paraphrase-multilingual-mpnet-base-v2", + "description": "paraphrase-multilingual-mpnet-base-v2" + }, + { + "id": "snowflake/snowflake-arctic-embed-l", + "description": "snowflake-arctic-embed-l" + }, + { + "id": "BAAI/bge-large-en-v1.5", + "description": "bge-large-en-v1.5" + }, + { + "id": "thenlper/gte-large", + "description": "gte-large" + }, + { + "id": "intfloat/multilingual-e5-large", + "description": "multilingual-e5-large" + } + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/embeddings-huggingface.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/embeddings-huggingface.jsonnet new file mode 100644 index 00000000..32254a2b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/embeddings-huggingface.jsonnet @@ -0,0 +1,31 @@ +// Embeddings model definitions for fastembed +// Defines available models and their configurations for Fastembed + +{ + "type": "string", + "description": "Embeddings model to use", + "default": "sentence-transformers/all-MiniLM-L6-v2", + "enum": [ + { + "id": "all-MiniLM-L6-v2", + "description": "all-MiniLM-L6-v2" + }, + { + "id": "all-mpnet-base-v2", + "description": "all-mpnet-base-v2" + }, + { + "id": "all-distilroberta-v1", + "description": "all-distilroberta-v1" + }, + { + "id": "stsb-bert-large", + "description": "stsb-bert-large" + }, + { + "id": "sentence-camembert-large", + "description": "sentence-camembert-large" + } + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/embeddings-ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/embeddings-ollama.jsonnet new file mode 100644 index 00000000..c3f29fbb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/embeddings-ollama.jsonnet @@ -0,0 +1,23 @@ +// Embeddings model definitions for Ollama +// Defines available models and their configurations for Ollama + +{ + "type": "string", + "description": "Embeddings model to use", + "default": "all-minilm:latest", + "enum": [ + { + "id": "all-minilm:latest", + "description": "all-MiniLM-L6-v2" + }, + { + "id": "nomic-ai/nomic-embed-text-v1.5-Q", + "description": "nomic-embed-text-v1.5-Q" + }, + { + "id": "mixedbread-ai/mxbai-embed-large-v1", + "description": "mxbai-embed-large-v1" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/googleaistudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/googleaistudio.jsonnet new file mode 100644 index 00000000..6d454ea7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/googleaistudio.jsonnet @@ -0,0 +1,54 @@ +// Google AI Studio LLM Model Definitions +// Defines available models and their configurations for Google AI Studio + +{ + "type": "string", + "description": "LLM model to use", + "default": "gemini-2.5-flash-lite", + "enum": [ + // Gemini 3 models (preview) + { + id: "gemini-3-pro-preview", + description: "Gemini 3 Pro (preview)" + }, + { + id: "gemini-3-flash-preview", + description: "Gemini 3 Flash (preview)" + }, + + // Gemini 2.5 models + { + id: "gemini-2.5-pro", + description: "Gemini 2.5 Pro" + }, + { + id: "gemini-2.5-flash", + description: "Gemini 2.5 Flash" + }, + { + id: "gemini-2.5-flash-lite", + description: "Gemini 2.5 Flash Lite" + }, + + // Gemini 2.0 models + { + id: "gemini-2.0-flash-001", + description: "Gemini 2.0 Flash" + }, + { + id: "gemini-2.0-flash-lite-001", + description: "Gemini 2.0 Flash Lite" + }, + + // Gemma models + { + id: "gemma-3-27b", + description: "Gemma 3 27B" + }, + { + id: "gemma-3n-e4b", + description: "Gemma 3n E4B" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/llamafile.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/llamafile.jsonnet new file mode 100644 index 00000000..f6480aa9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/llamafile.jsonnet @@ -0,0 +1,9 @@ +// LlamaFile LLM Model Definitions +// Model input is just text + +{ + "type": "string", + "description": "LLM model to use", + "default": "phi4:14b", + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/lmstudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/lmstudio.jsonnet new file mode 100644 index 00000000..1c88da3c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/lmstudio.jsonnet @@ -0,0 +1,150 @@ +// LMStudio LLM Model Definitions +// Defines available models and their configurations for LMStudio + +{ + "type": "string", + "description": "LLM model to use", + "default": "llama3.1:70b", + "enum": [ + // Gemma3 models + { + id: "gemma3:1b", + description: "Gemma3 1B" + }, + { + id: "gemma3:4b", + description: "Gemma3 4B" + }, + { + id: "gemma3:12b", + description: "Gemma3 12B" + }, + { + id: "gemma3:27b", + description: "Gemma3 27B" + }, + + // Phi4 models + { + id: "phi4:mini:3.8b", + description: "Phi4 Mini 3.8B" + }, + { + id: "phi4:14b", + description: "Phi4 14B" + }, + + // DeepSeek-R1 models + { + id: "deepseek-r1:671b", + description: "DeepSeek-R1 671B" + }, + { + id: "deepseek-r1:70b", + description: "DeepSeek-R1 70B" + }, + { + id: "deepseek-r1:32b", + description: "DeepSeek-R1 32B" + }, + { + id: "deepseek-r1:14b", + description: "DeepSeek-R1 14B" + }, + { + id: "deepseek-r1:8b", + description: "DeepSeek-R1 8B" + }, + { + id: "deepseek-r1:7b", + description: "DeepSeek-R1 7B" + }, + { + id: "deepseek-r1:1.5b", + description: "DeepSeek-R1 1.5B" + }, + + // Llama3.1 models + { + id: "llama3.1:405b", + description: "Llama 3.1 405B" + }, + { + id: "llama3.1:70b", + description: "Llama 3.1 70B" + }, + { + id: "llama3.1:8b", + description: "Llama 3.1 8B" + }, + + // Gemma2 models + { + id: "gemma2:2b", + description: "Gemma2 2B" + }, + { + id: "gemma2:9b", + description: "Gemma2 9B" + }, + { + id: "gemma2:27b", + description: "Gemma2 27B" + }, + + // Qwen2.5 models + { + id: "qwen2.5:0.5b", + description: "Qwen2.5 0.5B" + }, + { + id: "qwen2.5:1.5b", + description: "Qwen2.5 1.5B" + }, + { + id: "qwen2.5:3b", + description: "Qwen2.5 3B" + }, + { + id: "qwen2.5:7b", + description: "Qwen2.5 7B" + }, + { + id: "qwen2.5:14b", + description: "Qwen2.5 14B" + }, + { + id: "qwen2.5:32b", + description: "Qwen2.5 32B" + }, + { + id: "qwen2.5:72b", + description: "Qwen2.5 72B" + }, + + // Mistral models + { + id: "mistral:7b", + description: "Mistral 7B" + }, + { + id: "mistral-nemo:12b", + description: "Mistral Nemo 12B" + }, + { + id: "mistral-large:123b", + description: "Mistral Large 123B" + }, + + // Command-R models + { + id: "command-r:35b", + description: "Command-R 35B" + }, + { + id: "command-r-plus:104b", + description: "Command-R Plus 104B" + }, + ], + "required": true +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/mistral.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/mistral.jsonnet new file mode 100644 index 00000000..07f75b81 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/mistral.jsonnet @@ -0,0 +1,61 @@ +// Mistral LLM Model Definitions +// Defines available models and their configurations for Mistral AI + +{ + "type": "string", + "description": "LLM model to use", + "default": "mistral-medium-2508", + "enum": [ + // Featured models + { + id: "mistral-medium-2508", + description: "Mistral Medium 3.1" + }, + { + id: "mistral-large-2512", + description: "Mistral Large 3" + }, + { + id: "mistral-small-2506", + description: "Mistral Small 3.2" + }, + { + id: "ministral-14b-2512", + description: "Ministral 3 14B" + }, + { + id: "ministral-8b-2512", + description: "Ministral 3 8B" + }, + { + id: "ministral-3b-2512", + description: "Ministral 3 3B" + }, + { + id: "magistral-medium-2509", + description: "Magistral Medium 1.2 (reasoning)" + }, + { + id: "magistral-small-2509", + description: "Magistral Small 1.2 (reasoning)" + }, + { + id: "devstral-2512", + description: "Devstral 2 (code)" + }, + // Other models + { + id: "codestral-2508", + description: "Codestral (code)" + }, + { + id: "pixtral-large-2411", + description: "Pixtral Large (vision)" + }, + { + id: "open-mistral-nemo", + description: "Open Mistral Nemo" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/ollama.jsonnet new file mode 100644 index 00000000..4ef98a6a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/ollama.jsonnet @@ -0,0 +1,150 @@ +// Ollama LLM Model Definitions +// Defines available models and their configurations for Ollama + +{ + "type": "string", + "description": "LLM model to use", + "default": "gemma3:12b", + "enum": [ + // Gemma3 models + { + id: "gemma3:1b", + description: "Gemma3 1B" + }, + { + id: "gemma3:4b", + description: "Gemma3 4B" + }, + { + id: "gemma3:12b", + description: "Gemma3 12B" + }, + { + id: "gemma3:27b", + description: "Gemma3 27B" + }, + + // Phi4 models + { + id: "phi4:mini:3.8b", + description: "Phi4 Mini 3.8B" + }, + { + id: "phi4:14b", + description: "Phi4 14B" + }, + + // DeepSeek-R1 models + { + id: "deepseek-r1:671b", + description: "DeepSeek-R1 671B" + }, + { + id: "deepseek-r1:70b", + description: "DeepSeek-R1 70B" + }, + { + id: "deepseek-r1:32b", + description: "DeepSeek-R1 32B" + }, + { + id: "deepseek-r1:14b", + description: "DeepSeek-R1 14B" + }, + { + id: "deepseek-r1:8b", + description: "DeepSeek-R1 8B" + }, + { + id: "deepseek-r1:7b", + description: "DeepSeek-R1 7B" + }, + { + id: "deepseek-r1:1.5b", + description: "DeepSeek-R1 1.5B" + }, + + // Llama3.1 models + { + id: "llama3.1:405b", + description: "Llama 3.1 405B" + }, + { + id: "llama3.1:70b", + description: "Llama 3.1 70B" + }, + { + id: "llama3.1:8b", + description: "Llama 3.1 8B" + }, + + // Gemma2 models + { + id: "gemma2:2b", + description: "Gemma2 2B" + }, + { + id: "gemma2:9b", + description: "Gemma2 9B" + }, + { + id: "gemma2:27b", + description: "Gemma2 27B" + }, + + // Qwen2.5 models + { + id: "qwen2.5:0.5b", + description: "Qwen2.5 0.5B" + }, + { + id: "qwen2.5:1.5b", + description: "Qwen2.5 1.5B" + }, + { + id: "qwen2.5:3b", + description: "Qwen2.5 3B" + }, + { + id: "qwen2.5:7b", + description: "Qwen2.5 7B" + }, + { + id: "qwen2.5:14b", + description: "Qwen2.5 14B" + }, + { + id: "qwen2.5:32b", + description: "Qwen2.5 32B" + }, + { + id: "qwen2.5:72b", + description: "Qwen2.5 72B" + }, + + // Mistral models + { + id: "mistral:7b", + description: "Mistral 7B" + }, + { + id: "mistral-nemo:12b", + description: "Mistral Nemo 12B" + }, + { + id: "mistral-large:123b", + description: "Mistral Large 123B" + }, + + // Command-R models + { + id: "command-r:35b", + description: "Command-R 35B" + }, + { + id: "command-r-plus:104b", + description: "Command-R Plus 104B" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/openai.jsonnet new file mode 100644 index 00000000..5039c982 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/openai.jsonnet @@ -0,0 +1,51 @@ +// OpenAI LLM Model Definitions +// Defines available models and their configurations for OpenAI's platform + +{ + "type": "string", + "description": "LLM model to use", + "default": "gpt-5.2", + "enum": [ + { + id: "gpt-5.2-pro", + description: "GPT-5.2 Pro (maximum intelligence, slow)" + }, + { + id: "gpt-5.2", + description: "GPT-5.2 (flagship, best general-purpose)" + }, + { + id: "gpt-5.2-codex", + description: "GPT-5.2 Codex (optimized for agentic coding)" + }, + { + id: "gpt-5.1", + description: "GPT-5.1 (previous flagship)" + }, + { + id: "gpt-5", + description: "GPT-5 (previous gen reasoning)" + }, + { + id: "gpt-4.1", + description: "GPT-4.1 (coding + long context, 1M tokens)" + }, + { + id: "gpt-4.1-mini", + description: "GPT-4.1 Mini (fast + affordable)" + }, + { + id: "gpt-4.1-nano", + description: "GPT-4.1 Nano (ultra-fast, cheapest)" + }, + { + id: "gpt-4o", + description: "GPT-4o (legacy multimodal)" + }, + { + id: "gpt-4o-mini", + description: "GPT-4o Mini (legacy, budget)" + }, + ], + "required": true +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/temperature-param-types.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/temperature-param-types.jsonnet new file mode 100644 index 00000000..f5523e88 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/temperature-param-types.jsonnet @@ -0,0 +1,15 @@ +// LLM temperature definitions + +{ + "llm-temperature": { + "type": "float", + "description": "LLM temperature", + "placeholder": 0.3, + "helper": "A floating point number between 0 and 1", + "default": 0.3, + "min": 0.0, + "max": 10.0, + "required": true + } +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/vertexai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/vertexai.jsonnet new file mode 100644 index 00000000..34318967 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/vertexai.jsonnet @@ -0,0 +1,81 @@ +// VertexAI LLM Model Definitions +// Defines available models and their configurations for Google's VertexAI platform + +{ + "type": "string", + "description": "LLM model to use", + "default": "gemini-2.5-flash-lite", + "enum": [ + // Gemini 3 models (preview) + { + id: "gemini-3-pro-preview", + description: "Gemini 3 Pro (preview)" + }, + { + id: "gemini-3-flash-preview", + description: "Gemini 3 Flash (preview)" + }, + + // Gemini 2.5 models + { + id: "gemini-2.5-pro", + description: "Gemini 2.5 Pro" + }, + { + id: "gemini-2.5-flash", + description: "Gemini 2.5 Flash" + }, + { + id: "gemini-2.5-flash-lite", + description: "Gemini 2.5 Flash Lite" + }, + + // Gemini 2.0 models + { + id: "gemini-2.0-flash-001", + description: "Gemini 2.0 Flash" + }, + { + id: "gemini-2.0-flash-lite-001", + description: "Gemini 2.0 Flash Lite" + }, + + // Gemma models + { + id: "gemma-3-27b", + description: "Gemma 3 27B" + }, + { + id: "gemma-3n-e4b", + description: "Gemma 3n E4B" + }, + + // Claude models on VertexAI + { + id: "claude-opus-4-6", + description: "Claude Opus 4.6" + }, + { + id: "claude-opus-4-5", + description: "Claude Opus 4.5" + }, + { + id: "claude-sonnet-4-5", + description: "Claude Sonnet 4.5" + }, + { + id: "claude-haiku-4-5", + description: "Claude Haiku 4.5" + }, + { + id: "claude-opus-4-1", + description: "Claude Opus 4.1" + }, + { + id: "claude-sonnet-4", + description: "Claude Sonnet 4" + }, + ], + "required": true +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/vllm.jsonnet new file mode 100644 index 00000000..120342b2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/parameters/vllm.jsonnet @@ -0,0 +1,18 @@ +// vLLM Model Definitions +// Defines available models and their configurations for vLLM +// vLLM works with any HuggingFace model. We have to use the model +// vLLM was initialised with + +{ + "type": "string", + "description": "LLM model to use", + "default": "model", + "enum": [ + // Llama 3.1 models + { + id: "model", + description: "Pre-defined model" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/profiles/memory-profile-low.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/profiles/memory-profile-low.jsonnet new file mode 100644 index 00000000..e6341062 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/profiles/memory-profile-low.jsonnet @@ -0,0 +1,169 @@ +// Low memory profile - reduces memory allocation across components. +// Include this at the END of your configuration to override default memory +// settings with lower values suitable for memory-constrained environments. +// +// Usage: {"name": "memory-profile-low", "parameters": {}} +// +// Note: This trades some performance headroom for reduced memory usage. +// Monitor for OOM errors under heavy load. + +{ + + // Override Pulsar stack memory settings + "pulsar" +: { + + // Zookeeper: 512M -> 300M + "zk-memory-limit":: "300M", + "zk-memory-reservation":: "200M", + "zk-heap":: "128m", + "zk-direct-memory":: "64m", + + // Bookie: 1024M -> 600M + "bookie-memory-limit":: "600M", + "bookie-memory-reservation":: "400M", + "bookie-heap":: "128m", + "bookie-direct-memory":: "128m", + + // Broker: 800M -> 512M + "broker-memory-limit":: "512M", + "broker-memory-reservation":: "400M", + "broker-heap":: "192m", + "broker-direct-memory":: "192m", + + // Pulsar-init: 256M -> 128M + "init-memory-limit":: "128M", + "init-memory-reservation":: "128M", + "init-heap":: "64m", + "init-direct-memory":: "64m", + + }, + + // Override Cassandra memory settings: 1000M -> 600M + "cassandra" +: { + "memory-limit":: "600M", + "memory-reservation":: "500M", + "heap":: "200M", + }, + + // Override Qdrant memory settings: 1024M -> 600M + // Also enables mmap for vectors/payloads (trades latency for memory) + "qdrant" +: { + "memory-limit":: "600M", + "memory-reservation":: "500M", + "memmap-threshold-kb":: "1", + "on-disk-payload":: "true", + }, + + // TrustGraph core services - 50% memory reservations + "api-gateway" +: { + "memory-reservation":: "256M", // 512M -> 256M + }, + + "chunker" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "config-svc" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "pdf-decoder" +: { + "memory-reservation":: "256M", // 512M -> 256M + }, + + "mcp-tool" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "mcp-server" +: { + "memory-reservation":: "128M", // 256M -> 128M + }, + + "metering" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "metering-rag" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "kg-store" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "kg-manager" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "prompt" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "prompt-rag" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "document-rag" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "document-embeddings" +: { + "memory-reservation":: "256M", // 512M -> 256M + }, + + "librarian" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "agent-manager" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + // Graph RAG services + "kg-extract-definitions" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "kg-extract-relationships" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "kg-extract-agent" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "kg-extract-ontology" +: { + "memory-reservation":: "150M", // 300M -> 150M + }, + + "kg-extract-objects" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "graph-rag" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "graph-embeddings" +: { + "memory-reservation":: "256M", // 512M -> 256M + }, + + // Structured data services + "nlp-query" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "structured-query" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "structured-diag" +: { + "memory-reservation":: "48M", // 96M -> 48M + }, + + // Init service + "init-trustgraph" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/agent-kg-extract.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/agent-kg-extract.txt new file mode 100644 index 00000000..36dbd74f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/agent-kg-extract.txt @@ -0,0 +1,28 @@ +Analyze the following text and extract both entity definitions and relationships. + +For definitions, extract entities and their explanations or descriptions. +For relationships, extract subject-predicate-object triples where subjects and objects are entities, and predicates are relationship types. + +Text: {{text}} + +Output format: JSONL (one JSON object per line, no array wrapper) +Each line must include a "type" field to distinguish between definitions and relationships. + +For definitions, output: +{"type": "definition", "entity": "entity_name", "definition": "definition_text"} + +For relationships, output: +{"type": "relationship", "subject": "subject_entity", "predicate": "relationship_type", "object": "object_entity_or_literal", "object-entity": true} + +Requirements: +- Each line must be a complete, valid JSON object +- No commas between lines +- No [ ] array brackets +- No markdown formatting or prefixes +- Do not provide explanations, only output JSONL + +Example output: +{"type": "definition", "entity": "DNA", "definition": "Deoxyribonucleic acid, a molecule carrying genetic instructions"} +{"type": "definition", "entity": "RNA", "definition": "Ribonucleic acid, essential for coding and gene expression"} +{"type": "relationship", "subject": "DNA", "predicate": "transcribes_to", "object": "RNA", "object-entity": true} +{"type": "relationship", "subject": "DNA", "predicate": "located_in", "object": "cell nucleus", "object-entity": true} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/agent-prompt.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/agent-prompt.txt new file mode 100644 index 00000000..9155d20f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/agent-prompt.txt @@ -0,0 +1,104 @@ +# ReAct Agent System Prompt + +You are an AI assistant that uses the ReAct (Reasoning + Acting) framework to solve problems through systematic reasoning and tool use. + +## Core Instructions + +For each user query, work through the problem step-by-step using this cycle: +1. **Thought**: Reason about the current situation and determine what you need to do next +2. **Action**: Take ONE specific action using an available tool +3. Wait for **Observation**: The system will provide the result of your action +4. Continue with the next **Thought** based on the observation + +**CRITICAL**: Generate exactly ONE Thought followed by ONE Action, then STOP. Do not generate multiple Thought/Action pairs in a single response. Do not generate Observations yourself - the system will provide them. + +## Response Format + +Use this exact format for each step: + +``` +Thought: [Your reasoning about what to do next - be specific about why this action is needed] +Action: [tool_name] +Args: { + "parameter_name": "value", + "another_parameter": 123, + "list_parameter": ["item1", "item2"] +} +``` + +When you have finished provide the final answer: + +``` +Thought: [Your reasoning about why the process is complete] +Final Answer: [The final answer] +``` + +When providing a final answer, do not provide an Action or Args. + +## Action Format Rules + +1. **Tool Name**: Write "Action: " followed by the exact tool name on its own line +2. **Arguments**: Write "Args: " followed by a valid JSON object containing all parameters +3. **JSON Requirements**: + - Use double quotes for all string keys and values + - Numbers don't need quotes: `"count": 5` + - Booleans: `"enabled": true` or `"enabled": false` + - Arrays: `"items": ["a", "b", "c"]` + - Nested objects: `"config": {"setting": "value"}` + - Null values: `"optional_field": null` +4. **Required Parameters**: Include all required parameters for the tool +5. **No Extra Text**: Don't add explanations or comments within the Action block +1. **Final answer**: Write "Final Answer: " followed by the final answer + +## Available Tools + +{% for tool in tools %}- **{{ tool.name }}**: {{ tool.description }} +{% for arg in tool.arguments %} - Required: `"{{ arg.name }}"` ({{ arg.type }}): {{ arg.description }} +{% endfor %} +{% endfor %} + +## Behavior Rules + +1. **One Step at a Time**: Generate exactly one Thought and one Action, then wait for the system to provide an Observation +2. **Be Specific**: Your Thought should clearly explain why you're taking the specific action +3. **Use Context**: Build on previous Observations to inform your next steps +4. **Error Handling**: If an action fails, reason about the error and try a different approach +5. **Completion**: When you have enough information to fully answer the user's query, generate a final Thought explaining your conclusion, but do not take further actions + +## Error Responses + +If an action fails, you'll see: +``` +Observation: Error: [specific error message] +``` + +When this happens: +- Generate a Thought analyzing what went wrong +- Take a corrective Action with different parameters or a different tool +- If a tool is completely unavailable, explain this limitation in your next Thought + +## Termination + +The conversation ends when: +- You determine you have sufficient information to answer the user's query completely and provide a final answer. +- You encounter an unrecoverable error that prevents task completion +- The system reaches the maximum iteration limit + +## Important Notes + +- **Never generate Observations yourself** - only the system provides these +- **Always validate your JSON** - malformed JSON will cause action failures +- **Stay focused** - each Thought should directly relate to solving the user's query +- **Be efficient** - choose actions that gather the most relevant information for the task + +# Proceed + +Question: {{question}} + +{% for h in history %} +Action: "{{h.action}}" +Args: { +{% for k, v in h.arguments.items() %} "{{k}}": "{{v}}" +{% endfor %}} +Observation: "{{h.observation}}" +{% endfor %} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/cohere.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/cohere.jsonnet new file mode 100644 index 00000000..9541e4c2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/cohere.jsonnet @@ -0,0 +1,42 @@ +// For Cohere. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/default-prompts.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/default-prompts.jsonnet new file mode 100644 index 00000000..fd23d917 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/default-prompts.jsonnet @@ -0,0 +1,285 @@ + +// Prompt templates. For tidy JSONNET use, don't change these templates +// here, but use over-rides in the prompt directory + +{ + + "system-template":: "You are a helpful assistant.", + + "templates":: { + + "question":: { + "prompt": "{{question}}", + }, + + "extract-concepts":: { + "prompt": importstr "extract-concepts.txt", + "response-type": "text", + }, + + "extract-definitions":: { + "prompt": importstr "extract-definitions.txt", + "response-type": "jsonl", + "object-schema": { + "type": "object", + "properties": { + "entity": { + "type": "string" + }, + "definition": { + "type": "string" + } + }, + "required": [ + "entity", + "definition" + ] + } + }, + + "extract-relationships":: { + "prompt": importstr "extract-relationships.txt", + "response-type": "jsonl", + "object-schema": { + "type": "object", + "properties": { + "subject": { + "type": "string" + }, + "predicate": { + "type": "string" + }, + "object": { + "type": "string" + }, + "object-entity": { + "type": "boolean" + } + }, + "required": [ + "subject", + "predicate", + "object", + "object-entity" + ] + } + }, + + "extract-topics":: { + "prompt": importstr "extract-topics.txt", + "response-type": "jsonl", + "object-schema": { + "type": "object", + "properties": { + "topic": { + "type": "string" + }, + "definition": { + "type": "string" + } + }, + "required": [ + "topic", + "definition" + ] + } + }, + + "extract-rows":: { + "prompt": importstr "extract-rows.txt", + "response-type": "jsonl", + }, + + "kg-prompt":: { + "prompt": importstr "kg-prompt.txt", + "response-type": "text", + }, + + "kg-edge-reasoning":: { + "prompt": importstr "kg-edge-reasoning.txt", + "response-type": "jsonl", + "object-schema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "reasoning": { + "type": "string" + } + }, + "required": [ + "id", + "reasoning" + ] + } + }, + + "kg-edge-scoring":: { + "prompt": importstr "kg-edge-scoring.txt", + "response-type": "jsonl", + "object-schema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "score": { + "type": "number", + "minimum": 1, + "maximum": 10 + } + }, + "required": [ + "id", + "score" + ] + } + }, + + "kg-synthesis":: { + "prompt": importstr "kg-synthesis.txt", + "response-type": "text", + }, + + "document-prompt":: { + "prompt": importstr "document-prompt.txt", + "response-type": "text", + }, + + "agent-react":: { + "prompt": importstr "agent-prompt.txt", + "response-type": "text" + }, + + "agent-kg-extract":: { + "prompt": importstr "agent-kg-extract.txt", + "response-type": "jsonl", + "object-schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { "const": "definition" }, + "entity": { "type": "string" }, + "definition": { "type": "string" } + }, + "required": ["type", "entity", "definition"] + }, + { + "type": "object", + "properties": { + "type": { "const": "relationship" }, + "subject": { "type": "string" }, + "predicate": { "type": "string" }, + "object": { "type": "string" }, + "object-entity": { "type": "boolean" } + }, + "required": ["type", "subject", "predicate", "object"] + } + ] + } + }, + + "schema-selection":: { + "prompt": importstr "schema-selection.txt", + "response-type": "json", + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "description": "An array of schema names that are relevant to answering the given question" + } + }, + + "graphql-generation":: { + "prompt": importstr "graphql-generation.txt", + "response-type": "json", + "schema": { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "The GraphQL query string generated to answer the question" + }, + "variables": { + "type": "object", + "description": "Object containing any GraphQL variables needed for the query", + "additionalProperties": true + }, + "confidence": { + "type": "number", + "minimum": 0.0, + "maximum": 1.0, + "description": "Float between 0.0-1.0 indicating confidence in the generated query" + } + }, + "required": ["query", "variables", "confidence"], + "additionalProperties": false + } + }, + + "diagnose-structured-data":: { + "prompt": importstr "diagnose-structured-data.txt", + "response-type": "json", + }, + + "diagnose-xml":: { + "prompt": importstr "diagnose-xml.txt", + "response-type": "json", + }, + "diagnose-json":: { + "prompt": importstr "diagnose-json.txt", + "response-type": "json", + }, + "diagnose-csv":: { + "prompt": importstr "diagnose-csv.txt", + "response-type": "json", + }, + + "extract-with-ontologies":: { + "prompt": importstr "ontology-prompt.txt", + "response-type": "jsonl", + "object-schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { "const": "entity" }, + "entity": { "type": "string" }, + "entity_type": { "type": "string" } + }, + "required": ["type", "entity", "entity_type"] + }, + { + "type": "object", + "properties": { + "type": { "const": "relationship" }, + "subject": { "type": "string" }, + "subject_type": { "type": "string" }, + "relation": { "type": "string" }, + "object": { "type": "string" }, + "object_type": { "type": "string" } + }, + "required": ["type", "subject", "subject_type", "relation", "object", "object_type"] + }, + { + "type": "object", + "properties": { + "type": { "const": "attribute" }, + "entity": { "type": "string" }, + "entity_type": { "type": "string" }, + "attribute": { "type": "string" }, + "value": { "type": "string" } + }, + "required": ["type", "entity", "entity_type", "attribute", "value"] + } + ] + } + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/diagnose-csv.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/diagnose-csv.txt new file mode 100644 index 00000000..ad9725ba --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/diagnose-csv.txt @@ -0,0 +1,370 @@ +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for data import pipelines, with particular expertise in CSV processing and delimiter-separated value formats. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured CSV data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## CSV Processing Expertise + +When working with CSV data, you must: + +1. **Analyze CSV Structure** - Examine headers, delimiters, quoting, and data patterns +2. **Identify Column Mappings** - Map source column names to target fields +3. **Handle Complex CSV Patterns** - Support various CSV formats including: + - Standard comma-separated values: `name,age,city` + - Alternative delimiters: tab-separated (TSV), pipe-separated, semicolon-separated + - Quoted fields with embedded delimiters: `"Last, First",25,"New York, NY"` + - Headers with spaces or special characters: `"Customer Name","Order Date","Total Amount"` + - Files with or without headers + - Multi-line fields with embedded newlines + +## CSV Format Configuration Guidelines + +For CSV format configurations, use these patterns: + +**Basic CSV Configuration:** +```json +{ + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + "delimiter": ",", + "quote_char": "\"", + "has_header": true, + "skip_rows": 0 + } + } +} +``` + +**Advanced CSV Options:** +```json +{ + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + "delimiter": "\t", // Tab-separated + "quote_char": "\"", + "escape_char": "\\", + "has_header": true, + "skip_rows": 2, // Skip metadata rows + "null_values": ["", "NULL", "N/A"], + "trim_whitespace": true, + "skip_blank_lines": true + } + } +} +``` + +**CRITICAL: Source Field Names in Mappings** + +When processing CSV files, the parser uses column headers (if present) or generates column indices as field names. Your source field names in mappings must match exactly: + +**CORRECT Example with Headers:** +CSV file: +```csv +Customer Name,Order Date,Total Amount,Status +John Smith,2024-01-15,1000.50,Active +Jane Doe,2024-01-16,750.25,Pending +``` + +Becomes parsed data: +```json +{ + "Customer Name": "John Smith", + "Order Date": "2024-01-15", + "Total Amount": "1000.50", + "Status": "Active" +} +``` + +Your mappings should use: +```json +{ + "source_field": "Customer Name", // ✅ Correct - matches header exactly + "source_field": "Order Date", // ✅ Correct - matches header exactly + "source_field": "Total Amount", // ✅ Correct - matches header exactly + "source_field": "Status" // ✅ Correct - matches header exactly +} +``` + +**CORRECT Example without Headers:** +CSV file without headers uses column indices: +```csv +John Smith,2024-01-15,1000.50,Active +Jane Doe,2024-01-16,750.25,Pending +``` + +Becomes parsed data: +```json +{ + "0": "John Smith", + "1": "2024-01-15", + "2": "1000.50", + "3": "Active" +} +``` + +Your mappings should use: +```json +{ + "source_field": "0", // ✅ Correct - first column + "source_field": "1", // ✅ Correct - second column + "source_field": "2", // ✅ Correct - third column + "source_field": "3" // ✅ Correct - fourth column +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **Source Data Format** + - Delimiter character (comma, tab, pipe, semicolon, etc.) + - Quote character and escape character + - **For CSV**: Does the file have headers? Sample structure + - Text encoding (UTF-8, Windows-1252, etc.) + - Any rows to skip (metadata, blank lines) + - How null/empty values are represented + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source column → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + - Date/time format conversions + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or invalid data + - Duplicate handling strategy + - Row-level validation rules + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## CSV Structure Analysis + +When presented with CSV data, analyze: + +1. **Delimiter Detection**: What character separates the fields? +2. **Header Presence**: Does the first row contain column names? +3. **Quote Pattern**: Are fields quoted? What quote character is used? +4. **Data Types**: What types are present in each column? +5. **Null Representation**: How are empty/null values represented? +6. **Special Characters**: Are there embedded commas, quotes, or newlines? +7. **Encoding Issues**: Are there any character encoding problems? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + // CSV-specific parsing options + // delimiter, quote_char, has_header, skip_rows, etc. + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` +- `split`, `strip_quotes` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` +- `parse_number`, `parse_currency` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` +- `clean_whitespace`, `normalize_encoding` + +**Date/Time Operations:** +- `parse_date`, `format_date`, `date_component` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` +- `numeric_range`, `date_range` + +## CSV-Specific Best Practices + +1. **Detect delimiters accurately** - test with sample data to confirm delimiter +2. **Handle quoted fields properly** - account for embedded delimiters and quotes +3. **Trim whitespace consistently** - decide whether to preserve or remove extra spaces +4. **Validate data types early** - catch type conversion errors at the field level +5. **Handle empty values explicitly** - distinguish between empty strings and nulls +6. **Account for encoding issues** - especially with international characters +7. **Validate row structure** - ensure consistent column counts across rows + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes +9. **Handle CSV-specific edge cases** like malformed quotes, inconsistent delimiters +10. **Test with sample data** to validate parsing configuration + +## Complete CSV Example + +Given this CSV structure: +```csv +Customer Name,Order Date,Total Amount,Currency,Status +"Smith, John",2024-01-15,1000.50,USD,Active +"Doe, Jane",2024-01-16,750.25,EUR,Pending +"Johnson, Bob",2024-01-17,,USD,Cancelled +``` + +The parser will: +1. Use `delimiter: ","` and `quote_char: "\""` to parse fields correctly +2. Use `has_header: true` to treat first row as column names +3. Create this parsed data structure for each record: + ```json + { + "Customer Name": "Smith, John", + "Order Date": "2024-01-15", + "Total Amount": "1000.50", + "Currency": "USD", + "Status": "Active" + } + ``` + +Generate this COMPLETE configuration: +```json +{ + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + "delimiter": ",", + "quote_char": "\"", + "has_header": true, + "trim_whitespace": true, + "null_values": ["", "NULL"] + } + }, + "mappings": [ + { + "source_field": "Customer Name", // ✅ Matches header exactly + "target_field": "customer_name", + "transforms": [{"type": "trim"}] + }, + { + "source_field": "Order Date", // ✅ Matches header exactly + "target_field": "order_date", + "transforms": [{"type": "to_date", "format": "YYYY-MM-DD"}], + "validation": [{"type": "required"}] + }, + { + "source_field": "Total Amount", // ✅ Matches header exactly + "target_field": "amount", + "transforms": [ + {"type": "to_float"}, + {"type": "default", "value": 0.0} + ] + }, + { + "source_field": "Currency", // ✅ Matches header exactly + "target_field": "currency_code", + "transforms": [{"type": "upper"}], + "validation": [{"type": "in_list", "values": ["USD", "EUR", "GBP"]}] + }, + { + "source_field": "Status", // ✅ Matches header exactly + "target_field": "order_status", + "transforms": [{"type": "lower"}] + } + ] +} +``` + +**KEY RULE: source_field names must match the column headers exactly, or use column indices (0, 1, 2, etc.) for files without headers.** + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the CSV structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to delimiter detection, header identification, quoting patterns, and data type inference: + +{{sample}} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/diagnose-json.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/diagnose-json.txt new file mode 100644 index 00000000..2fe6341d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/diagnose-json.txt @@ -0,0 +1,327 @@ +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for data import pipelines, with particular expertise in JSON processing and JSONPath expressions. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured JSON data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## JSON Processing Expertise + +When working with JSON data, you must: + +1. **Analyze JSON Structure** - Examine the hierarchy, array patterns, and object nesting +2. **Generate Proper JSONPath Expressions** - Create efficient JSONPath selectors for record extraction +3. **Handle Complex JSON Patterns** - Support various JSON formats including: + - Array of objects: `[{"name": "John", "age": 30}, {...}]` + - Nested object arrays: `{"data": {"records": [{"id": 1}, {...}]}}` + - Mixed hierarchies with both arrays and nested objects + - Single object records: `{"record": {"field1": "value1"}}` + +## JSONPath Expression Guidelines + +For JSON format configurations, use these JSONPath patterns: + +**Record Path Examples:** +- Root array: `$[*]` (for arrays at the root level) +- Nested arrays: `$.data.records[*]` or `$.response.items[*]` +- Single object: `$.record` (when there's one record per file) +- Deep nesting: `$.data.results.items[*]` + +**Field Access Patterns:** +- Direct properties: Use property names directly in mappings +- Nested properties: Use dot notation like `address.street` or `contact.email` +- Array elements: Use bracket notation like `tags[0]` for first element + +**CRITICAL: Source Field Names in Mappings** + +When processing JSON, the parser creates a flat or nested dictionary based on the record structure. Your source field names in mappings must match the actual property names in the parsed records: + +**CORRECT Example:** +```json +{ + "Country or Area": "Albania", + "Trade (USD)": "1000.50", + "metadata": { + "source": "UN", + "year": 2024 + } +} +``` + +Your mappings should use: +```json +{ + "source_field": "Country or Area", // ✅ Correct - matches property name + "source_field": "Trade (USD)", // ✅ Correct - matches property name + "source_field": "metadata.source", // ✅ Correct - nested property access + "source_field": "metadata.year" // ✅ Correct - nested property access +} +``` + +**JSON Format Configuration Template:** +```json +{ + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + "record_path": "$[*]", // JSONPath to extract records + "flatten_nested": true, // Whether to flatten nested objects + "array_handling": "expand" // How to handle arrays: expand, first, concat + } + } +} +``` + +**Alternative JSON Options:** +```json +{ + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + "record_path": "$.data.items[*]", // For nested array structures + "flatten_nested": false, // Keep nested structure + "null_value_handling": "skip" // skip, empty_string, or preserve + } + } +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **Source Data Format** + - JSON structure type (array of objects, nested objects, single records) + - **For JSON**: Sample structure, nesting patterns, array locations + - Sample data or field descriptions + - Any format-specific details (encoding, special null handling, etc.) + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source field → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + - Nested object flattening requirements + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or null values + - Duplicate handling strategy + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## JSON Structure Analysis + +When presented with JSON data, analyze: + +1. **Root Structure**: Is it an array, object, or nested structure? +2. **Record Location**: Where are individual records located in the hierarchy? +3. **Field Pattern**: How are field names and values structured? + - Direct properties: `{"name": "John"}` + - Nested objects: `{"contact": {"email": "john@example.com"}}` + - Arrays: `{"tags": ["red", "blue"]}` + - Mixed types: `{"data": [{"id": 1, "details": {"name": "John"}}]}` +4. **Data Types**: What types are present (strings, numbers, booleans, nulls, arrays, objects)? +5. **Hierarchy Depth**: How deeply nested are the records and fields? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + // JSON-specific parsing options + // record_path (JSONPath), flatten_nested, array_handling, etc. + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` +- `flatten_object`, `extract_array_element`, `join_array` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` + +## JSON-Specific Best Practices + +1. **Use efficient JSONPath expressions** - Prefer specific paths over broad searches +2. **Handle nested objects appropriately** - decide whether to flatten or preserve structure +3. **Consider array handling strategies** - expand arrays to multiple records or extract specific elements +4. **Account for null vs undefined** values in field mappings +5. **Handle mixed data types** within the same field across records +6. **Use appropriate flattening** for deeply nested structures + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes +9. **Handle JSON-specific edge cases** like empty arrays, null objects, mixed types + +## Complete JSON Example + +Given this JSON structure: +```json +{ + "data": { + "records": [ + { + "Country": "USA", + "Year": 2024, + "Amount": 1000.50, + "metadata": { + "source": "World Bank", + "confidence": 0.95 + } + } + ] + } +} +``` + +The parser will: +1. Use `record_path: "$.data.records[*]"` to extract record objects from the array +2. Create this parsed data structure for each record: + ```json + { + "Country": "USA", + "Year": 2024, + "Amount": 1000.50, + "metadata.source": "World Bank", // If flattened + "metadata.confidence": 0.95 // If flattened + } + ``` + +Generate this COMPLETE configuration: +```json +{ + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + "record_path": "$.data.records[*]", + "flatten_nested": true, + "array_handling": "expand" + } + }, + "mappings": [ + { + "source_field": "Country", // ✅ Matches property name + "target_field": "country_name" + }, + { + "source_field": "Year", // ✅ Matches property name + "target_field": "year", + "transforms": [{"type": "to_int"}] + }, + { + "source_field": "Amount", // ✅ Matches property name + "target_field": "amount", + "transforms": [{"type": "to_float"}] + }, + { + "source_field": "metadata.source", // ✅ Flattened nested field + "target_field": "data_source" + } + ] +} +``` + +**KEY RULE: source_field names must match the actual property names in the parsed JSON records, using dot notation for nested properties when flattened.** + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the JSON structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to JSON hierarchy, object patterns, array structures, and generate appropriate JSONPath expressions: + +{{sample}} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/diagnose-structured-data.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/diagnose-structured-data.txt new file mode 100644 index 00000000..84c4b8be --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/diagnose-structured-data.txt @@ -0,0 +1,309 @@ + +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for data import pipelines, with particular expertise in XML processing and XPath expressions. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## XML Processing Expertise + +When working with XML data, you must: + +1. **Analyze XML Structure** - Examine the hierarchy, namespaces, and element patterns +2. **Generate Proper XPath Expressions** - Create efficient XPath selectors for record extraction +3. **Handle Complex XML Patterns** - Support various XML formats including: + - Standard element structures: `John` + - Attribute-based fields: `USA` + - Mixed content and nested hierarchies + - Namespaced XML documents + +## XPath Expression Guidelines + +For XML format configurations, use these XPath patterns: + +**Record Path Examples:** +- Simple records: `//record` or `//customer` +- Nested records: `//data/records/record` or `//customers/customer` +- Absolute paths: `/ROOT/data/record` (will be converted to relative paths automatically) +- With namespaces: `//ns:record` or `//soap:Body/data/record` + +**Field Attribute Patterns:** +- When fields use name attributes: set `field_attribute: "name"` for `value` +- For other attribute patterns: set appropriate attribute name + +**CRITICAL: Source Field Names in Mappings** + +When using `field_attribute`, the XML parser extracts field names from the attribute values and creates a flat dictionary. Your source field names in mappings must match these extracted names: + +**CORRECT Example:** +```xml +Albania +1000.50 +``` + +Becomes parsed data: +```json +{ + "Country or Area": "Albania", + "Trade (USD)": "1000.50" +} +``` + +So your mappings should use: +```json +{ + "source_field": "Country or Area", // ✅ Correct - matches parsed field name + "source_field": "Trade (USD)" // ✅ Correct - matches parsed field name +} +``` + +**INCORRECT Example:** +```json +{ + "source_field": "Field[@name='Country or Area']", // ❌ Wrong - XPath not needed here + "source_field": "field[@name='Trade (USD)']" // ❌ Wrong - XPath not needed here +} +``` + +**XML Format Configuration Template:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//data/record", // XPath to find record elements + "field_attribute": "name" // For value pattern + } + } +} +``` + +**Alternative XML Options:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//customer", // Direct element-based records + // No field_attribute needed for standard XML + } + } +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **Source Data Format** + - File type (CSV, JSON, XML, Excel, fixed-width, etc.) + - **For XML**: Sample structure, namespace prefixes, record element patterns + - Sample data or field descriptions + - Any format-specific details (delimiters, encoding, namespaces, etc.) + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source field → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or invalid data + - Duplicate handling strategy + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## XML Structure Analysis + +When presented with XML data, analyze: + +1. **Document Root**: What is the root element? +2. **Record Container**: Where are individual records located? +3. **Field Pattern**: How are field names and values structured? + - Direct child elements: `John` + - Attribute-based: `John` + - Mixed patterns +4. **Namespaces**: Are there any namespace prefixes? +5. **Hierarchy Depth**: How deeply nested are the records? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "[csv|json|xml|fixed-width|excel]", + "encoding": "utf-8", + "options": { + // Format-specific parsing options + // For XML: record_path (XPath), field_attribute (if applicable) + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` + +## XML-Specific Best Practices + +1. **Use efficient XPath expressions** - Prefer specific paths over broad searches +2. **Handle namespace prefixes** when present +3. **Identify field attribute patterns** correctly +4. **Test XPath expressions** mentally against the provided structure +5. **Consider XML element vs attribute data** in field mappings +6. **Account for mixed content** and nested structures + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes + +## Complete XML Example + +Given this XML structure: +```xml + + + + USA + 2024 + 1000.50 + + + +``` + +The parser will: +1. Use `record_path: "/ROOT/data/record"` to find record elements +2. Use `field_attribute: "name"` to extract field names from the name attribute +3. Create this parsed data structure: `{"Country": "USA", "Year": "2024", "Amount": "1000.50"}` + +Generate this COMPLETE configuration: +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "/ROOT/data/record", + "field_attribute": "name" + } + }, + "mappings": [ + { + "source_field": "Country", // ✅ Matches parsed field name + "target_field": "country_name" + }, + { + "source_field": "Year", // ✅ Matches parsed field name + "target_field": "year", + "transforms": [{"type": "to_int"}] + }, + { + "source_field": "Amount", // ✅ Matches parsed field name + "target_field": "amount", + "transforms": [{"type": "to_float"}] + } + ] +} +``` + +**KEY RULE: source_field names must match the extracted field names, NOT the XML element structure.** + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the XML structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to XML hierarchy, element patterns, and generate appropriate XPath expressions: + +{{sample}} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/diagnose-xml.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/diagnose-xml.txt new file mode 100644 index 00000000..a3d8efa4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/diagnose-xml.txt @@ -0,0 +1,453 @@ +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for XML data import pipelines, with particular expertise in XML processing and XPath expressions. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured XML data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## XML Processing Expertise + +When working with XML data, you must: + +1. **Analyze XML Structure** - Examine the hierarchy, namespaces, and element patterns +2. **Generate Proper XPath Expressions** - Create efficient XPath selectors for record extraction +3. **Handle Complex XML Patterns** - Support various XML formats including: + - Standard element structures: `John` + - Attribute-based fields: `USA` + - Mixed content and nested hierarchies + - Namespaced XML documents + - CDATA sections and text nodes + +## XPath Expression Guidelines + +For XML format configurations, use these XPath patterns: + +**Record Path Examples:** +- Simple records: `//record` or `//customer` +- Nested records: `//data/records/record` or `//customers/customer` +- Absolute paths: `/ROOT/data/record` +- With namespaces: `//ns:record` or `//soap:Body/data/record` +- Attribute-filtered: `//record[@type='customer']` + +**Field Attribute Patterns:** +- When fields use name attributes: set `field_attribute: "name"` for `value` +- For other attribute patterns: set appropriate attribute name like `field_attribute: "id"` or `field_attribute: "type"` + +## CRITICAL: Source Field Names in Mappings + +The behavior depends on whether you use `field_attribute`: + +### With field_attribute (Attribute-Based Fields) + +When using `field_attribute`, the XML parser extracts field names from the attribute values and creates a flat dictionary: + +**Example:** +```xml + + Albania + 1000.50 + Active + +``` + +Parser configuration: +```json +{ + "record_path": "//record", + "field_attribute": "name" +} +``` + +Becomes parsed data: +```json +{ + "Country or Area": "Albania", + "Trade (USD)": "1000.50", + "Status": "Active" +} +``` + +Your mappings should use: +```json +{ + "source_field": "Country or Area", // ✅ Correct - matches parsed field name + "source_field": "Trade (USD)", // ✅ Correct - matches parsed field name + "source_field": "Status" // ✅ Correct - matches parsed field name +} +``` + +### Without field_attribute (Element-Based Fields) + +When NOT using `field_attribute`, use direct element names or XPath expressions: + +**Example:** +```xml + + Albania + 1000.50 + Active + + UN + 2024 + + +``` + +Parser configuration: +```json +{ + "record_path": "//record" + // No field_attribute specified +} +``` + +Your mappings should use: +```json +{ + "source_field": "country", // ✅ Direct element name + "source_field": "trade_amount", // ✅ Direct element name + "source_field": "metadata/source", // ✅ Nested element path + "source_field": "metadata/year" // ✅ Nested element path +} +``` + +## XML Format Configuration Templates + +**For Attribute-Based Fields:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//data/record", // XPath to find record elements + "field_attribute": "name", // Extract field names from 'name' attribute + "namespace_prefixes": { // Optional: define namespaces + "ns": "http://example.com/namespace" + } + } + } +} +``` + +**For Element-Based Fields:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//customer", // XPath to find record elements + "preserve_namespaces": true, // Optional: keep namespace info + "ignore_attributes": false // Optional: include element attributes + } + } +} +``` + +**For Complex XML with Namespaces:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//soap:Body//data:record", + "namespace_prefixes": { + "soap": "http://schemas.xmlsoap.org/soap/envelope/", + "data": "http://example.com/data" + }, + "field_attribute": "name" + } + } +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **XML Structure Details** + - Sample XML structure or schema + - Root element and record location + - Field organization (elements vs attributes) + - Namespace declarations and prefixes + - Text encoding (UTF-8, ISO-8859-1, etc.) + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source field → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or invalid data + - Duplicate handling strategy + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## XML Structure Analysis + +When presented with XML data, analyze: + +1. **Document Root**: What is the root element? +2. **Namespaces**: Are there namespace declarations? What prefixes are used? +3. **Record Container**: Where are individual records located in the hierarchy? +4. **Field Pattern**: How are field names and values structured? + - Direct child elements: `John` + - Attribute-based: `John` + - Mixed patterns: `John` +5. **Data Location**: Are values in element text, attributes, or both? +6. **Hierarchy Depth**: How deeply nested are the records? +7. **Special Content**: Any CDATA sections, mixed content, or special characters? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + // XML-specific parsing options + // record_path (XPath), field_attribute (if applicable), namespace_prefixes + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` +- `strip_cdata`, `decode_html_entities` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` +- `parse_xml_date`, `parse_iso_date` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` +- `extract_attribute`, `get_text_content` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` +- `valid_xml_name`, `namespace_check` + +## XML-Specific Best Practices + +1. **Use efficient XPath expressions** - Prefer specific paths over broad searches like `//` when possible +2. **Handle namespace prefixes** when present - define them in `namespace_prefixes` +3. **Identify field attribute patterns** correctly - determine if using `field_attribute` +4. **Test XPath expressions** mentally against the provided structure +5. **Consider XML element vs attribute data** in field mappings +6. **Account for mixed content** and nested structures +7. **Handle CDATA sections** and special characters properly +8. **Preserve or normalize whitespace** as appropriate +9. **Consider XML schema validation** if XSD is available + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes +9. **Handle XML-specific edge cases** like empty elements, mixed content, processing instructions + +## Complete XML Examples + +### Example 1: Attribute-Based Fields + +Given this XML structure: +```xml + + + + USA + 2024 + 1000.50 + + + +``` + +The parser will: +1. Use `record_path: "//data/record"` to find record elements +2. Use `field_attribute: "name"` to extract field names from the name attribute +3. Create this parsed data structure: `{"Country": "USA", "Year": "2024", "Amount": "1000.50"}` + +Generate this configuration: +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//data/record", + "field_attribute": "name" + } + }, + "mappings": [ + { + "source_field": "Country", // ✅ Matches parsed field name + "target_field": "country_name" + }, + { + "source_field": "Year", // ✅ Matches parsed field name + "target_field": "year", + "transforms": [{"type": "to_int"}] + }, + { + "source_field": "Amount", // ✅ Matches parsed field name + "target_field": "amount", + "transforms": [{"type": "to_float"}] + } + ] +} +``` + +### Example 2: Element-Based Fields + +Given this XML structure: +```xml + + + John Smith + john@example.com +
+ 123 Main St + New York +
+ + + 456 + 100.50 + + +
+
+``` + +Generate this configuration: +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//customer" + } + }, + "mappings": [ + { + "source_field": "name", // ✅ Direct element name + "target_field": "customer_name" + }, + { + "source_field": "email", // ✅ Direct element name + "target_field": "email_address" + }, + { + "source_field": "address/street", // ✅ Nested element path + "target_field": "street_address" + }, + { + "source_field": "address/city", // ✅ Nested element path + "target_field": "city" + }, + { + "source_field": "@id", // ✅ Attribute reference + "target_field": "customer_id", + "transforms": [{"type": "to_int"}] + } + ] +} +``` + +**KEY RULES:** +- With `field_attribute`: source_field names must match the extracted attribute values +- Without `field_attribute`: use direct element names, nested paths, or XPath expressions +- Use `@attribute` notation for XML element attributes + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the XML structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to XML hierarchy, element patterns, namespace usage, and generate appropriate XPath expressions: + +{{sample}} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/document-prompt.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/document-prompt.txt new file mode 100644 index 00000000..5e5d65ba --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/document-prompt.txt @@ -0,0 +1,7 @@ +Study the following context. Use only the information provided in the context in your response. Do not speculate if the answer is not found in the provided set of knowledge statements. + +Here is the context: +{{documents}} + +Use only the provided knowledge statements to respond to the following: +{{query}} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/extract-concepts.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/extract-concepts.txt new file mode 100644 index 00000000..cc4b600c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/extract-concepts.txt @@ -0,0 +1,12 @@ +Extract core semantic concepts from the following query. These concepts will be used for semantic search and embedding. + +Output one concept per line. No numbering, no bullet points, no explanation. + +Focus on: +- Key nouns and noun phrases +- Domain-specific terminology +- Languages or time periods mentioned +- Technical terms + +Query: +{{query}} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/extract-definitions.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/extract-definitions.txt new file mode 100644 index 00000000..410ecddf --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/extract-definitions.txt @@ -0,0 +1,28 @@ + +Study the following text and derive definitions for any discovered entities. +Do not provide definitions for entities whose definitions are incomplete +or unknown. + +Output each definition as a separate JSON object on its own line (JSONL format). +Each object must have fields: +- entity: the name of the entity +- definition: English text which defines the entity + + + +{{text}} + + + +Output format: JSONL (one JSON object per line, no array wrapper) +- Each line must be a complete, valid JSON object +- No commas between lines +- No [ ] array brackets +- No markdown formatting or prefixes +- Do not use special characters in the definition text +- Do not include null or unknown definitions + +Example output format: +{"entity": "photosynthesis", "definition": "The process by which plants convert sunlight into energy"} +{"entity": "chlorophyll", "definition": "Green pigment in plants that absorbs light"} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/extract-relationships.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/extract-relationships.txt new file mode 100644 index 00000000..31bcd2d4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/extract-relationships.txt @@ -0,0 +1,28 @@ + +Study the following text and derive entity relationships. For each +relationship, derive the subject, predicate and object of the relationship. + +Output each relationship as a separate JSON object on its own line (JSONL format). +Each object must have fields: +- subject: the subject of the relationship +- predicate: the predicate +- object: the object of the relationship +- object-entity: false if the object is a simple data type (name, value, date). true if it is an entity. + + + +{{text}} + + + +Output format: JSONL (one JSON object per line, no array wrapper) +- Each line must be a complete, valid JSON object +- No commas between lines +- No [ ] array brackets +- No markdown formatting or prefixes +- Do not use special characters in the text fields + +Example output format: +{"subject": "Earth", "predicate": "orbits", "object": "Sun", "object-entity": true} +{"subject": "Earth", "predicate": "has diameter", "object": "12742 km", "object-entity": false} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/extract-rows.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/extract-rows.txt new file mode 100644 index 00000000..7cea8b7d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/extract-rows.txt @@ -0,0 +1,27 @@ + +Study the following text and derive objects which match the schema provided. + +Output each discovered object as a separate JSON object on its own line (JSONL format). +Each object's fields must carry the name field specified in the schema. + + + +{{schema}} + + + +{{text}} + + + +Output format: JSONL (one JSON object per line, no array wrapper) +- Each line must be a complete, valid JSON object matching the schema +- No commas between lines +- No [ ] array brackets +- No markdown formatting or prefixes +- Do not provide explanations + +Example output format (assuming schema has fields: name, age, city): +{"name": "Alice", "age": 30, "city": "London"} +{"name": "Bob", "age": 25, "city": "Paris"} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/extract-topics.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/extract-topics.txt new file mode 100644 index 00000000..bb4b1193 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/extract-topics.txt @@ -0,0 +1,26 @@ +You are a helpful assistant that performs information extraction tasks for a provided text. +Read the provided text. You will identify topics and their definitions. + +Reading Instructions: +- Ignore document formatting in the provided text. +- Study the provided text carefully. + +Here is the text: +{{text}} + +Response Instructions: +- Output each topic as a separate JSON object on its own line (JSONL format) +- Each object must have keys "topic" and "definition" +- Do not respond with special characters +- Return only topics that are concepts and unique to the provided text + +Output format: JSONL (one JSON object per line, no array wrapper) +- Each line must be a complete, valid JSON object +- No commas between lines +- No [ ] array brackets +- No markdown formatting or prefixes +- Do not write any additional text or explanations + +Example output format: +{"topic": "machine learning", "definition": "A subset of AI that enables systems to learn from data"} +{"topic": "neural network", "definition": "A computing system inspired by biological neural networks"} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/gemini.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/gemini.jsonnet new file mode 100644 index 00000000..b9a1e0c0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/gemini.jsonnet @@ -0,0 +1,42 @@ +// For VertexAI Gemini. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/graphql-generation.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/graphql-generation.txt new file mode 100644 index 00000000..58af61dc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/graphql-generation.txt @@ -0,0 +1,59 @@ +You are a GraphQL query generation expert. Given a natural language question and provided database schemas, generate a valid GraphQL query embedded in valid JSON. + +## Question: +{{ question }} + +## Provided Schemas: +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %}- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif %}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif %}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ field.enum_values|join(', ') }}]{% endif %} +{% endfor %} +{% endfor %} + +## GraphQL Query Rules: +1. Use the schema names as GraphQL query fields (e.g., `customers`, `orders`) +2. Apply filters using the `where` parameter with nested filter objects +3. Available filter operators per field type: + - String fields: `eq`, `contains`, `startsWith`, `endsWith`, `in`, `not`, `not_in` + - Integer/Float fields: `eq`, `gt`, `gte`, `lt`, `lte`, `in`, `not`, `not_in` +4. Use `order_by` for sorting (field name as string) +5. Use `direction` for sort direction: `ASC` or `DESC` +6. Use `limit` to restrict number of results +7. Select specific fields in the query body + +## Task Instructions: +1. Analyze the question to identify: + - What data to retrieve (which fields to select) + - What filters to apply (where conditions) + - What sorting is needed (order_by, direction) + - How many results (limit) + +2. Generate a GraphQL query that: + - Uses only the provided schema names and field names + - Applies appropriate filters based on the question + - Selects relevant fields for the response + - Includes reasonable limits (default 100 if not specified) + +3. If variables are needed, include them in the response + +## Output Format: +Return ONLY a valid JSON object (no markdown, no code blocks) with these fields: +- "query": the GraphQL query string +- "variables": object with any GraphQL variables (empty object if none) +- "confidence": float between 0.0-1.0 indicating confidence in the query + +Important: Return raw JSON only, with no markdown formatting, no code blocks, and no backticks. + +## Examples: + +Question: "Show me customers from California" +{"query": "query { customers(where: {state: {eq: \"California\"}}, limit: 100) { customer_id name email state } }", "variables": {}, "confidence": 0.92} + +Question: "Top 10 products by price" +{"query": "query { products(order_by: \"price\", direction: DESC, limit: 10) { product_id name price category } }", "variables": {}, "confidence": 0.88} + +Question: "Recent orders over $100" +{"query": "query { orders(where: {total_amount: {gt: 100}, order_date: {gte: \"2024-01-01\"}}, order_by: \"order_date\", direction: DESC, limit: 50) { order_id customer_id total_amount order_date status } }", "variables": {}, "confidence": 0.96} + +Now generate the GraphQL query for the question above. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/kg-edge-reasoning.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/kg-edge-reasoning.txt new file mode 100644 index 00000000..8aeb0c3c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/kg-edge-reasoning.txt @@ -0,0 +1,25 @@ +Study the following knowledge statements in Cypher format, extracted from a knowledge graph. Each relationship has an id property. + +Here are the knowledge statements: +{% for edge in knowledge %}({{edge.s}})-[{{edge.p}} {id: "{{edge.id}}"}]->({{edge.o}}) +{% endfor %} + +For each knowledge statement, explain briefly (5-10 words) why it is relevant to answering the question below. + +Output format: JSONL (one JSON object per line, no array wrapper) +Each line must include an "id" field and a "reasoning" field. + +{"id": "edge_id", "reasoning": "brief explanation of relevance"} + +Requirements: +- Each line must be a complete, valid JSON object +- No commas between lines +- No [ ] array brackets +- No markdown formatting or prefixes +- Do not provide explanations, only output JSONL + +Example output: +{"id": "a3f9b2c1", "reasoning": "Establishes employment relationship relevant to query"} +{"id": "b7e2d4f9", "reasoning": "Identifies the organisation's sector"} + +Question: {{query}} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/kg-edge-scoring.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/kg-edge-scoring.txt new file mode 100644 index 00000000..40b92773 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/kg-edge-scoring.txt @@ -0,0 +1,25 @@ +Study the following knowledge statements in Cypher format, extracted from a knowledge graph. Each relationship has an id property. + +Here are the knowledge statements: +{% for edge in knowledge %}({{edge.s}})-[{{edge.p}} {id: "{{edge.id}}"}]->({{edge.o}}) +{% endfor %} + +Score each knowledge statement for relevance to the question below. Assign a score from 1 (not relevant) to 10 (highly relevant). + +Output format: JSONL (one JSON object per line, no array wrapper) +Each line must include an "id" field and a "score" field. + +{"id": "edge_id", "score": N} + +Requirements: +- Each line must be a complete, valid JSON object +- No commas between lines +- No [ ] array brackets +- No markdown formatting or prefixes +- Do not provide explanations, only output JSONL + +Example output: +{"id": "a3f9b2c1", "score": 9} +{"id": "b7e2d4f9", "score": 3} + +Question: {{query}} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/kg-edge-selection.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/kg-edge-selection.txt new file mode 100644 index 00000000..a8ea44c8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/kg-edge-selection.txt @@ -0,0 +1,26 @@ +Study the following knowledge statements in Cypher format, extracted from a knowledge graph. Each relationship has an id property. + +Here are the knowledge statements: +{% for edge in knowledge %}({{edge.s}})-[{{edge.p}} {id: "{{edge.id}}"}]->({{edge.o}}) +{% endfor %} + +Select the knowledge statements that are relevant to answering the question below. + +Output format: JSONL (one JSON object per line, no array wrapper) +Each line must include an "id" field and a "reasoning" field. + +For each relevant edge, output: +{"id": "edge_id", "reasoning": "explanation of why this edge is relevant"} + +Requirements: +- Each line must be a complete, valid JSON object +- No commas between lines +- No [ ] array brackets +- No markdown formatting or prefixes +- Do not provide explanations, only output JSONL + +Example output: +{"id": "a3f9b2c1", "reasoning": "Establishes employment relationship directly relevant to the query"} +{"id": "b7e2d4f9", "reasoning": "Identifies the organisation's sector which contextualises the answer"} + +Question: {{query}} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/kg-prompt.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/kg-prompt.txt new file mode 100644 index 00000000..431c0d73 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/kg-prompt.txt @@ -0,0 +1,8 @@ +Study the following set of knowledge statements. The statements are written in Cypher format that has been extracted from a knowledge graph. Use only the provided set of knowledge statements in your response. Do not speculate if the answer is not found in the provided set of knowledge statements. + +Here's the knowledge statements: +{% for edge in knowledge %}({{edge.s}})-[{{edge.p}}]->({{edge.o}}) +{%endfor%} + +Use only the provided knowledge statements to respond to the following: +{{query}} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/kg-synthesis.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/kg-synthesis.txt new file mode 100644 index 00000000..a80c5594 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/kg-synthesis.txt @@ -0,0 +1,8 @@ +Study the following knowledge statements in Cypher format. Use only these provided statements in your response. Do not speculate if the answer is not found in the provided statements. Answer in natural language. Be helpful and give as complete an answer as possible. + +Here are the knowledge statements: +{% for edge in knowledge %}({{edge.s}})-[{{edge.p}}]->({{edge.o}}) +{% endfor %} + +Use only the provided knowledge statements to respond to the following: +{{query}} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/mixtral.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/mixtral.jsonnet new file mode 100644 index 00000000..cd56e7ef --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/mixtral.jsonnet @@ -0,0 +1,42 @@ +// For Mixtral. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/ontology-prompt.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/ontology-prompt.txt new file mode 100644 index 00000000..1b451d9c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/ontology-prompt.txt @@ -0,0 +1,82 @@ +You are a knowledge extraction expert. Your task is to find entities, relationships, and attributes in text based on a provided schema. + +## Entity Types + +These are the types of entities you should look for: + +{% for class_id, class_def in classes.items() %} +- **{{class_id}}**{% if class_def.subclass_of %} (subclass of {{class_def.subclass_of}}){% endif %}{% if class_def.comment %}: {{class_def.comment}}{% endif %} +{% endfor %} + +## Relationships + +These relationships connect entities to other entities: + +{% for prop_id, prop_def in object_properties.items() %} +- **{{prop_id}}**{% if prop_def.domain and prop_def.range %} ({{prop_def.domain}} → {{prop_def.range}}){% endif %}{% if prop_def.comment %}: {{prop_def.comment}}{% endif %} +{% endfor %} + +## Attributes + +These attributes describe entity properties (text, numbers, etc.): + +{% for prop_id, prop_def in datatype_properties.items() %} +- **{{prop_id}}**{% if prop_def.domain and prop_def.range %} ({{prop_def.domain}} → {{prop_def.range}}){% endif %}{% if prop_def.comment %}: {{prop_def.comment}}{% endif %} +{% endfor %} + +## Text to Analyze + +{{text}} + +## Your Task + +Extract the following from the text above: + +1. **Entities**: Things mentioned in the text and their types +2. **Relationships**: How entities relate to each other +3. **Attributes**: Properties of entities (like quantities, descriptions, etc.) + +## Output Format + +Output format: JSONL (one JSON object per line, no array wrapper) +Each line must include a "type" field to distinguish between entities, relationships, and attributes. + +For entities, output: +{"type": "entity", "entity": "entity name as it appears in text", "entity_type": "EntityType"} + +For relationships, output: +{"type": "relationship", "subject": "subject entity name", "subject_type": "SubjectType", "relation": "relationship_name", "object": "object entity name", "object_type": "ObjectType"} + +For attributes, output: +{"type": "attribute", "entity": "entity name", "entity_type": "EntityType", "attribute": "attribute_name", "value": "literal value"} + +## Important Rules + +1. **Entity names**: Use the exact text as it appears (e.g., "Cornish pasty", "beef") +2. **Types**: Use the EXACT type identifiers from the schema above (e.g., "fo/Recipe", "fo/Food") +3. **Relationships**: Use the EXACT relationship names from the schema (e.g., "fo/has_ingredient") +4. **Attributes**: Use the EXACT attribute names from the schema (e.g., "fo/serves") +5. **No array brackets**: Output one JSON object per line, not an array +6. **No markdown**: Do not wrap output in code blocks + +## Requirements + +- Each line must be a complete, valid JSON object +- No commas between lines +- No [ ] array brackets +- No markdown formatting, code blocks, or prefixes +- Do not provide explanations, only output JSONL + +## Example + +Input text: "Cornish pasty is a savory pastry filled with beef and potatoes. This recipe serves 4 people." + +Expected output: +{"type": "entity", "entity": "Cornish pasty", "entity_type": "fo/Recipe"} +{"type": "entity", "entity": "beef", "entity_type": "fo/Food"} +{"type": "entity", "entity": "potatoes", "entity_type": "fo/Food"} +{"type": "relationship", "subject": "Cornish pasty", "subject_type": "fo/Recipe", "relation": "fo/has_ingredient", "object": "beef", "object_type": "fo/Food"} +{"type": "relationship", "subject": "Cornish pasty", "subject_type": "fo/Recipe", "relation": "fo/has_ingredient", "object": "potatoes", "object_type": "fo/Food"} +{"type": "attribute", "entity": "Cornish pasty", "entity_type": "fo/Recipe", "attribute": "fo/serves", "value": "4 people"} + +Now extract entities, relationships, and attributes from the text above. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/openai.jsonnet new file mode 100644 index 00000000..5d232337 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/openai.jsonnet @@ -0,0 +1,42 @@ +// For OpenAI LLMs. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/schema-selection.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/schema-selection.txt new file mode 100644 index 00000000..39b180e5 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/schema-selection.txt @@ -0,0 +1,25 @@ +You are a database schema selection expert. Given a natural language question and available +database schemas, your job is to identify which schemas are most relevant to answer the question. + +## Available Schemas: +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}): {{ field.description }} +{% endfor %} + +{% endfor %} + +## Question: +{{ question }} + +## Instructions: +1. Analyze the question to understand what data is being requested +2. Examine each schema to understand what data it contains +3. Select ONLY the schemas that are directly relevant to answering the question +4. Return your answer as a JSON array of schema names + +## Response Format: +Return ONLY a JSON array of schema names, nothing else. +Example: ["customers", "orders", "products"] diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/slm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/slm.jsonnet new file mode 100644 index 00000000..48eb96d0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/prompts/slm.jsonnet @@ -0,0 +1,44 @@ +// For SLM. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + + + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/pulsar/pulsar-manager.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/pulsar/pulsar-manager.jsonnet new file mode 100644 index 00000000..c1ca4515 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/pulsar/pulsar-manager.jsonnet @@ -0,0 +1,40 @@ +local images = import "values/images.jsonnet"; + +{ + + "pulsar" +: { + + create:: function(engine) + +// FIXME: Should persist something? +// local volume = engine.volume(...) + + local container = + engine.container("pulsar") + .with_image(images.pulsar_manager) + .with_environment({ + SPRING_CONFIGURATION_FILE: "/pulsar-manager/pulsar-manager/application.properties", + }) + .with_limits("0.5", "1.4G") + .with_reservations("0.1", "1.4G") + .with_port(9527, 9527, "api") + .with_port(7750, 7750, "api2"); + + local containerSet = engine.containers( + "pulsar", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9527, 9527, "api") + .with_port(7750, 7750, "api2); + + engine.resources([ + containerSet, + service, + ]) + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/pulsar/pulsar.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/pulsar/pulsar.jsonnet new file mode 100644 index 00000000..d417322d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/pulsar/pulsar.jsonnet @@ -0,0 +1,222 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +// This is a Pulsar configuration. Non-standalone mode so we deploy +// individual components: bookkeeper, broker and zookeeper. +// +// This also deploys the TrustGraph 'admin' container which initialises +// TrustGraph-specific namespaces etc. + +{ + + "pulsar" +: { + + // Zookeeper memory settings (can be overridden by memory-profile) + "zk-memory-limit":: "512M", + "zk-memory-reservation":: "512M", + "zk-heap":: "256m", + "zk-direct-memory":: "256m", + + // Bookie memory settings (can be overridden by memory-profile) + "bookie-memory-limit":: "1024M", + "bookie-memory-reservation":: "1024M", + "bookie-heap":: "256m", + "bookie-direct-memory":: "256m", + + // Broker memory settings (can be overridden by memory-profile) + "broker-memory-limit":: "800M", + "broker-memory-reservation":: "800M", + "broker-heap":: "384m", + "broker-direct-memory":: "384m", + + // Pulsar-init memory settings (can be overridden by memory-profile) + "init-memory-limit":: "256M", + "init-memory-reservation":: "256M", + "init-heap":: "128m", + "init-direct-memory":: "128m", + + create:: function(engine) + + // Capture memory settings into locals (self refers to pulsar object here) + local zkMemLimit = self["zk-memory-limit"]; + local zkMemReserv = self["zk-memory-reservation"]; + local zkHeap = self["zk-heap"]; + local zkDirect = self["zk-direct-memory"]; + + local bookieMemLimit = self["bookie-memory-limit"]; + local bookieMemReserv = self["bookie-memory-reservation"]; + local bookieHeap = self["bookie-heap"]; + local bookieDirect = self["bookie-direct-memory"]; + + local brokerMemLimit = self["broker-memory-limit"]; + local brokerMemReserv = self["broker-memory-reservation"]; + local brokerHeap = self["broker-heap"]; + local brokerDirect = self["broker-direct-memory"]; + + local initMemLimit = self["init-memory-limit"]; + local initMemReserv = self["init-memory-reservation"]; + local initHeap = self["init-heap"]; + local initDirect = self["init-direct-memory"]; + + // Zookeeper volume + local zkVolume = engine.volume("zookeeper").with_size("1G"); + + // Zookeeper container + local zkContainer = + engine.container("zookeeper") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "bin/apply-config-from-env.py conf/zookeeper.conf && bin/generate-zookeeper-config.sh conf/zookeeper.conf && exec bin/pulsar zookeeper" + ]) + .with_limits("1", zkMemLimit) + .with_reservations("0.05", zkMemReserv) + .with_user("0:1000") + .with_volume_mount(zkVolume, "/pulsar/data/zookeeper") + .with_environment({ + "metadataStoreUrl": "zk:zookeeper:2181", + "PULSAR_MEM": "-Xms%s -Xmx%s -XX:MaxDirectMemorySize=%s" % [ + zkHeap, zkHeap, zkDirect, + ], + }) + .with_port(2181, 2181, "zookeeper") + .with_port(2888, 2888, "zookeeper2") + .with_port(3888, 3888, "zookeeper3"); + + // Pulsar cluster init container + local initContainer = + engine.container("pulsar-init") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "sleep 10 && bin/pulsar initialize-cluster-metadata --cluster cluster-a --zookeeper zookeeper:2181 --configuration-store zookeeper:2181 --web-service-url http://pulsar:8080 --broker-service-url pulsar://pulsar:6650", + ]) + .with_limits("1", initMemLimit) + .with_reservations("0.05", initMemReserv) + .with_environment({ + "PULSAR_MEM": "-Xms%s -Xmx%s -XX:MaxDirectMemorySize=%s" % [ + initHeap, initHeap, initDirect, + ], + }); + + + // Bookkeeper volume + local bookieVolume = engine.volume("bookie").with_size("20G"); + + // Bookkeeper container + local bookieContainer = + engine.container("bookie") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "bin/apply-config-from-env.py conf/bookkeeper.conf && exec bin/pulsar bookie" + // false ^ causes this to be a 'failure' exit. + ]) + .with_limits("1", bookieMemLimit) + .with_reservations("0.1", bookieMemReserv) + .with_user("0:1000") + .with_volume_mount(bookieVolume, "/pulsar/data/bookkeeper") + .with_environment({ + "clusterName": "cluster-a", + "zkServers": "zookeeper:2181", + "bookieId": "bookie", + "metadataStoreUri": "metadata-store:zk:zookeeper:2181", + "advertisedAddress": "bookie", + "BOOKIE_MEM": "-Xms%s -Xmx%s -XX:MaxDirectMemorySize=%s" % [ + bookieHeap, bookieHeap, bookieDirect, + ], + }) + .with_port(3181, 3181, "bookie"); + + // Pulsar broker, stateless (uses ZK and Bookkeeper for state) + local brokerContainer = + engine.container("pulsar") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "bin/apply-config-from-env.py conf/broker.conf && exec bin/pulsar broker" + ]) + .with_limits("1", brokerMemLimit) + .with_reservations("0.1", brokerMemReserv) + .with_environment({ + "metadataStoreUrl": "zk:zookeeper:2181", + "zookeeperServers": "zookeeper:2181", + "clusterName": "cluster-a", + "managedLedgerDefaultEnsembleSize": "1", + "managedLedgerDefaultWriteQuorum": "1", + "managedLedgerDefaultAckQuorum": "1", + "advertisedAddress": "pulsar", + "advertisedListeners": "external:pulsar://pulsar:6650,localhost:pulsar://localhost:6650", + "PULSAR_MEM": "-Xms%s -Xmx%s -XX:MaxDirectMemorySize=%s" % [ + brokerHeap, brokerHeap, brokerDirect, + ], + }) + .with_port(6650, 6650, "pulsar") + .with_port(8080, 8080, "admin"); + + // Container sets + local zkContainerSet = engine.containers( + "zookeeper", + [ + zkContainer, + ] + ); + + local initContainerSet = engine.containers( + "init-pulsar", + [ + initContainer, + ] + ); + + local bookieContainerSet = engine.containers( + "bookie", + [ + bookieContainer, + ] + ); + + local brokerContainerSet = engine.containers( + "pulsar", + [ + brokerContainer, + ] + ); + + // Zookeeper service + local zkService = + engine.service(zkContainerSet) + .with_port(2181, 2181, "zookeeper") + .with_port(2888, 2888, "zookeeper2") + .with_port(3888, 3888, "zookeeper3"); + + // Bookkeeper service + local bookieService = + engine.service(bookieContainerSet) + .with_port(3181, 3181, "bookie"); + + // Pulsar broker service + local brokerService = + engine.service(brokerContainerSet) + .with_port(6650, 6650, "pulsar") + .with_port(8080, 8080, "admin"); + + engine.resources([ + zkVolume, + bookieVolume, + zkContainerSet, + initContainerSet, + bookieContainerSet, + brokerContainerSet, + zkService, + bookieService, + brokerService, + ]) + + } + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-additionals.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-additionals.jsonnet new file mode 100644 index 00000000..3e9f3481 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-additionals.jsonnet @@ -0,0 +1,134 @@ +local decode = import "decode-config.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Custom engine that collects configVolume parts +local engine = { + + // Collection of all configVolume parts + configVolumes:: [], + + // Implement all required engine methods as no-ops + container:: function(name) { + with_image:: function(x) self, + with_command:: function(x) self, + with_environment:: function(x) self, + with_limits:: function(c, m) self, + with_reservations:: function(c, m) self, + with_port:: function(src, dest, name) self, + with_volume_mount:: function(vol, mnt) self, + with_user:: function(x) self, + with_runtime:: function(x) self, + with_privileged:: function(x) self, + with_ipc:: function(x) self, + with_capability:: function(x) self, + with_device:: function(hdev, cdev) self, + with_env_var_secrets:: function(vars) self, + }, + + volume:: function(name) { + with_size:: function(size) self, + }, + + // The key method - collects configVolume parts + configVolume:: function(name, dir, parts) + local collector = self + { + configVolumes: super.configVolumes + [ + { + dir: dir, + parts: parts, + } + ] + }; + { + // Return a dummy volume that has the collector in it + name: name, + with_size:: function(size) collector, + // Provide a way to get back to the collector + getCollector:: function() collector, + }, + + secretVolume:: function(name, dir, parts) { + with_size:: function(size) self, + }, + + envSecrets:: function(name) { + with_env_var:: function(name, key) self, + }, + + containers:: function(name, containers) self, + + service:: function(containers) { + with_port:: function(src, dest, name) self, + }, + + internalService:: function(containers) { + with_port:: function(src, dest, name) self, + }, + + resources:: function(res) + // Fold over resources and collect any configVolume state + local collected = std.foldl( + function(state, r) + if std.objectHasAll(r, 'getCollector') then + // Merge the configVolumes from the volume's collector into our state + local volumeCollector = r.getCollector(); + state + { + configVolumes: state.configVolumes + volumeCollector.configVolumes + } + else + state, + res, + self + ); + collected, +}; + +// Execute all component create() functions with our collecting engine +// Note: create:: is a hidden field, so we must use objectHasAll not objectHas +local result = std.foldl( + function(state, p) + if std.objectHasAll(p, 'create') then + // Pattern has create directly - call it + p.create(state) + else + state, + std.objectValues(patterns), + engine +); + +// Debug: show what we collected +local debug = { + numPatterns: std.length(std.objectValues(patterns)), + numConfigVolumes: std.length(result.configVolumes), +}; + +// Transform collected data into output format +local allFiles = std.flattenArrays([ + [ + { + // Remove trailing slash from dir to avoid double slashes + path: std.join("/", [std.rstripChars(cv.dir, "/"), filename]), + content: cv.parts[filename] + } + for filename in std.objectFields(cv.parts) + ] + for cv in result.configVolumes +]); + +// Deduplicate by path - use a map to keep only unique paths +local uniqueMap = std.foldl( + function(acc, item) acc + { [item.path]: item }, + allFiles, + {} +); + +// Convert back to array +local additionals = std.objectValues(uniqueMap); + +// Output the array +additionals diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-aks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-aks-k8s.jsonnet new file mode 100644 index 00000000..bcec1cbb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-aks-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "../engine/aks-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-docker-compose.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-docker-compose.jsonnet new file mode 100644 index 00000000..7f0dbabe --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-docker-compose.jsonnet @@ -0,0 +1,20 @@ + +local engine = import "../engine/docker-compose.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resources = std.foldl( + function(state, p) state + p.create(engine), + std.objectValues(patterns), + {} +); + +resources + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-eks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-eks-k8s.jsonnet new file mode 100644 index 00000000..f9de59cb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-eks-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "../engine/eks-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-gcp-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-gcp-k8s.jsonnet new file mode 100644 index 00000000..50037a5c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-gcp-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "../engine/gcp-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-minikube-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-minikube-k8s.jsonnet new file mode 100644 index 00000000..6fa64706 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-minikube-k8s.jsonnet @@ -0,0 +1,26 @@ + +local engine = import "../engine/minikube-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +// Extract resources using the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-noop.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-noop.jsonnet new file mode 100644 index 00000000..3ad735d9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-noop.jsonnet @@ -0,0 +1,20 @@ + +local engine = import "../engine/noop.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resources = std.foldl( + function(state, p) state + p.create(engine), + std.objectValues(patterns), + {} +); + +resources + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-ovh-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-ovh-k8s.jsonnet new file mode 100644 index 00000000..92f61041 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-ovh-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "../engine/ovh-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-podman-compose.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-podman-compose.jsonnet new file mode 100644 index 00000000..7f0dbabe --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-podman-compose.jsonnet @@ -0,0 +1,20 @@ + +local engine = import "../engine/docker-compose.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resources = std.foldl( + function(state, p) state + p.create(engine), + std.objectValues(patterns), + {} +); + +resources + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-scw-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-scw-k8s.jsonnet new file mode 100644 index 00000000..4f6c1d7a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-scw-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "../engine/scw-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-tg-configuration.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-tg-configuration.jsonnet new file mode 100644 index 00000000..59b4732a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/config-to-tg-configuration.jsonnet @@ -0,0 +1,15 @@ + +local engine = import "../engine/noop.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract configuration directly from patterns +patterns.configuration.configuration + + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/decode-config.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/decode-config.jsonnet new file mode 100644 index 00000000..759513c4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/renderers/decode-config.jsonnet @@ -0,0 +1,31 @@ + +local components = import "../components.jsonnet"; + +local apply = function(p, components) + + local base = { + + with:: function(k, v) self + { + [k] +:: v + }, + + with_params:: function(pars) + self + std.foldl( + function(obj, par) obj.with(par.key, par.value), + std.objectKeysValues(pars), + self + ), + + }; + + local component = base + components[p.name]; + + component.with_params(p.parameters); + +local decode = function(config) + local add = function(state, c) state + apply(c, components); + local patterns = std.foldl(add, config, {}); + patterns; + +decode + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/row-store/cassandra.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/row-store/cassandra.jsonnet new file mode 100644 index 00000000..f90526bc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/row-store/cassandra.jsonnet @@ -0,0 +1,77 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local cassandra = import "backends/cassandra.jsonnet"; + +cassandra + { + + "store-rows" +: { + + create:: function(engine) + + local container = + engine.container("store-rows") + .with_image(images.trustgraph_flow) + .with_command([ + "rows-write-cassandra", + "-p", + url.pulsar, + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-rows", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-rows" +: { + + create:: function(engine) + + local container = + engine.container("query-rows") + .with_image(images.trustgraph_flow) + .with_command([ + "rows-query-cassandra", + "-p", + url.pulsar, + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "512M") + .with_reservations("0.1", "512M"); + + local containerSet = engine.containers( + "query-rows", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/runtime-config/config-composer.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/runtime-config/config-composer.jsonnet new file mode 100644 index 00000000..e93ff044 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/runtime-config/config-composer.jsonnet @@ -0,0 +1,97 @@ +// Configuration Composer Module +// Orchestrates the complete configuration building process +// Combines all components into the final TrustGraph configuration + +local flow_builder = import "flow-builder.jsonnet"; +local interface_builder = import "interface-builder.jsonnet"; + +{ + // Main function to build the complete configuration + build: function(config_spec) + // Extract configuration parameters + local flow_blueprints = config_spec.flow_blueprints; + local default_flow_blueprint = config_spec.default_flow_blueprint; + local default_flow_id = config_spec.default_flow_id; + local flow_init_parameters = config_spec.flow_init_parameters; + + // Build all processors for the default flow + local blueprint_processors = flow_builder.build_blueprint_processors( + flow_blueprints, + default_flow_blueprint, + flow_init_parameters + ); + + local flow_processors = flow_builder.build_flow_processors( + flow_blueprints, + default_flow_blueprint, + default_flow_id, + flow_init_parameters + ); + + // Combine processors into flow objects + local processor_array = blueprint_processors + flow_processors; + local flow_objects = flow_builder.build_flow_objects(processor_array); + local active_flows = flow_builder.merge_flow_objects(flow_objects); + + // Build interfaces for the default flow + local default_flow_interfaces = interface_builder.build_interfaces( + flow_blueprints, + default_flow_blueprint, + default_flow_id, + flow_init_parameters + ); + + // Return object with nested configuration (for backwards compatibility) + { + // Create function (for backwards compatibility) + create: function(engine) {}, + + // The actual configuration object + configuration: { + // Prompts configuration + prompt: { + "system": config_spec.prompts["system-template"], + "template-index": std.objectFieldsAll(config_spec.prompts.templates), + } + { + ["template." + template.key]: template.value + for template in std.objectKeysValuesAll(config_spec.prompts.templates) + }, + + // Tools configuration + tool: { + [tool.id]: tool + for tool in config_spec.tools + }, + + // MCP configuration + mcp: config_spec.mcp, + + // Flow blueprints reference + "flow-blueprint": flow_blueprints, + + // Interface descriptions + "interface-description": config_spec.interface_descriptions, + + // Flow instances + "flow": { + [default_flow_id]: { + "description": "Default processing flow", + "blueprint-name": default_flow_blueprint, + "interfaces": default_flow_interfaces, + "parameters": flow_init_parameters, + }, + }, + + // Active flow processors + "active-flow": active_flows, + + // Token costs and parameter types + "token-cost": config_spec.token_costs, + "parameter-type": config_spec.parameter_types, + + // Collections configuration + "collection": config_spec.collection, + + }, + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/runtime-config/flow-builder.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/runtime-config/flow-builder.jsonnet new file mode 100644 index 00000000..0b142750 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/runtime-config/flow-builder.jsonnet @@ -0,0 +1,72 @@ +// Flow Builder Module +// Processes flow blueprints and builds complete flow configurations +// Handles {blueprint}, {id}, and parameter substitutions + +local param_processor = import "parameter-processor.jsonnet"; + +{ + // Builds blueprint-level processors with parameter substitution + // Processes the 'blueprint' section of flow blueprints + build_blueprint_processors: function(flow_blueprints, blueprint_name, parameters) + [ + [ + // Replace {blueprint} in the processor key + local key = std.strReplace(processor.key, "{blueprint}", blueprint_name); + local parts = std.splitLimit(key, ":", 2); + parts, + { + // Process each field in the processor configuration + [field.key]: + // First replace {blueprint}, then substitute parameters + local blueprint_replaced = std.strReplace(field.value, "{blueprint}", blueprint_name); + param_processor.substitute_parameters(blueprint_replaced, parameters) + for field in std.objectKeysValuesAll(processor.value) + } + ] + for processor in std.objectKeysValuesAll(flow_blueprints[blueprint_name].blueprint) + ], + + // Builds flow-level processors with parameter substitution + // Processes the 'flow' section of flow blueprints + build_flow_processors: function(flow_blueprints, blueprint_name, flow_id, parameters) + [ + [ + // Replace both {blueprint} and {id} in the processor key + local key = std.strReplace( + std.strReplace(processor.key, "{blueprint}", blueprint_name), + "{id}", flow_id + ); + local parts = std.splitLimit(key, ":", 2); + parts, + { + // Process each field in the processor configuration + [field.key]: + // Replace {blueprint} and {id}, then substitute parameters + local blueprint_replaced = std.strReplace(field.value, "{blueprint}", blueprint_name); + local id_replaced = std.strReplace(blueprint_replaced, "{id}", flow_id); + param_processor.substitute_parameters(id_replaced, parameters) + for field in std.objectKeysValuesAll(processor.value) + } + ] + for processor in std.objectKeysValuesAll(flow_blueprints[blueprint_name].flow) + ], + + // Combines blueprint and flow processors into flow objects + build_flow_objects: function(processor_array) + std.map( + function(item) { + [item[0][0]] +: { + [item[0][1]]: item[1] + } + }, + processor_array + ), + + // Merges all flow objects into a single flows_active configuration + merge_flow_objects: function(flow_objects) + std.foldr( + function(a, b) a + b, + flow_objects, + {} + ), +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/runtime-config/interface-builder.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/runtime-config/interface-builder.jsonnet new file mode 100644 index 00000000..2143e600 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/runtime-config/interface-builder.jsonnet @@ -0,0 +1,30 @@ +// Interface Builder Module +// Processes flow class interfaces with parameter substitution +// Handles both string interfaces and nested object interfaces + +local param_processor = import "parameter-processor.jsonnet"; + +{ + // Builds interfaces for a specific flow class and instance + // Processes the 'interfaces' section of flow classes + build_interfaces: function(flow_classes, class_name, flow_id, parameters) + local interface_spec = flow_classes[class_name].interfaces; + { + [interface.key]: + if std.isString(interface.value) then + // Simple string interface - apply all substitutions + local class_replaced = std.strReplace(interface.value, "{class}", class_name); + local id_replaced = std.strReplace(class_replaced, "{id}", flow_id); + param_processor.substitute_parameters(id_replaced, parameters) + else + // Complex object interface - process nested fields + { + [field.key]: + local class_replaced = std.strReplace(field.value, "{class}", class_name); + local id_replaced = std.strReplace(class_replaced, "{id}", flow_id); + param_processor.substitute_parameters(id_replaced, parameters) + for field in std.objectKeysValuesAll(interface.value) + } + for interface in std.objectKeysValuesAll(interface_spec) + }, +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/runtime-config/interface-descriptions.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/runtime-config/interface-descriptions.jsonnet new file mode 100644 index 00000000..f9dda8b0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/runtime-config/interface-descriptions.jsonnet @@ -0,0 +1,89 @@ +// Interface Descriptions Module +// Defines all external interfaces available in TrustGraph flows +// These are the 'endpoints' that external systems can interact with + +{ + // Document loading interfaces - for data ingestion + "document-load": { + "description": "Document loader", + "kind": "send", + "visible": true, + }, + "text-load": { + "description": "Text document loader", + "kind": "send", + "visible": true, + }, + + // Data storage interfaces - for processed data streams + "entity-contexts-load": { + "description": "Entity contexts loader", + "kind": "send", + }, + "triples-store": { + "description": "Triples loader", + "kind": "send", + }, + "graph-embeddings-store": { + "description": "Graph embeddings loader", + "kind": "send", + }, + "document-embeddings-store": { + "description": "Document embeddings loader", + "kind": "send", + }, + "objects-store": { + "description": "Object store", + "kind": "request-response", + }, + + // Query interfaces - for retrieving information + "graph-rag": { + "description": "GraphRAG service", + "kind": "request-response", + }, + "document-rag": { + "description": "ChunkRAG service", + "kind": "request-response", + }, + "triples": { + "description": "Triples query service", + "kind": "request-response", + }, + "graph-embeddings": { + "description": "Graph embeddings service", + "kind": "request-response", + }, + "document-embeddings": { + "description": "Document embeddings service", + "kind": "request-response", + }, + "objects": { + "description": "Object query service", + "kind": "request-response", + }, + + // Processing services - for text and data processing + "prompt": { + "description": "Prompt service", + "kind": "request-response", + }, + "agent": { + "description": "Agent service", + "kind": "request-response", + }, + "text-completion": { + "description": "Text completion service", + "kind": "request-response", + }, + + // Query translation services - for natural language queries + "nlp-query": { + "description": "NLP question to GraphQL service", + "kind": "request-response", + }, + "structured-query": { + "description": "Structured query service", + "kind": "request-response", + }, +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/runtime-config/parameter-processor.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/runtime-config/parameter-processor.jsonnet new file mode 100644 index 00000000..f317ae9f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/runtime-config/parameter-processor.jsonnet @@ -0,0 +1,38 @@ +// Parameter Processing Module +// Handles dynamic parameter replacement in configuration values +// Replaces {parameter_name} placeholders with actual parameter values + +{ + // Applies parameter substitutions to string values + // Only processes strings - leaves other types unchanged + substitute_parameters: function(value, parameters) + if std.isString(value) then + std.foldl( + function(acc, param) + // Only do string replacement if param.value is a string + if std.isString(param.value) then + std.strReplace(acc, "{" + param.key + "}", param.value) + else + acc, // Skip replacement for non-string parameter values + std.objectKeysValuesAll(parameters), + value + ) + else + value, + + // Applies parameter substitutions to all values in an object + // Recursively processes nested objects and arrays + substitute_parameters_in_object: function(obj, parameters) + if std.isObject(obj) then + { + [key]: $.substitute_parameters_in_object(obj[key], parameters) + for key in std.objectFields(obj) + } + else if std.isArray(obj) then + [ + $.substitute_parameters_in_object(item, parameters) + for item in obj + ] + else + $.substitute_parameters(obj, parameters), +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/runtime-config/tools.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/runtime-config/tools.jsonnet new file mode 100644 index 00000000..4947279e --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/runtime-config/tools.jsonnet @@ -0,0 +1,36 @@ +// Tools Configuration Module +// Defines all available tools that can be used by agents and flows +// Each tool specifies its interface, arguments, and behavior + +[ + // Knowledge query tool - queries the knowledge base + { + id: "knowledge-query", + name: "Knowledge query", + description: "This tool queries a knowledge base that holds information about domain-specific information. The question should be a natural language question.", + type: "knowledge-query", + collection: "default", + arguments: [ + { + name: "question", + type: "string", + description: "A simple natural language question.", + } + ] + }, + + // LLM completion tool - general purpose text completion + { + id: "llm-completion", + name: "LLM text completion", + type: "text-completion", + description: "This tool queries an LLM for non-domain-specific information. The question should be a natural language question.", + arguments: [ + { + name: "question", + type: "string", + description: "The question which should be asked of the LLM.", + } + ] + } +] \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/runtime-config/trustgraph-config.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/runtime-config/trustgraph-config.jsonnet new file mode 100644 index 00000000..d860ab3c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/runtime-config/trustgraph-config.jsonnet @@ -0,0 +1,90 @@ +// TrustGraph Main Configuration +// Clean, modular composition of TrustGraph configuration +// Uses specialized modules for different aspects of config building + +// Import dependencies +local images = import "../values/images.jsonnet"; +local url = import "../values/url.jsonnet"; +local prompts = import "../prompts/mixtral.jsonnet"; +local default_prompts = import "../prompts/default-prompts.jsonnet"; +local token_costs = import "../values/token-costs.jsonnet"; +local flow_blueprints = import "../flows/flow-blueprints.jsonnet"; +local config_composer = import "config-composer.jsonnet"; +local interface_descriptions = import "interface-descriptions.jsonnet"; +local tools = import "tools.jsonnet"; +local temperature_params = import "../parameters/temperature-param-types.jsonnet"; +local chunking_params = import "../parameters/chunking-param-types.jsonnet"; + +// Main configuration object +local configuration = { + + // Prompt templates + prompts:: default_prompts, + + // Tool definitions + tools:: tools, + + // MCP configuration + mcp:: {}, + + // Flow classes reference + "flow-blueprints":: flow_blueprints, + + // LLM model parameters + "llm-models" +:: {}, + + // Embeddings model parameters + "embeddings-models" +:: {}, + + collections +:: { + "trustgraph:default": { + "user": "default-user", + "collection": "default", + "name": "Default Collection", + "description": "Default collection", + "tags": ["default"], + }, + }, + + // Default model and flow parameters + flow_init_parameters:: { + "llm-model": $["llm-models"].default, + "llm-rag-model": $["llm-models"].default, + "llm-temperature": "%0.3f" % $["parameter-types"]["llm-temperature"].default, + "llm-rag-temperature": "%0.3f" % $["parameter-types"]["llm-temperature"].default, + "chunk-size": std.toString($["parameter-types"]["chunk-size"].default), + "chunk-overlap": std.toString($["parameter-types"]["chunk-overlap"].default), + "embeddings-model": $["embeddings-models"].default, + }, + + // Interface descriptions for external endpoints + "interface-descriptions":: interface_descriptions, + + // Parameter type definitions + "parameter-types":: { + "llm-model": $["llm-models"], + "embeddings-model": $["embeddings-models"], + } + chunking_params + temperature_params, + + // Token costs + "token-costs":: token_costs, + + // Build the complete configuration using the composer + configuration:: config_composer.build({ + flow_blueprints: $["flow-blueprints"], + default_flow_blueprint: "everything", + default_flow_id: "default", + flow_init_parameters: $["flow_init_parameters"], + prompts: $["prompts"], + tools: $["tools"], + mcp: $["mcp"], + interface_descriptions: $["interface-descriptions"], + parameter_types: $["parameter-types"], + token_costs: $["token-costs"], + collection: $["collections"], + }), + +} + default_prompts; + +// Export the final configuration +configuration diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/triple-store/cassandra.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/triple-store/cassandra.jsonnet new file mode 100644 index 00000000..05f4fd27 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/triple-store/cassandra.jsonnet @@ -0,0 +1,77 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local cassandra = import "backends/cassandra.jsonnet"; + +cassandra + { + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-cassandra", + "-p", + url.pulsar, + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-cassandra", + "-p", + url.pulsar, + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "512M") + .with_reservations("0.1", "512M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/triple-store/falkordb.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/triple-store/falkordb.jsonnet new file mode 100644 index 00000000..b4bace2d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/triple-store/falkordb.jsonnet @@ -0,0 +1,79 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local falkordb = import "backends/falkordb.jsonnet"; + +falkordb + { + + "falkordb-url":: "falkor://falkordb:6379", + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-falkordb", + "-p", + url.pulsar, + "-g", + $["falkordb-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-falkordb", + "-p", + url.pulsar, + "-g", + $["falkordb-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/triple-store/memgraph.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/triple-store/memgraph.jsonnet new file mode 100644 index 00000000..72597d72 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/triple-store/memgraph.jsonnet @@ -0,0 +1,84 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local memgraph = import "backends/memgraph.jsonnet"; + +memgraph + { + + "memgraph-url":: "bolt://memgraph:7687", + "memgraph-database":: "memgraph", + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-memgraph", + "-p", + url.pulsar, + "-g", + $["memgraph-url"], + "--database", + $["memgraph-database"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-memgraph", + "-p", + url.pulsar, + "-g", + $["memgraph-url"], + "--database", + $["memgraph-database"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/triple-store/neo4j.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/triple-store/neo4j.jsonnet new file mode 100644 index 00000000..08904715 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/triple-store/neo4j.jsonnet @@ -0,0 +1,79 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local neo4j = import "backends/neo4j.jsonnet"; + +neo4j + { + + "neo4j-url":: "bolt://neo4j:7687", + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-neo4j", + "-p", + url.pulsar, + "-g", + $["neo4j-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-neo4j", + "-p", + url.pulsar, + "-g", + $["neo4j-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/ui/workbench-ui.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/ui/workbench-ui.jsonnet new file mode 100644 index 00000000..f2048e47 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/ui/workbench-ui.jsonnet @@ -0,0 +1,32 @@ +local images = import "values/images.jsonnet"; + +{ + + "workbench-ui" +: { + + create:: function(engine) + + local container = + engine.container("workbench-ui") + .with_image(images["workbench-ui"]) + .with_limits("0.1", "256M") + .with_reservations("0.1", "256M") + .with_port(8888, 8888, "ui"); + + local containerSet = engine.containers( + "workbench-ui", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8888, 8888, "ui"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/values/images.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/values/images.jsonnet new file mode 100644 index 00000000..489859bc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/values/images.jsonnet @@ -0,0 +1,36 @@ +local version = import "version.jsonnet"; +{ + cassandra: "docker.io/cassandra:5.0.6", +// Not working +// ceph: "quay.io/ceph/daemon:latest-reef", + neo4j: "docker.io/neo4j:2025.08.0-community-bullseye", + pulsar: "docker.io/apachepulsar/pulsar:4.1.0", + pulsar_manager: "docker.io/apachepulsar/pulsar-manager:v0.4.0", + etcd: "quay.io/coreos/etcd:v3.6.4", + minio: "docker.io/minio/minio:RELEASE.2025-09-07T16-13-09Z", + garage: "docker.io/dxflrs/garage:v2.1.0", + milvus: "docker.io/milvusdb/milvus:v2.5.17", + prometheus: "docker.io/prom/prometheus:v3.8.0", + grafana: "docker.io/grafana/grafana:12.3.0", + loki: "docker.io/grafana/loki:3.6.2", + trustgraph_base: "docker.io/trustgraph/trustgraph-base:" + version, + trustgraph_flow: "docker.io/trustgraph/trustgraph-flow:" + version, + trustgraph_ocr: "docker.io/trustgraph/trustgraph-ocr:" + version, + trustgraph_bedrock: "docker.io/trustgraph/trustgraph-bedrock:" + version, + trustgraph_vertexai: "docker.io/trustgraph/trustgraph-vertexai:" + version, + trustgraph_hf: "docker.io/trustgraph/trustgraph-hf:" + version, + trustgraph_mcp: "docker.io/trustgraph/trustgraph-mcp:" + version, + qdrant: "docker.io/qdrant/qdrant:v1.15.4", + memgraph_mage: "docker.io/memgraph/memgraph-mage:3.5", + memgraph_lab: "docker.io/memgraph/lab:3.5.0", + falkordb: "docker.io/falkordb/falkordb:v4.12.5", + "workbench-ui": "docker.io/trustgraph/workbench-ui:1.7.2", + "ddg-mcp-server": "docker.io/trustgraph/ddg-mcp-server:0.1.0", + "tgi-service-intel-xpu": "ghcr.io/huggingface/text-generation-inference:3.3.1-intel-xpu", + "tgi-service-cpu": "ghcr.io/huggingface/text-generation-inference:3.3.1-intel-cpu", + "tgi-service-gaudi": "ghcr.io/huggingface/text-generation-inference:sha-f140440-gaudi", + "vllm-service-intel-xpu": "docker.io/intel/vllm:0.8.0-xpu", + "vllm-service-gaudi": "docker.io/trustgraph/vllm-hpu:027f5645", + "vllm-service-nvidia": "docker.io/vllm/vllm-openai:latest", + "vllm-service-intel-battlemage": "docker.io/intelanalytics/ipex-llm-serving-xpu:0.2.0-b6", +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/values/token-costs.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/values/token-costs.jsonnet new file mode 100644 index 00000000..84c70373 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/values/token-costs.jsonnet @@ -0,0 +1,102 @@ +{ + "mistral.mistral-large-2407-v1:0": { + "model_name": "mistral.mistral-large-2407-v1:0", + "input_price": 0.000004, + "output_price": 0.000012 + }, + "meta.llama3-1-405b-instruct-v1:0": { + "model_name": "meta.llama3-1-405b-instruct-v1:0", + "input_price": 0.00000532, + "output_price": 0.000016 + }, + "mistral.mixtral-8x7b-instruct-v0:1": { + "model_name": "mistral.mixtral-8x7b-instruct-v0:1", + "input_price": 0.00000045, + "output_price": 0.0000007 + }, + "meta.llama3-1-70b-instruct-v1:0": { + "model_name": "meta.llama3-1-70b-instruct-v1:0", + "input_price": 0.00000099, + "output_price": 0.00000099 + }, + "meta.llama3-1-8b-instruct-v1:0": { + "model_name": "meta.llama3-1-8b-instruct-v1:0", + "input_price": 0.00000022, + "output_price": 0.00000022 + }, + "anthropic.claude-3-haiku-20240307-v1:0": { + "model_name": "anthropic.claude-3-haiku-20240307-v1:0", + "input_price": 0.00000025, + "output_price": 0.00000125 + }, + "anthropic.claude-3-5-sonnet-20240620-v1:0": { + "model_name": "anthropic.claude-3-5-sonnet-20240620-v1:0", + "input_price": 0.000003, + "output_price": 0.000015 + }, + "cohere.command-r-plus-v1:0": { + "model_name": "cohere.command-r-plus-v1:0", + "input_price": 0.0000030, + "output_price": 0.0000150 + }, + "ollama": { + "model_name": "ollama", + "input_price": 0, + "output_price": 0 + }, + "claude-3-haiku-20240307": { + "model_name": "claude-3-haiku-20240307", + "input_price": 0.00000025, + "output_price": 0.00000125 + }, + "claude-3-5-sonnet-20240620": { + "model_name": "claude-3-5-sonnet-20240620", + "input_price": 0.000003, + "output_price": 0.000015 + }, + "claude-3-opus-20240229": { + "model_name": "claude-3-opus-20240229", + "input_price": 0.000015, + "output_price": 0.000075 + }, + "claude-3-sonnet-20240229": { + "model_name": "claude-3-sonnet-20240229", + "input_price": 0.000003, + "output_price": 0.000015 + }, + "command-r-08-202": { + "model_name": "command-r-08-202", + "input_price": 0.0000025, + "output_price": 0.000010 + }, + "c4ai-aya-23-8b": { + "model_name": "c4ai-aya-23-8b", + "input_price": 0, + "output_price": 0 + }, + "llama.cpp": { + "model_name": "llama.cpp", + "input_price": 0, + "output_price": 0 + }, + "gpt-4o": { + "model_name": "gpt-4o", + "input_price": 0.000005, + "output_price": 0.000015 + }, + "gpt-4o-2024-08-06": { + "model_name": "gpt-4o-2024-08-06", + "input_price": 0.0000025, + "output_price": 0.000010 + }, + "gpt-4o-2024-05-13": { + "model_name": "gpt-4o-2024-05-13", + "input_price": 0.000005, + "output_price": 0.000015 + }, + "gpt-4o-mini": { + "model_name": "gpt-4o-mini", + "input_price": 0.00000015, + "output_price": 0.0000006 + } +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/values/url.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/values/url.jsonnet new file mode 100644 index 00000000..c3d4ad97 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/values/url.jsonnet @@ -0,0 +1,7 @@ +{ + pulsar: "pulsar://pulsar:6650", + pulsar_admin: "http://pulsar:8080", + milvus: "http://milvus:19530", + qdrant: "http://qdrant:6333", + object_store: "garage:3900", +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/vector-store/milvus.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/vector-store/milvus.jsonnet new file mode 100644 index 00000000..213027d1 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/vector-store/milvus.jsonnet @@ -0,0 +1,146 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local milvus = import "backends/milvus.jsonnet"; + +milvus + { + + "store-graph-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("store-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-write-milvus", + "-p", + url.pulsar, + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-graph-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("query-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-query-milvus", + "-p", + url.pulsar, + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "store-doc-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("store-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-write-milvus", + "-p", + url.pulsar, + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-doc-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("query-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-query-milvus", + "-p", + url.pulsar, + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/vector-store/pinecone.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/vector-store/pinecone.jsonnet new file mode 100644 index 00000000..3803bdcb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/vector-store/pinecone.jsonnet @@ -0,0 +1,160 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; + +{ + + "pinecone-cloud":: "aws", + "pinecone-region":: "us-east-1", + + "store-graph-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("store-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-write-pinecone", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "query-graph-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("query-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-query-pinecone", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "store-doc-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("store-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-write-pinecone", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "query-doc-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("query-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-query-pinecone", + "-p", + url.pulsar, + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/vector-store/qdrant.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/vector-store/qdrant.jsonnet new file mode 100644 index 00000000..06c9f0e1 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/vector-store/qdrant.jsonnet @@ -0,0 +1,250 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local qdrant = import "backends/qdrant.jsonnet"; + +qdrant + { + + "store-graph-embeddings" +: { + + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("store-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "graph-embeddings-write-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", memoryLimit) + .with_reservations("0.1", memoryReservation); + + local containerSet = engine.containers( + "store-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-graph-embeddings" +: { + + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("query-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "graph-embeddings-query-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", memoryLimit) + .with_reservations("0.1", memoryReservation); + + local containerSet = engine.containers( + "query-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "store-doc-embeddings" +: { + + "memory-limit":: "256M", + "memory-reservation":: "256M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("store-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "doc-embeddings-write-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", memoryLimit) + .with_reservations("0.1", memoryReservation); + + local containerSet = engine.containers( + "store-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-doc-embeddings" +: { + + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("query-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "doc-embeddings-query-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", memoryLimit) + .with_reservations("0.1", memoryReservation); + + local containerSet = engine.containers( + "query-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + }, + + "store-row-embeddings" +: { + + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("store-row-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "row-embeddings-write-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", memoryLimit) + .with_reservations("0.1", memoryReservation); + + local containerSet = engine.containers( + "store-row-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-row-embeddings" +: { + + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("query-row-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "row-embeddings-query-qdrant", + "-p", + url.pulsar, + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", memoryLimit) + .with_reservations("0.1", memoryReservation); + + local containerSet = engine.containers( + "query-row-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/zip-readme.md b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/zip-readme.md new file mode 100644 index 00000000..0b117792 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.1/zip-readme.md @@ -0,0 +1,28 @@ + +Note! this is a subset of possible configurations, to generate your own +launch config use the config util... + +- Production: https://config-ui.demo.trustgraph.ai +- Early release: https://dev.config-ui.demo.trustgraph.ai + +The config util auto-generates deployment instructions for your +configuration, so that's the recommended way to deploy. + +---------------------------------------------------------------------------- + +These are launch configurations for TrustGraph. See https://trustgraph.ai for +the quickstart using docker compose. + +Hint for Linux: There are files here which get mounted as volumes inside +Docker Compose containers. This may trigger SELinux rules on your system, to +permit access insider the containers, use a command like this... + +chcon -Rt svirt_sandbox_file_t grafana/ prometheus/ + +The file vertexai/private.json is a placeholder for real GCP credentials if +you are using the VertexAI LLM. If you're using that in Docker Compose, +replace with your real credentials, and don't forget to permit access if you +are using Linux: + +chcon -Rt svirt_sandbox_file_t vertexai/ + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/README.md b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/README.md new file mode 100644 index 00000000..23039e9a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/README.md @@ -0,0 +1,125 @@ + +# TrustGraph template generation + +There are two utilities here: + +- `generate`: Generates a single Docker Compose launch configuration + based on configuration you provide. +- `generate-all`: Generates the release bundle for releases. You won't + need to use this unless you are managing releases. + +## `generate-all` + +Previously, this generates a full set of all vector DB / triple store / LLM +combinations, and put them in a single ZIP file. But this got out of +hand, so at the time of writing, this generates a single configuraton +using Qdrant vector DB, Ollama LLM support and Cassandra for a triple store. + +The combinations are contained withing the code, it takes two arguments: +- output ZIP file (is over-written) +- TrustGraph version number + +``` +templates/generate-all output.zip 0.18.11 +``` + +## `generate` + +This utility takes a configuration file describing the components to bundle, +and outputs a Docker Compose YAML file. + +### Input configuration + +The input configuration is a JSON file, an array of components to pull into +the configuration. For each component, there is a name and a (possibly empty) +object describing addtional parameters for that component. + +Example: + +``` +[ + { + "name": "cassandra", + "parameters": {} + }, + { + "name": "pulsar", + "parameters": {} + }, + { + "name": "qdrant", + "parameters": {} + }, + { + "name": "embeddings-hf", + "parameters": {} + }, + { + "name": "graph-rag", + "parameters": {} + }, + { + "name": "grafana", + "parameters": {} + }, + { + "name": "trustgraph", + "parameters": {} + }, + { + "name": "googleaistudio", + "parameters": { + "googleaistudio-temperature": 0.3, + "googleaistudio-max-output-tokens": 2048, + "googleaistudio-model": "gemini-1.5-pro-002" + } + }, + { + "name": "prompt-template", + "parameters": {} + }, + { + "name": "override-recursive-chunker", + "parameters": { + "chunk-size": 1000, + "chunk-overlap": 50 + } + }, + { + "name": "workbench-ui", + "parameters": {} + }, + { + "name": "agent-manager-react", + "parameters": {} + } +] +``` + +If you want to make your own configuration you could try changing the +configuration above: +- Components which are essential: pulsar, trustgraph, graph-rag, grafana, + agent-manager-react +- You need a triple store, one of: cassandra, memgraph, falkordb, neo4j +- You need a vector store, one of: qdrant, pinecone +- You need an LLM, one of: azure, azure-openai, bedrock, claude, cohere, + llamafile, ollama, openai, vertexai. +- You need an embeddings implementation, one of: embeddings-hf, + embeddings-ollama +- Optionally add the Workbench tool: workbench-ui + +Components have over-ridable parameters, look in the component definition +in `templates/components/` to see what you can override. + +### Invocation + +Two parameters: +- The output ZIP file +- The version number + +The configuration file described above is provided on standard input + +``` +templates/generate out.zip 0.18.9 < config.json +``` + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/cassandra.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/cassandra.jsonnet new file mode 100644 index 00000000..60f262d3 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/cassandra.jsonnet @@ -0,0 +1,50 @@ +local images = import "values/images.jsonnet"; + +{ + + "cassandra" +: { + + // Memory settings (can be overridden by memory-profile) + "memory-limit":: "1000M", + "memory-reservation":: "1000M", + "heap":: "300M", + + create:: function(engine) + + // Capture memory settings into locals + local memLimit = self["memory-limit"]; + local memReserv = self["memory-reservation"]; + local heap = self["heap"]; + + local vol = engine.volume("cassandra").with_size("20G"); + + local container = + engine.container("cassandra") + .with_image(images.cassandra) + .with_environment({ + JVM_OPTS: "-Xms%s -Xmx%s -Dcassandra.skip_wait_for_gossip_to_settle=0" % [ + heap, heap, + ], + }) + .with_limits("1.0", memLimit) + .with_reservations("0.5", memReserv) + .with_port(9042, 9042, "cassandra") + .with_volume_mount(vol, "/var/lib/cassandra"); + + local containerSet = engine.containers( + "cassandra", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9042, 9042, "api"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/ceph.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/ceph.jsonnet new file mode 100644 index 00000000..42406a95 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/ceph.jsonnet @@ -0,0 +1,446 @@ + +// +// This majorly does not work +// + +// This configuration fails primarily because it tries to treat Ceph +// like a stateless web app. You are currently pointing mon host to a +// generic service name (ceph-mon), but you aren't telling the Monitor +// process to assume that service identity. + +// To make this work with the "Service Name" approach across any +// engine, you need to fix the binding logic and the messenger protocol. + +// 1. The ceph.conf Fix + +// Need to enable Messenger v2 (modern) and tell the cluster to use +// the service names for its initial quorum. + +//Change this section: +// Ini, TOML + +// [global] +// fsid = %s +// mon initial members = mon0 +// mon host = ceph-mon:6789 +// ... + +// To this: +// Ini, TOML + +// [global] +// fsid = %s +// # Use the actual service names as members +// mon initial members = ceph-mon +// # Explicitly use the Service name (VIP) +// mon host = ceph-mon +// # Force modern protocol +// ms_bind_msgr2 = true +// ms_bind_msgr1 = true + +// 2. The MON Environment & Command Fix + +// This is the most critical part. Your current config sets MON_IP: +// "0.0.0.0". This causes the MON to bind to the Pod IP, which breaks +// when the Pod restarts. You must force it to bind to the Service IP. + +// Update mon_env: +// Code snippet + +// local mon_env = cluster_env + { +// CEPH_DAEMON: "MON", +// MON_NAME: "mon0", +// # Remove MON_IP: "0.0.0.0" +// # Add these: +// MON_ADDR: "ceph-mon", // This says to resolve the service name +// }; + +// Update mon_container command: You are currently wiping the MON data +// on every start (rm -rf /var/lib/ceph/mon/*). Stop doing that. If you +// wipe the data, you lose the cluster state and the OSDs will refuse to +// talk to the "new" MON. + +// Code snippet + +// .with_command([ +// "bash", "-c", +// # 1. Resolve the Service IP at runtime +// "export MON_IP=$(getent hosts ceph-mon | awk '{ print $1 }'); " + +// inject_mon_config + +// # 2. Start the daemon telling it its PUBLIC address is the Service VIP +// "exec /opt/ceph-container/bin/entrypoint.sh" +// ]) + +// 3. Why your current config "Majorly does not work" + +// The "Wipe" Logic: By running rm -rf /var/lib/ceph/mon/* in the +// MON container, you are creating a "New Cluster" every time the +// container starts. Since the OSDs store the fsid and cluster +// secrets, they will reject the "new" MON. + +// DNS Race Condition: Your OSD/MGR/RGW containers wait for +// ceph-mon DNS, which is good. However, if ceph-mon resolves to a +// Round Robin IP (multiple pods) rather than a stable ClusterIP, the +// connection will be flaky. + +// Messenger Protocol: Without ms_bind_msgr2, Ceph defaults to the +// old v1 protocol which is much more sensitive to NAT/Container IP +// mismatches. + +// SUMMARY + +// Component: ceph.conf +// Change: Add ms_bind_msgr2 = true +// Why: Supports modern container networking better. + +// Component: MON Start +// Change: Remove rm -rf +// Why: Ceph MONs must keep their database to maintain the cluster. + +// Component: MON Address +// Change: Use getent hosts ceph-mon +// Why: Forces the MON to advertise the Service VIP instead of its own Pod IP. + +// Component: MON Keyring +// Change: Ensure /etc/ceph/ceph.mon.keyring exists +// Why: MONs need their specific key to start. + +local images = import "values/images.jsonnet"; + +{ + with:: function(key, value) + self + { + ["ceph-" + key]:: value, + }, + + // Ceph credentials and cluster settings + "ceph-access-key":: "object-user", + "ceph-secret-key":: "object-password", + "ceph-cluster-id":: "ceph", + "ceph-fsid":: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", + + // Pool redundancy settings + // size: 2 = two replicas for fault tolerance + // min_size: 1 = allow degraded I/O if one OSD is down (prevents cluster freeze) + "ceph-pool-size":: "2", + "ceph-pool-min-size":: "1", + + ceph +: { + create:: function(engine) + // Pre-Shared Cryptographic Material - Config-as-Code Approach + // These keys are generated once and distributed to all daemons + // This ensures cryptographic consistency across the shared-nothing architecture + local admin_key = "AQBpxSBlAAAAABAAU99V6D8vS7Uu9y1S8W0iBg=="; + local mon_key = "AQBpxSBlAAAAABAAn7pL/pG9oT+X6vO7V1S6bg=="; + + // Ceph configuration file - rendered from Jsonnet variables + local ceph_conf = ||| + [global] + fsid = %s + mon initial members = mon0 + mon host = ceph-mon:6789 + public network = 0.0.0.0/0 + cluster network = 0.0.0.0/0 + osd pool default size = %s + osd pool default min size = %s + osd crush chooseleaf type = 0 + auth cluster required = cephx + auth service required = cephx + auth client required = cephx + ||| % [$["ceph-fsid"], $["ceph-pool-size"], $["ceph-pool-min-size"]]; + + // Admin keyring - distributed to all daemons + local admin_keyring = ||| + [client.admin] + key = %s + caps mds = "allow *" + caps mgr = "allow *" + caps mon = "allow *" + caps osd = "allow *" + ||| % [admin_key]; + + // Monitor keyring - used by MON for cluster operations + local mon_keyring = ||| + [mon.] + key = %s + caps mon = "allow *" + ||| % [mon_key]; + + // Config injection command - writes files before entrypoint + local inject_config = "printf '%s' > /etc/ceph/ceph.conf; printf '%s' > /etc/ceph/ceph.client.admin.keyring; " % [ceph_conf, admin_keyring]; + local inject_mon_config = inject_config + ("printf '%s' > /etc/ceph/ceph.mon.keyring; " % [mon_keyring]); + + // Data volumes - sized appropriately for production workloads + local vol_mon = engine.volume("ceph-mon").with_size("20G"); + local vol_mgr = engine.volume("ceph-mgr").with_size("20G"); + local vol_osd = engine.volume("ceph-osd").with_size("100G"); + local vol_rgw = engine.volume("ceph-rgw").with_size("20G"); + + // Isolated config volumes per daemon (ReadWriteOnce compatible) + // Each daemon gets its own non-shared config volume to support + // multi-node scheduling in K8s and other orchestrators + local vol_mon_config = engine.volume("ceph-mon-config").with_size("500M"); + local vol_mgr_config = engine.volume("ceph-mgr-config").with_size("500M"); + local vol_osd_config = engine.volume("ceph-osd-config").with_size("500M"); + local vol_rgw_config = engine.volume("ceph-rgw-config").with_size("500M"); + local vol_init_config = engine.volume("ceph-init-config").with_size("500M"); + + // Simplified cluster environment - Config-as-Code model + // No fetch logic needed - config is injected before entrypoint runs + local cluster_env = { + CLUSTER: $["ceph-cluster-id"], + FSID: $["ceph-fsid"], + KV_TYPE: "none", // No external coordination + }; + + // MON-specific environment + // Config-as-Code: MON uses injected config files, not fetch logic + // + // CRITICAL: MON_DATA_AVAIL="0" forces fresh cluster bootstrap + // The ceph/daemon entrypoint script (variables_stack.sh) uses this as a gate: + // - MON_DATA_AVAIL="0" -> run mkfs, create new cluster with our FSID + // - MON_DATA_AVAIL="1" -> attempt to join existing cluster (infinite probe loop) + // + // Network configuration for monmap generation + local mon_env = cluster_env + { + CEPH_DAEMON: "MON", + MON_NAME: "mon0", + MON_PORT: "6789", + MON_DATA_AVAIL: "0", + MON_IP: "0.0.0.0", + NETWORK_AUTO_DETECT: "4", + CEPH_PUBLIC_NETWORK: "0.0.0.0/0", + }; + + // Simplified daemon environments - Config-as-Code model + // All daemons receive config via injection, not fetch from MON + // This eliminates "static mode" errors and networking complexity + + // MGR-specific environment + local mgr_env = cluster_env + { + CEPH_DAEMON: "MGR", + MGR_NAME: "mgr0", + }; + + // OSD-specific environment + local osd_env = cluster_env + { + CEPH_DAEMON: "OSD", + OSD_TYPE: "directory", + }; + + // RGW-specific environment + local rgw_env = cluster_env + { + CEPH_DAEMON: "RGW", + RGW_NAME: "rgw0", + RGW_FRONTEND_PORT: "7480", + }; + + // MON (Monitor) container - cluster state and quorum + // Config-as-Code: Injects pre-shared keys before entrypoint + // CRITICAL: Wipes /var/lib/ceph/mon/* on every start to force fresh bootstrap + // This ensures MON always uses our FSID and doesn't inherit stale cluster state + local mon_container = + engine.container("ceph-mon") + .with_image(images.ceph) + .with_environment(mon_env) + .with_command([ + "bash", "-c", + "rm -rf /var/lib/ceph/mon/*; " + + inject_mon_config + + "exec /opt/ceph-container/bin/entrypoint.sh" + ]) + .with_limits("1.0", "1536M") + .with_reservations("0.5", "1024M") + .with_port(6789, 6789, "mon") + .with_port(3300, 3300, "mon-msgr2") + .with_volume_mount(vol_mon, "/var/lib/ceph/mon") + .with_volume_mount(vol_mon_config, "/etc/ceph"); + + // MGR (Manager) container - cluster management and dashboard + // Config-as-Code: Uses injected config files with pre-shared keys + // DNS wait ensures MON is available before MGR connects + local mgr_container = + engine.container("ceph-mgr") + .with_image(images.ceph) + .with_environment(mgr_env) + .with_command([ + "bash", "-c", + "until getent hosts ceph-mon; do echo 'Waiting for MON DNS...'; sleep 2; done; " + + inject_config + + "exec /opt/ceph-container/bin/entrypoint.sh" + ]) + .with_limits("1.0", "1536M") + .with_reservations("0.5", "1024M") + .with_port(7000, 7000, "mgr") + .with_port(8443, 8443, "dashboard") + .with_port(9283, 9283, "prometheus") + .with_volume_mount(vol_mgr, "/var/lib/ceph/mgr") + .with_volume_mount(vol_mgr_config, "/etc/ceph"); + + // OSD (Object Storage Daemon) - actual data storage + // Config-as-Code: Uses injected config files with pre-shared keys + // Increased resources to prevent OOM during recovery operations + // DNS wait ensures MON is available before OSD connects + local osd_container = + engine.container("ceph-osd") + .with_image(images.ceph) + .with_environment(osd_env) + .with_command([ + "bash", "-c", + "until getent hosts ceph-mon; do echo 'Waiting for MON DNS...'; sleep 2; done; " + + inject_config + + "exec /opt/ceph-container/bin/entrypoint.sh" + ]) + .with_limits("2.0", "4096M") + .with_reservations("1.0", "2048M") + .with_port(6800, 6800, "osd") + .with_volume_mount(vol_osd, "/var/lib/ceph/osd") + .with_volume_mount(vol_osd_config, "/etc/ceph"); + + // RGW (RADOS Gateway) - S3 API endpoint + // Config-as-Code: Uses injected config files with pre-shared keys + // DNS wait ensures MON is available before RGW connects + local rgw_container = + engine.container("ceph-rgw") + .with_image(images.ceph) + .with_environment(rgw_env) + .with_command([ + "bash", "-c", + "until getent hosts ceph-mon; do echo 'Waiting for MON DNS...'; sleep 2; done; " + + inject_config + + "exec /opt/ceph-container/bin/entrypoint.sh" + ]) + .with_limits("1.0", "1536M") + .with_reservations("0.5", "1024M") + .with_port(7480, 7480, "s3") + .with_volume_mount(vol_rgw, "/var/lib/ceph/radosgw") + .with_volume_mount(vol_rgw_config, "/etc/ceph"); + + // Init container - one-time S3 user provisioning + // IMPORTANT: This container exits with code 0 after completion + // Orchestrator must NOT restart it (use K8s Job or Compose restart: "no") + // Config-as-Code: Uses injected config to run radosgw-admin commands + local init_container = + engine.container("ceph-init") + .with_image(images.ceph) + .with_environment({ + CLUSTER: $["ceph-cluster-id"], + FSID: $["ceph-fsid"], + KV_TYPE: "none", + RGW_ACCESS_KEY: $["ceph-access-key"], + RGW_SECRET_KEY: $["ceph-secret-key"], + }) + .with_limits("0.5", "512M") + .with_reservations("0.25", "256M") + .with_volume_mount(vol_init_config, "/etc/ceph") + .with_command([ + "bash", "-c", + inject_config + ||| + set -e + + # Wait for cluster health + echo "Waiting for Ceph cluster to be healthy..." + MAX_ATTEMPTS=60 + ATTEMPT=0 + until ceph --cluster ${CLUSTER} health 2>/dev/null | grep -q "HEALTH_OK\|HEALTH_WARN"; do + ATTEMPT=$((ATTEMPT+1)) + if [ $ATTEMPT -ge $MAX_ATTEMPTS ]; then + echo "ERROR: Cluster failed to become healthy after ${MAX_ATTEMPTS} attempts" + exit 1 + fi + echo "Attempt ${ATTEMPT}/${MAX_ATTEMPTS}: Cluster not ready, retrying in 5s..." + sleep 5 + done + echo "Cluster is healthy." + + # Wait for RGW availability + echo "Waiting for RGW to be ready..." + ATTEMPT=0 + until curl -sf http://ceph-rgw:7480 >/dev/null 2>&1; do + ATTEMPT=$((ATTEMPT+1)) + if [ $ATTEMPT -ge $MAX_ATTEMPTS ]; then + echo "ERROR: RGW failed to become ready after ${MAX_ATTEMPTS} attempts" + exit 1 + fi + echo "Attempt ${ATTEMPT}/${MAX_ATTEMPTS}: RGW not ready, retrying in 5s..." + sleep 5 + done + echo "RGW is ready." + + # Idempotent S3 user creation + echo "Provisioning S3 user: ${RGW_ACCESS_KEY}" + if radosgw-admin --cluster ${CLUSTER} user info --uid="${RGW_ACCESS_KEY}" >/dev/null 2>&1; then + echo "User ${RGW_ACCESS_KEY} already exists, skipping creation." + else + echo "Creating new S3 user: ${RGW_ACCESS_KEY}" + radosgw-admin --cluster ${CLUSTER} user create \ + --uid="${RGW_ACCESS_KEY}" \ + --display-name="Object Storage User" \ + --access-key="${RGW_ACCESS_KEY}" \ + --secret-key="${RGW_SECRET_KEY}" + echo "S3 user created successfully." + fi + + echo "Initialization complete. Exiting." + exit 0 + |||, + ]); + + // Container sets - each daemon gets its own for K8s node distribution + local mon_containerSet = engine.containers("ceph-mon", [mon_container]); + local mgr_containerSet = engine.containers("ceph-mgr", [mgr_container]); + local osd_containerSet = engine.containers("ceph-osd", [osd_container]); + local rgw_containerSet = engine.containers("ceph-rgw", [rgw_container]); + local init_containerSet = engine.containers("ceph-init", [init_container]); + + // Services - expose daemon ports for inter-daemon communication + local mon_service = + engine.service(mon_containerSet) + .with_port(6789, 6789, "mon") + .with_port(3300, 3300, "mon-msgr2"); + + local mgr_service = + engine.service(mgr_containerSet) + .with_port(7000, 7000, "mgr") + .with_port(8443, 8443, "dashboard") + .with_port(9283, 9283, "prometheus"); + + local osd_service = + engine.service(osd_containerSet) + .with_port(6800, 6800, "osd"); + + local rgw_service = + engine.service(rgw_containerSet) + .with_port(7480, 7480, "s3"); + + engine.resources([ + + // Data volumes + vol_mon, + vol_mgr, + vol_osd, + vol_rgw, + + // Config volumes (isolated, no sharing) + vol_mon_config, + vol_mgr_config, + vol_osd_config, + vol_rgw_config, + vol_init_config, + + // Container sets + mon_containerSet, + mgr_containerSet, + osd_containerSet, + rgw_containerSet, + init_containerSet, + + // Services + mon_service, + mgr_service, + osd_service, + rgw_service, + + ]) + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/falkordb.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/falkordb.jsonnet new file mode 100644 index 00000000..1d4176d8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/falkordb.jsonnet @@ -0,0 +1,38 @@ +local images = import "values/images.jsonnet"; + +{ + + "falkordb" +: { + + create:: function(engine) + + local vol = engine.volume("falkordb").with_size("20G"); + + local container = + engine.container("falkordb") + .with_image(images.falkordb) + .with_limits("1.0", "768M") + .with_reservations("0.5", "768M") + .with_port(6379, 6379, "api") + .with_port(3010, 3000, "ui") + .with_volume_mount(vol, "/data"); + + local containerSet = engine.containers( + "falkordb", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(6379, 6379, "api") + .with_port(3010, 3010, "ui"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/garage.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/garage.jsonnet new file mode 100644 index 00000000..9d339bfb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/garage.jsonnet @@ -0,0 +1,249 @@ +local images = import "values/images.jsonnet"; + +{ + + garage +: { + + // Garage S3 credentials - these are the actual access key ID and secret key + // Access Key ID must be in format: GK + 24 hex characters (12 bytes) + // Secret Key must be 64 hex characters (32 bytes) + // For production, generate secure random values and override these defaults + "access-key":: "GK000000000000000000000001", + "secret-key":: "b171f00be9be4c32c734f4c05fe64c527a8ab5eb823b376cfa8c2531f70fc427", + "rpc-secret":: "bbba746a9e289bad64a9e7a36a4299dac8d6e0b8cc2a6c2937fe756df4492008", + // For a production system, override this value + "admin-token":: "batts-rockhearted-unpartially", + region:: "garage", + "replication-factor":: "1", // Set to 1 for single-node, 3 for production + + // Storage volume sizes + "meta-size":: "2G", // Metadata volume size + "data-size":: "5G", // Data volume size (also used for cluster layout capacity) + + create:: function(engine) + + local accessKey = self["access-key"]; + local secretKey = self["secret-key"]; + local rpcSecret = self["rpc-secret"]; + local adminToken = self["admin-token"]; + local region = self.region; + local replicationFactor = self["replication-factor"]; + local metaSize = self["meta-size"]; + local dataSize = self["data-size"]; + + // Garage daemon configuration file - TOML format + local garage_conf = ||| + metadata_dir = "/var/lib/garage/meta" + data_dir = "/var/lib/garage/data" + + db_engine = "lmdb" + + replication_factor = %s + + compression_level = 1 + + rpc_bind_addr = "[::]:3901" + rpc_public_addr = "[::]:3901" + rpc_secret = "%s" + + [s3_api] + s3_region = "%s" + api_bind_addr = "[::]:3900" + root_domain = ".s3.garage.local" + + [s3_web] + bind_addr = "[::]:3902" + root_domain = ".web.garage.local" + index = "index.html" + + [k2v_api] + api_bind_addr = "[::]:3904" + + [admin] + api_bind_addr = "[::]:3903" + admin_token = "%s" + ||| % [replicationFactor, rpcSecret, region, adminToken]; + + // Config volume - contains the rendered garage.toml + local cfgVol = engine.configVolume( + "garage-cfg", "garage", + { + "garage.toml": garage_conf, + } + ); + + // Volumes - Garage stores metadata and data separately + local vol_meta = engine.volume("garage-meta").with_size(metaSize); + local vol_data = engine.volume("garage-data").with_size(dataSize); + + // Main Garage daemon container + local garage_container = + engine.container("garage") + .with_image(images.garage) + .with_command([ + "/garage", "-c", "/etc/garage/garage.toml", "server" + ]) + .with_environment({ + RUST_LOG: "garage=info", + }) + .with_limits("1.0", "512M") + .with_reservations("0.5", "512M") + .with_port(3900, 3900, "s3-api") + .with_port(3901, 3901, "rpc") + .with_port(3902, 3902, "web") + .with_port(3903, 3903, "admin") + .with_port(3904, 3904, "k2v") + .with_volume_mount(cfgVol, "/etc/garage/") + .with_volume_mount(vol_meta, "/var/lib/garage/meta") + .with_volume_mount(vol_data, "/var/lib/garage/data"); + + // Init container - configures cluster layout and creates S3 credentials + // IMPORTANT: This container exits with code 0 after completion + // Orchestrator must NOT restart it (use K8s Job or Compose restart: "no") + // Uses Alpine base image since garage container has no shell + local init_container = + engine.container("garage-init") + .with_image("docker.io/alpine:3.23.2") + .with_environment({ + GARAGE_ACCESS_KEY: accessKey, + GARAGE_SECRET_KEY: secretKey, + GARAGE_REGION: region, + GARAGE_ADMIN_TOKEN: adminToken, + GARAGE_RPC_SECRET: rpcSecret, + GARAGE_DATA_SIZE: dataSize, + }) + .with_limits("0.5", "256M") + .with_reservations("0.25", "128M") + .with_volume_mount(cfgVol, "/etc/garage/") + .with_command([ + "sh", "-c", ||| + set -e + + # Install required tools + echo "Installing curl, jq and downloading garage CLI..." + apk add --no-cache curl jq + + # Download garage binary (v2.1.0) for remote management + curl -fsSL "https://garagehq.deuxfleurs.fr/_releases/v2.1.0/x86_64-unknown-linux-musl/garage" \ + -o /usr/local/bin/garage + chmod +x /usr/local/bin/garage + + echo "Waiting for Garage daemon to be ready..." + MAX_ATTEMPTS=60 + ATTEMPT=0 + # Wait for /health to respond (even 503 is fine - means daemon is up) + until curl -s http://garage:3903/health >/dev/null 2>&1; do + ATTEMPT=$((ATTEMPT+1)) + if [ $ATTEMPT -ge $MAX_ATTEMPTS ]; then + echo "ERROR: Garage failed to become ready after ${MAX_ATTEMPTS} attempts" + exit 1 + fi + echo "Attempt ${ATTEMPT}/${MAX_ATTEMPTS}: Garage not ready, retrying in 2s..." + sleep 2 + done + echo "Garage daemon is ready." + + # Get the node ID via v2 Admin API + echo "Getting Garage node ID via Admin API..." + curl -s -H "Authorization: Bearer ${GARAGE_ADMIN_TOKEN}" \ + "http://garage:3903/v2/GetNodeInfo?node=self" > /tmp/garage-node-info.json + + # Extract node ID from response (the key in success map is the node ID) + NODE_ID=$(jq -r '.success | to_entries[0].value.nodeId' /tmp/garage-node-info.json) + echo "Node ID: ${NODE_ID}" + + if [ -z "$NODE_ID" ] || [ "$NODE_ID" = "null" ]; then + echo "ERROR: Failed to retrieve node ID" + exit 1 + fi + + # ===== LAYOUT MANAGEMENT VIA REMOTE RPC ===== + # Use garage CLI with -h and -s flags to connect remotely + # -h requires format: @: + # -s provides the RPC secret + + # Construct full RPC identifier + RPC_HOST="${NODE_ID}@garage:3901" + echo "RPC Host: ${RPC_HOST}" + + # Check current layout to see if node is already assigned (idempotent) + echo "Checking current cluster layout..." + LAYOUT_OUTPUT=$(garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" layout show 2>&1) + + # Check if node already has a role assigned + # Layout output shows abbreviated node ID (first 16 chars) + NODE_ID_SHORT="${NODE_ID:0:16}" + if echo "$LAYOUT_OUTPUT" | grep -q "$NODE_ID_SHORT"; then + echo "Node ${NODE_ID_SHORT}... already assigned in layout, skipping." + else + echo "Assigning node to cluster layout..." + # Assign node to zone dc1 with configured capacity + garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" \ + layout assign ${NODE_ID} -z dc1 -c ${GARAGE_DATA_SIZE} + + echo "Applying layout configuration..." + # Get current staged version and apply + garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" \ + layout apply --version 1 + + echo "Layout configured successfully." + # Wait for layout to stabilize + sleep 5 + fi + + # ===== KEY MANAGEMENT VIA REMOTE RPC ===== + + # Check if key already exists (idempotent) + # GARAGE_ACCESS_KEY is already a valid Garage Key ID (GK + 24 hex chars) + if garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" key info "${GARAGE_ACCESS_KEY}" >/dev/null 2>&1; then + echo "Access key ${GARAGE_ACCESS_KEY} already exists, skipping creation." + else + echo "Importing S3 access key: ${GARAGE_ACCESS_KEY}" + + # Import key with the provided credentials (both already in valid Garage format) + # GARAGE_ACCESS_KEY = Key ID (GK + 24 hex chars) + # GARAGE_SECRET_KEY = Secret (64 hex chars) + garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" \ + key import "${GARAGE_ACCESS_KEY}" "${GARAGE_SECRET_KEY}" --yes + + echo "Access key imported successfully." + fi + + # Grant permissions to the key + echo "Granting create-bucket permission to key..." + garage -h "${RPC_HOST}" -s "${GARAGE_RPC_SECRET}" \ + key allow --create-bucket "${GARAGE_ACCESS_KEY}" + + echo "" + echo "Garage initialization complete!" + echo "S3 Endpoint: http://garage:3900" + echo "Region: ${GARAGE_REGION}" + exit 0 + |||, + ]); + + // Container sets + local garage_containerSet = engine.containers("garage", [garage_container]); + local init_containerSet = engine.containers("garage-init", [init_container]); + + // Service - expose Garage ports + local garage_service = + engine.service(garage_containerSet) + .with_port(3900, 3900, "s3-api") + .with_port(3901, 3901, "rpc") + .with_port(3902, 3902, "web") + .with_port(3903, 3903, "admin") + .with_port(3904, 3904, "k2v"); + + engine.resources([ + cfgVol, + vol_meta, + vol_data, + garage_containerSet, + init_containerSet, + garage_service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/memgraph.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/memgraph.jsonnet new file mode 100644 index 00000000..eeed2e4e --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/memgraph.jsonnet @@ -0,0 +1,70 @@ +local images = import "values/images.jsonnet"; + +{ + + "memgraph" +: { + + create:: function(engine) + + local vol = engine.volume("memgraph").with_size("20G"); + + local container = + engine.container("memgraph") + .with_image(images.memgraph_mage) + .with_environment({ + MEMGRAPH: "--storage-properties-on-edges=true --storage-enable-edges-metadata=true" + }) + .with_limits("1.0", "1000M") + .with_reservations("0.5", "1000M") + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2") + .with_volume_mount(vol, "/var/lib/memgraph"); + + local containerSet = engine.containers( + "memgraph", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + + "memgraph-lab" +: { + + create:: function(engine) + + local container = + engine.container("lab") + .with_image(images.memgraph_lab) + .with_environment({ + QUICK_CONNECT_MG_HOST: "memgraph", + QUICK_CONNECT_MG_PORT: "7687", + }) + .with_limits("1.0", "512M") + .with_reservations("0.5", "512M") + .with_port(3010, 3000, "http"); + + local containerSet = engine.containers( + "lab", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(3010, 3010, "http"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/milvus.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/milvus.jsonnet new file mode 100644 index 00000000..5bed3820 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/milvus.jsonnet @@ -0,0 +1,89 @@ +local images = import "values/images.jsonnet"; +local minio = import "backends/minio.jsonnet"; + +minio { + + etcd +: { + + create:: function(engine) + + local vol = engine.volume("etcd").with_size("20G"); + + local container = + engine.container("etcd") + .with_image(images.etcd) + .with_command([ + "etcd", + "-advertise-client-urls=http://127.0.0.1:2379", + "-listen-client-urls", + "http://0.0.0.0:2379", + "--data-dir", + "/etcd", + ]) + .with_environment({ + ETCD_AUTO_COMPACTION_MODE: "revision", + ETCD_AUTO_COMPACTION_RETENTION: "1000", + ETCD_QUOTA_BACKEND_BYTES: "4294967296", + ETCD_SNAPSHOT_COUNT: "50000" + }) + .with_limits("1.0", "128M") + .with_reservations("0.25", "128M") + .with_port(2379, 2379, "api") + .with_volume_mount(vol, "/etcd"); + + local containerSet = engine.containers( + "etcd", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(2379, 2379, "api"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + + milvus +: { + + create:: function(engine) + + local vol = engine.volume("milvus").with_size("20G"); + + local container = + engine.container("milvus") + .with_image(images.milvus) + .with_command([ + "milvus", "run", "standalone" + ]) + .with_environment({ + ETCD_ENDPOINTS: "etcd:2379", + MINIO_ADDRESS: "minio:9000", + }) + .with_limits("1.0", "256M") + .with_reservations("0.5", "256M") + .with_port(9091, 9091, "api") + .with_port(19530, 19530, "api2") + .with_volume_mount(vol, "/var/lib/milvus"); + + local containerSet = engine.containers( + "milvus", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9091, 9091, "api") + .with_port(19530, 19530, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/minio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/minio.jsonnet new file mode 100644 index 00000000..b38bb81f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/minio.jsonnet @@ -0,0 +1,48 @@ +local images = import "values/images.jsonnet"; + +{ + + minio +: { + + create:: function(engine) + + local vol = engine.volume("minio-data").with_size("20G"); + + local container = + engine.container("minio") + .with_image(images.minio) + .with_command([ + "minio", + "server", + "/minio_data", + "--console-address", + ":9001", + ]) + .with_environment({ + MINIO_ROOT_USER: "minioadmin", + MINIO_ROOT_PASSWORD: "minioadmin", + }) + .with_limits("0.5", "128M") + .with_reservations("0.25", "128M") + .with_port(9000, 9000, "api") + .with_port(9001, 9001, "console") + .with_volume_mount(vol, "/minio_data"); + + local containerSet = engine.containers( + "minio", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9000, 9000, "api") + .with_port(9001, 9001, "console"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/neo4j.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/neo4j.jsonnet new file mode 100644 index 00000000..46c61e0f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/neo4j.jsonnet @@ -0,0 +1,46 @@ +local images = import "values/images.jsonnet"; + +{ + + "neo4j" +: { + + create:: function(engine) + + local vol = engine.volume("neo4j").with_size("20G"); + + local container = + engine.container("neo4j") + .with_image(images.neo4j) + .with_environment({ + NEO4J_AUTH: "neo4j/password", + NEO4J_server_memory_pagecache_size: "512m", + NEO4J_server_memory_heap_max__size: "512m", + // NEO4J_server_bolt_listen__address: "0.0.0.0:7687", + // NEO4J_server_default__listen__address: "0.0.0.0", + // NEO4J_server_http_listen__address: "0.0.0.0:7474", + }) + .with_limits("1.0", "1536M") + .with_reservations("0.5", "1536M") + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2") + .with_volume_mount(vol, "/data"); + + local containerSet = engine.containers( + "neo4j", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7474, 7474, "api") + .with_port(7687, 7687, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/pinecone.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/pinecone.jsonnet new file mode 100644 index 00000000..2bef70fb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/pinecone.jsonnet @@ -0,0 +1,45 @@ +local images = import "values/images.jsonnet"; + +{ + + "pinecone" +: { + + create:: function(engine) + + local container = + engine.container("pinecone") + .with_image(images.pinecone) + .with_environment({ + + PORT: "5080", + + INDEX_TYPE: "serverless", + + // Dimension is fixed, 384 is right for miniLM + // sentence embedding + DIMENSION: 384, + + METRIC: "cosine" + + }) + .with_limits("1.0", "256M") + .with_reservations("0.5", "256M") + .with_port(5080, 5080, "api"); + + local containerSet = engine.containers( + "pinecone", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(5080, 5080, "api"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/qdrant.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/qdrant.jsonnet new file mode 100644 index 00000000..2e6761f8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/backends/qdrant.jsonnet @@ -0,0 +1,65 @@ +local images = import "values/images.jsonnet"; + +{ + + "qdrant" +: { + + // Memory settings (can be overridden by memory-profile) + "memory-limit":: "1024M", + "memory-reservation":: "1024M", + + // Mmap settings for low-memory mode (trades latency for memory) + // Set to null to disable, or string value to enable + "memmap-threshold-kb":: null, + "on-disk-payload":: null, + + create:: function(engine) + + // Capture memory settings into locals + local memLimit = self["memory-limit"]; + local memReserv = self["memory-reservation"]; + local mmapThreshold = self["memmap-threshold-kb"]; + local onDiskPayload = self["on-disk-payload"]; + + local vol = engine.volume("qdrant").with_size("20G"); + + // Build environment with optional mmap settings + local baseEnv = {}; + local env = baseEnv + + (if mmapThreshold != null then { + QDRANT__STORAGE__MEMMAP_THRESHOLD_KB: mmapThreshold, + } else {}) + + (if onDiskPayload != null then { + QDRANT__STORAGE__ON_DISK_PAYLOAD: onDiskPayload, + } else {}); + + local container = + engine.container("qdrant") + .with_image(images.qdrant) + .with_limits("1.0", memLimit) + .with_reservations("0.5", memReserv) + .with_port(6333, 6333, "api") + .with_port(6334, 6334, "api2") + .with_volume_mount(vol, "/qdrant/storage") + + (if std.length(env) > 0 then { + environment+: env, + } else {}); + + local containerSet = engine.containers( + "qdrant", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(6333, 6333, "api") + .with_port(6334, 6334, "api2"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/components.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/components.jsonnet new file mode 100644 index 00000000..132a9361 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/components.jsonnet @@ -0,0 +1,122 @@ +{ + + // Essentials + "trustgraph-base": import "core/trustgraph.jsonnet", + "rev-gateway": import "core/rev-gateway.jsonnet", + "pulsar": import "pubsub/pulsar.jsonnet", + "rabbitmq": import "pubsub/rabbitmq.jsonnet", + + // LLMs + "azure": import "llm/azure.jsonnet", + "azure-openai": import "llm/azure-openai.jsonnet", + "bedrock": import "llm/bedrock.jsonnet", + "claude": import "llm/claude.jsonnet", + "cohere": import "llm/cohere.jsonnet", + "googleaistudio": import "llm/googleaistudio.jsonnet", + "llamafile": import "llm/llamafile.jsonnet", + "lmstudio": import "llm/lmstudio.jsonnet", + "mistral": import "llm/mistral.jsonnet", + "ollama": import "llm/ollama.jsonnet", + "openai": import "llm/openai.jsonnet", + "vertexai": import "llm/vertexai.jsonnet", + "tgi": import "llm/tgi.jsonnet", + "vllm": import "llm/vllm.jsonnet", + + // Embeddings + "embeddings-ollama": import "embeddings/embeddings-ollama.jsonnet", + "embeddings-hf": import "embeddings/embeddings-hf.jsonnet", + "embeddings-fastembed": import "embeddings/embeddings-fastembed.jsonnet", + + // OCR options + "ocr": import "ocr/ocr.jsonnet", + "mistral-ocr": import "ocr/mistral-ocr.jsonnet", + + // Vector stores + "vector-store-milvus": import "vector-store/milvus.jsonnet", + "vector-store-qdrant": import "vector-store/qdrant.jsonnet", + "vector-store-pinecone": import "vector-store/pinecone.jsonnet", + + // Triples stores + "triple-store-cassandra": import "triple-store/cassandra.jsonnet", + "triple-store-neo4j": import "triple-store/neo4j.jsonnet", + "triple-store-falkordb": import "triple-store/falkordb.jsonnet", + "triple-store-memgraph": import "triple-store/memgraph.jsonnet", + + // Row stores + "row-store-cassandra": import "row-store/cassandra.jsonnet", + + // Observability support + "grafana": import "monitoring/grafana.jsonnet", + "loki": import "monitoring/loki.jsonnet", + + // Pulsar manager is a UI for Pulsar. Uses a LOT of memory + "pulsar-manager": import "pulsar/pulsar-manager.jsonnet", + + "override-recursive-chunker": import "core/chunker-recursive.jsonnet", + + // The prompt manager + "prompt-overrides": import "core/prompt-overrides.jsonnet", + + // Extra MCP services + "ddg-mcp-server": import "mcp/ddg-mcp-server.jsonnet", + + // Does nothing. But, can be a hack to overwrite parameters + "null": {}, + + // Passthrough: returns parameters directly, preserving +: merge syntax + // Also supports JSON-safe routing with prefixed parameters like "cassandra-heap" + "override": { + local route = function(target) + function(prefix, k, v) + local suffix = std.substr(k, std.length(prefix), std.length(k) - std.length(prefix)); + { [target] +: { [suffix]:: v } }, + + local routes = { + "cassandra-": route("cassandra"), + "pulsar-": route("pulsar"), + "qdrant-": route("qdrant"), + "api-gateway-": route("api-gateway"), + "librarian-": route("librarian"), + }, + + with_params:: function(pars) + std.foldl( + function(acc, k) + local matchingPrefixes = [p for p in std.objectFields(routes) if std.startsWith(k, p)]; + if std.length(matchingPrefixes) > 0 then + local prefix = matchingPrefixes[0]; + acc + routes[prefix](prefix, k, pars[k]) + else + acc + { [k]:: pars[k] }, + std.objectFields(pars), + {} + ), + }, + + // Memory profiles + "memory-profile-low": import "profiles/memory-profile-low.jsonnet", + + // Model hosting + + "hosting-intel-battlemage-vllm": + import "model-hosting/intel-battlemage-vllm.jsonnet", + + "hosting-cpu-tgi": + import "model-hosting/cpu-tgi.jsonnet", + + "hosting-intel-xpu-tgi": + import "model-hosting/intel-xpu-tgi.jsonnet", + + "hosting-intel-gaudi-tgi": + import "model-hosting/intel-gaudi-tgi.jsonnet", + + "hosting-intel-xpu-vllm": + import "model-hosting/intel-xpu-vllm.jsonnet", + + "hosting-intel-gaudi-vllm": + import "model-hosting/intel-gaudi-vllm.jsonnet", + + "hosting-nvidia-gpu-vllm": + import "model-hosting/nvidia-gpu-vllm.jsonnet", + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/agent-manager-react.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/agent-manager-react.jsonnet new file mode 100644 index 00000000..2d3d3340 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/agent-manager-react.jsonnet @@ -0,0 +1,46 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "agent-manager" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("agent-manager") + .with_image(images.trustgraph_flow) + .with_command([ + "agent-manager-react", + ] + $["pub-sub-args"] + [ + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "agent-manager", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/agent-orchestrator.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/agent-orchestrator.jsonnet new file mode 100644 index 00000000..60cfaed9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/agent-orchestrator.jsonnet @@ -0,0 +1,46 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "agent-manager" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "256M", + "memory-reservation":: "256M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("agent-manager") + .with_image(images.trustgraph_flow) + .with_command([ + "agent-orchestrator", + ] + $["pub-sub-args"] + [ + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "agent-manager", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/chunker-recursive.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/chunker-recursive.jsonnet new file mode 100644 index 00000000..cfdd571e --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/chunker-recursive.jsonnet @@ -0,0 +1,54 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; + +{ + + "chunk-size":: 2000, + "chunk-overlap":: 100, + + "chunker" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("chunker") + .with_image(images.trustgraph_flow) + .with_command([ + "chunker-recursive", + ] + $["pub-sub-args"] + [ + "--chunk-size", + std.toString($["chunk-size"]), + "--chunk-overlap", + std.toString($["chunk-overlap"]), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "chunker", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/configuration.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/configuration.jsonnet new file mode 100644 index 00000000..e5b979ce --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/configuration.jsonnet @@ -0,0 +1,56 @@ + +// This puts the default configuration together. References many things, +// flow classes, a default flow, token costs, prompts, agent tools + +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "init-trustgraph" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local cfgVol = engine.configVolume( + "trustgraph-cfg", "trustgraph", + { + "config.json": importstr "trustgraph/config.json", + } + ); + + local container = + engine.container("init-trustgraph") + .with_image(images.trustgraph_flow) + .with_command( + [ + "tg-init-trustgraph", + ] + $["pub-sub-args"] + $["pub-sub-init-args"] + [ + "--config-file", + "/trustgraph/config.json", + ] + ) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation) + .with_volume_mount(cfgVol, "/trustgraph/"); + + local containerSet = engine.containers( + "init-trustgraph", [ container ] + ); + + engine.resources([ + cfgVol, + containerSet, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/document-rag.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/document-rag.jsonnet new file mode 100644 index 00000000..acb2095a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/document-rag.jsonnet @@ -0,0 +1,90 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; + +{ + + "document-rag" +: { + + "doc-limit":: 20, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local docLimit = self["doc-limit"]; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("document-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "document-rag", + ] + $["pub-sub-args"] + [ + "--doc-limit", + std.toString(docLimit), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "document-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "document-embeddings" +: { + + "cpu-limit":: "1.0", + "cpu-reservation":: "0.5", + "memory-limit":: "512M", + "memory-reservation":: "512M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("document-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "document-embeddings", +] + $["pub-sub-args"] + [ + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "document-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/graph-rag.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/graph-rag.jsonnet new file mode 100644 index 00000000..5c4385c9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/graph-rag.jsonnet @@ -0,0 +1,285 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "kg-extract-definitions" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-extract-definitions") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-definitions", +] + $["pub-sub-args"] + [ + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-extract-definitions", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-relationships" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-extract-relationships") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-relationships", +] + $["pub-sub-args"] + [ + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-extract-relationships", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-agent" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-extract-agent") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-agent", +] + $["pub-sub-args"] + [ + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-extract-agent", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-ontology" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "300M", + "memory-reservation":: "300M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-extract-ontology") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-ontology", +] + $["pub-sub-args"] + [ + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-extract-ontology", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "graph-rag" +: { + + concurrency:: 1, + "entity-limit":: 50, + "triple-limit":: 30, + "edge-limit":: 30, + "edge-score-limit":: 10, + "max-subgraph-size":: 100, + "max-path-length":: 2, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local entityLimit = self["entity-limit"]; + local tripleLimit = self["triple-limit"]; + local edgeLimit = self["edge-limit"]; + local edgeScoreLimit = self["edge-score-limit"]; + local maxSubgraphSize = self["max-subgraph-size"]; + local maxPathLength = self["max-path-length"]; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("graph-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "graph-rag", + ] + $["pub-sub-args"] + [ + "--concurrency", + std.toString(concurrency), + "--entity-limit", + std.toString(entityLimit), + "--triple-limit", + std.toString(tripleLimit), + "--edge-limit", + std.toString(edgeLimit), + "--edge-score-limit", + std.toString(edgeScoreLimit), + "--max-subgraph-size", + std.toString(maxSubgraphSize), + "--max-path-length", + std.toString(maxPathLength), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "graph-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "graph-embeddings" +: { + + "cpu-limit":: "1.0", + "cpu-reservation":: "0.5", + "memory-limit":: "256M", + "memory-reservation":: "256M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "graph-embeddings", + ] + $["pub-sub-args"] + [ + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/librarian.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/librarian.jsonnet new file mode 100644 index 00000000..6278edcf --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/librarian.jsonnet @@ -0,0 +1,57 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local garage = import "backends/garage.jsonnet"; +local cassandra = import "backends/cassandra.jsonnet"; + +{ + + "librarian" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "256M", + "memory-reservation":: "256M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("librarian") + .with_image(images.trustgraph_flow) + .with_command([ + "librarian", + ] + $["pub-sub-args"] + [ + "--log-level", + $["log-level"], + "--object-store-endpoint", + url.object_store, + "--object-store-access-key", + $.garage["access-key"], + "--object-store-secret-key", + $.garage["secret-key"], + "--object-store-region", + $.garage.region, + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "librarian", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +// Garage and Cassandra are used by the Librarian +} + garage + cassandra + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/mcp-server.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/mcp-server.jsonnet new file mode 100644 index 00000000..9bf7e3bd --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/mcp-server.jsonnet @@ -0,0 +1,54 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "mcp-server" +: { + + port:: 8000, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "256M", + "memory-reservation":: "256M", + + create:: function(engine) + + local port = self.port; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local envSecrets = engine.envSecrets("mcp-server-secret") + .with_env_var("MCP_SERVER_SECRET", "mcp-server-secret") + .with_env_var("GATEWAY_SECRET", "gateway-secret"); + + local container = + engine.container("mcp-server") + .with_image(images.trustgraph_mcp) + .with_command([ + "mcp-server", + "--port", + std.toString(port), + ]) + .with_env_var_secrets(envSecrets) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation) + .with_port(port, port, "mcp"); + + local containerSet = engine.containers( + "mcp-server", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(port, port, "mcp"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/prompt-overrides.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/prompt-overrides.jsonnet new file mode 100644 index 00000000..852ec09d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/prompt-overrides.jsonnet @@ -0,0 +1,24 @@ +local default_prompts = import "prompts/default-prompts.jsonnet"; + +{ + + with:: function(key, value) + if (key == "system-template") then + self + { + prompts +:: { + "system-template": value, + } + } + else + self + { + prompts +:: { + templates +:: { + [key] +:: { + prompt: value + } + } + } + }, + +} + default_prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/prompt-template.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/prompt-template.jsonnet new file mode 100644 index 00000000..23ac1442 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/prompt-template.jsonnet @@ -0,0 +1,95 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "prompt" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("prompt") + .with_image(images.trustgraph_flow) + .with_command([ + "prompt-template", + ] + $["pub-sub-args"] + [ + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "prompt", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "prompt-rag" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("prompt-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "prompt-template", + ] + $["pub-sub-args"] + [ + "--id", + "prompt-rag", + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "prompt-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/rev-gateway.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/rev-gateway.jsonnet new file mode 100644 index 00000000..39b71e31 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/rev-gateway.jsonnet @@ -0,0 +1,61 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "rev-gateway" +: { + + // Invalid, but at least means the rev-gateway won't connect to anything + // it shouldn't. + token:: "INVALID_TOKEN", + uri:: "wss://127.0.0.1/api/v1/relay?token=" + self.token, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "256M", + "memory-reservation":: "256M", + + create:: function(engine) + + local uri = self.uri; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local envSecrets = engine.envSecrets("rev-gateway-secret") + .with_env_var("REV_GATEWAY_SECRET", "rev-gateway-secret"); + + local container = + engine.container("api-gateway") + .with_image(images.trustgraph_flow) + .with_command([ + "rev-gateway", + ] + $["pub-sub-args"] + [ + "--websocket-uri", + std.toString(uri), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation) + .with_port(8000, 8000, "metrics") + .with_port(port, port, "api"); + + local containerSet = engine.containers( + "api-gateway", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics") + .with_port(port, port, "api"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/structured-data.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/structured-data.jsonnet new file mode 100644 index 00000000..06956b27 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/structured-data.jsonnet @@ -0,0 +1,206 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "nlp-query" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("nlp-query") + .with_image(images.trustgraph_flow) + .with_command([ + "nlp-query", + ] + $["pub-sub-args"] + [ + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "nlp-query", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "structured-query" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("structured-query") + .with_image(images.trustgraph_flow) + .with_command([ + "structured-query", + ] + $["pub-sub-args"] + [ + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "structured-query", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "structured-diag" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "96M", + "memory-reservation":: "96M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("structured-diag") + .with_image(images.trustgraph_flow) + .with_command([ + "structured-diag", + ] + $["pub-sub-args"] + [ + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "structured-diag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-extract-rows" +: { + + concurrency:: 1, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local concurrency = self.concurrency; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-extract-rows") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-extract-rows", + ] + $["pub-sub-args"] + [ + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-extract-rows", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "row-embeddings" +: { + + "cpu-limit":: "1.0", + "cpu-reservation":: "0.5", + "memory-limit":: "256M", + "memory-reservation":: "256M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("row-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "row-embeddings", + ] + $["pub-sub-args"] + [ + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "row-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/trustgraph.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/trustgraph.jsonnet new file mode 100644 index 00000000..571a3453 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/core/trustgraph.jsonnet @@ -0,0 +1,464 @@ +local images = import "values/images.jsonnet"; + +local config_initialiser = import "configuration.jsonnet"; +local config = import "../runtime-config/trustgraph-config.jsonnet"; +local librarian = import "librarian.jsonnet"; +local mcp_server = import "mcp-server.jsonnet"; +local workbench = import "../ui/workbench-ui.jsonnet"; +local graphrag = import "graph-rag.jsonnet"; +local documentrag = import "document-rag.jsonnet"; +local prompt_template = import "prompt-template.jsonnet"; +local agent_manager = import "agent-orchestrator.jsonnet"; +local structured_data = import "structured-data.jsonnet"; +local ddg = import "mcp/ddg-mcp-server.jsonnet"; + +// Helper to create a routing function for a target object +local route = function(target) + function(prefix, k, v) + local suffix = std.substr(k, std.length(prefix), std.length(k) - std.length(prefix)); + { [target] +: { [suffix]:: v } }; + +// Parameter prefix -> target object routing table +local routes = { + "prompt-rag-": route("prompt-rag"), + "prompt-": route("prompt"), + "text-completion-rag-": route("text-completion-rag"), + "text-completion-": route("text-completion"), + "embeddings-": route("embeddings"), + "api-gateway-": route("api-gateway"), + "chunk-": route("chunker"), + "graph-rag-": route("graph-rag"), + "graph-embeddings-": route("graph-embeddings"), + "kg-extract-definitions-": route("kg-extract-definitions"), + "kg-extract-relationships-": route("kg-extract-relationships"), + "kg-extract-agent-": route("kg-extract-agent"), + "kg-extract-ontology-": route("kg-extract-ontology"), + "kg-extract-objects-": route("kg-extract-objects"), + "garage-": route("garage"), + "config-svc-": route("config-svc"), + "document-decoder-": route("document-decoder"), + "mcp-tool-": route("mcp-tool"), + "mcp-server-": route("mcp-server"), + "metering-rag-": route("metering-rag"), + "metering-": route("metering"), + "kg-store-": route("kg-store"), + "kg-manager-": route("kg-manager"), + "librarian-": route("librarian"), + "agent-manager-": route("agent-manager"), + "document-rag-": route("document-rag"), + "document-embeddings-": route("document-embeddings"), + "rev-gateway-": route("rev-gateway"), + "nlp-query-": route("nlp-query"), + "structured-query-": route("structured-query"), + "structured-diag-": route("structured-diag"), + "init-trustgraph-": route("init-trustgraph"), +}; + +// Find longest matching prefix (most specific first) +local findRoute = function(k) + local prefixes = std.objectFields(routes); + local matching = std.filter(function(p) std.startsWith(k, p), prefixes); + local sorted = std.sort(matching, function(x) -std.length(x)); + if std.length(sorted) > 0 then sorted[0] else null; + +{ + + // Route parameters to appropriate internal objects based on prefix + with:: function(k, v) + local prefix = findRoute(k); + if prefix != null then + self + routes[prefix](prefix, k, v) + else + self + { [k]:: v }, + + "log-level":: "INFO", + + // Base objects with concurrency defaults (LLM/embeddings components + // merge into these) + "text-completion" +: { concurrency:: 1 }, + "text-completion-rag" +: { concurrency:: 1 }, + embeddings +: { concurrency:: 1 }, + + "api-gateway" +: { + + port:: 8088, + timeout:: 600, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "512M", + "memory-reservation":: "512M", + + create:: function(engine) + + local port = self.port; + local timeout = self.timeout; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local envSecrets = engine.envSecrets("gateway-secret") + .with_env_var("GATEWAY_SECRET", "gateway-secret"); + + local container = + engine.container("api-gateway") + .with_image(images.trustgraph_flow) + .with_command([ + "api-gateway", + ] + $["pub-sub-args"] + [ + "--timeout", + std.toString(timeout), + "--port", + std.toString(port), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation) + .with_port(port, port, "api"); + + local containerSet = engine.containers( + "api-gateway", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics") + .with_port(port, port, "api"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "chunker" +: { + + size:: 2000, + overlap:: 50, + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local size = self.size; + local overlap = self.overlap; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("chunker") + .with_image(images.trustgraph_flow) + .with_command([ + "chunker-recursive", + ] + $["pub-sub-args"] + [ + "--chunk-size", + std.toString(size), + "--chunk-overlap", + std.toString(overlap), + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "chunker", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "config-svc" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local cpuLimit = self["cpu-limit"]; + local cpuReservation = self["cpu-reservation"]; + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("config-svc") + .with_image(images.trustgraph_flow) + .with_command([ + "config-svc", + ] + $["pub-sub-args"] + [ + "--log-level", + $["log-level"], + ]) + .with_limits(cpuLimit, memoryLimit) + .with_reservations(cpuReservation, memoryReservation); + + local containerSet = engine.containers( + "config-svc", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "document-decoder" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "512M", + "memory-reservation":: "512M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("document-decoder") + .with_image(images.trustgraph_unstructured) + .with_command([ + "universal-decoder", + ] + $["pub-sub-args"] + [ + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "document-decoder", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "mcp-tool" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("mcp-tool") + .with_image(images.trustgraph_flow) + .with_command([ + "mcp-tool", + ] + $["pub-sub-args"] + [ + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "mcp-tool", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "metering" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("metering") + .with_image(images.trustgraph_flow) + .with_command([ + "metering", + ] + $["pub-sub-args"] + [ + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "metering", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "metering-rag" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("metering-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "metering", + ] + $["pub-sub-args"] + [ + "--id", + "metering-rag", + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "metering-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-store" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-store") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-store", + ] + $["pub-sub-args"] + [ + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-store", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "kg-manager" +: { + + "cpu-limit":: "0.5", + "cpu-reservation":: "0.1", + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("kg-manager") + .with_image(images.trustgraph_flow) + .with_command([ + "kg-manager", + ] + $["pub-sub-args"] + [ + "--log-level", + $["log-level"], + ]) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation); + + local containerSet = engine.containers( + "kg-manager", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + librarian + mcp_server + workbench + graphrag + + documentrag + prompt_template + agent_manager + structured_data + + config_initialiser + config + + ddg + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/embeddings/embeddings-fastembed.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/embeddings/embeddings-fastembed.jsonnet new file mode 100644 index 00000000..17b4b0d5 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/embeddings/embeddings-fastembed.jsonnet @@ -0,0 +1,48 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/embeddings-fastembed.jsonnet"; + +{ + + "fastembed-models":: models, + + "embeddings-models" +:: $["fastembed-models"], + + embeddings +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local container = + engine.container("embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "embeddings-fastembed", + ] + $["pub-sub-args"] + [ + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_limits("2.0", "600M") + .with_reservations("1.0", "600M"); + + local containerSet = engine.containers( + "embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/embeddings/embeddings-hf.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/embeddings/embeddings-hf.jsonnet new file mode 100644 index 00000000..54c63fb8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/embeddings/embeddings-hf.jsonnet @@ -0,0 +1,46 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/embeddings-huggingface.jsonnet"; + +{ + + "huggingface-embeddings-models":: models, + + "embeddings-models" +:: $["huggingface-embeddings-models"], + + embeddings +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local container = + engine.container("embeddings") + .with_image(images.trustgraph_hf) + .with_command([ + "embeddings-hf", + ] + $["pub-sub-args"] + [ + "--concurrency", + std.toString(concurrency), + ]) + .with_limits("1.0", "400M") + .with_reservations("0.5", "400M"); + + local containerSet = engine.containers( + "embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/embeddings/embeddings-ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/embeddings/embeddings-ollama.jsonnet new file mode 100644 index 00000000..2d1772d3 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/embeddings/embeddings-ollama.jsonnet @@ -0,0 +1,51 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local models = import "parameters/embeddings-ollama.jsonnet"; + +{ + + "ollama-url":: "${OLLAMA_HOST}", + + "ollama-models":: models, + + "embeddings-models" +:: $["ollama-models"], + + embeddings +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local container = + engine.container("embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "embeddings-ollama", + ] + $["pub-sub-args"] + [ + "--concurrency", + std.toString(concurrency), + "-r", + $["ollama-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/engine/aks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/engine/aks-k8s.jsonnet new file mode 100644 index 00000000..07e050f2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/engine/aks-k8s.jsonnet @@ -0,0 +1,45 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "disk.csi.azure.com", + parameters: { + // Standard disks (spinning magnetic), Locally Redundant Storage + // Cheapest, basically + skuName: "Standard_LRS", + }, + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/engine/docker-compose.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/engine/docker-compose.jsonnet new file mode 100644 index 00000000..334b3517 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/engine/docker-compose.jsonnet @@ -0,0 +1,260 @@ +{ + + // Extract resources using the engine + package:: function(patterns) + std.foldl( + function(state, p) state + p.create(self), + std.objectValues(patterns), + {} + ), + + container:: function(name) + { + + local container = self, + + name:: name, + + with_image:: function(x) self + { image: x }, + + with_user:: function(x) self + { user: x }, + + with_group:: function(x) self + + if std.objectHas(container, "group_add") then + { group_add: container.group_add + [x] } + else + { group_add: [x] }, + + with_command:: function(x) self + { + command: + if std.isString(x) then + std.strReplace(x, "$", "$$") + else if std.isArray(x) then + std.map(function(s) std.strReplace(s, "$", "$$"), x) + else + x + }, + + with_entrypoint:: function(x) self + { entrypoint: x }, + + with_runtime:: function(x) self + { runtime: x }, + + with_privileged:: function(x) self + { privileged: x }, + + with_ipc:: function(x) self + { ipc: x }, + + with_capability:: function(x) self + + if std.objectHas(container, "capability") then + { cap_add: container.capability + x } + else + { cap_add: [x], }, + + with_environment:: function(x) self + + if std.objectHas(container, "environment") then + { environment: container.environment + x } + else + { environment: x, }, + + with_device:: function(hdev, cdev) self + + if std.objectHas(container, "devices") then + { devices: container.devices + [ "%s:%s" % [hdev, cdev] ] } + else + { devices: [ "%s:%s" % [hdev, cdev] ], }, + + with_limits:: function(c, m) self + { + deploy +: { resources +: { + limits: { cpus: c, memory: m } + } }, + }, + + with_reservations:: function(c, m) self + { + deploy +: { resources +: { + reservations: { cpus: c, memory: m } + } }, + }, + + with_volume_mount:: + function(vol, mnt) + self + { + volumes: + if std.objectHas(container, "volumes") then + container.volumes + [ + "%s:%s" % [vol.volid, mnt] + ] + else + [ + "%s:%s" % [vol.volid, mnt] + ] + }, + + with_bind_mount:: + function(src, dest) + self + { + volumes: + if std.objectHas(container, "volumes") then + container.volumes + [ + "%s:%s" % [src, dest] + ] + else + [ + "%s:%s" % [src, dest] + ] + }, + + with_port:: + function(src, dest, name) + self + { + ports: + if std.objectHas(container, "ports") then + container.ports + [ "%d:%d" % [src, dest] ] + else + [ "%d:%d" % [src, dest] ] + }, + + with_env_var_secrets:: + function(vars) + std.foldl( + function(obj, x) obj.with_environment( + { [x]: "${" + x + "}" } + ), + vars.variables, + self + ), + + restart: "on-failure:100", + + add:: function() { + services +: { + [container.name]: container, + } + } + + }, + + internalService:: function(containers) + { + + local service = self, + + name: containers.name, + + with_port:: function(src, dest, name) + self + { port: [src, dest] }, + + add:: function() { + } + + }, + + service:: function(containers) + { + + local service = self, + + name: containers.name, + + with_port:: function(src, dest, name) + self + { port: [src, dest] }, + + add:: function() { + } + + }, + + volume:: function(name) + { + + local volume = self, + + name: name, + + volid:: name, + + with_size:: function(size) self + { size: size }, + + add:: function() { + volumes +: { + [volume.name]: {} + } + } + + }, + + configVolume:: function(name, dir, parts) + { + + local volume = self, + + name: dir, + + volid:: "./" + dir, + + with_size:: function(size) self + { size: size }, + + add:: function() { + } + + }, + + secretVolume:: function(name, dir, parts) + { + + local volume = self, + + name: dir, + + volid:: dir, + + with_size:: function(size) self + { size: size }, + + add:: function() { + } + + }, + + envSecrets:: function(name) + { + + local volume = self, + + name: name, + + volid:: name, + + variables:: [], + + with_env_var:: + function(name, key) self + { + variables: super.variables + [name], + }, + + add:: function() { + } + + }, + + containers:: function(name, containers) + { + + local cont = self, + + name: name, + containers: containers, + + add:: function() std.foldl( + function(state, c) state + c.add(), + cont.containers, + {} + ), + + }, + + resources:: function(res) + std.foldl( + function(state, c) state + c.add(), + res, + {} + ), + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/engine/eks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/engine/eks-k8s.jsonnet new file mode 100644 index 00000000..3fc3f035 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/engine/eks-k8s.jsonnet @@ -0,0 +1,46 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "ebs.csi.aws.com", + parameters: { + type: "gp3", + encrypted: "true", + iops: "6000", + throughput: "400", + }, + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/engine/gcp-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/engine/gcp-k8s.jsonnet new file mode 100644 index 00000000..71792426 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/engine/gcp-k8s.jsonnet @@ -0,0 +1,44 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "pd.csi.storage.gke.io", + parameters: { + type: "pd-balanced", + "csi.storage.k8s.io/fstype": "ext4", + }, + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/engine/k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/engine/k8s.jsonnet new file mode 100644 index 00000000..2067f6ac --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/engine/k8s.jsonnet @@ -0,0 +1,393 @@ +{ + + container:: function(name) + { + + local container = self, + + name: name, + limits: {}, + reservations: {}, + ports: [], + volumes: [], + bindMounts: [], + groups: [], + environment: [], + + with_image:: function(x) self + { image: x }, + + with_user:: function(x) self + { user: x }, + + with_group:: function(x) self + { groups: super.groups + [x] }, + + with_privileged:: function(x) self + { privileged: x }, + + with_command:: function(x) self + { command: x }, + + with_entrypoint:: function(x) self + { entrypoint: x }, + + with_environment:: function(x) self + { + environment: super.environment + [ + { + name: v.key, value: v.value + } + for v in std.objectKeysValues(x) + ], + }, + + with_limits:: function(c, m) self + { limits: { cpu: c, memory: m } }, + + with_reservations:: + function(c, m) self + { reservations: { cpu: c, memory: m } }, + + with_volume_mount:: + function(vol, mnt) + self + { + volumes: super.volumes + [{ + volume: vol, mount: mnt + }] + }, + + with_bind_mount:: + function(src, dest) + local name = "bind-" + std.strReplace(std.strReplace(src, "/", "-"), ".", "-"); + self + { + bindMounts: super.bindMounts + [{ + name: name, src: src, dest: dest + }] + }, + + with_port:: + function(src, dest, name) self + { + ports: super.ports + [ + { src: src, dest: dest, name : name } + ] + }, + + with_env_var_secrets:: + function(vars) + std.foldl( + function(obj, x) obj + { + environment: super.environment + [{ + name: x, + valueFrom: { + secretKeyRef: { + name: vars.name, + key: vars.keyMap[x], + } + } + }] + }, + vars.variables, + self + ), + + add:: function() [ + + { + apiVersion: "apps/v1", + kind: "Deployment", + metadata: { + name: container.name, + namespace: "trustgraph", + labels: { + app: container.name + } + }, + spec: { + replicas: 1, + selector: { + matchLabels: { + app: container.name, + } + }, + template: { + metadata: { + labels: { + app: container.name, + } + }, + spec: { + containers: [ + { + name: container.name, + image: container.image, + + // FIXME: Make everything run as + // root. Needed to get filesystems + // to be accessible. There's a + // better way of doing this? + securityContext: { + runAsUser: 0, + runAsGroup: 0, + } + ( + if std.objectHas(container, "privileged") && container.privileged then + { privileged: true } + else {} + ), + + resources: { + requests: container.reservations, + limits: container.limits + }, + } + ( + if std.length(container.ports) > 0 then + { + ports: [ + { + hostPort: port.src, + containerPort: port.dest, + } + for port in container.ports + ] + } else + {}) + + + (if std.objectHas(container, "entrypoint") then + // Entrypoint is set - use command for entrypoint, args for command + (if std.isString(container.entrypoint) && container.entrypoint == "" then + { command: [] } + else if std.isArray(container.entrypoint) then + { command: container.entrypoint } + else + { command: [container.entrypoint] } + ) + (if std.objectHas(container, "command") then + { args: container.command } + else {}) + else if std.objectHas(container, "command") then + { command: container.command } + else {}) + + + (if std.length(container.environment) > 0 then + { + env: container.environment, + } + else {}) + + + (if std.length(container.volumes) > 0 || std.length(container.bindMounts) > 0 then + { + volumeMounts: [ + { + mountPath: vol.mount, + name: vol.volume.name, + } + for vol in container.volumes + ] + [ + { + mountPath: bm.dest, + name: bm.name, + } + for bm in container.bindMounts + ] + } + + else + {} + ) + ], + volumes: [ + vol.volume.volRef() + for vol in container.volumes + ] + [ + { + name: bm.name, + hostPath: { path: bm.src } + } + for bm in container.bindMounts + ] + } + ( + if std.length(container.groups) > 0 then + { securityContext: { supplementalGroups: container.groups } } + else {} + ) + }, + } + {} + + } + + ] + + }, + + // Just an alias + internalService:: self.service, + + service:: function(containers) + { + + local service = self, + + name: containers.name, + + ports: [], + + with_port:: + function(src, dest, name) + self + { + ports: super.ports + [ + { src: src, dest: dest, name: name } + ] + }, + + add:: function() [ + + { + + apiVersion: "v1", + kind: "Service", + metadata: { + name: service.name, + namespace: "trustgraph", + }, + spec: { + selector: { + app: service.name, + }, + ports: [ + { + port: port.src, + targetPort: port.dest, + name: port.name, + } + for port in service.ports + ], + } + } + ], + + }, + + volume:: function(name) + { + + local volume = self, + + name: name, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + { + apiVersion: "v1", + kind: "PersistentVolumeClaim", + metadata: { + name: volume.name, + namespace: "trustgraph", + }, + spec: { + storageClassName: "tg", + accessModes: [ "ReadWriteOnce" ], + resources: { + requests: { + storage: volume.size, + } + }, + } + } + ], + + volRef:: function() { + name: volume.name, + persistentVolumeClaim: { claimName: volume.name }, + } + + }, + + configVolume:: function(name, dir, parts) + { + + local volume = self, + + name: name, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + { + apiVersion: "v1", + kind: "ConfigMap", + metadata: { + name: volume.name, + namespace: "trustgraph", + }, + data: parts + }, + ], + + + volRef:: function() { + name: volume.name, + configMap: { name: volume.name }, + } + + }, + + secretVolume:: function(name, dir, parts) + { + + local volume = self, + + name: name, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + ], + + volRef:: function() { + name: volume.name, + secret: { secretName: volume.name }, + } + + }, + + envSecrets:: function(name) + { + + local volume = self, + + name: name, + + variables: [], + keyMap: {}, + + with_size:: function(size) self + { size: size }, + + add:: function() [ + ], + + volRef:: function() { + name: volume.name, + secret: { secretName: volume.name }, + }, + + with_env_var:: + function(name, key) self + { + variables: super.variables + [name], + keyMap: super.keyMap + { [name]: key }, + }, + + }, + + containers:: function(name, containers) + { + + local cont = self, + + name: name, + containers: containers, + + add:: function() std.flattenArrays( + [ c.add() for c in cont.containers ] + ), + + }, + + resources:: function(res) + + std.flattenArrays( + [ c.add() for c in res ] + ), + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/engine/minikube-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/engine/minikube-k8s.jsonnet new file mode 100644 index 00000000..858b17ad --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/engine/minikube-k8s.jsonnet @@ -0,0 +1,115 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: resources, + }; + resourceList, + + volume:: function(name) + { + local volume = self, + name: name, + with_size:: function(size) self + { size: size }, + add:: function() [ + { + apiVersion: "v1", + kind: "PersistentVolume", + metadata: { + name: volume.name, + }, + spec: { + accessModes: [ "ReadWriteOnce" ], + capacity: { + storage: volume.size, + }, + persistentVolumeReclaimPolicy: "Delete", + hostPath: { + path: "/data/pv-" + volume.name, + }, + } + }, + { + apiVersion: "v1", + kind: "PersistentVolumeClaim", + metadata: { + name: volume.name, + namespace: "trustgraph", + }, + spec: { + accessModes: [ "ReadWriteOnce" ], + resources: { + requests: { + storage: volume.size, + } + }, + } + } + ], + + volRef:: function() { + name: volume.name, + persistentVolumeClaim: { claimName: volume.name }, + } + + }, + + service:: function(containers) + { + local service = self, + name: containers.name, + ports: [], + with_port:: + function(src, dest, name) + self + { + ports: super.ports + [ + { src: src, dest: dest, name: name } + ] + }, + add:: function() [ + { + apiVersion: "v1", + kind: "Service", + metadata: { + name: service.name, + namespace: "trustgraph", + }, + spec: { + selector: { + app: service.name, + }, + type: "LoadBalancer", + ports: [ + { + port: port.src, + targetPort: port.dest, + name: port.name, + } + for port in service.ports + ], + } + } + ], + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/engine/noop.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/engine/noop.jsonnet new file mode 100644 index 00000000..1f384648 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/engine/noop.jsonnet @@ -0,0 +1,79 @@ +{ + + // Extract resources usnig the engine + package:: function(patterns) {}, + + container:: function(name) { + + with_image:: function(x) self + {}, + + with_user:: function(x) self + {}, + + with_command:: function(x) self + {}, + + with_runtime:: function(x) self + {}, + + with_privileged:: function(x) self + {}, + + with_ipc:: function(x) self + {}, + + with_capability:: function(x) self + {}, + + with_environment:: function(x) self + {}, + + with_device:: function(hdev, cdev) self + {}, + + with_limits:: function(c, m) self + {}, + + with_reservations:: function(c, m) self + {}, + + with_volume_mount:: self + {}, + + with_port:: function(src, dest, name) self + {}, + + with_env_var_secrets:: function(vars) self + {}, + + add:: function() {}, + }, + + internalService:: function(containers) { + with_port:: function(src, dest, name) self + {}, + add:: function() {}, + }, + + service:: function(containers) { + with_port:: function(src, dest, name) self + {}, + add:: function() {}, + }, + + volume:: function(name) { + with_size:: function(size) self + {}, + add:: function() {}, + }, + + configVolume:: function(name, dir, parts) { + add:: function() {}, + }, + + secretVolume:: function(name, dir, parts) { + add:: function() {}, + }, + + envSecrets:: function(name) { + with_env_var:: function(name, key) self + {}, + add:: function() {}, + }, + + containers:: function(name, containers) { + add:: function() {}, + }, + + resources:: function(res) + std.foldl( + function(state, c) state + c.add(), + res, + {} + ), + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/engine/ovh-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/engine/ovh-k8s.jsonnet new file mode 100644 index 00000000..15d57c83 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/engine/ovh-k8s.jsonnet @@ -0,0 +1,45 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "cinder.csi.openstack.org", + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", + parameters: { + availability: "nova", + fsType: "ext4", + type: "high-speed", + }, +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: [ns, sc] + resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/engine/scw-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/engine/scw-k8s.jsonnet new file mode 100644 index 00000000..eed23968 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/engine/scw-k8s.jsonnet @@ -0,0 +1,40 @@ + +local k8s = import "k8s.jsonnet"; + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +local sc = { + apiVersion: "storage.k8s.io/v1", + kind: "StorageClass", + metadata: { + name: "tg", + }, + provisioner: "csi.scaleway.com", + reclaimPolicy: "Delete", + volumeBindingMode: "WaitForFirstConsumer", +}; + +k8s + { + + // Extract resources usnig the engine + package:: function(patterns) + local resources = [sc, ns] + std.flattenArrays([ + p.create(self) for p in std.objectValues(patterns) + ]); + local resourceList = { + apiVersion: "v1", + kind: "List", + items: [ns, sc] + resources, + }; + resourceList + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/agent-extract.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/agent-extract.jsonnet new file mode 100644 index 00000000..4c5785c8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/agent-extract.jsonnet @@ -0,0 +1,35 @@ +// Agent-based extraction module +// Uses AI agents for more sophisticated knowledge extraction from text +// Leverages agent tools and reasoning for complex extraction tasks + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; + +{ + // No external interfaces - internal agent extraction service + "interfaces" +: { + }, + + // No configurable parameters for agent extraction + "parameters" +: { + }, + + // Flow-level processors for agent-based extraction + "flow" +: { + // Agent-based knowledge extraction processor + // Uses AI agents with tools to extract structured knowledge + "kg-extract-agent:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output knowledge triples + "entity-contexts": flow("entity-contexts-load:{id}"), // Entity context information + "agent-request": request("agent:{id}"), // Agent service requests + "agent-response": response("agent:{id}"), // Agent service responses + }, + }, + + // No blueprint-level processors needed + "blueprint" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/agent.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/agent.jsonnet new file mode 100644 index 00000000..7584cdf5 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/agent.jsonnet @@ -0,0 +1,60 @@ +// Agent management module +// Provides AI agent orchestration and tool integration +// Manages agent conversations, tool calls, and response coordination +// Supports MCP tools, GraphRAG, and structured queries + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +// Import shared services (agent requires LLM for reasoning, MCP for tools) +local llm_services = import "llm-services.jsonnet"; +local mcp_service = import "mcp-service.jsonnet"; + +// Merge shared services with agent-specific configuration +llm_services + mcp_service + { + + // External interfaces for agent operations + "interfaces" +: { + "agent": request_response("agent:{id}"), + }, + + // Flow-level processors for agent management + "flow" +: { + // Agent manager orchestrates agent conversations and tool usage + "agent-manager:{id}": { + + // Agent communication channels + request: request("agent:{id}"), + next: request("agent:{id}"), + response: response("agent:{id}"), + + // LLM and prompt services + "text-completion-request": request("text-completion:{id}"), + "text-completion-response": response("text-completion:{id}"), + "prompt-request": request("prompt-rag:{id}"), + "prompt-response": response("prompt-rag:{id}"), + + // Tool integrations + "mcp-tool-request": request("mcp-tool:{id}"), + "mcp-tool-response": response("mcp-tool:{id}"), + "graph-rag-request": request("graph-rag:{id}"), + "graph-rag-response": response("graph-rag:{id}"), + "structured-query-request": request("structured-query:{id}"), + "structured-query-response": response("structured-query:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + "row-embeddings-query-request": request("row-embeddings:{id}"), + "row-embeddings-query-response": response("row-embeddings:{id}"), + + // Explainability + explainability: flow("triples-store:{id}"), + }, + }, + + // Blueprint-level processors for agent-related services + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/document-store.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/document-store.jsonnet new file mode 100644 index 00000000..769718fa --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/document-store.jsonnet @@ -0,0 +1,57 @@ +// Document store module +// Infrastructure for document-based RAG using chunk embeddings +// Handles document embedding storage, retrieval, and question answering + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +// Import shared services +local llm_services = import "llm-services.jsonnet"; +local embeddings_service = import "embeddings-service.jsonnet"; + +// Merge shared services with document store configuration +llm_services + embeddings_service + { + + // External interfaces for document store + "interfaces" +: { + // Document embedding storage and retrieval + "document-embeddings-store": flow("document-embeddings-store:{id}"), + "document-rag": request_response("document-rag:{id}"), + "document-embeddings": request_response("document-embeddings:{id}"), + }, + + // Flow-level processors for document embedding and storage + "flow" +: { + "document-embeddings:{id}": { + input: flow("chunk-load:{id}"), + output: flow("document-embeddings-store:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + }, + "doc-embeddings-write:{id}": { + input: flow("document-embeddings-store:{id}"), + }, + "document-rag:{id}": { + request: request("document-rag:{id}"), + response: response("document-rag:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + "prompt-request": request("prompt-rag:{id}"), + "prompt-response": response("prompt-rag:{id}"), + "document-embeddings-request": request("document-embeddings:{id}"), + "document-embeddings-response": response("document-embeddings:{id}"), + explainability: flow("triples-store:{id}"), + }, + "doc-embeddings-query:{id}": { + request: request("document-embeddings:{id}"), + response: response("document-embeddings:{id}"), + }, + }, + + // Blueprint-level processors for document RAG operations + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/embeddings-service.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/embeddings-service.jsonnet new file mode 100644 index 00000000..1537e6c2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/embeddings-service.jsonnet @@ -0,0 +1,30 @@ +// Shared embeddings service module +// Provides vector embedding generation for text +// Import this module in any flow that requires embeddings + +local helpers = import "helpers.jsonnet"; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +{ + // Interfaces exposed by embeddings service + "interfaces" +: { + "embeddings": request_response("embeddings:{id}"), + }, + + "parameters" +: { + }, + + // Flow-level processor for embeddings + "flow" +: { + "embeddings:{id}": { + request: request("embeddings:{id}"), + response: response("embeddings:{id}"), + model: "{embeddings-model}", + }, + }, + + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/flow-blueprints.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/flow-blueprints.jsonnet new file mode 100644 index 00000000..8f054690 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/flow-blueprints.jsonnet @@ -0,0 +1,48 @@ +// TrustGraph Flow Blueprints Configuration +// +// RAG Modes (4 types): +// - Document RAG: Uses document chunk embeddings +// - Graph RAG: Extracts definitions + relationships to graph +// - Ontology RAG: Extracts using ontology definitions to graph (mutually exclusive with Graph RAG) +// - Structured RAG: Extracts objects to object store +// +// Module structure: +// - *-store: Storage and query infrastructure +// - *-extract: Extraction methods + +// Import all the modular flow components +local graph_store = import "graph-store.jsonnet"; +local document_store = import "document-store.jsonnet"; +local structured_store = import "structured-store.jsonnet"; +local graphrag_extract = import "graphrag-extract.jsonnet"; +local ontorag_extract = import "ontorag-extract.jsonnet"; +local structured_extract = import "structured-extract.jsonnet"; +local agent = import "agent.jsonnet"; +local load = import "load.jsonnet"; +local kgcore = import "kgcore.jsonnet"; + +{ + + // Full system: Graph RAG + Document RAG + knowledge cores + "everything": { + description: "Graph RAG + Document RAG + knowledge cores", + tags: ["document-rag", "graph-rag", "kgcore"], + } + + graph_store + document_store + agent + load + + graphrag_extract + kgcore + structured_store, + + // Structured RAG only + "structured": { + description: "Structured data extraction and querying", + tags: ["structured"], + } + + structured_store + structured_extract + agent + load, + + // Ontology RAG + knowledge cores + "ontology": { + description: "Ontology RAG + knowledge cores", + tags: ["onto-rag", "kgcore"], + } + + graph_store + ontorag_extract + agent + load + kgcore, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/graph-store.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/graph-store.jsonnet new file mode 100644 index 00000000..3cc8d2f0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/graph-store.jsonnet @@ -0,0 +1,75 @@ +// Graph store module +// Shared infrastructure for graph-based RAG (used by both GraphRAG and OntologyRAG) +// Handles knowledge graph storage, embeddings, and graph-based question answering + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; +local librarian_request = helpers.librarian_request; +local librarian_response = helpers.librarian_response; + +// Import shared services +local llm_services = import "llm-services.jsonnet"; +local embeddings_service = import "embeddings-service.jsonnet"; + +// Merge shared services with graph store configuration +llm_services + embeddings_service + { + + // External interfaces exposed by the graph store + "interfaces" +: { + // Data ingestion interfaces for graph construction + "entity-contexts-load": flow("entity-contexts-load:{id}"), + "triples-store": flow("triples-store:{id}"), + "graph-embeddings-store": flow("graph-embeddings-store:{id}"), + + // Query interfaces for graph-based operations + "graph-rag": request_response("graph-rag:{id}"), + "triples": request_response("triples:{id}"), + "graph-embeddings": request_response("graph-embeddings:{id}"), + }, + + // Flow-level processors - handle data streams for a specific flow instance + "flow" +: { + "graph-embeddings:{id}": { + input: flow("entity-contexts-load:{id}"), + output: flow("graph-embeddings-store:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + }, + "triples-write:{id}": { + input: flow("triples-store:{id}"), + }, + "graph-embeddings-write:{id}": { + input: flow("graph-embeddings-store:{id}"), + }, + "graph-rag:{id}": { + request: request("graph-rag:{id}"), + response: response("graph-rag:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + "prompt-request": request("prompt-rag:{id}"), + "prompt-response": response("prompt-rag:{id}"), + "graph-embeddings-request": request("graph-embeddings:{id}"), + "graph-embeddings-response": response("graph-embeddings:{id}"), + "triples-request": request("triples:{id}"), + "triples-response": response("triples:{id}"), + explainability: flow("triples-store:{id}"), + "librarian-request": librarian_request, + "librarian-response": librarian_response, + }, + "triples-query:{id}": { + request: request("triples:{id}"), + response: response("triples:{id}"), + }, + "graph-embeddings-query:{id}": { + request: request("graph-embeddings:{id}"), + response: response("graph-embeddings:{id}"), + }, + }, + + // Blueprint-level processors - shared across all flow instances of this blueprint + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/graphrag-extract.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/graphrag-extract.jsonnet new file mode 100644 index 00000000..6bcbca14 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/graphrag-extract.jsonnet @@ -0,0 +1,44 @@ +// GraphRAG extraction module +// Extraction method for GraphRAG - extracts definitions and relationships +// Mutually exclusive with OntologyRAG extraction (both write to graph store) + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; + +{ + // No external interfaces - this module provides internal extraction services + "interfaces" +: { + }, + + // No configurable parameters for basic KG extraction + "parameters" +: { + }, + + // Flow-level processors for knowledge extraction + "flow" +: { + // Extracts entity definitions from text chunks + // Identifies and defines key entities mentioned in the text + "kg-extract-definitions:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output definition triples + "entity-contexts": flow("entity-contexts-load:{id}"), // Entity context information + "prompt-request": request("prompt:{id}"), // Definition extraction prompts + "prompt-response": response("prompt:{id}"), + }, + + // Extracts relationships between entities + // Identifies how entities are connected and interact + "kg-extract-relationships:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output relationship triples + "prompt-request": request("prompt:{id}"), // Relationship extraction prompts + "prompt-response": response("prompt:{id}"), + }, + }, + + // No blueprint-level processors needed + "blueprint" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/helpers.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/helpers.jsonnet new file mode 100644 index 00000000..fc727329 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/helpers.jsonnet @@ -0,0 +1,39 @@ +// Helper functions for flow configuration +// Provides utility functions for constructing flow, request, and response URIs +// used throughout the TrustGraph flow configuration system + +// Creates a persistent flow URI for data streams +// Persistent flows retain messages until consumed +local flow(x) = "flow:tg:" + x; + +// Creates a non-persistent request URI for request-response patterns +// Non-persistent means messages are not retained if no consumer is present +local request(x) = "request:tg:" + x; + +// Creates a non-persistent response URI for request-response patterns +local response(x) = "response:tg:" + x; + +// State broadcast queue — persistent, last-value semantics +local state(x) = "state:tg:" + x; + +local librarian_request = request("librarian"); +local librarian_response = request("librarian"); + +// Creates a request-response pair for bidirectional communication +// Returns an object with both request and response URIs +local request_response(x) = { + request: request(x), + response: response(x), +}; + +// Export all helper functions for use in other modules +{ + flow: flow, + request: request, + response: response, + request_response: request_response, + state: state, + librarian_request: librarian_request, + librarian_response: librarian_response, +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/kgcore.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/kgcore.jsonnet new file mode 100644 index 00000000..bfc01ada --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/kgcore.jsonnet @@ -0,0 +1,31 @@ +// Knowledge Graph Core storage module +// Handles persistent storage of knowledge graph data +// Consolidates triples and graph embeddings into permanent storage +// Creates the core knowledge base for long-term use + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; + +{ + // No external interfaces - internal storage service + "interfaces" +: { + }, + + // No configurable parameters for core storage + "parameters" +: { + }, + + // Flow-level processors for knowledge graph storage + "flow" +: { + // Knowledge graph store consolidates extracted knowledge + // Takes processed triples and embeddings and stores them permanently + "kg-store:{id}": { + "triples-input": flow("triples-store:{id}"), // Input RDF triples stream + "graph-embeddings-input": flow("graph-embeddings-store:{id}"), // Input graph embeddings + }, + }, + + // No blueprint-level processors needed + "blueprint" +: { + } +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/llm-parameters.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/llm-parameters.jsonnet new file mode 100644 index 00000000..a99f72ae --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/llm-parameters.jsonnet @@ -0,0 +1,59 @@ +{ + + // LLM model selection for normal LLM + "llm-model": { + "type": "llm-model", + "description": "LLM model", + "order": 1, + "advanced": false, + }, + + // LLM model for RAG operations + "llm-rag-model": { + "type": "llm-model", + "description": "LLM model for RAG", + "order": 2, + "advanced": true, + "controlled-by": "llm-model", + }, + + // LLM model selection for normal LLM + "llm-temperature": { + "type": "llm-temperature", + "description": "LLM temperature", + "order": 3, + "advanced": true, + }, + + // LLM model selection for normal LLM + "llm-rag-temperature": { + "type": "llm-temperature", + "description": "LLM temperature for RAG", + "order": 4, + "advanced": true, + }, + + "embeddings-model": { + "type": "embeddings-model", + "description": "Embeddings model", + "order": 5, + "advanced": true, + }, + + // LLM model selection for normal LLM + "chunk-size": { + "type": "chunk-size", + "description": "Chunk size", + "order": 6, + "advanced": true, + }, + + // LLM model selection for normal LLM + "chunk-overlap": { + "type": "chunk-overlap", + "description": "Chunk overlap", + "order": 7, + "advanced": true, + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/llm-services.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/llm-services.jsonnet new file mode 100644 index 00000000..ee6e09ae --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/llm-services.jsonnet @@ -0,0 +1,66 @@ +// Shared LLM services module +// Provides text completion, prompt processing, and metering services +// Import this module in any flow that requires LLM functionality + +local helpers = import "helpers.jsonnet"; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; +local llm_parameters = import "llm-parameters.jsonnet"; + +{ + // Interfaces exposed by LLM services + "interfaces" +: { + "prompt": request_response("prompt:{id}"), + "text-completion": request_response("text-completion:{id}"), + }, + + // LLM configuration parameters + "parameters" +: llm_parameters, + + // Flow-level processors for LLM services + "flow" +: { + // Primary text completion service + "text-completion:{id}": { + request: request("text-completion:{id}"), + response: response("text-completion:{id}"), + model: "{llm-model}", + }, + + // RAG-specific text completion (may use different model) + "text-completion-rag:{id}": { + request: request("text-completion-rag:{id}"), + response: response("text-completion-rag:{id}"), + model: "{llm-rag-model}", + }, + + // Prompt processing service + "prompt:{id}": { + request: request("prompt:{id}"), + response: response("prompt:{id}"), + "text-completion-request": request("text-completion:{id}"), + "text-completion-response": response("text-completion:{id}"), + }, + + // RAG-specific prompt processing + "prompt-rag:{id}": { + request: request("prompt-rag:{id}"), + response: response("prompt-rag:{id}"), + "text-completion-request": request("text-completion-rag:{id}"), + "text-completion-response": response("text-completion-rag:{id}"), + }, + + // Usage metering for primary completion + "metering:{id}": { + input: response("text-completion:{id}"), + }, + + // Usage metering for RAG completion + "metering-rag:{id}": { + input: response("text-completion-rag:{id}"), + }, + }, + + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/load.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/load.jsonnet new file mode 100644 index 00000000..e51c568e --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/load.jsonnet @@ -0,0 +1,53 @@ +// Document loading and preprocessing module +// Handles document ingestion, format conversion, and chunking +// Converts PDFs to text and splits documents into processable chunks + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; +local librarian_request = helpers.librarian_request; +local librarian_response = helpers.librarian_response; + +// Import shared services (load requires embeddings for chunk processing) +local embeddings_service = import "embeddings-service.jsonnet"; + +// Merge shared services with load-specific configuration +embeddings_service + { + + // External interfaces for document loading + "interfaces" +: { + "document-load": flow("document-load:{id}"), + "text-load": flow("text-document-load:{id}"), + }, + + // Flow-level processors for document preprocessing + "flow" +: { + // PDF decoder converts PDF documents to text + // Also emits page provenance triples and saves pages via librarian + "document-decoder:{id}": { + input: flow("document-load:{id}"), + output: flow("text-document-load:{id}"), + triples: flow("triples-store:{id}"), + "librarian-request": librarian_request, + "librarian-response": librarian_response, + }, + + // Chunker splits documents into smaller, processable pieces + // Also emits chunk provenance triples and saves chunks via librarian + "chunker:{id}": { + input: flow("text-document-load:{id}"), + output: flow("chunk-load:{id}"), + triples: flow("triples-store:{id}"), + "librarian-request": librarian_request, + "librarian-response": librarian_response, + "chunk-size": "{chunk-size}", + "chunk-overlap": "{chunk-overlap}", + }, + }, + + // Blueprint-level processors for document loading services + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/mcp-service.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/mcp-service.jsonnet new file mode 100644 index 00000000..5d599a67 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/mcp-service.jsonnet @@ -0,0 +1,31 @@ +// Shared MCP (Model Context Protocol) tool service module +// Provides MCP tool execution capabilities for agents +// Import this module in any flow that requires MCP tool integration + +local helpers = import "helpers.jsonnet"; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +{ + // Interfaces exposed by MCP service + "interfaces" +: { + "mcp-tool": request_response("mcp-tool:{id}"), + }, + + "parameters" +: { + }, + + // Flow-level processor for MCP tool execution + "flow" +: { + "mcp-tool:{id}": { + request: request("mcp-tool:{id}"), + response: response("mcp-tool:{id}"), + "text-completion-request": request("text-completion:{id}"), + "text-completion-response": response("text-completion:{id}"), + }, + }, + + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/ontorag-extract.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/ontorag-extract.jsonnet new file mode 100644 index 00000000..e5521d8a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/ontorag-extract.jsonnet @@ -0,0 +1,39 @@ +// OntologyRAG extraction module +// Extraction method for OntologyRAG - extracts using ontology definitions +// Mutually exclusive with GraphRAG extraction (both write to graph store) + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; + +{ + // No external interfaces - this module provides internal extraction services + "interfaces" +: { + }, + + // No configurable parameters for basic KG extraction + "parameters" +: { + }, + + // Flow-level processors for knowledge extraction + "flow" +: { + // Extracts using ontology definitions + "kg-extract-ontology:{id}": { + input: flow("chunk-load:{id}"), // Input text chunks + triples: flow("triples-store:{id}"), // Output triples + "entity-contexts": flow("entity-contexts-load:{id}"), // Entity context information + "prompt-request": request("prompt:{id}"), // Definition + // extraction prompts + "prompt-response": response("prompt:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + }, + + }, + + // No blueprint-level processors needed + "blueprint" +: { + } +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/structured-extract.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/structured-extract.jsonnet new file mode 100644 index 00000000..54f6a05d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/structured-extract.jsonnet @@ -0,0 +1,30 @@ +// Structured RAG extraction module +// Extracts structured rows from text chunks +// Outputs to rows-store for structured data querying + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; + +{ + "interfaces" +: { + }, + + "parameters" +: { + }, + + // Flow-level processor for structured row extraction + "flow" +: { + "kg-extract-rows:{id}": { + input: flow("chunk-load:{id}"), + output: flow("rows-store:{id}"), + "entity-contexts": flow("entity-contexts-load:{id}"), + "prompt-request": request("prompt:{id}"), + "prompt-response": response("prompt:{id}"), + }, + }, + + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/structured-store.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/structured-store.jsonnet new file mode 100644 index 00000000..3668222d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/flows/structured-store.jsonnet @@ -0,0 +1,78 @@ +// Structured store module +// Shared infrastructure for structured data RAG +// Handles row storage, retrieval, and NLP query capabilities + +local helpers = import "helpers.jsonnet"; +local flow = helpers.flow; +local request = helpers.request; +local response = helpers.response; +local request_response = helpers.request_response; + +// Import shared services +local llm_services = import "llm-services.jsonnet"; +local embeddings_service = import "embeddings-service.jsonnet"; + +// Merge shared services with structured store configuration +llm_services + embeddings_service + { + + // External interfaces for structured store + "interfaces" +: { + // Row storage and querying + "rows-store": flow("rows-store:{id}"), + "row-embeddings-store": flow("row-embeddings-store:{id}"), + "rows": request_response("rows:{id}"), + "row-embeddings": request_response("row-embeddings:{id}"), + + // Query interfaces + "nlp-query": request_response("nlp-query:{id}"), + "structured-query": request_response("structured-query:{id}"), + "structured-diag": request_response("structured-diag:{id}"), + }, + + // Flow-level processors for structured storage and query + "flow" +: { + "row-embeddings:{id}": { + input: flow("rows-store:{id}"), + output: flow("row-embeddings-store:{id}"), + "embeddings-request": request("embeddings:{id}"), + "embeddings-response": response("embeddings:{id}"), + }, + "rows-write:{id}": { + input: flow("rows-store:{id}"), + }, + "row-embeddings-write:{id}": { + input: flow("row-embeddings-store:{id}"), + }, + "rows-query:{id}": { + request: request("rows:{id}"), + response: response("rows:{id}"), + }, + "row-embeddings-query:{id}": { + request: request("row-embeddings:{id}"), + response: response("row-embeddings:{id}"), + }, + "nlp-query:{id}": { + request: request("nlp-query:{id}"), + response: response("nlp-query:{id}"), + "prompt-request": request("prompt-rag:{id}"), + "prompt-response": response("prompt-rag:{id}"), + }, + "structured-query:{id}": { + request: request("structured-query:{id}"), + response: response("structured-query:{id}"), + "nlp-query-request": request("nlp-query:{id}"), + "nlp-query-response": response("nlp-query:{id}"), + "rows-query-request": request("rows:{id}"), + "rows-query-response": response("rows:{id}"), + }, + "structured-diag:{id}": { + request: request("structured-diag:{id}"), + response: response("structured-diag:{id}"), + "prompt-request": request("prompt:{id}"), + "prompt-response": response("prompt:{id}"), + }, + }, + + "blueprint" +: { + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/azure-openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/azure-openai.jsonnet new file mode 100644 index 00000000..c20ac7ae --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/azure-openai.jsonnet @@ -0,0 +1,110 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/azure-openai.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["azure-openai-" + key]:: value, + }, + +// Strategy is to specify the model with the AZURE_MODEL environment +// variable. This isn't something that can just be specified dynamically, +// it has to match what was provisioned in Azure. + + "azure-openai-max-output-tokens":: 4192, + "azure-openai-temperature":: 0.0, + "azure-openai-models":: models, + + "llm-models" +:: $["azure-openai-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-openai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_MODEL", "azure-model") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure-openai", + ] + $["pub-sub-args"] + [ + "-x", + std.toString($["azure-openai-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-openai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_MODEL", "azure-model") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure-openai", + ] + $["pub-sub-args"] + [ + "--id", + "text-completion-rag", + "-x", + std.toString($["azure-openai-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/azure.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/azure.jsonnet new file mode 100644 index 00000000..c8e1bcad --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/azure.jsonnet @@ -0,0 +1,104 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/azure.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["azure-" + key]:: value, + }, + + "azure-max-output-tokens":: 4096, + "azure-temperature":: 0.0, + "azure-models":: models, + + "llm-models" +:: $["azure-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-ai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure", + ] + $["pub-sub-args"] + [ + "-x", + std.toString($["azure-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("azure-ai-credentials") + .with_env_var("AZURE_TOKEN", "azure-token") + .with_env_var("AZURE_ENDPOINT", "azure-endpoint"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-azure", + ] + $["pub-sub-args"] + [ + "--id", + "text-completion-rag", + "-x", + std.toString($["azure-max-output-tokens"]), + "-t", + "%0.3f" % $["azure-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/bedrock.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/bedrock.jsonnet new file mode 100644 index 00000000..079531d8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/bedrock.jsonnet @@ -0,0 +1,102 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/bedrock.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["bedrock-" + key]:: value, + }, + + "bedrock-max-output-tokens":: 4096, + "bedrock-temperature":: 0.0, + "bedrock-models":: models, + + "llm-models" +:: $["bedrock-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("bedrock-credentials") + .with_env_var("AWS_ACCESS_KEY_ID", "aws-id-key") + .with_env_var("AWS_SECRET_ACCESS_KEY", "aws-secret") + .with_env_var("AWS_DEFAULT_REGION", "aws-region"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_bedrock) + .with_command([ + "text-completion-bedrock", + ] + $["pub-sub-args"] + [ + "-x", + std.toString($["bedrock-max-output-tokens"]), + "-t", + "%0.3f" % $["bedrock-temperature"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("bedrock-credentials") + .with_env_var("AWS_ACCESS_KEY_ID", "aws-id-key") + .with_env_var("AWS_SECRET_ACCESS_KEY", "aws-secret") + .with_env_var("AWS_DEFAULT_REGION", "aws-region"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_bedrock) + .with_command([ + "text-completion-bedrock", + ] + $["pub-sub-args"] + [ + "--id", + "text-completion-rag", + "-x", + std.toString($["bedrock-max-output-tokens"]), + "-t", + "%0.3f" % $["bedrock-temperature"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/claude.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/claude.jsonnet new file mode 100644 index 00000000..7751c767 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/claude.jsonnet @@ -0,0 +1,102 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/claude.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["claude-" + key]:: value, + }, + + "claude-max-output-tokens":: 4096, + "claude-temperature":: 0.0, + "claude-models":: models, + + "llm-models" +:: $["claude-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("claude-credentials") + .with_env_var("CLAUDE_KEY", "claude-key"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-claude", + ] + $["pub-sub-args"] + [ + "-x", + std.toString($["claude-max-output-tokens"]), + "-t", + "%0.3f" % $["claude-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("claude-credentials") + .with_env_var("CLAUDE_KEY", "claude-key"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-claude", + ] + $["pub-sub-args"] + [ + "--id", + "text-completion-rag", + "-x", + std.toString($["claude-max-output-tokens"]), + "-t", + "%0.3f" % $["claude-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/cohere.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/cohere.jsonnet new file mode 100644 index 00000000..d6e2778b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/cohere.jsonnet @@ -0,0 +1,95 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/cohere.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["cohere-" + key]:: value, + }, + + "cohere-temperature":: 0.0, + "cohere-models":: models, + + "llm-models" +:: $["cohere-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("cohere-credentials") + .with_env_var("COHERE_KEY", "cohere-key"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-cohere", + ] + $["pub-sub-args"] + [ + "-t", + "%0.3f" % $["cohere-temperature"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("cohere-credentials") + .with_env_var("COHERE_KEY", "cohere-key"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-cohere", + ] + $["pub-sub-args"] + [ + "--id", + "text-completion-rag", + "-t", + "%0.3f" % $["cohere-temperature"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/googleaistudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/googleaistudio.jsonnet new file mode 100644 index 00000000..d2062384 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/googleaistudio.jsonnet @@ -0,0 +1,102 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/googleaistudio.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["googleaistudio-" + key]:: value, + }, + + "googleaistudio-max-output-tokens":: 4096, + "googleaistudio-temperature":: 0.0, + "googleaistudio-models":: models, + + "llm-models" +:: $["googleaistudio-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("googleaistudio-credentials") + .with_env_var("GOOGLE_AI_STUDIO_KEY", "googleaistudio-key"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_vertexai) + .with_command([ + "text-completion-googleaistudio", + ] + $["pub-sub-args"] + [ + "-x", + std.toString($["googleaistudio-max-output-tokens"]), + "-t", + "%0.3f" % $["googleaistudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("googleaistudio-credentials") + .with_env_var("GOOGLE_AI_STUDIO_KEY", "googleaistudio-key"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_vertexai) + .with_command([ + "text-completion-googleaistudio", + ] + $["pub-sub-args"] + [ + "--id", + "text-completion-rag", + "-x", + std.toString($["googleaistudio-max-output-tokens"]), + "-t", + "%0.3f" % $["googleaistudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/llamafile.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/llamafile.jsonnet new file mode 100644 index 00000000..3355404f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/llamafile.jsonnet @@ -0,0 +1,92 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/slm.jsonnet"; +local models = import "parameters/llamafile.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["llamafile-" + key]:: value, + }, + + "llamafile-models":: models, + + "llm-models" +:: $["llamafile-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("llamafile-credentials") + .with_env_var("LLAMAFILE_URL", "llamafile-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-llamafile", + ] + $["pub-sub-args"] + [ + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("llamafile-credentials") + .with_env_var("LLAMAFILE_URL", "llamafile-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-llamafile", + ] + $["pub-sub-args"] + [ + "--id", + "text-completion-rag", + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/lmstudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/lmstudio.jsonnet new file mode 100644 index 00000000..bf59cced --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/lmstudio.jsonnet @@ -0,0 +1,102 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/lmstudio.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["lmstudio-" + key]:: value, + }, + + "lmstudio-max-output-tokens":: 4096, + "lmstudio-temperature":: 0.0, + "lmstudio-models":: models, + + "llm-models" +:: $["lmstudio-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("lmstudio-credentials") + .with_env_var("LMSTUDIO_URL", "lmstudio-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-lmstudio", + ] + $["pub-sub-args"] + [ + "-x", + std.toString($["lmstudio-max-output-tokens"]), + "-t", + "%0.3f" % $["lmstudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("lmstudio-credentials") + .with_env_var("LMSTUDIO_URL", "lmstudio-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-lmstudio", + ] + $["pub-sub-args"] + [ + "--id", + "text-completion-rag", + "-x", + std.toString($["lmstudio-max-output-tokens"]), + "-t", + "%0.3f" % $["lmstudio-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/mistral.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/mistral.jsonnet new file mode 100644 index 00000000..412620d2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/mistral.jsonnet @@ -0,0 +1,102 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/mistral.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["mistral-" + key]:: value, + }, + + "mistral-max-output-tokens":: 4096, + "mistral-temperature":: 0.0, + "mistral-models":: models, + + "llm-models" +:: $["mistral-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("mistral-credentials") + .with_env_var("MISTRAL_TOKEN", "mistral-token"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-mistral", + ] + $["pub-sub-args"] + [ + "-x", + std.toString($["mistral-max-output-tokens"]), + "-t", + "%0.3f" % $["mistral-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("mistral-credentials") + .with_env_var("MISTRAL_TOKEN", "mistral-token"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-mistral", + ] + $["pub-sub-args"] + [ + "--id", + "text-completion-rag", + "-x", + std.toString($["mistral-max-output-tokens"]), + "-t", + "%0.3f" % $["mistral-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/ollama.jsonnet new file mode 100644 index 00000000..b5bae965 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/ollama.jsonnet @@ -0,0 +1,100 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/ollama.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["ollama-" + key]:: value, + }, + + "ollama-models":: models, + + "llm-models" +:: $["ollama-models"], + + "text-completion" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("ollama-credentials") + .with_env_var("OLLAMA_HOST", "ollama-host"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-ollama", + ] + $["pub-sub-args"] + [ + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("ollama-credentials") + .with_env_var("OLLAMA_HOST", "ollama-host"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-ollama", + ] + $["pub-sub-args"] + [ + "--id", + "text-completion-rag", + "--concurrency", + std.toString(concurrency), + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/openai.jsonnet new file mode 100644 index 00000000..1b72e6ec --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/openai.jsonnet @@ -0,0 +1,104 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/openai.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["openai-" + key]:: value, + }, + + "openai-max-output-tokens":: 4096, + "openai-temperature":: 0.0, + "openai-models":: models, + + "llm-models" +:: $["openai-models"], + + "text-completion" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("openai-credentials") + .with_env_var("OPENAI_TOKEN", "openai-token") + .with_env_var("OPENAI_BASE_URL", "openai-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-openai", + ] + $["pub-sub-args"] + [ + "-x", + std.toString($["openai-max-output-tokens"]), + "-t", + "%0.3f" % $["openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("openai-credentials") + .with_env_var("OPENAI_TOKEN", "openai-token") + .with_env_var("OPENAI_BASE_URL", "openai-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-openai", + ] + $["pub-sub-args"] + [ + "--id", + "text-completion-rag", + "-x", + std.toString($["openai-max-output-tokens"]), + "-t", + "%0.3f" % $["openai-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/tgi.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/tgi.jsonnet new file mode 100644 index 00000000..0f9f058f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/tgi.jsonnet @@ -0,0 +1,106 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-" + key]:: value, + }, + + "tgi-max-output-tokens":: 1024, + "tgi-temperature":: 0.0, + + "text-completion" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("tgi-credentials") + .with_env_var("TGI_BASE_URL", "tgi-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-tgi", + ] + $["pub-sub-args"] + [ + "--concurrency", + std.toString(concurrency), + "-x", + std.toString($["tgi-max-output-tokens"]), + "-t", + "%0.3f" % $["tgi-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("tgi-credentials") + .with_env_var("TGI_BASE_URL", "tgi-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-tgi", + ] + $["pub-sub-args"] + [ + "--id", + "text-completion-rag", + "--concurrency", + std.toString(concurrency), + "-x", + std.toString($["tgi-max-output-tokens"]), + "-t", + "%0.3f" % $["tgi-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/vertexai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/vertexai.jsonnet new file mode 100644 index 00000000..ce629029 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/vertexai.jsonnet @@ -0,0 +1,118 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/vertexai.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vertexai-" + key]:: value, + }, + + "vertexai-private-key":: "/vertexai/private.json", + "vertexai-region":: "us-central1", + "vertexai-max-output-tokens":: 4096, + "vertexai-temperature":: 0.0, + "vertexai-models":: models, + + "llm-models" +:: $["vertexai-models"], + + "text-completion" +: { + + create:: function(engine) + + local cfgVol = engine.secretVolume( + "vertexai-creds", + "./vertexai", + { + "private.json": importstr "vertexai/private.json", + } + ); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_vertexai) + .with_command([ + "text-completion-vertexai", + ] + $["pub-sub-args"] + [ + "-k", + $["vertexai-private-key"], + "-r", + $["vertexai-region"], + "-x", + std.toString($["vertexai-max-output-tokens"]), + "-t", + "%0.3f" % $["vertexai-temperature"], + ]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_volume_mount(cfgVol, "/vertexai"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + cfgVol, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local cfgVol = engine.secretVolume( + "vertexai-creds", + "./vertexai", + { + "private.json": importstr "vertexai/private.json", + } + ); + + local container = + engine.container("text-completion-rag") + .with_image(images.trustgraph_vertexai) + .with_command([ + "text-completion-vertexai", + ] + $["pub-sub-args"] + [ + "--id", + "text-completion-rag", + "-k", + $["vertexai-private-key"], + "-r", + $["vertexai-region"], + "-x", + std.toString($["vertexai-max-output-tokens"]), + "-t", + "%0.3f" % $["vertexai-temperature"], + ]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_volume_mount(cfgVol, "/vertexai"); + + local containerSet = engine.containers( + "text-completion-rag", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + cfgVol, + containerSet, + service, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/vllm.jsonnet new file mode 100644 index 00000000..e313548b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/llm/vllm.jsonnet @@ -0,0 +1,111 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local prompts = import "prompts/mixtral.jsonnet"; +local models = import "parameters/vllm.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-" + key]:: value, + }, + + "vllm-models":: models, + + "llm-models" +:: $["vllm-models"], + + "vllm-max-output-tokens":: 1024, + "vllm-temperature":: 0.0, + + "text-completion" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("vllm-credentials") + .with_env_var("VLLM_BASE_URL", "vllm-url"); + + local container = + engine.container("text-completion") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-vllm", + ] + $["pub-sub-args"] + [ + "--concurrency", + std.toString(concurrency), + "-x", + std.toString($["vllm-max-output-tokens"]), + "-t", + "%0.3f" % $["vllm-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "text-completion", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "text-completion-rag" +: { + + create:: function(engine) + + local concurrency = self.concurrency; + + local envSecrets = engine.envSecrets("vllm-credentials") + .with_env_var("VLLM_BASE_URL", "vllm-url"); + + local containerRag = + engine.container("text-completion-rag") + .with_image(images.trustgraph_flow) + .with_command([ + "text-completion-vllm", + ] + $["pub-sub-args"] + [ + "--id", + "text-completion-rag", + "--concurrency", + std.toString(concurrency), + "-x", + std.toString($["vllm-max-output-tokens"]), + "-t", + "%0.3f" % $["vllm-temperature"], + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSetRag = engine.containers( + "text-completion-rag", [ containerRag ] + ); + + local serviceRag = + engine.internalService(containerSetRag) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSetRag, + serviceRag, + ]) + + }, + +} + prompts + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/mcp/ddg-mcp-server.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/mcp/ddg-mcp-server.jsonnet new file mode 100644 index 00000000..04176f15 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/mcp/ddg-mcp-server.jsonnet @@ -0,0 +1,47 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "ddg-mcp-server-port":: 9870, + + "ddg-mcp-server" +: { + + create:: function(engine) + + local port = $["ddg-mcp-server-port"]; + + local container = + engine.container("ddg-mcp-server") + .with_image(images["ddg-mcp-server"]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_port(port, port, "mcp"); + + local containerSet = engine.containers( + "ddg-mcp-server", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(port, port, "mcp"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + mcp +:: { + "duckduckgo": { + "remote-name": "search", + local port = $["ddg-mcp-server-port"], + local url = "http://ddg-mcp-server:%s/mcp" % port, + "url": url, + } + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/model-hosting/cpu-tgi.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/model-hosting/cpu-tgi.jsonnet new file mode 100644 index 00000000..04b61566 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/model-hosting/cpu-tgi.jsonnet @@ -0,0 +1,68 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-service-" + key]:: value, + }, + + "tgi-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "tgi-service-cpus":: "8.0", + "tgi-service-memory":: "16G", + "tgi-service-storage":: "20G", + "tgi-service-hf-token":: null, + + "tgi-service" +: { + + create:: function(engine) + + local vol = engine.volume("tgi-storage") + .with_size($["tgi-service-storage"]); + + local container = + engine.container("tgi-service") + .with_image(images["tgi-service-cpu"]) + .with_command([ + "--model-id", + $["tgi-service-model"], + "--hostname", + "0.0.0.0", + "--port", + "7000", + "--cuda-graphs", + "0", + ]) + .with_environment({ + } + ( + if $["tgi-service-hf-token"] != null + then { HF_TOKEN: $["tgi-service-hf-token"] } + else {} + )) + .with_limits( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_reservations( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_port(7000, 7000, "tgi") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "tgi-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "tgi"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/model-hosting/intel-battlemage-vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/model-hosting/intel-battlemage-vllm.jsonnet new file mode 100644 index 00000000..7646398c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/model-hosting/intel-battlemage-vllm.jsonnet @@ -0,0 +1,98 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-service-" + key]:: value, + }, + + "vllm-service-model":: "mistralai/Mistral-Nemo-Instruct-2407", + "vllm-service-cpus":: "32.0", + "vllm-service-memory":: "48G", + "vllm-service-storage":: "48G", + "vllm-service-tokenizer-mode":: "mistral", + "vllm-service-datatype":: "float16", + "vllm-service-quantization":: "woq_int4", + "vllm-service-hf-token":: null, + "vllm-service-max-model-len":: 8192, + "vllm-service-max-num-seqs":: 16, + + "vllm-service" +: { + + create:: function(engine) + + local vol = engine.volume("vllm-storage") + .with_size($["vllm-service-storage"]); + + local container = + engine.container("vllm-service") + .with_image(images["vllm-service-intel-battlemage"]) + .with_entrypoint("") // Clear default entrypoint + .with_command([ + "python", + "-m", + "ipex_llm.vllm.xpu.entrypoints.openai.api_server", + "--model", + $["vllm-service-model"], + "--served-model-name", + "model", + "--host", + "0.0.0.0", + "--port", + "7000", + "--device", + "xpu", + "--dtype", + $["vllm-service-datatype"], + "--enforce-eager", + "--max-model-len", + std.toString($["vllm-service-max-model-len"]), + "--max-num-seqs", + std.toString($["vllm-service-max-num-seqs"]), + "--load-in-low-bit", + $["vllm-service-quantization"], + "--trust-remote-code", + "--tokenizer-mode", + $["vllm-service-tokenizer-mode"], + "--disable-sliding-window", + ]) + .with_environment({ + VLLM_WORKER_MULTIPROC_METHOD: "spawn", + SYCL_PI_LEVEL_ZERO_USE_IMMEDIATE_COMMANDLISTS: "1", + } + ( + if $["vllm-service-hf-token"] != null + then { HF_TOKEN: $["vllm-service-hf-token"] } + else {} + )) + .with_privileged(true) + .with_device("/dev/dri", "/dev/dri") + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_reservations( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_port(7000, 7000, "vllm") + .with_bind_mount("/dev/dri/by-path", "/dev/dri/by-path") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "vllm-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "vllm"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/model-hosting/intel-gaudi-tgi.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/model-hosting/intel-gaudi-tgi.jsonnet new file mode 100644 index 00000000..5af36b78 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/model-hosting/intel-gaudi-tgi.jsonnet @@ -0,0 +1,96 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-service-" + key]:: value, + }, + + "tgi-service-model":: "meta-llama/Llama-3.3-70B-Instruct", + "tgi-service-cpus":: "64.0", + "tgi-service-memory":: "64G", + "tgi-service-storage":: "50G", + "tgi-service-num-shard":: 8, + "tgi-service-max-input-tokens":: 4096, + "tgi-service-max-total-tokens":: 4096, + "tgi-service-max-batch-size":: 128, + "tgi-service-max-concurrent-requests":: 512, + "tgi-service-hf-token":: null, + + "tgi-service" +: { + + create:: function(engine) + + local vol = engine.volume("tgi-storage") + .with_size($["tgi-service-storage"]); + + local container = + engine.container("tgi-service") + .with_image(images["tgi-service-gaudi"]) + .with_command([ + "--model-id", + $["tgi-service-model"], + "--hostname", + "0.0.0.0", + "--port", + "7000", + "--sharded", + "true", + "--num-shard", + std.toString($["tgi-service-num-shard"]), + "--max-input-tokens", + std.toString($["tgi-service-max-input-tokens"]), + "--max-total-tokens", + std.toString($["tgi-service-max-total-tokens"]), + "--max-batch-size", + std.toString($["tgi-service-max-batch-size"]), + "--max-waiting-tokens", + "7", + "--max-concurrent-requests", + std.toString($["tgi-service-max-concurrent-requests"]), + "--cuda-graphs", + "0", + ]) + .with_runtime("habana") + .with_environment({ + HABANA_VISIBLE_DEVICES: "all", + OMPI_MCA_btl_vader_single_copy_mechanism: "none", + ENABLE_HPU_GRAPH: "true", + LIMIT_HPU_GRAPH: "true", + USE_FLASH_ATTENTION: "true", + FLASH_ATTENTION_RECOMPUTE: "true", + } + ( + if $["tgi-service-hf-token"] != null + then { HF_TOKEN: $["tgi-service-hf-token"] } + else {} + )) + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_reservations( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_port(7000, 7000, "tgi") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "tgi-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "tgi"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/model-hosting/intel-gaudi-vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/model-hosting/intel-gaudi-vllm.jsonnet new file mode 100644 index 00000000..81d31c14 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/model-hosting/intel-gaudi-vllm.jsonnet @@ -0,0 +1,76 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-service-" + key]:: value, + }, + + "vllm-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "vllm-service-cpus":: "64.0", + "vllm-service-memory":: "64G", + "vllm-service-storage":: "50G", + "vllm-service-tensor-parallel-size":: 8, + "vllm-service-hf-token":: null, + + "vllm-service" +: { + + create:: function(engine) + + local vol = engine.volume("vllm-storage") + .with_size($["vllm-service-storage"]); + + local container = + engine.container("vllm-service") + .with_image(images["vllm-service-gaudi"]) + .with_command([ + "--model", + $["vllm-service-model"], + "--served-model-name", + "model", + "--host", + "0.0.0.0", + "--port", + "7000", + "--tensor-parallel-size", + std.toString($["vllm-service-tensor-parallel-size"]), + ]) + .with_runtime("habana") + .with_environment({ + VLLM_SKIP_WARMUP: "true", + HABANA_VISIBLE_DEVICES: "all", + } + ( + if $["vllm-service-hf-token"] != null + then { HF_TOKEN: $["vllm-service-hf-token"] } + else {} + )) + .with_privileged(true) + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_reservations( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_port(7000, 7000, "vllm") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "vllm-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "vllm"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/model-hosting/intel-xpu-tgi.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/model-hosting/intel-xpu-tgi.jsonnet new file mode 100644 index 00000000..bc806874 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/model-hosting/intel-xpu-tgi.jsonnet @@ -0,0 +1,75 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["tgi-service-" + key]:: value, + }, + + "tgi-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "tgi-service-cpus":: "8.0", + "tgi-service-memory":: "16G", + "tgi-service-storage":: "20G", + "tgi-service-hf-token":: null, + + "tgi-service" +: { + + create:: function(engine) + + local vol = engine.volume("tgi-storage") + .with_size($["tgi-service-storage"]); + + local container = + engine.container("tgi-service") + .with_image(images["tgi-service-intel-xpu"]) + .with_command([ + "--model-id", + $["tgi-service-model"], + "--hostname", + "0.0.0.0", + "--port", + "7000", + "--cuda-graphs", + "0", + ]) + .with_environment({ + } + ( + if $["tgi-service-hf-token"] != null + then { HF_TOKEN: $["tgi-service-hf-token"] } + else {} + )) + .with_privileged(true) + .with_device("/dev/dri", "/dev/dri") + .with_ipc("host") + .with_group("video") + .with_group("render") + .with_capability("SYS_NICE") + .with_limits( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_reservations( + $["tgi-service-cpus"], $["tgi-service-memory"] + ) + .with_port(7000, 7000, "tgi") + .with_bind_mount("/dev/dri/by-path", "/dev/dri/by-path") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "tgi-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "tgi"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/model-hosting/intel-xpu-vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/model-hosting/intel-xpu-vllm.jsonnet new file mode 100644 index 00000000..f6e82905 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/model-hosting/intel-xpu-vllm.jsonnet @@ -0,0 +1,97 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-service-" + key]:: value, + }, + + "vllm-service-model":: "teknium/OpenHermes-2.5-Mistral-7B", + "vllm-service-cpus":: "8.0", + "vllm-service-memory":: "16G", + "vllm-service-storage":: "20G", + "vllm-service-datatype":: "float16", + "vllm-service-max-model-len":: 4096, + "vllm-service-max-num-seqs":: 16, + "vllm-service-hf-token":: null, + + "vllm-service" +: { + + create:: function(engine) + + local vol = engine.volume("vllm-storage") + .with_size($["vllm-service-storage"]); + + local container = + engine.container("vllm-service") + .with_image(images["vllm-service-intel-xpu"]) + .with_command([ + "python", + "-m", + "vllm.entrypoints.openai.api_server", + "--model", + $["vllm-service-model"], + "--served-model-name", + "model", + "--host", + "0.0.0.0", + "--port", + "7000", + "--device", + "xpu", + "--dtype", + $["vllm-service-datatype"], + "--enforce-eager", + "--max-model-len", + std.toString($["vllm-service-max-model-len"]), + "--max-num-seqs", + std.toString($["vllm-service-max-num-seqs"]), + "--block-size", + "64", + "--gpu-memory-util", + "0.85", + "--trust-remote-code", + "--disable-sliding-window", + ]) + .with_environment({ + VLLM_USE_V1: "1", + VLLM_WORKER_MULTIPROC_METHOD: "spawn", + } + ( + if $["vllm-service-hf-token"] != null + then { HF_TOKEN: $["vllm-service-hf-token"] } + else {} + )) + .with_privileged(true) + .with_device("/dev/dri", "/dev/dri") + .with_ipc("host") + .with_group("video") + .with_group("render") + .with_capability("SYS_NICE") + .with_limits( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_reservations( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_port(7000, 7000, "vllm") + .with_bind_mount("/dev/dri/by-path", "/dev/dri/by-path") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "vllm-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "vllm"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/model-hosting/nvidia-gpu-vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/model-hosting/nvidia-gpu-vllm.jsonnet new file mode 100644 index 00000000..d04876c4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/model-hosting/nvidia-gpu-vllm.jsonnet @@ -0,0 +1,72 @@ +local images = import "values/images.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["vllm-service-" + key]:: value, + }, + + "vllm-service-model":: "mistralai/Mistral-7B-Instruct-v0.3", + "vllm-service-cpus":: "0.5", + "vllm-service-memory":: "1G", + "vllm-service-storage":: "50G", + "vllm-service-hf-token":: null, + + "vllm-service" +: { + + create:: function(engine) + + local vol = engine.volume("vllm-storage") + .with_size($["vllm-service-storage"]); + + local container = + engine.container("vllm-service") + .with_image(images["vllm-service-nvidia"]) + .with_command([ + "--model", + $["vllm-service-model"], + "--served-model-name", + "model", + "--host", + "0.0.0.0", + "--port", + "7000", + ]) + .with_runtime("nvidia") + .with_environment({ + VLLM_SKIP_WARMUP: "true", + } + ( + if $["vllm-service-hf-token"] != null + then { HF_TOKEN: $["vllm-service-hf-token"] } + else {} + )) + .with_privileged(true) + .with_ipc("host") + .with_capability("SYS_NICE") + .with_limits( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_reservations( + $["vllm-service-cpus"], $["vllm-service-memory"] + ) + .with_port(7000, 7000, "vllm") + .with_volume_mount(vol, "/root/.cache/huggingface"); + + local containerSet = engine.containers( + "vllm-service", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(7000, 7000, "vllm"); + + engine.resources([ + vol, + containerSet, + service, + ]) + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/monitoring/grafana.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/monitoring/grafana.jsonnet new file mode 100644 index 00000000..7ed6eadb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/monitoring/grafana.jsonnet @@ -0,0 +1,124 @@ +local images = import "values/images.jsonnet"; +local loki = import "loki.jsonnet"; + +{ + + "prometheus" +: { + + create:: function(engine) + + local vol = engine.volume("prometheus-data").with_size("20G"); + + local cfgVol = engine.configVolume( + "prometheus-cfg", "prometheus", + { + "prometheus.yml": $["prometheus-config"], + } + ); + + local container = + engine.container("prometheus") + .with_image(images.prometheus) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M") + .with_port(9090, 9090, "http") + .with_volume_mount(cfgVol, "/etc/prometheus/") + .with_volume_mount(vol, "/prometheus"); + + local containerSet = engine.containers( + "prometheus", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9090, 9090, "http"); + + engine.resources([ + cfgVol, + vol, + containerSet, + service, + ]) + + }, + + "grafana" +: { + + create:: function(engine) + + local vol = engine.volume("grafana-storage").with_size("20G"); + + local provDashVol = engine.configVolume( + "prov-dash", "grafana/provisioning/", + { + "dashboard.yml": + importstr "grafana/provisioning/dashboard.yml", + } + + ); + + local provDataVol = engine.configVolume( + "prov-data", "grafana/provisioning/", + { + "datasource.yml": + importstr "grafana/provisioning/datasource.yml", + } + + ); + + local dashVol = engine.configVolume( + "dashboards", "grafana/dashboards/", + { + "overview-dashboard.json": + $["overview-dashboard"], + "log-dashboard.json": + importstr "grafana/dashboards/log-dashboard.json", + } + + ); + + local container = + engine.container("grafana") + .with_image(images.grafana) + .with_environment({ + // GF_AUTH_ANONYMOUS_ORG_ROLE: "Admin", + // GF_AUTH_ANONYMOUS_ENABLED: "true", + // GF_ORG_ROLE: "Admin", + GF_ORG_NAME: "trustgraph.ai", + // GF_SERVER_ROOT_URL: "https://example.com", + }) + .with_limits("1.0", "256M") + .with_reservations("0.5", "256M") + .with_port(3000, 3000, "cassandra") + .with_volume_mount(vol, "/var/lib/grafana") + .with_volume_mount( + provDashVol, "/etc/grafana/provisioning/dashboards/" + ) + .with_volume_mount( + provDataVol, "/etc/grafana/provisioning/datasources/" + ) + .with_volume_mount( + dashVol, "/var/lib/grafana/dashboards/" + ); + + local containerSet = engine.containers( + "grafana", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(3000, 3000, "http"); + + engine.resources([ + vol, + provDashVol, + provDataVol, + dashVol, + containerSet, + service, + ]) + + }, + +} + loki + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/monitoring/loki.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/monitoring/loki.jsonnet new file mode 100644 index 00000000..0774f6cf --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/monitoring/loki.jsonnet @@ -0,0 +1,45 @@ +local images = import "values/images.jsonnet"; + +{ + + "loki" +: { + + create:: function(engine) + + local vol = engine.volume("loki-data").with_size("20G"); + + local cfgVol = engine.configVolume( + "loki-cfg", "loki", + { + "local-config.yaml": importstr "loki/local-config.yaml", + } + ); + + local container = + engine.container("loki") + .with_image(images.loki) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M") + .with_port(3100, 3100, "http") + .with_volume_mount(cfgVol, "/etc/loki/") + .with_volume_mount(vol, "/loki"); + + local containerSet = engine.containers( + "loki", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(3100, 3100, "http"); + + engine.resources([ + cfgVol, + vol, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/ocr/mistral-ocr.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/ocr/mistral-ocr.jsonnet new file mode 100644 index 00000000..cf8085ff --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/ocr/mistral-ocr.jsonnet @@ -0,0 +1,48 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + with:: function(key, value) + self + { + ["mistral-" + key]:: value, + }, + + "document-decoder" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("mistral-credentials") + .with_env_var("MISTRAL_TOKEN", "mistral-token"); + + local container = + engine.container("mistral-ocr") + .with_image(images.trustgraph_flow) + .with_command([ + "pdf-ocr-mistral", + ] + $["pub-sub-args"] + [ + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "mistral-ocr", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/ocr/ocr.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/ocr/ocr.jsonnet new file mode 100644 index 00000000..8f103198 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/ocr/ocr.jsonnet @@ -0,0 +1,36 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +{ + + "document-decoder" +: { + + create:: function(engine) + + local container = + engine.container("pdf-ocr") + .with_image(images.trustgraph_ocr) + .with_command([ + "pdf-ocr", + ] + $["pub-sub-args"] + [ + ]) + .with_limits("1.0", "512M") + .with_reservations("0.1", "512M"); + + local containerSet = engine.containers( + "pdf-ocr", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/azure-openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/azure-openai.jsonnet new file mode 100644 index 00000000..dcf706d4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/azure-openai.jsonnet @@ -0,0 +1,9 @@ +// Azure OpenAI LLM Model Definitions +// Model input is just text + +{ + "type": "string", + "description": "LLM model to use", + "default": "gpt-5.2", + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/azure.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/azure.jsonnet new file mode 100644 index 00000000..6ffa5fdf --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/azure.jsonnet @@ -0,0 +1,9 @@ +// Azure LLM Model Definitions +// Model input is just text + +{ + "type": "string", + "description": "LLM model to use", + "default": "phi4:14b", + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/bedrock.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/bedrock.jsonnet new file mode 100644 index 00000000..6c2c7e90 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/bedrock.jsonnet @@ -0,0 +1,84 @@ +// AWS Bedrock LLM Model Definitions +// Defines available models and their configurations for AWS Bedrock + +{ + "type": "string", + "description": "LLM model to use", + "default": "global.anthropic.claude-sonnet-4-5-20250929-v1:0", + "enum": [ + // ── Anthropic Claude ────────────────────────────────────── + { + id: "global.anthropic.claude-opus-4-6-v1", + description: "Claude Opus 4.6 (maximum intelligence)" + }, + { + id: "global.anthropic.claude-opus-4-5-20251101-v1:0", + description: "Claude Opus 4.5 (frontier coding + agents)" + }, + { + id: "global.anthropic.claude-sonnet-4-5-20250929-v1:0", + description: "Claude Sonnet 4.5 (complex agents + coding)" + }, + { + id: "global.anthropic.claude-haiku-4-5-20251001-v1:0", + description: "Claude Haiku 4.5 (fastest with near-frontier intelligence)" + }, + { + id: "global.anthropic.claude-opus-4-1-20250805-v1:0", + description: "Claude Opus 4.1 (agentic search + reasoning)" + }, + { + id: "global.anthropic.claude-sonnet-4-20250514-v1:0", + description: "Claude Sonnet 4.0" + }, + { + id: "anthropic.claude-3-5-haiku-20241022-v1:0", + description: "Claude 3.5 Haiku (legacy)" + }, + + // ── Meta Llama ──────────────────────────────────────────── + { + id: "us.meta.llama4-maverick-17b-instruct-v1:0", + description: "Llama 4 Maverick 17B (128 experts, 400B params, multimodal)" + }, + { + id: "us.meta.llama4-scout-17b-instruct-v1:0", + description: "Llama 4 Scout 17B (16 experts, 3.5M context)" + }, + { + id: "us.meta.llama3-3-70b-instruct-v1:0", + description: "Llama 3.3 70B Instruct" + }, + + // ── Mistral AI ──────────────────────────────────────────── + { + id: "us.mistral.mistral-large-2511-v1:0", + description: "Mistral Large 3 (flagship text, 128K context)" + }, + { + id: "us.mistral.magistral-small-2506-v1:0", + description: "Magistral Small 1.2 (reasoning, cost-effective)" + }, + + // ── DeepSeek ────────────────────────────────────────────── + { + id: "us.deepseek.r1-v1:0", + description: "DeepSeek-R1 (reasoning)" + }, + + // ── Amazon Nova ─────────────────────────────────────────── + { + id: "us.amazon.nova-pro-v1:0", + description: "Amazon Nova Pro (multimodal, balanced)" + }, + { + id: "us.amazon.nova-lite-v1:0", + description: "Amazon Nova Lite (fast, multimodal)" + }, + { + id: "us.amazon.nova-micro-v1:0", + description: "Amazon Nova Micro (text-only, cheapest)" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/chunking-param-types.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/chunking-param-types.jsonnet new file mode 100644 index 00000000..f82fa3f7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/chunking-param-types.jsonnet @@ -0,0 +1,25 @@ +// Chunk parameter type definitions + +{ + "chunk-size": { + "type": "integer", + "description": "Chunk size", + "placeholder": 2000, + "helper": "An integer, usually 2000 .. 8000", + "default": 2000, + "min": 0, + "max": 32768, + "required": true + }, + "chunk-overlap": { + "type": "integer", + "description": "Chunk overlap", + "placeholder": 50, + "helper": "An integer, usually 50 .. 100", + "default": 50, + "min": 0, + "max": 8000, + "required": true + }, +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/claude.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/claude.jsonnet new file mode 100644 index 00000000..b14a317c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/claude.jsonnet @@ -0,0 +1,39 @@ +// Claude LLM Model Definitions +// Defines available models and their configurations for Anthropic's Claude + +{ + "type": "string", + "description": "LLM model to use", + "default": "claude-sonnet-4-5-20250929", + "enum": [ + { + id: "claude-opus-4-6", + description: "Claude Opus 4.6 (maximum intelligence)" + }, + { + id: "claude-opus-4-5-20251101", + description: "Claude Opus 4.5 (frontier coding + agents)" + }, + { + id: "claude-sonnet-4-5-20250929", + description: "Claude Sonnet 4.5 (complex agents + coding)" + }, + { + id: "claude-haiku-4-5-20251001", + description: "Claude Haiku 4.5 (fast)" + }, + { + id: "claude-opus-4-1-20250805", + description: "Claude Opus 4.1 (agentic search + reasoning)" + }, + { + id: "claude-sonnet-4-20250514", + description: "Claude Sonnet 4.0" + }, + { + id: "claude-3-5-haiku-20241022", + description: "Claude 3.5 Haiku" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/cohere.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/cohere.jsonnet new file mode 100644 index 00000000..5a5865a3 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/cohere.jsonnet @@ -0,0 +1,35 @@ +// Cohere LLM Model Definitions +// Defines available models and their configurations for Cohere + +{ + "type": "string", + "description": "LLM model to use", + "default": "command-r-plus-08-2024", + "enum": [ + { + id: "command-r-plus-08-2024", + description: "Command R+ (August 2024)" + }, + { + id: "command-r-08-2024", + description: "Command R (August 2024)" + }, + { + id: "command-r-plus", + description: "Command R+ (legacy)" + }, + { + id: "command-r", + description: "Command R (legacy)" + }, + { + id: "command", + description: "Command" + }, + { + id: "command-light", + description: "Command Light" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/embeddings-fastembed.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/embeddings-fastembed.jsonnet new file mode 100644 index 00000000..477e25dc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/embeddings-fastembed.jsonnet @@ -0,0 +1,127 @@ +// Embeddings model definitions for fastembed +// Defines available models and their configurations for Fastembed + +{ + "type": "string", + "description": "Embeddings model to use", + "default": "sentence-transformers/all-MiniLM-L6-v2", + "enum": [ + { + "id": "sentence-transformers/all-MiniLM-L6-v2", + "description": "all-MiniLM-L6-v2" + }, + { + "id": "BAAI/bge-small-en-v1.5", + "description": "bge-small-en-v1.5" + }, + { + "id": "BAAI/bge-small-zh-v1.5", + "description": "bge-small-zh-v1.5" + }, + { + "id": "snowflake/snowflake-arctic-embed-xs", + "description": "snowflake-arctic-embed-xs" + }, + { + "id": "jinaai/jina-embeddings-v2-small-en", + "description": "jina-embeddings-v2-small-en" + }, + { + "id": "nomic-ai/nomic-embed-text-v1.5-Q", + "description": "nomic-embed-text-v1.5-Q" + }, + { + "id": "snowflake/snowflake-arctic-embed-s", + "description": "snowflake-arctic-embed-s" + }, + { + "id": "BAAI/bge-small-en", + "description": "bge-small-en" + }, + { + "id": "BAAI/bge-base-en-v1.5", + "description": "bge-base-en-v1.5" + }, + { + "id": "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2", + "description": "paraphrase-multilingual-MiniLM-L12-v2" + }, + { + "id": "Qdrant/clip-ViT-B-32-text", + "description": "clip-ViT-B-32-text" + }, + { + "id": "jinaai/jina-embeddings-v2-base-de", + "description": "jina-embeddings-v2-base-de" + }, + { + "id": "BAAI/bge-base-en", + "description": "bge-base-en" + }, + { + "id": "snowflake/snowflake-arctic-embed-m", + "description": "snowflake-arctic-embed-m" + }, + { + "id": "thenlper/gte-base", + "description": "gte-base" + }, + { + "id": "jinaai/jina-embeddings-v2-base-en", + "description": "jina-embeddings-v2-base-en" + }, + { + "id": "nomic-ai/nomic-embed-text-v1", + "description": "nomic-embed-text-v1" + }, + { + "id": "nomic-ai/nomic-embed-text-v1.5", + "description": "nomic-embed-text-v1.5" + }, + { + "id": "snowflake/snowflake-arctic-embed-m-long", + "description": "snowflake-arctic-embed-m-long" + }, + { + "id": "jinaai/jina-clip-v1", + "description": "jina-clip-v1" + }, + { + "id": "mixedbread-ai/mxbai-embed-large-v1", + "description": "mxbai-embed-large-v1" + }, + { + "id": "jinaai/jina-embeddings-v2-base-es", + "description": "jina-embeddings-v2-base-es" + }, + { + "id": "jinaai/jina-embeddings-v2-base-code", + "description": "jina-embeddings-v2-base-code" + }, + { + "id": "jinaai/jina-embeddings-v2-base-zh", + "description": "jina-embeddings-v2-base-zh" + }, + { + "id": "sentence-transformers/paraphrase-multilingual-mpnet-base-v2", + "description": "paraphrase-multilingual-mpnet-base-v2" + }, + { + "id": "snowflake/snowflake-arctic-embed-l", + "description": "snowflake-arctic-embed-l" + }, + { + "id": "BAAI/bge-large-en-v1.5", + "description": "bge-large-en-v1.5" + }, + { + "id": "thenlper/gte-large", + "description": "gte-large" + }, + { + "id": "intfloat/multilingual-e5-large", + "description": "multilingual-e5-large" + } + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/embeddings-huggingface.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/embeddings-huggingface.jsonnet new file mode 100644 index 00000000..32254a2b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/embeddings-huggingface.jsonnet @@ -0,0 +1,31 @@ +// Embeddings model definitions for fastembed +// Defines available models and their configurations for Fastembed + +{ + "type": "string", + "description": "Embeddings model to use", + "default": "sentence-transformers/all-MiniLM-L6-v2", + "enum": [ + { + "id": "all-MiniLM-L6-v2", + "description": "all-MiniLM-L6-v2" + }, + { + "id": "all-mpnet-base-v2", + "description": "all-mpnet-base-v2" + }, + { + "id": "all-distilroberta-v1", + "description": "all-distilroberta-v1" + }, + { + "id": "stsb-bert-large", + "description": "stsb-bert-large" + }, + { + "id": "sentence-camembert-large", + "description": "sentence-camembert-large" + } + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/embeddings-ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/embeddings-ollama.jsonnet new file mode 100644 index 00000000..c3f29fbb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/embeddings-ollama.jsonnet @@ -0,0 +1,23 @@ +// Embeddings model definitions for Ollama +// Defines available models and their configurations for Ollama + +{ + "type": "string", + "description": "Embeddings model to use", + "default": "all-minilm:latest", + "enum": [ + { + "id": "all-minilm:latest", + "description": "all-MiniLM-L6-v2" + }, + { + "id": "nomic-ai/nomic-embed-text-v1.5-Q", + "description": "nomic-embed-text-v1.5-Q" + }, + { + "id": "mixedbread-ai/mxbai-embed-large-v1", + "description": "mxbai-embed-large-v1" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/googleaistudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/googleaistudio.jsonnet new file mode 100644 index 00000000..6d454ea7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/googleaistudio.jsonnet @@ -0,0 +1,54 @@ +// Google AI Studio LLM Model Definitions +// Defines available models and their configurations for Google AI Studio + +{ + "type": "string", + "description": "LLM model to use", + "default": "gemini-2.5-flash-lite", + "enum": [ + // Gemini 3 models (preview) + { + id: "gemini-3-pro-preview", + description: "Gemini 3 Pro (preview)" + }, + { + id: "gemini-3-flash-preview", + description: "Gemini 3 Flash (preview)" + }, + + // Gemini 2.5 models + { + id: "gemini-2.5-pro", + description: "Gemini 2.5 Pro" + }, + { + id: "gemini-2.5-flash", + description: "Gemini 2.5 Flash" + }, + { + id: "gemini-2.5-flash-lite", + description: "Gemini 2.5 Flash Lite" + }, + + // Gemini 2.0 models + { + id: "gemini-2.0-flash-001", + description: "Gemini 2.0 Flash" + }, + { + id: "gemini-2.0-flash-lite-001", + description: "Gemini 2.0 Flash Lite" + }, + + // Gemma models + { + id: "gemma-3-27b", + description: "Gemma 3 27B" + }, + { + id: "gemma-3n-e4b", + description: "Gemma 3n E4B" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/llamafile.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/llamafile.jsonnet new file mode 100644 index 00000000..f6480aa9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/llamafile.jsonnet @@ -0,0 +1,9 @@ +// LlamaFile LLM Model Definitions +// Model input is just text + +{ + "type": "string", + "description": "LLM model to use", + "default": "phi4:14b", + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/lmstudio.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/lmstudio.jsonnet new file mode 100644 index 00000000..1c88da3c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/lmstudio.jsonnet @@ -0,0 +1,150 @@ +// LMStudio LLM Model Definitions +// Defines available models and their configurations for LMStudio + +{ + "type": "string", + "description": "LLM model to use", + "default": "llama3.1:70b", + "enum": [ + // Gemma3 models + { + id: "gemma3:1b", + description: "Gemma3 1B" + }, + { + id: "gemma3:4b", + description: "Gemma3 4B" + }, + { + id: "gemma3:12b", + description: "Gemma3 12B" + }, + { + id: "gemma3:27b", + description: "Gemma3 27B" + }, + + // Phi4 models + { + id: "phi4:mini:3.8b", + description: "Phi4 Mini 3.8B" + }, + { + id: "phi4:14b", + description: "Phi4 14B" + }, + + // DeepSeek-R1 models + { + id: "deepseek-r1:671b", + description: "DeepSeek-R1 671B" + }, + { + id: "deepseek-r1:70b", + description: "DeepSeek-R1 70B" + }, + { + id: "deepseek-r1:32b", + description: "DeepSeek-R1 32B" + }, + { + id: "deepseek-r1:14b", + description: "DeepSeek-R1 14B" + }, + { + id: "deepseek-r1:8b", + description: "DeepSeek-R1 8B" + }, + { + id: "deepseek-r1:7b", + description: "DeepSeek-R1 7B" + }, + { + id: "deepseek-r1:1.5b", + description: "DeepSeek-R1 1.5B" + }, + + // Llama3.1 models + { + id: "llama3.1:405b", + description: "Llama 3.1 405B" + }, + { + id: "llama3.1:70b", + description: "Llama 3.1 70B" + }, + { + id: "llama3.1:8b", + description: "Llama 3.1 8B" + }, + + // Gemma2 models + { + id: "gemma2:2b", + description: "Gemma2 2B" + }, + { + id: "gemma2:9b", + description: "Gemma2 9B" + }, + { + id: "gemma2:27b", + description: "Gemma2 27B" + }, + + // Qwen2.5 models + { + id: "qwen2.5:0.5b", + description: "Qwen2.5 0.5B" + }, + { + id: "qwen2.5:1.5b", + description: "Qwen2.5 1.5B" + }, + { + id: "qwen2.5:3b", + description: "Qwen2.5 3B" + }, + { + id: "qwen2.5:7b", + description: "Qwen2.5 7B" + }, + { + id: "qwen2.5:14b", + description: "Qwen2.5 14B" + }, + { + id: "qwen2.5:32b", + description: "Qwen2.5 32B" + }, + { + id: "qwen2.5:72b", + description: "Qwen2.5 72B" + }, + + // Mistral models + { + id: "mistral:7b", + description: "Mistral 7B" + }, + { + id: "mistral-nemo:12b", + description: "Mistral Nemo 12B" + }, + { + id: "mistral-large:123b", + description: "Mistral Large 123B" + }, + + // Command-R models + { + id: "command-r:35b", + description: "Command-R 35B" + }, + { + id: "command-r-plus:104b", + description: "Command-R Plus 104B" + }, + ], + "required": true +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/mistral.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/mistral.jsonnet new file mode 100644 index 00000000..07f75b81 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/mistral.jsonnet @@ -0,0 +1,61 @@ +// Mistral LLM Model Definitions +// Defines available models and their configurations for Mistral AI + +{ + "type": "string", + "description": "LLM model to use", + "default": "mistral-medium-2508", + "enum": [ + // Featured models + { + id: "mistral-medium-2508", + description: "Mistral Medium 3.1" + }, + { + id: "mistral-large-2512", + description: "Mistral Large 3" + }, + { + id: "mistral-small-2506", + description: "Mistral Small 3.2" + }, + { + id: "ministral-14b-2512", + description: "Ministral 3 14B" + }, + { + id: "ministral-8b-2512", + description: "Ministral 3 8B" + }, + { + id: "ministral-3b-2512", + description: "Ministral 3 3B" + }, + { + id: "magistral-medium-2509", + description: "Magistral Medium 1.2 (reasoning)" + }, + { + id: "magistral-small-2509", + description: "Magistral Small 1.2 (reasoning)" + }, + { + id: "devstral-2512", + description: "Devstral 2 (code)" + }, + // Other models + { + id: "codestral-2508", + description: "Codestral (code)" + }, + { + id: "pixtral-large-2411", + description: "Pixtral Large (vision)" + }, + { + id: "open-mistral-nemo", + description: "Open Mistral Nemo" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/ollama.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/ollama.jsonnet new file mode 100644 index 00000000..4ef98a6a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/ollama.jsonnet @@ -0,0 +1,150 @@ +// Ollama LLM Model Definitions +// Defines available models and their configurations for Ollama + +{ + "type": "string", + "description": "LLM model to use", + "default": "gemma3:12b", + "enum": [ + // Gemma3 models + { + id: "gemma3:1b", + description: "Gemma3 1B" + }, + { + id: "gemma3:4b", + description: "Gemma3 4B" + }, + { + id: "gemma3:12b", + description: "Gemma3 12B" + }, + { + id: "gemma3:27b", + description: "Gemma3 27B" + }, + + // Phi4 models + { + id: "phi4:mini:3.8b", + description: "Phi4 Mini 3.8B" + }, + { + id: "phi4:14b", + description: "Phi4 14B" + }, + + // DeepSeek-R1 models + { + id: "deepseek-r1:671b", + description: "DeepSeek-R1 671B" + }, + { + id: "deepseek-r1:70b", + description: "DeepSeek-R1 70B" + }, + { + id: "deepseek-r1:32b", + description: "DeepSeek-R1 32B" + }, + { + id: "deepseek-r1:14b", + description: "DeepSeek-R1 14B" + }, + { + id: "deepseek-r1:8b", + description: "DeepSeek-R1 8B" + }, + { + id: "deepseek-r1:7b", + description: "DeepSeek-R1 7B" + }, + { + id: "deepseek-r1:1.5b", + description: "DeepSeek-R1 1.5B" + }, + + // Llama3.1 models + { + id: "llama3.1:405b", + description: "Llama 3.1 405B" + }, + { + id: "llama3.1:70b", + description: "Llama 3.1 70B" + }, + { + id: "llama3.1:8b", + description: "Llama 3.1 8B" + }, + + // Gemma2 models + { + id: "gemma2:2b", + description: "Gemma2 2B" + }, + { + id: "gemma2:9b", + description: "Gemma2 9B" + }, + { + id: "gemma2:27b", + description: "Gemma2 27B" + }, + + // Qwen2.5 models + { + id: "qwen2.5:0.5b", + description: "Qwen2.5 0.5B" + }, + { + id: "qwen2.5:1.5b", + description: "Qwen2.5 1.5B" + }, + { + id: "qwen2.5:3b", + description: "Qwen2.5 3B" + }, + { + id: "qwen2.5:7b", + description: "Qwen2.5 7B" + }, + { + id: "qwen2.5:14b", + description: "Qwen2.5 14B" + }, + { + id: "qwen2.5:32b", + description: "Qwen2.5 32B" + }, + { + id: "qwen2.5:72b", + description: "Qwen2.5 72B" + }, + + // Mistral models + { + id: "mistral:7b", + description: "Mistral 7B" + }, + { + id: "mistral-nemo:12b", + description: "Mistral Nemo 12B" + }, + { + id: "mistral-large:123b", + description: "Mistral Large 123B" + }, + + // Command-R models + { + id: "command-r:35b", + description: "Command-R 35B" + }, + { + id: "command-r-plus:104b", + description: "Command-R Plus 104B" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/openai.jsonnet new file mode 100644 index 00000000..5039c982 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/openai.jsonnet @@ -0,0 +1,51 @@ +// OpenAI LLM Model Definitions +// Defines available models and their configurations for OpenAI's platform + +{ + "type": "string", + "description": "LLM model to use", + "default": "gpt-5.2", + "enum": [ + { + id: "gpt-5.2-pro", + description: "GPT-5.2 Pro (maximum intelligence, slow)" + }, + { + id: "gpt-5.2", + description: "GPT-5.2 (flagship, best general-purpose)" + }, + { + id: "gpt-5.2-codex", + description: "GPT-5.2 Codex (optimized for agentic coding)" + }, + { + id: "gpt-5.1", + description: "GPT-5.1 (previous flagship)" + }, + { + id: "gpt-5", + description: "GPT-5 (previous gen reasoning)" + }, + { + id: "gpt-4.1", + description: "GPT-4.1 (coding + long context, 1M tokens)" + }, + { + id: "gpt-4.1-mini", + description: "GPT-4.1 Mini (fast + affordable)" + }, + { + id: "gpt-4.1-nano", + description: "GPT-4.1 Nano (ultra-fast, cheapest)" + }, + { + id: "gpt-4o", + description: "GPT-4o (legacy multimodal)" + }, + { + id: "gpt-4o-mini", + description: "GPT-4o Mini (legacy, budget)" + }, + ], + "required": true +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/temperature-param-types.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/temperature-param-types.jsonnet new file mode 100644 index 00000000..f5523e88 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/temperature-param-types.jsonnet @@ -0,0 +1,15 @@ +// LLM temperature definitions + +{ + "llm-temperature": { + "type": "float", + "description": "LLM temperature", + "placeholder": 0.3, + "helper": "A floating point number between 0 and 1", + "default": 0.3, + "min": 0.0, + "max": 10.0, + "required": true + } +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/vertexai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/vertexai.jsonnet new file mode 100644 index 00000000..34318967 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/vertexai.jsonnet @@ -0,0 +1,81 @@ +// VertexAI LLM Model Definitions +// Defines available models and their configurations for Google's VertexAI platform + +{ + "type": "string", + "description": "LLM model to use", + "default": "gemini-2.5-flash-lite", + "enum": [ + // Gemini 3 models (preview) + { + id: "gemini-3-pro-preview", + description: "Gemini 3 Pro (preview)" + }, + { + id: "gemini-3-flash-preview", + description: "Gemini 3 Flash (preview)" + }, + + // Gemini 2.5 models + { + id: "gemini-2.5-pro", + description: "Gemini 2.5 Pro" + }, + { + id: "gemini-2.5-flash", + description: "Gemini 2.5 Flash" + }, + { + id: "gemini-2.5-flash-lite", + description: "Gemini 2.5 Flash Lite" + }, + + // Gemini 2.0 models + { + id: "gemini-2.0-flash-001", + description: "Gemini 2.0 Flash" + }, + { + id: "gemini-2.0-flash-lite-001", + description: "Gemini 2.0 Flash Lite" + }, + + // Gemma models + { + id: "gemma-3-27b", + description: "Gemma 3 27B" + }, + { + id: "gemma-3n-e4b", + description: "Gemma 3n E4B" + }, + + // Claude models on VertexAI + { + id: "claude-opus-4-6", + description: "Claude Opus 4.6" + }, + { + id: "claude-opus-4-5", + description: "Claude Opus 4.5" + }, + { + id: "claude-sonnet-4-5", + description: "Claude Sonnet 4.5" + }, + { + id: "claude-haiku-4-5", + description: "Claude Haiku 4.5" + }, + { + id: "claude-opus-4-1", + description: "Claude Opus 4.1" + }, + { + id: "claude-sonnet-4", + description: "Claude Sonnet 4" + }, + ], + "required": true +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/vllm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/vllm.jsonnet new file mode 100644 index 00000000..120342b2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/parameters/vllm.jsonnet @@ -0,0 +1,18 @@ +// vLLM Model Definitions +// Defines available models and their configurations for vLLM +// vLLM works with any HuggingFace model. We have to use the model +// vLLM was initialised with + +{ + "type": "string", + "description": "LLM model to use", + "default": "model", + "enum": [ + // Llama 3.1 models + { + id: "model", + description: "Pre-defined model" + }, + ], + "required": true +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/profiles/memory-profile-low.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/profiles/memory-profile-low.jsonnet new file mode 100644 index 00000000..7a6829e3 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/profiles/memory-profile-low.jsonnet @@ -0,0 +1,169 @@ +// Low memory profile - reduces memory allocation across components. +// Include this at the END of your configuration to override default memory +// settings with lower values suitable for memory-constrained environments. +// +// Usage: {"name": "memory-profile-low", "parameters": {}} +// +// Note: This trades some performance headroom for reduced memory usage. +// Monitor for OOM errors under heavy load. + +{ + + // Override Pulsar stack memory settings + "pulsar" +: { + + // Zookeeper: 512M -> 300M + "zk-memory-limit":: "300M", + "zk-memory-reservation":: "200M", + "zk-heap":: "128m", + "zk-direct-memory":: "64m", + + // Bookie: 1024M -> 600M + "bookie-memory-limit":: "600M", + "bookie-memory-reservation":: "400M", + "bookie-heap":: "128m", + "bookie-direct-memory":: "128m", + + // Broker: 800M -> 512M + "broker-memory-limit":: "512M", + "broker-memory-reservation":: "400M", + "broker-heap":: "192m", + "broker-direct-memory":: "192m", + + // Pulsar-init: 256M -> 128M + "init-memory-limit":: "128M", + "init-memory-reservation":: "128M", + "init-heap":: "64m", + "init-direct-memory":: "64m", + + }, + + // Override Cassandra memory settings: 1000M -> 600M + "cassandra" +: { + "memory-limit":: "600M", + "memory-reservation":: "500M", + "heap":: "200M", + }, + + // Override Qdrant memory settings: 1024M -> 600M + // Also enables mmap for vectors/payloads (trades latency for memory) + "qdrant" +: { + "memory-limit":: "600M", + "memory-reservation":: "500M", + "memmap-threshold-kb":: "1", + "on-disk-payload":: "true", + }, + + // TrustGraph core services - 50% memory reservations + "api-gateway" +: { + "memory-reservation":: "256M", // 512M -> 256M + }, + + "chunker" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "config-svc" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "document-decoder" +: { + "memory-reservation":: "256M", // 512M -> 256M + }, + + "mcp-tool" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "mcp-server" +: { + "memory-reservation":: "128M", // 256M -> 128M + }, + + "metering" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "metering-rag" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "kg-store" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "kg-manager" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "prompt" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "prompt-rag" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "document-rag" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "document-embeddings" +: { + "memory-reservation":: "256M", // 512M -> 256M + }, + + "librarian" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "agent-manager" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + // Graph RAG services + "kg-extract-definitions" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "kg-extract-relationships" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "kg-extract-agent" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "kg-extract-ontology" +: { + "memory-reservation":: "150M", // 300M -> 150M + }, + + "kg-extract-objects" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "graph-rag" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "graph-embeddings" +: { + "memory-reservation":: "256M", // 512M -> 256M + }, + + // Structured data services + "nlp-query" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "structured-query" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + + "structured-diag" +: { + "memory-reservation":: "48M", // 96M -> 48M + }, + + // Init service + "init-trustgraph" +: { + "memory-reservation":: "64M", // 128M -> 64M + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/agent-kg-extract.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/agent-kg-extract.txt new file mode 100644 index 00000000..36dbd74f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/agent-kg-extract.txt @@ -0,0 +1,28 @@ +Analyze the following text and extract both entity definitions and relationships. + +For definitions, extract entities and their explanations or descriptions. +For relationships, extract subject-predicate-object triples where subjects and objects are entities, and predicates are relationship types. + +Text: {{text}} + +Output format: JSONL (one JSON object per line, no array wrapper) +Each line must include a "type" field to distinguish between definitions and relationships. + +For definitions, output: +{"type": "definition", "entity": "entity_name", "definition": "definition_text"} + +For relationships, output: +{"type": "relationship", "subject": "subject_entity", "predicate": "relationship_type", "object": "object_entity_or_literal", "object-entity": true} + +Requirements: +- Each line must be a complete, valid JSON object +- No commas between lines +- No [ ] array brackets +- No markdown formatting or prefixes +- Do not provide explanations, only output JSONL + +Example output: +{"type": "definition", "entity": "DNA", "definition": "Deoxyribonucleic acid, a molecule carrying genetic instructions"} +{"type": "definition", "entity": "RNA", "definition": "Ribonucleic acid, essential for coding and gene expression"} +{"type": "relationship", "subject": "DNA", "predicate": "transcribes_to", "object": "RNA", "object-entity": true} +{"type": "relationship", "subject": "DNA", "predicate": "located_in", "object": "cell nucleus", "object-entity": true} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/agent-prompt.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/agent-prompt.txt new file mode 100644 index 00000000..9155d20f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/agent-prompt.txt @@ -0,0 +1,104 @@ +# ReAct Agent System Prompt + +You are an AI assistant that uses the ReAct (Reasoning + Acting) framework to solve problems through systematic reasoning and tool use. + +## Core Instructions + +For each user query, work through the problem step-by-step using this cycle: +1. **Thought**: Reason about the current situation and determine what you need to do next +2. **Action**: Take ONE specific action using an available tool +3. Wait for **Observation**: The system will provide the result of your action +4. Continue with the next **Thought** based on the observation + +**CRITICAL**: Generate exactly ONE Thought followed by ONE Action, then STOP. Do not generate multiple Thought/Action pairs in a single response. Do not generate Observations yourself - the system will provide them. + +## Response Format + +Use this exact format for each step: + +``` +Thought: [Your reasoning about what to do next - be specific about why this action is needed] +Action: [tool_name] +Args: { + "parameter_name": "value", + "another_parameter": 123, + "list_parameter": ["item1", "item2"] +} +``` + +When you have finished provide the final answer: + +``` +Thought: [Your reasoning about why the process is complete] +Final Answer: [The final answer] +``` + +When providing a final answer, do not provide an Action or Args. + +## Action Format Rules + +1. **Tool Name**: Write "Action: " followed by the exact tool name on its own line +2. **Arguments**: Write "Args: " followed by a valid JSON object containing all parameters +3. **JSON Requirements**: + - Use double quotes for all string keys and values + - Numbers don't need quotes: `"count": 5` + - Booleans: `"enabled": true` or `"enabled": false` + - Arrays: `"items": ["a", "b", "c"]` + - Nested objects: `"config": {"setting": "value"}` + - Null values: `"optional_field": null` +4. **Required Parameters**: Include all required parameters for the tool +5. **No Extra Text**: Don't add explanations or comments within the Action block +1. **Final answer**: Write "Final Answer: " followed by the final answer + +## Available Tools + +{% for tool in tools %}- **{{ tool.name }}**: {{ tool.description }} +{% for arg in tool.arguments %} - Required: `"{{ arg.name }}"` ({{ arg.type }}): {{ arg.description }} +{% endfor %} +{% endfor %} + +## Behavior Rules + +1. **One Step at a Time**: Generate exactly one Thought and one Action, then wait for the system to provide an Observation +2. **Be Specific**: Your Thought should clearly explain why you're taking the specific action +3. **Use Context**: Build on previous Observations to inform your next steps +4. **Error Handling**: If an action fails, reason about the error and try a different approach +5. **Completion**: When you have enough information to fully answer the user's query, generate a final Thought explaining your conclusion, but do not take further actions + +## Error Responses + +If an action fails, you'll see: +``` +Observation: Error: [specific error message] +``` + +When this happens: +- Generate a Thought analyzing what went wrong +- Take a corrective Action with different parameters or a different tool +- If a tool is completely unavailable, explain this limitation in your next Thought + +## Termination + +The conversation ends when: +- You determine you have sufficient information to answer the user's query completely and provide a final answer. +- You encounter an unrecoverable error that prevents task completion +- The system reaches the maximum iteration limit + +## Important Notes + +- **Never generate Observations yourself** - only the system provides these +- **Always validate your JSON** - malformed JSON will cause action failures +- **Stay focused** - each Thought should directly relate to solving the user's query +- **Be efficient** - choose actions that gather the most relevant information for the task + +# Proceed + +Question: {{question}} + +{% for h in history %} +Action: "{{h.action}}" +Args: { +{% for k, v in h.arguments.items() %} "{{k}}": "{{v}}" +{% endfor %}} +Observation: "{{h.observation}}" +{% endfor %} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/cohere.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/cohere.jsonnet new file mode 100644 index 00000000..9541e4c2 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/cohere.jsonnet @@ -0,0 +1,42 @@ +// For Cohere. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/default-prompts.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/default-prompts.jsonnet new file mode 100644 index 00000000..681b5e4a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/default-prompts.jsonnet @@ -0,0 +1,348 @@ + +// Prompt templates. For tidy JSONNET use, don't change these templates +// here, but use over-rides in the prompt directory + +{ + + "system-template":: "You are a helpful assistant.", + + "templates":: { + + "question":: { + "prompt": "{{question}}", + }, + + "extract-concepts":: { + "prompt": importstr "extract-concepts.txt", + "response-type": "text", + }, + + "extract-definitions":: { + "prompt": importstr "extract-definitions.txt", + "response-type": "jsonl", + "object-schema": { + "type": "object", + "properties": { + "entity": { + "type": "string" + }, + "definition": { + "type": "string" + } + }, + "required": [ + "entity", + "definition" + ] + } + }, + + "extract-relationships":: { + "prompt": importstr "extract-relationships.txt", + "response-type": "jsonl", + "object-schema": { + "type": "object", + "properties": { + "subject": { + "type": "string" + }, + "predicate": { + "type": "string" + }, + "object": { + "type": "string" + }, + "object-entity": { + "type": "boolean" + } + }, + "required": [ + "subject", + "predicate", + "object", + "object-entity" + ] + } + }, + + "extract-topics":: { + "prompt": importstr "extract-topics.txt", + "response-type": "jsonl", + "object-schema": { + "type": "object", + "properties": { + "topic": { + "type": "string" + }, + "definition": { + "type": "string" + } + }, + "required": [ + "topic", + "definition" + ] + } + }, + + "extract-rows":: { + "prompt": importstr "extract-rows.txt", + "response-type": "jsonl", + }, + + "kg-prompt":: { + "prompt": importstr "kg-prompt.txt", + "response-type": "text", + }, + + "kg-edge-reasoning":: { + "prompt": importstr "kg-edge-reasoning.txt", + "response-type": "jsonl", + "object-schema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "reasoning": { + "type": "string" + } + }, + "required": [ + "id", + "reasoning" + ] + } + }, + + "kg-edge-scoring":: { + "prompt": importstr "kg-edge-scoring.txt", + "response-type": "jsonl", + "object-schema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "score": { + "type": "number", + "minimum": 1, + "maximum": 10 + } + }, + "required": [ + "id", + "score" + ] + } + }, + + "kg-synthesis":: { + "prompt": importstr "kg-synthesis.txt", + "response-type": "text", + }, + + "document-prompt":: { + "prompt": importstr "document-prompt.txt", + "response-type": "text", + }, + + "agent-react":: { + "prompt": importstr "agent-prompt.txt", + "response-type": "text" + }, + + "agent-kg-extract":: { + "prompt": importstr "agent-kg-extract.txt", + "response-type": "jsonl", + "object-schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { "const": "definition" }, + "entity": { "type": "string" }, + "definition": { "type": "string" } + }, + "required": ["type", "entity", "definition"] + }, + { + "type": "object", + "properties": { + "type": { "const": "relationship" }, + "subject": { "type": "string" }, + "predicate": { "type": "string" }, + "object": { "type": "string" }, + "object-entity": { "type": "boolean" } + }, + "required": ["type", "subject", "predicate", "object"] + } + ] + } + }, + + "schema-selection":: { + "prompt": importstr "schema-selection.txt", + "response-type": "json", + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "description": "An array of schema names that are relevant to answering the given question" + } + }, + + "graphql-generation":: { + "prompt": importstr "graphql-generation.txt", + "response-type": "json", + "schema": { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "The GraphQL query string generated to answer the question" + }, + "variables": { + "type": "object", + "description": "Object containing any GraphQL variables needed for the query", + "additionalProperties": true + }, + "confidence": { + "type": "number", + "minimum": 0.0, + "maximum": 1.0, + "description": "Float between 0.0-1.0 indicating confidence in the generated query" + } + }, + "required": ["query", "variables", "confidence"], + "additionalProperties": false + } + }, + + "diagnose-structured-data":: { + "prompt": importstr "diagnose-structured-data.txt", + "response-type": "json", + }, + + "diagnose-xml":: { + "prompt": importstr "diagnose-xml.txt", + "response-type": "json", + }, + "diagnose-json":: { + "prompt": importstr "diagnose-json.txt", + "response-type": "json", + }, + "diagnose-csv":: { + "prompt": importstr "diagnose-csv.txt", + "response-type": "json", + }, + + "extract-with-ontologies":: { + "prompt": importstr "ontology-prompt.txt", + "response-type": "jsonl", + "object-schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { "const": "entity" }, + "entity": { "type": "string" }, + "entity_type": { "type": "string" } + }, + "required": ["type", "entity", "entity_type"] + }, + { + "type": "object", + "properties": { + "type": { "const": "relationship" }, + "subject": { "type": "string" }, + "subject_type": { "type": "string" }, + "relation": { "type": "string" }, + "object": { "type": "string" }, + "object_type": { "type": "string" } + }, + "required": ["type", "subject", "subject_type", "relation", "object", "object_type"] + }, + { + "type": "object", + "properties": { + "type": { "const": "attribute" }, + "entity": { "type": "string" }, + "entity_type": { "type": "string" }, + "attribute": { "type": "string" }, + "value": { "type": "string" } + }, + "required": ["type", "entity", "entity_type", "attribute", "value"] + } + ] + } + }, + + "task-type-classify":: { + "prompt": importstr "task-type-classify.txt", + "response-type": "text", + }, + + "pattern-select":: { + "prompt": importstr "pattern-select.txt", + "response-type": "text", + }, + + "plan-create":: { + "prompt": importstr "plan-create.txt", + "response-type": "json", + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "goal": {"type": "string"}, + "tool_hint": {"type": "string"}, + "depends_on": { + "type": "array", + "items": {"type": "integer"}, + }, + }, + "required": ["goal"], + }, + } + }, + + + "plan-step-execute": { + "prompt": importstr "plan-step-execute.txt", + "response-type": "json", + "schema": { + "type": "object", + "properties": { + "tool": {"type": "string"}, + "arguments": {"type": "object"} + }, + "required": ["tool", "arguments"] + } + }, + + "plan-synthesise":: { + "prompt": importstr "plan-synthesise.txt", + "response-type": "text", + }, + + "supervisor-decompose":: { + "prompt": importstr "supervisor-decompose.txt", + "response-type": "json", + "schema": { + "type": "array", + "items": {"type": "string"}, + } + }, + + "supervisor-synthesise":: { + "prompt": importstr "supervisor-synthesise.txt", + "response-type": "text", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/diagnose-csv.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/diagnose-csv.txt new file mode 100644 index 00000000..ad9725ba --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/diagnose-csv.txt @@ -0,0 +1,370 @@ +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for data import pipelines, with particular expertise in CSV processing and delimiter-separated value formats. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured CSV data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## CSV Processing Expertise + +When working with CSV data, you must: + +1. **Analyze CSV Structure** - Examine headers, delimiters, quoting, and data patterns +2. **Identify Column Mappings** - Map source column names to target fields +3. **Handle Complex CSV Patterns** - Support various CSV formats including: + - Standard comma-separated values: `name,age,city` + - Alternative delimiters: tab-separated (TSV), pipe-separated, semicolon-separated + - Quoted fields with embedded delimiters: `"Last, First",25,"New York, NY"` + - Headers with spaces or special characters: `"Customer Name","Order Date","Total Amount"` + - Files with or without headers + - Multi-line fields with embedded newlines + +## CSV Format Configuration Guidelines + +For CSV format configurations, use these patterns: + +**Basic CSV Configuration:** +```json +{ + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + "delimiter": ",", + "quote_char": "\"", + "has_header": true, + "skip_rows": 0 + } + } +} +``` + +**Advanced CSV Options:** +```json +{ + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + "delimiter": "\t", // Tab-separated + "quote_char": "\"", + "escape_char": "\\", + "has_header": true, + "skip_rows": 2, // Skip metadata rows + "null_values": ["", "NULL", "N/A"], + "trim_whitespace": true, + "skip_blank_lines": true + } + } +} +``` + +**CRITICAL: Source Field Names in Mappings** + +When processing CSV files, the parser uses column headers (if present) or generates column indices as field names. Your source field names in mappings must match exactly: + +**CORRECT Example with Headers:** +CSV file: +```csv +Customer Name,Order Date,Total Amount,Status +John Smith,2024-01-15,1000.50,Active +Jane Doe,2024-01-16,750.25,Pending +``` + +Becomes parsed data: +```json +{ + "Customer Name": "John Smith", + "Order Date": "2024-01-15", + "Total Amount": "1000.50", + "Status": "Active" +} +``` + +Your mappings should use: +```json +{ + "source_field": "Customer Name", // ✅ Correct - matches header exactly + "source_field": "Order Date", // ✅ Correct - matches header exactly + "source_field": "Total Amount", // ✅ Correct - matches header exactly + "source_field": "Status" // ✅ Correct - matches header exactly +} +``` + +**CORRECT Example without Headers:** +CSV file without headers uses column indices: +```csv +John Smith,2024-01-15,1000.50,Active +Jane Doe,2024-01-16,750.25,Pending +``` + +Becomes parsed data: +```json +{ + "0": "John Smith", + "1": "2024-01-15", + "2": "1000.50", + "3": "Active" +} +``` + +Your mappings should use: +```json +{ + "source_field": "0", // ✅ Correct - first column + "source_field": "1", // ✅ Correct - second column + "source_field": "2", // ✅ Correct - third column + "source_field": "3" // ✅ Correct - fourth column +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **Source Data Format** + - Delimiter character (comma, tab, pipe, semicolon, etc.) + - Quote character and escape character + - **For CSV**: Does the file have headers? Sample structure + - Text encoding (UTF-8, Windows-1252, etc.) + - Any rows to skip (metadata, blank lines) + - How null/empty values are represented + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source column → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + - Date/time format conversions + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or invalid data + - Duplicate handling strategy + - Row-level validation rules + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## CSV Structure Analysis + +When presented with CSV data, analyze: + +1. **Delimiter Detection**: What character separates the fields? +2. **Header Presence**: Does the first row contain column names? +3. **Quote Pattern**: Are fields quoted? What quote character is used? +4. **Data Types**: What types are present in each column? +5. **Null Representation**: How are empty/null values represented? +6. **Special Characters**: Are there embedded commas, quotes, or newlines? +7. **Encoding Issues**: Are there any character encoding problems? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + // CSV-specific parsing options + // delimiter, quote_char, has_header, skip_rows, etc. + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` +- `split`, `strip_quotes` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` +- `parse_number`, `parse_currency` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` +- `clean_whitespace`, `normalize_encoding` + +**Date/Time Operations:** +- `parse_date`, `format_date`, `date_component` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` +- `numeric_range`, `date_range` + +## CSV-Specific Best Practices + +1. **Detect delimiters accurately** - test with sample data to confirm delimiter +2. **Handle quoted fields properly** - account for embedded delimiters and quotes +3. **Trim whitespace consistently** - decide whether to preserve or remove extra spaces +4. **Validate data types early** - catch type conversion errors at the field level +5. **Handle empty values explicitly** - distinguish between empty strings and nulls +6. **Account for encoding issues** - especially with international characters +7. **Validate row structure** - ensure consistent column counts across rows + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes +9. **Handle CSV-specific edge cases** like malformed quotes, inconsistent delimiters +10. **Test with sample data** to validate parsing configuration + +## Complete CSV Example + +Given this CSV structure: +```csv +Customer Name,Order Date,Total Amount,Currency,Status +"Smith, John",2024-01-15,1000.50,USD,Active +"Doe, Jane",2024-01-16,750.25,EUR,Pending +"Johnson, Bob",2024-01-17,,USD,Cancelled +``` + +The parser will: +1. Use `delimiter: ","` and `quote_char: "\""` to parse fields correctly +2. Use `has_header: true` to treat first row as column names +3. Create this parsed data structure for each record: + ```json + { + "Customer Name": "Smith, John", + "Order Date": "2024-01-15", + "Total Amount": "1000.50", + "Currency": "USD", + "Status": "Active" + } + ``` + +Generate this COMPLETE configuration: +```json +{ + "format": { + "type": "csv", + "encoding": "utf-8", + "options": { + "delimiter": ",", + "quote_char": "\"", + "has_header": true, + "trim_whitespace": true, + "null_values": ["", "NULL"] + } + }, + "mappings": [ + { + "source_field": "Customer Name", // ✅ Matches header exactly + "target_field": "customer_name", + "transforms": [{"type": "trim"}] + }, + { + "source_field": "Order Date", // ✅ Matches header exactly + "target_field": "order_date", + "transforms": [{"type": "to_date", "format": "YYYY-MM-DD"}], + "validation": [{"type": "required"}] + }, + { + "source_field": "Total Amount", // ✅ Matches header exactly + "target_field": "amount", + "transforms": [ + {"type": "to_float"}, + {"type": "default", "value": 0.0} + ] + }, + { + "source_field": "Currency", // ✅ Matches header exactly + "target_field": "currency_code", + "transforms": [{"type": "upper"}], + "validation": [{"type": "in_list", "values": ["USD", "EUR", "GBP"]}] + }, + { + "source_field": "Status", // ✅ Matches header exactly + "target_field": "order_status", + "transforms": [{"type": "lower"}] + } + ] +} +``` + +**KEY RULE: source_field names must match the column headers exactly, or use column indices (0, 1, 2, etc.) for files without headers.** + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the CSV structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to delimiter detection, header identification, quoting patterns, and data type inference: + +{{sample}} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/diagnose-json.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/diagnose-json.txt new file mode 100644 index 00000000..2fe6341d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/diagnose-json.txt @@ -0,0 +1,327 @@ +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for data import pipelines, with particular expertise in JSON processing and JSONPath expressions. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured JSON data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## JSON Processing Expertise + +When working with JSON data, you must: + +1. **Analyze JSON Structure** - Examine the hierarchy, array patterns, and object nesting +2. **Generate Proper JSONPath Expressions** - Create efficient JSONPath selectors for record extraction +3. **Handle Complex JSON Patterns** - Support various JSON formats including: + - Array of objects: `[{"name": "John", "age": 30}, {...}]` + - Nested object arrays: `{"data": {"records": [{"id": 1}, {...}]}}` + - Mixed hierarchies with both arrays and nested objects + - Single object records: `{"record": {"field1": "value1"}}` + +## JSONPath Expression Guidelines + +For JSON format configurations, use these JSONPath patterns: + +**Record Path Examples:** +- Root array: `$[*]` (for arrays at the root level) +- Nested arrays: `$.data.records[*]` or `$.response.items[*]` +- Single object: `$.record` (when there's one record per file) +- Deep nesting: `$.data.results.items[*]` + +**Field Access Patterns:** +- Direct properties: Use property names directly in mappings +- Nested properties: Use dot notation like `address.street` or `contact.email` +- Array elements: Use bracket notation like `tags[0]` for first element + +**CRITICAL: Source Field Names in Mappings** + +When processing JSON, the parser creates a flat or nested dictionary based on the record structure. Your source field names in mappings must match the actual property names in the parsed records: + +**CORRECT Example:** +```json +{ + "Country or Area": "Albania", + "Trade (USD)": "1000.50", + "metadata": { + "source": "UN", + "year": 2024 + } +} +``` + +Your mappings should use: +```json +{ + "source_field": "Country or Area", // ✅ Correct - matches property name + "source_field": "Trade (USD)", // ✅ Correct - matches property name + "source_field": "metadata.source", // ✅ Correct - nested property access + "source_field": "metadata.year" // ✅ Correct - nested property access +} +``` + +**JSON Format Configuration Template:** +```json +{ + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + "record_path": "$[*]", // JSONPath to extract records + "flatten_nested": true, // Whether to flatten nested objects + "array_handling": "expand" // How to handle arrays: expand, first, concat + } + } +} +``` + +**Alternative JSON Options:** +```json +{ + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + "record_path": "$.data.items[*]", // For nested array structures + "flatten_nested": false, // Keep nested structure + "null_value_handling": "skip" // skip, empty_string, or preserve + } + } +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **Source Data Format** + - JSON structure type (array of objects, nested objects, single records) + - **For JSON**: Sample structure, nesting patterns, array locations + - Sample data or field descriptions + - Any format-specific details (encoding, special null handling, etc.) + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source field → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + - Nested object flattening requirements + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or null values + - Duplicate handling strategy + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## JSON Structure Analysis + +When presented with JSON data, analyze: + +1. **Root Structure**: Is it an array, object, or nested structure? +2. **Record Location**: Where are individual records located in the hierarchy? +3. **Field Pattern**: How are field names and values structured? + - Direct properties: `{"name": "John"}` + - Nested objects: `{"contact": {"email": "john@example.com"}}` + - Arrays: `{"tags": ["red", "blue"]}` + - Mixed types: `{"data": [{"id": 1, "details": {"name": "John"}}]}` +4. **Data Types**: What types are present (strings, numbers, booleans, nulls, arrays, objects)? +5. **Hierarchy Depth**: How deeply nested are the records and fields? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + // JSON-specific parsing options + // record_path (JSONPath), flatten_nested, array_handling, etc. + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` +- `flatten_object`, `extract_array_element`, `join_array` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` + +## JSON-Specific Best Practices + +1. **Use efficient JSONPath expressions** - Prefer specific paths over broad searches +2. **Handle nested objects appropriately** - decide whether to flatten or preserve structure +3. **Consider array handling strategies** - expand arrays to multiple records or extract specific elements +4. **Account for null vs undefined** values in field mappings +5. **Handle mixed data types** within the same field across records +6. **Use appropriate flattening** for deeply nested structures + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes +9. **Handle JSON-specific edge cases** like empty arrays, null objects, mixed types + +## Complete JSON Example + +Given this JSON structure: +```json +{ + "data": { + "records": [ + { + "Country": "USA", + "Year": 2024, + "Amount": 1000.50, + "metadata": { + "source": "World Bank", + "confidence": 0.95 + } + } + ] + } +} +``` + +The parser will: +1. Use `record_path: "$.data.records[*]"` to extract record objects from the array +2. Create this parsed data structure for each record: + ```json + { + "Country": "USA", + "Year": 2024, + "Amount": 1000.50, + "metadata.source": "World Bank", // If flattened + "metadata.confidence": 0.95 // If flattened + } + ``` + +Generate this COMPLETE configuration: +```json +{ + "format": { + "type": "json", + "encoding": "utf-8", + "options": { + "record_path": "$.data.records[*]", + "flatten_nested": true, + "array_handling": "expand" + } + }, + "mappings": [ + { + "source_field": "Country", // ✅ Matches property name + "target_field": "country_name" + }, + { + "source_field": "Year", // ✅ Matches property name + "target_field": "year", + "transforms": [{"type": "to_int"}] + }, + { + "source_field": "Amount", // ✅ Matches property name + "target_field": "amount", + "transforms": [{"type": "to_float"}] + }, + { + "source_field": "metadata.source", // ✅ Flattened nested field + "target_field": "data_source" + } + ] +} +``` + +**KEY RULE: source_field names must match the actual property names in the parsed JSON records, using dot notation for nested properties when flattened.** + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the JSON structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to JSON hierarchy, object patterns, array structures, and generate appropriate JSONPath expressions: + +{{sample}} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/diagnose-structured-data.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/diagnose-structured-data.txt new file mode 100644 index 00000000..84c4b8be --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/diagnose-structured-data.txt @@ -0,0 +1,309 @@ + +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for data import pipelines, with particular expertise in XML processing and XPath expressions. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## XML Processing Expertise + +When working with XML data, you must: + +1. **Analyze XML Structure** - Examine the hierarchy, namespaces, and element patterns +2. **Generate Proper XPath Expressions** - Create efficient XPath selectors for record extraction +3. **Handle Complex XML Patterns** - Support various XML formats including: + - Standard element structures: `John` + - Attribute-based fields: `USA` + - Mixed content and nested hierarchies + - Namespaced XML documents + +## XPath Expression Guidelines + +For XML format configurations, use these XPath patterns: + +**Record Path Examples:** +- Simple records: `//record` or `//customer` +- Nested records: `//data/records/record` or `//customers/customer` +- Absolute paths: `/ROOT/data/record` (will be converted to relative paths automatically) +- With namespaces: `//ns:record` or `//soap:Body/data/record` + +**Field Attribute Patterns:** +- When fields use name attributes: set `field_attribute: "name"` for `value` +- For other attribute patterns: set appropriate attribute name + +**CRITICAL: Source Field Names in Mappings** + +When using `field_attribute`, the XML parser extracts field names from the attribute values and creates a flat dictionary. Your source field names in mappings must match these extracted names: + +**CORRECT Example:** +```xml +Albania +1000.50 +``` + +Becomes parsed data: +```json +{ + "Country or Area": "Albania", + "Trade (USD)": "1000.50" +} +``` + +So your mappings should use: +```json +{ + "source_field": "Country or Area", // ✅ Correct - matches parsed field name + "source_field": "Trade (USD)" // ✅ Correct - matches parsed field name +} +``` + +**INCORRECT Example:** +```json +{ + "source_field": "Field[@name='Country or Area']", // ❌ Wrong - XPath not needed here + "source_field": "field[@name='Trade (USD)']" // ❌ Wrong - XPath not needed here +} +``` + +**XML Format Configuration Template:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//data/record", // XPath to find record elements + "field_attribute": "name" // For value pattern + } + } +} +``` + +**Alternative XML Options:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//customer", // Direct element-based records + // No field_attribute needed for standard XML + } + } +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **Source Data Format** + - File type (CSV, JSON, XML, Excel, fixed-width, etc.) + - **For XML**: Sample structure, namespace prefixes, record element patterns + - Sample data or field descriptions + - Any format-specific details (delimiters, encoding, namespaces, etc.) + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source field → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or invalid data + - Duplicate handling strategy + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## XML Structure Analysis + +When presented with XML data, analyze: + +1. **Document Root**: What is the root element? +2. **Record Container**: Where are individual records located? +3. **Field Pattern**: How are field names and values structured? + - Direct child elements: `John` + - Attribute-based: `John` + - Mixed patterns +4. **Namespaces**: Are there any namespace prefixes? +5. **Hierarchy Depth**: How deeply nested are the records? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "[csv|json|xml|fixed-width|excel]", + "encoding": "utf-8", + "options": { + // Format-specific parsing options + // For XML: record_path (XPath), field_attribute (if applicable) + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` + +## XML-Specific Best Practices + +1. **Use efficient XPath expressions** - Prefer specific paths over broad searches +2. **Handle namespace prefixes** when present +3. **Identify field attribute patterns** correctly +4. **Test XPath expressions** mentally against the provided structure +5. **Consider XML element vs attribute data** in field mappings +6. **Account for mixed content** and nested structures + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes + +## Complete XML Example + +Given this XML structure: +```xml + + + + USA + 2024 + 1000.50 + + + +``` + +The parser will: +1. Use `record_path: "/ROOT/data/record"` to find record elements +2. Use `field_attribute: "name"` to extract field names from the name attribute +3. Create this parsed data structure: `{"Country": "USA", "Year": "2024", "Amount": "1000.50"}` + +Generate this COMPLETE configuration: +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "/ROOT/data/record", + "field_attribute": "name" + } + }, + "mappings": [ + { + "source_field": "Country", // ✅ Matches parsed field name + "target_field": "country_name" + }, + { + "source_field": "Year", // ✅ Matches parsed field name + "target_field": "year", + "transforms": [{"type": "to_int"}] + }, + { + "source_field": "Amount", // ✅ Matches parsed field name + "target_field": "amount", + "transforms": [{"type": "to_float"}] + } + ] +} +``` + +**KEY RULE: source_field names must match the extracted field names, NOT the XML element structure.** + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the XML structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to XML hierarchy, element patterns, and generate appropriate XPath expressions: + +{{sample}} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/diagnose-xml.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/diagnose-xml.txt new file mode 100644 index 00000000..a3d8efa4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/diagnose-xml.txt @@ -0,0 +1,453 @@ +You are an expert data engineer specializing in creating Structured Data Descriptor configurations for XML data import pipelines, with particular expertise in XML processing and XPath expressions. Your task is to generate a complete JSON configuration that describes how to parse, transform, and import structured XML data. + +## Your Role +Generate a comprehensive Structured Data Descriptor configuration based on the user's requirements. The descriptor should be production-ready, include appropriate error handling, and follow best practices for data quality and transformation. + +## XML Processing Expertise + +When working with XML data, you must: + +1. **Analyze XML Structure** - Examine the hierarchy, namespaces, and element patterns +2. **Generate Proper XPath Expressions** - Create efficient XPath selectors for record extraction +3. **Handle Complex XML Patterns** - Support various XML formats including: + - Standard element structures: `John` + - Attribute-based fields: `USA` + - Mixed content and nested hierarchies + - Namespaced XML documents + - CDATA sections and text nodes + +## XPath Expression Guidelines + +For XML format configurations, use these XPath patterns: + +**Record Path Examples:** +- Simple records: `//record` or `//customer` +- Nested records: `//data/records/record` or `//customers/customer` +- Absolute paths: `/ROOT/data/record` +- With namespaces: `//ns:record` or `//soap:Body/data/record` +- Attribute-filtered: `//record[@type='customer']` + +**Field Attribute Patterns:** +- When fields use name attributes: set `field_attribute: "name"` for `value` +- For other attribute patterns: set appropriate attribute name like `field_attribute: "id"` or `field_attribute: "type"` + +## CRITICAL: Source Field Names in Mappings + +The behavior depends on whether you use `field_attribute`: + +### With field_attribute (Attribute-Based Fields) + +When using `field_attribute`, the XML parser extracts field names from the attribute values and creates a flat dictionary: + +**Example:** +```xml + + Albania + 1000.50 + Active + +``` + +Parser configuration: +```json +{ + "record_path": "//record", + "field_attribute": "name" +} +``` + +Becomes parsed data: +```json +{ + "Country or Area": "Albania", + "Trade (USD)": "1000.50", + "Status": "Active" +} +``` + +Your mappings should use: +```json +{ + "source_field": "Country or Area", // ✅ Correct - matches parsed field name + "source_field": "Trade (USD)", // ✅ Correct - matches parsed field name + "source_field": "Status" // ✅ Correct - matches parsed field name +} +``` + +### Without field_attribute (Element-Based Fields) + +When NOT using `field_attribute`, use direct element names or XPath expressions: + +**Example:** +```xml + + Albania + 1000.50 + Active + + UN + 2024 + + +``` + +Parser configuration: +```json +{ + "record_path": "//record" + // No field_attribute specified +} +``` + +Your mappings should use: +```json +{ + "source_field": "country", // ✅ Direct element name + "source_field": "trade_amount", // ✅ Direct element name + "source_field": "metadata/source", // ✅ Nested element path + "source_field": "metadata/year" // ✅ Nested element path +} +``` + +## XML Format Configuration Templates + +**For Attribute-Based Fields:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//data/record", // XPath to find record elements + "field_attribute": "name", // Extract field names from 'name' attribute + "namespace_prefixes": { // Optional: define namespaces + "ns": "http://example.com/namespace" + } + } + } +} +``` + +**For Element-Based Fields:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//customer", // XPath to find record elements + "preserve_namespaces": true, // Optional: keep namespace info + "ignore_attributes": false // Optional: include element attributes + } + } +} +``` + +**For Complex XML with Namespaces:** +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//soap:Body//data:record", + "namespace_prefixes": { + "soap": "http://schemas.xmlsoap.org/soap/envelope/", + "data": "http://example.com/data" + }, + "field_attribute": "name" + } + } +} +``` + +## Required Information to Gather + +Before generating the descriptor, ask the user for these details if not provided: + +1. **XML Structure Details** + - Sample XML structure or schema + - Root element and record location + - Field organization (elements vs attributes) + - Namespace declarations and prefixes + - Text encoding (UTF-8, ISO-8859-1, etc.) + +2. **Target Schema** + - What fields should be in the final output? + - What data types are expected? + - Any required vs optional fields? + +3. **Data Transformations Needed** + - Field mappings (source field → target field) + - Data cleaning requirements (trim spaces, normalize case, etc.) + - Type conversions needed + - Any calculations or derived fields + - Lookup tables or reference data needed + +4. **Data Quality Requirements** + - Validation rules (format patterns, ranges, required fields) + - How to handle missing or invalid data + - Duplicate handling strategy + +5. **Processing Requirements** + - Any filtering needed (skip certain records) + - Sorting requirements + - Aggregation or grouping needs + - Error handling preferences + +## XML Structure Analysis + +When presented with XML data, analyze: + +1. **Document Root**: What is the root element? +2. **Namespaces**: Are there namespace declarations? What prefixes are used? +3. **Record Container**: Where are individual records located in the hierarchy? +4. **Field Pattern**: How are field names and values structured? + - Direct child elements: `John` + - Attribute-based: `John` + - Mixed patterns: `John` +5. **Data Location**: Are values in element text, attributes, or both? +6. **Hierarchy Depth**: How deeply nested are the records? +7. **Special Content**: Any CDATA sections, mixed content, or special characters? + +## Configuration Template Structure + +Generate a JSON configuration following this structure: + +```json +{ + "version": "1.0", + "metadata": { + "name": "[Descriptive name]", + "description": "[What this config does]", + "author": "[Author or team]", + "created": "[ISO date]" + }, + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + // XML-specific parsing options + // record_path (XPath), field_attribute (if applicable), namespace_prefixes + } + }, + "globals": { + "variables": { + // Global variables and constants + }, + "lookup_tables": { + // Reference data for transformations + } + }, + "preprocessing": [ + // Global filters and operations before field mapping + ], + "mappings": [ + // Field mapping definitions with transforms and validation + ], + "postprocessing": [ + // Global operations after field mapping + ], + "output": { + "format": "trustgraph-objects", + "schema_name": "[target schema name]", + "options": { + "confidence": 0.85, + "batch_size": 1000 + }, + "error_handling": { + "on_validation_error": "log_and_skip", + "on_transform_error": "log_and_skip", + "max_errors": 100 + } + } +} +``` + +## Transform Types Available + +Use these transform types in your mappings: + +**String Operations:** +- `trim`, `upper`, `lower`, `title_case` +- `replace`, `regex_replace`, `substring`, `pad_left` +- `strip_cdata`, `decode_html_entities` + +**Type Conversions:** +- `to_string`, `to_int`, `to_float`, `to_bool`, `to_date` +- `parse_xml_date`, `parse_iso_date` + +**Data Operations:** +- `default`, `lookup`, `concat`, `calculate`, `conditional` +- `extract_attribute`, `get_text_content` + +**Validation Types:** +- `required`, `not_null`, `min_length`, `max_length` +- `range`, `pattern`, `in_list`, `custom` +- `valid_xml_name`, `namespace_check` + +## XML-Specific Best Practices + +1. **Use efficient XPath expressions** - Prefer specific paths over broad searches like `//` when possible +2. **Handle namespace prefixes** when present - define them in `namespace_prefixes` +3. **Identify field attribute patterns** correctly - determine if using `field_attribute` +4. **Test XPath expressions** mentally against the provided structure +5. **Consider XML element vs attribute data** in field mappings +6. **Account for mixed content** and nested structures +7. **Handle CDATA sections** and special characters properly +8. **Preserve or normalize whitespace** as appropriate +9. **Consider XML schema validation** if XSD is available + +## Best Practices to Follow + +1. **Always include error handling** with appropriate policies +2. **Use meaningful field names** that match target schema +3. **Add validation** for critical fields +4. **Include default values** for optional fields +5. **Use lookup tables** for code translations +6. **Add preprocessing filters** to exclude invalid records +7. **Include metadata** for documentation and maintenance +8. **Consider performance** with appropriate batch sizes +9. **Handle XML-specific edge cases** like empty elements, mixed content, processing instructions + +## Complete XML Examples + +### Example 1: Attribute-Based Fields + +Given this XML structure: +```xml + + + + USA + 2024 + 1000.50 + + + +``` + +The parser will: +1. Use `record_path: "//data/record"` to find record elements +2. Use `field_attribute: "name"` to extract field names from the name attribute +3. Create this parsed data structure: `{"Country": "USA", "Year": "2024", "Amount": "1000.50"}` + +Generate this configuration: +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//data/record", + "field_attribute": "name" + } + }, + "mappings": [ + { + "source_field": "Country", // ✅ Matches parsed field name + "target_field": "country_name" + }, + { + "source_field": "Year", // ✅ Matches parsed field name + "target_field": "year", + "transforms": [{"type": "to_int"}] + }, + { + "source_field": "Amount", // ✅ Matches parsed field name + "target_field": "amount", + "transforms": [{"type": "to_float"}] + } + ] +} +``` + +### Example 2: Element-Based Fields + +Given this XML structure: +```xml + + + John Smith + john@example.com +
+ 123 Main St + New York +
+ + + 456 + 100.50 + + +
+
+``` + +Generate this configuration: +```json +{ + "format": { + "type": "xml", + "encoding": "utf-8", + "options": { + "record_path": "//customer" + } + }, + "mappings": [ + { + "source_field": "name", // ✅ Direct element name + "target_field": "customer_name" + }, + { + "source_field": "email", // ✅ Direct element name + "target_field": "email_address" + }, + { + "source_field": "address/street", // ✅ Nested element path + "target_field": "street_address" + }, + { + "source_field": "address/city", // ✅ Nested element path + "target_field": "city" + }, + { + "source_field": "@id", // ✅ Attribute reference + "target_field": "customer_id", + "transforms": [{"type": "to_int"}] + } + ] +} +``` + +**KEY RULES:** +- With `field_attribute`: source_field names must match the extracted attribute values +- Without `field_attribute`: use direct element names, nested paths, or XPath expressions +- Use `@attribute` notation for XML element attributes + +## Output Format + +Provide the configuration as ONLY a properly formatted JSON document. + +## Schema + +The following schema describes the target result format: + +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif +%}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif +%}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ +field.enum_values|join(', ') }}]{% endif %} +{% endfor %} + +{% endfor %} + +## Data sample + +Analyze the XML structure and produce a Structured Data Descriptor by diagnosing the following data sample. Pay special attention to XML hierarchy, element patterns, namespace usage, and generate appropriate XPath expressions: + +{{sample}} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/document-prompt.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/document-prompt.txt new file mode 100644 index 00000000..5e5d65ba --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/document-prompt.txt @@ -0,0 +1,7 @@ +Study the following context. Use only the information provided in the context in your response. Do not speculate if the answer is not found in the provided set of knowledge statements. + +Here is the context: +{{documents}} + +Use only the provided knowledge statements to respond to the following: +{{query}} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/extract-concepts.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/extract-concepts.txt new file mode 100644 index 00000000..cc4b600c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/extract-concepts.txt @@ -0,0 +1,12 @@ +Extract core semantic concepts from the following query. These concepts will be used for semantic search and embedding. + +Output one concept per line. No numbering, no bullet points, no explanation. + +Focus on: +- Key nouns and noun phrases +- Domain-specific terminology +- Languages or time periods mentioned +- Technical terms + +Query: +{{query}} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/extract-definitions.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/extract-definitions.txt new file mode 100644 index 00000000..410ecddf --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/extract-definitions.txt @@ -0,0 +1,28 @@ + +Study the following text and derive definitions for any discovered entities. +Do not provide definitions for entities whose definitions are incomplete +or unknown. + +Output each definition as a separate JSON object on its own line (JSONL format). +Each object must have fields: +- entity: the name of the entity +- definition: English text which defines the entity + + + +{{text}} + + + +Output format: JSONL (one JSON object per line, no array wrapper) +- Each line must be a complete, valid JSON object +- No commas between lines +- No [ ] array brackets +- No markdown formatting or prefixes +- Do not use special characters in the definition text +- Do not include null or unknown definitions + +Example output format: +{"entity": "photosynthesis", "definition": "The process by which plants convert sunlight into energy"} +{"entity": "chlorophyll", "definition": "Green pigment in plants that absorbs light"} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/extract-relationships.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/extract-relationships.txt new file mode 100644 index 00000000..31bcd2d4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/extract-relationships.txt @@ -0,0 +1,28 @@ + +Study the following text and derive entity relationships. For each +relationship, derive the subject, predicate and object of the relationship. + +Output each relationship as a separate JSON object on its own line (JSONL format). +Each object must have fields: +- subject: the subject of the relationship +- predicate: the predicate +- object: the object of the relationship +- object-entity: false if the object is a simple data type (name, value, date). true if it is an entity. + + + +{{text}} + + + +Output format: JSONL (one JSON object per line, no array wrapper) +- Each line must be a complete, valid JSON object +- No commas between lines +- No [ ] array brackets +- No markdown formatting or prefixes +- Do not use special characters in the text fields + +Example output format: +{"subject": "Earth", "predicate": "orbits", "object": "Sun", "object-entity": true} +{"subject": "Earth", "predicate": "has diameter", "object": "12742 km", "object-entity": false} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/extract-rows.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/extract-rows.txt new file mode 100644 index 00000000..7cea8b7d --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/extract-rows.txt @@ -0,0 +1,27 @@ + +Study the following text and derive objects which match the schema provided. + +Output each discovered object as a separate JSON object on its own line (JSONL format). +Each object's fields must carry the name field specified in the schema. + + + +{{schema}} + + + +{{text}} + + + +Output format: JSONL (one JSON object per line, no array wrapper) +- Each line must be a complete, valid JSON object matching the schema +- No commas between lines +- No [ ] array brackets +- No markdown formatting or prefixes +- Do not provide explanations + +Example output format (assuming schema has fields: name, age, city): +{"name": "Alice", "age": 30, "city": "London"} +{"name": "Bob", "age": 25, "city": "Paris"} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/extract-topics.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/extract-topics.txt new file mode 100644 index 00000000..bb4b1193 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/extract-topics.txt @@ -0,0 +1,26 @@ +You are a helpful assistant that performs information extraction tasks for a provided text. +Read the provided text. You will identify topics and their definitions. + +Reading Instructions: +- Ignore document formatting in the provided text. +- Study the provided text carefully. + +Here is the text: +{{text}} + +Response Instructions: +- Output each topic as a separate JSON object on its own line (JSONL format) +- Each object must have keys "topic" and "definition" +- Do not respond with special characters +- Return only topics that are concepts and unique to the provided text + +Output format: JSONL (one JSON object per line, no array wrapper) +- Each line must be a complete, valid JSON object +- No commas between lines +- No [ ] array brackets +- No markdown formatting or prefixes +- Do not write any additional text or explanations + +Example output format: +{"topic": "machine learning", "definition": "A subset of AI that enables systems to learn from data"} +{"topic": "neural network", "definition": "A computing system inspired by biological neural networks"} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/gemini.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/gemini.jsonnet new file mode 100644 index 00000000..b9a1e0c0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/gemini.jsonnet @@ -0,0 +1,42 @@ +// For VertexAI Gemini. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/graphql-generation.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/graphql-generation.txt new file mode 100644 index 00000000..58af61dc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/graphql-generation.txt @@ -0,0 +1,59 @@ +You are a GraphQL query generation expert. Given a natural language question and provided database schemas, generate a valid GraphQL query embedded in valid JSON. + +## Question: +{{ question }} + +## Provided Schemas: +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %}- {{ field.name }} ({{ field.type }}){% if field.description %}: {{ field.description }}{% endif %}{% if field.primary_key %} [PRIMARY KEY]{% endif %}{% if field.required %} [REQUIRED]{% endif %}{% if field.indexed %} [INDEXED]{% endif %}{% if field.enum_values %} [OPTIONS: {{ field.enum_values|join(', ') }}]{% endif %} +{% endfor %} +{% endfor %} + +## GraphQL Query Rules: +1. Use the schema names as GraphQL query fields (e.g., `customers`, `orders`) +2. Apply filters using the `where` parameter with nested filter objects +3. Available filter operators per field type: + - String fields: `eq`, `contains`, `startsWith`, `endsWith`, `in`, `not`, `not_in` + - Integer/Float fields: `eq`, `gt`, `gte`, `lt`, `lte`, `in`, `not`, `not_in` +4. Use `order_by` for sorting (field name as string) +5. Use `direction` for sort direction: `ASC` or `DESC` +6. Use `limit` to restrict number of results +7. Select specific fields in the query body + +## Task Instructions: +1. Analyze the question to identify: + - What data to retrieve (which fields to select) + - What filters to apply (where conditions) + - What sorting is needed (order_by, direction) + - How many results (limit) + +2. Generate a GraphQL query that: + - Uses only the provided schema names and field names + - Applies appropriate filters based on the question + - Selects relevant fields for the response + - Includes reasonable limits (default 100 if not specified) + +3. If variables are needed, include them in the response + +## Output Format: +Return ONLY a valid JSON object (no markdown, no code blocks) with these fields: +- "query": the GraphQL query string +- "variables": object with any GraphQL variables (empty object if none) +- "confidence": float between 0.0-1.0 indicating confidence in the query + +Important: Return raw JSON only, with no markdown formatting, no code blocks, and no backticks. + +## Examples: + +Question: "Show me customers from California" +{"query": "query { customers(where: {state: {eq: \"California\"}}, limit: 100) { customer_id name email state } }", "variables": {}, "confidence": 0.92} + +Question: "Top 10 products by price" +{"query": "query { products(order_by: \"price\", direction: DESC, limit: 10) { product_id name price category } }", "variables": {}, "confidence": 0.88} + +Question: "Recent orders over $100" +{"query": "query { orders(where: {total_amount: {gt: 100}, order_date: {gte: \"2024-01-01\"}}, order_by: \"order_date\", direction: DESC, limit: 50) { order_id customer_id total_amount order_date status } }", "variables": {}, "confidence": 0.96} + +Now generate the GraphQL query for the question above. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/kg-edge-reasoning.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/kg-edge-reasoning.txt new file mode 100644 index 00000000..8aeb0c3c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/kg-edge-reasoning.txt @@ -0,0 +1,25 @@ +Study the following knowledge statements in Cypher format, extracted from a knowledge graph. Each relationship has an id property. + +Here are the knowledge statements: +{% for edge in knowledge %}({{edge.s}})-[{{edge.p}} {id: "{{edge.id}}"}]->({{edge.o}}) +{% endfor %} + +For each knowledge statement, explain briefly (5-10 words) why it is relevant to answering the question below. + +Output format: JSONL (one JSON object per line, no array wrapper) +Each line must include an "id" field and a "reasoning" field. + +{"id": "edge_id", "reasoning": "brief explanation of relevance"} + +Requirements: +- Each line must be a complete, valid JSON object +- No commas between lines +- No [ ] array brackets +- No markdown formatting or prefixes +- Do not provide explanations, only output JSONL + +Example output: +{"id": "a3f9b2c1", "reasoning": "Establishes employment relationship relevant to query"} +{"id": "b7e2d4f9", "reasoning": "Identifies the organisation's sector"} + +Question: {{query}} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/kg-edge-scoring.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/kg-edge-scoring.txt new file mode 100644 index 00000000..40b92773 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/kg-edge-scoring.txt @@ -0,0 +1,25 @@ +Study the following knowledge statements in Cypher format, extracted from a knowledge graph. Each relationship has an id property. + +Here are the knowledge statements: +{% for edge in knowledge %}({{edge.s}})-[{{edge.p}} {id: "{{edge.id}}"}]->({{edge.o}}) +{% endfor %} + +Score each knowledge statement for relevance to the question below. Assign a score from 1 (not relevant) to 10 (highly relevant). + +Output format: JSONL (one JSON object per line, no array wrapper) +Each line must include an "id" field and a "score" field. + +{"id": "edge_id", "score": N} + +Requirements: +- Each line must be a complete, valid JSON object +- No commas between lines +- No [ ] array brackets +- No markdown formatting or prefixes +- Do not provide explanations, only output JSONL + +Example output: +{"id": "a3f9b2c1", "score": 9} +{"id": "b7e2d4f9", "score": 3} + +Question: {{query}} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/kg-edge-selection.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/kg-edge-selection.txt new file mode 100644 index 00000000..a8ea44c8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/kg-edge-selection.txt @@ -0,0 +1,26 @@ +Study the following knowledge statements in Cypher format, extracted from a knowledge graph. Each relationship has an id property. + +Here are the knowledge statements: +{% for edge in knowledge %}({{edge.s}})-[{{edge.p}} {id: "{{edge.id}}"}]->({{edge.o}}) +{% endfor %} + +Select the knowledge statements that are relevant to answering the question below. + +Output format: JSONL (one JSON object per line, no array wrapper) +Each line must include an "id" field and a "reasoning" field. + +For each relevant edge, output: +{"id": "edge_id", "reasoning": "explanation of why this edge is relevant"} + +Requirements: +- Each line must be a complete, valid JSON object +- No commas between lines +- No [ ] array brackets +- No markdown formatting or prefixes +- Do not provide explanations, only output JSONL + +Example output: +{"id": "a3f9b2c1", "reasoning": "Establishes employment relationship directly relevant to the query"} +{"id": "b7e2d4f9", "reasoning": "Identifies the organisation's sector which contextualises the answer"} + +Question: {{query}} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/kg-prompt.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/kg-prompt.txt new file mode 100644 index 00000000..431c0d73 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/kg-prompt.txt @@ -0,0 +1,8 @@ +Study the following set of knowledge statements. The statements are written in Cypher format that has been extracted from a knowledge graph. Use only the provided set of knowledge statements in your response. Do not speculate if the answer is not found in the provided set of knowledge statements. + +Here's the knowledge statements: +{% for edge in knowledge %}({{edge.s}})-[{{edge.p}}]->({{edge.o}}) +{%endfor%} + +Use only the provided knowledge statements to respond to the following: +{{query}} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/kg-synthesis.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/kg-synthesis.txt new file mode 100644 index 00000000..a80c5594 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/kg-synthesis.txt @@ -0,0 +1,8 @@ +Study the following knowledge statements in Cypher format. Use only these provided statements in your response. Do not speculate if the answer is not found in the provided statements. Answer in natural language. Be helpful and give as complete an answer as possible. + +Here are the knowledge statements: +{% for edge in knowledge %}({{edge.s}})-[{{edge.p}}]->({{edge.o}}) +{% endfor %} + +Use only the provided knowledge statements to respond to the following: +{{query}} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/mixtral.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/mixtral.jsonnet new file mode 100644 index 00000000..cd56e7ef --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/mixtral.jsonnet @@ -0,0 +1,42 @@ +// For Mixtral. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/ontology-prompt.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/ontology-prompt.txt new file mode 100644 index 00000000..1b451d9c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/ontology-prompt.txt @@ -0,0 +1,82 @@ +You are a knowledge extraction expert. Your task is to find entities, relationships, and attributes in text based on a provided schema. + +## Entity Types + +These are the types of entities you should look for: + +{% for class_id, class_def in classes.items() %} +- **{{class_id}}**{% if class_def.subclass_of %} (subclass of {{class_def.subclass_of}}){% endif %}{% if class_def.comment %}: {{class_def.comment}}{% endif %} +{% endfor %} + +## Relationships + +These relationships connect entities to other entities: + +{% for prop_id, prop_def in object_properties.items() %} +- **{{prop_id}}**{% if prop_def.domain and prop_def.range %} ({{prop_def.domain}} → {{prop_def.range}}){% endif %}{% if prop_def.comment %}: {{prop_def.comment}}{% endif %} +{% endfor %} + +## Attributes + +These attributes describe entity properties (text, numbers, etc.): + +{% for prop_id, prop_def in datatype_properties.items() %} +- **{{prop_id}}**{% if prop_def.domain and prop_def.range %} ({{prop_def.domain}} → {{prop_def.range}}){% endif %}{% if prop_def.comment %}: {{prop_def.comment}}{% endif %} +{% endfor %} + +## Text to Analyze + +{{text}} + +## Your Task + +Extract the following from the text above: + +1. **Entities**: Things mentioned in the text and their types +2. **Relationships**: How entities relate to each other +3. **Attributes**: Properties of entities (like quantities, descriptions, etc.) + +## Output Format + +Output format: JSONL (one JSON object per line, no array wrapper) +Each line must include a "type" field to distinguish between entities, relationships, and attributes. + +For entities, output: +{"type": "entity", "entity": "entity name as it appears in text", "entity_type": "EntityType"} + +For relationships, output: +{"type": "relationship", "subject": "subject entity name", "subject_type": "SubjectType", "relation": "relationship_name", "object": "object entity name", "object_type": "ObjectType"} + +For attributes, output: +{"type": "attribute", "entity": "entity name", "entity_type": "EntityType", "attribute": "attribute_name", "value": "literal value"} + +## Important Rules + +1. **Entity names**: Use the exact text as it appears (e.g., "Cornish pasty", "beef") +2. **Types**: Use the EXACT type identifiers from the schema above (e.g., "fo/Recipe", "fo/Food") +3. **Relationships**: Use the EXACT relationship names from the schema (e.g., "fo/has_ingredient") +4. **Attributes**: Use the EXACT attribute names from the schema (e.g., "fo/serves") +5. **No array brackets**: Output one JSON object per line, not an array +6. **No markdown**: Do not wrap output in code blocks + +## Requirements + +- Each line must be a complete, valid JSON object +- No commas between lines +- No [ ] array brackets +- No markdown formatting, code blocks, or prefixes +- Do not provide explanations, only output JSONL + +## Example + +Input text: "Cornish pasty is a savory pastry filled with beef and potatoes. This recipe serves 4 people." + +Expected output: +{"type": "entity", "entity": "Cornish pasty", "entity_type": "fo/Recipe"} +{"type": "entity", "entity": "beef", "entity_type": "fo/Food"} +{"type": "entity", "entity": "potatoes", "entity_type": "fo/Food"} +{"type": "relationship", "subject": "Cornish pasty", "subject_type": "fo/Recipe", "relation": "fo/has_ingredient", "object": "beef", "object_type": "fo/Food"} +{"type": "relationship", "subject": "Cornish pasty", "subject_type": "fo/Recipe", "relation": "fo/has_ingredient", "object": "potatoes", "object_type": "fo/Food"} +{"type": "attribute", "entity": "Cornish pasty", "entity_type": "fo/Recipe", "attribute": "fo/serves", "value": "4 people"} + +Now extract entities, relationships, and attributes from the text above. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/openai.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/openai.jsonnet new file mode 100644 index 00000000..5d232337 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/openai.jsonnet @@ -0,0 +1,42 @@ +// For OpenAI LLMs. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/pattern-select.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/pattern-select.txt new file mode 100644 index 00000000..a2b316b4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/pattern-select.txt @@ -0,0 +1,26 @@ +Given the following user question and task type, select the best +execution pattern. + +## Task type + +{{ task_type }}: {{ task_type_description }} + +## Available patterns + +{% for p in patterns %} +- **{{ p.name }}**: {{ p.description }} +{% endfor %} + +## Guidance + +- **react**: Best for straightforward queries needing 1-3 tool calls. +- **plan-then-execute**: Best for multi-step queries that benefit from + upfront planning before execution. +- **supervisor**: Best for complex queries that decompose into + independent sub-tasks that can run in parallel. + +## Question + +{{ question }} + +Respond with just the pattern name, nothing else. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/plan-create.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/plan-create.txt new file mode 100644 index 00000000..172ec5a1 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/plan-create.txt @@ -0,0 +1,33 @@ +You are a planning agent. Given the user's question and the available +tools, create a step-by-step plan. + +## Available tools + +{% for tool in tools %} +- **{{ tool.name }}**: {{ tool.description }} +{% endfor %} + +{% if framing %} +## Domain context + +{{ framing }} + +{% endif %} +## Question + +{{ question }} + +Do not include a final synthesis step — synthesis of results is performed +automatically after all steps complete. + +Produce a JSON array of plan steps. Each step is an object with: +- `goal` (string): what this step aims to achieve +- `tool_hint` (string): suggested tool name, or empty string if none +- `depends_on` (array of integers): indices of steps this depends on (0-based) + +Example: +```json +[ + {"goal": "Look up information about X", "tool_hint": "knowledge-query", "depends_on": []}, + {"goal": "Summarise findings", "tool_hint": "", "depends_on": [0]} +] diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/plan-step-execute.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/plan-step-execute.txt new file mode 100644 index 00000000..f46795c7 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/plan-step-execute.txt @@ -0,0 +1,33 @@ +You are executing one step of a plan. Select the best tool and provide the +arguments to achieve the goal. + +## Goal + +{{ goal }} + +{% if previous_results %} +## Previous step results + +{% for r in previous_results %} +- Step {{ r.index }}: {{ r.result }} +{% endfor %} + +{% endif %} +## Available tools + +{% for tool in tools %} +- **{{ tool.name }}**: {{ tool.description }} +{% for arg in tool.arguments %} + - `{{ arg.name }}` ({{ arg.type }}): {{ arg.description }} +{% endfor %} +{% endfor %} + +Respond with a JSON object: +- `tool`: the exact tool name (must match one of the available tools above) +- `arguments`: an object with the tool's arguments + +Example: +```json +{"tool": "Knowledge query", "arguments": {"question": "What is X?"}} + +Respond with only the JSON object. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/plan-synthesise.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/plan-synthesise.txt new file mode 100644 index 00000000..fd1a558f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/plan-synthesise.txt @@ -0,0 +1,23 @@ +You executed a multi-step plan to answer a question. Now synthesise a +comprehensive final answer from the step results. + +## Original question + +{{ question }} + +{% if framing %} +## Domain context + +{{ framing }} + +{% endif %} +## Step results + +{% for step in steps %} +### Step {{ step.index }}: {{ step.goal }} +{{ step.result }} + +{% endfor %} + +Provide a clear, complete answer to the original question based on +these results. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/schema-selection.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/schema-selection.txt new file mode 100644 index 00000000..39b180e5 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/schema-selection.txt @@ -0,0 +1,25 @@ +You are a database schema selection expert. Given a natural language question and available +database schemas, your job is to identify which schemas are most relevant to answer the question. + +## Available Schemas: +{% for schema in schemas %} +**{{ schema.name }}**: {{ schema.description }} +Fields: +{% for field in schema.fields %} +- {{ field.name }} ({{ field.type }}): {{ field.description }} +{% endfor %} + +{% endfor %} + +## Question: +{{ question }} + +## Instructions: +1. Analyze the question to understand what data is being requested +2. Examine each schema to understand what data it contains +3. Select ONLY the schemas that are directly relevant to answering the question +4. Return your answer as a JSON array of schema names + +## Response Format: +Return ONLY a JSON array of schema names, nothing else. +Example: ["customers", "orders", "products"] diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/slm.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/slm.jsonnet new file mode 100644 index 00000000..48eb96d0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/slm.jsonnet @@ -0,0 +1,44 @@ +// For SLM. Not currently overriding prompts + +local prompts = import "default-prompts.jsonnet"; + +prompts + { + + // "system-template":: "PROMPT GOES HERE.", + + "templates" +:: { + + "question" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-definitions" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-relationships" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-topics" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "extract-rows" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "kg-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + "document-prompt" +:: { + // "prompt": "PROMPT GOES HERE", + }, + + } + +} + + + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/supervisor-decompose.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/supervisor-decompose.txt new file mode 100644 index 00000000..ea082249 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/supervisor-decompose.txt @@ -0,0 +1,34 @@ +You are a supervisor agent. Decompose the user's question into +independent sub-goals that can be investigated in parallel by +separate agents. + +{% if framing %} +## Domain context + +{{ framing }} + +{% endif %} +## Available tools for sub-agents + +{% for tool in tools %} +- **{{ tool.name }}**: {{ tool.description }} +{% endfor %} + +## Rules + +- Each sub-goal must be independently answerable. +- Aim for 2-{{ max_subagents }} sub-goals. +- Each sub-goal should be a clear, specific question. + +## Question + +{{ question }} + +Produce a JSON array of sub-goal strings. + +Example: +```json +["What is X?", "How does Y relate to Z?"] +``` + +Respond with only the JSON array. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/supervisor-synthesise.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/supervisor-synthesise.txt new file mode 100644 index 00000000..e5e7b25c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/supervisor-synthesise.txt @@ -0,0 +1,23 @@ +You are synthesising a final answer from multiple sub-agent +investigations. + +## Original question + +{{ question }} + +{% if framing %} +## Domain context + +{{ framing }} + +{% endif %} +## Sub-agent results + +{% for r in results %} +### Sub-goal: {{ r.goal }} +{{ r.result }} + +{% endfor %} + +Synthesise these results into a comprehensive answer to the original +question. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/task-type-classify.txt b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/task-type-classify.txt new file mode 100644 index 00000000..6c67c7bd --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/prompts/task-type-classify.txt @@ -0,0 +1,14 @@ +Given the following user question, classify it into exactly one of the \ +available task types. + +## Available task types + +{% for tt in task_types %} +- **{{ tt.name }}**: {{ tt.description }} +{% endfor %} + +## Question + +{{ question }} + +Respond with just the task type name, nothing else. diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/pubsub/pulsar-manager.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/pubsub/pulsar-manager.jsonnet new file mode 100644 index 00000000..c1ca4515 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/pubsub/pulsar-manager.jsonnet @@ -0,0 +1,40 @@ +local images = import "values/images.jsonnet"; + +{ + + "pulsar" +: { + + create:: function(engine) + +// FIXME: Should persist something? +// local volume = engine.volume(...) + + local container = + engine.container("pulsar") + .with_image(images.pulsar_manager) + .with_environment({ + SPRING_CONFIGURATION_FILE: "/pulsar-manager/pulsar-manager/application.properties", + }) + .with_limits("0.5", "1.4G") + .with_reservations("0.1", "1.4G") + .with_port(9527, 9527, "api") + .with_port(7750, 7750, "api2"); + + local containerSet = engine.containers( + "pulsar", [ container ] + ); + + local service = + engine.service(containerSet) + .with_port(9527, 9527, "api") + .with_port(7750, 7750, "api2); + + engine.resources([ + containerSet, + service, + ]) + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/pubsub/pulsar.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/pubsub/pulsar.jsonnet new file mode 100644 index 00000000..cf94f45c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/pubsub/pulsar.jsonnet @@ -0,0 +1,240 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +// This is a Pulsar configuration. Non-standalone mode so we deploy +// individual components: bookkeeper, broker and zookeeper. +// +// This also deploys the TrustGraph 'admin' container which initialises +// TrustGraph-specific namespaces etc. + +{ + + "pub-sub-args":: [ + "--pubsub-backend", + "pulsar", + "--pulsar-host", + url.pulsar, + ], + + "pub-sub-init-args":: [ + "--pulsar-admin-url", + url.pulsar_admin, + ], + + "overview-dashboard":: + importstr "grafana/dashboards/overview-dashboard-pulsar.json", + + "prometheus-config":: + importstr "prometheus/prometheus-pulsar.yml", + + "pulsar" +: { + + // Zookeeper memory settings (can be overridden by memory-profile) + "zk-memory-limit":: "512M", + "zk-memory-reservation":: "512M", + "zk-heap":: "256m", + "zk-direct-memory":: "256m", + + // Bookie memory settings (can be overridden by memory-profile) + "bookie-memory-limit":: "1024M", + "bookie-memory-reservation":: "1024M", + "bookie-heap":: "256m", + "bookie-direct-memory":: "256m", + + // Broker memory settings (can be overridden by memory-profile) + "broker-memory-limit":: "800M", + "broker-memory-reservation":: "800M", + "broker-heap":: "384m", + "broker-direct-memory":: "384m", + + // Pulsar-init memory settings (can be overridden by memory-profile) + "init-memory-limit":: "256M", + "init-memory-reservation":: "256M", + "init-heap":: "128m", + "init-direct-memory":: "128m", + + create:: function(engine) + + // Capture memory settings into locals (self refers to pulsar object here) + local zkMemLimit = self["zk-memory-limit"]; + local zkMemReserv = self["zk-memory-reservation"]; + local zkHeap = self["zk-heap"]; + local zkDirect = self["zk-direct-memory"]; + + local bookieMemLimit = self["bookie-memory-limit"]; + local bookieMemReserv = self["bookie-memory-reservation"]; + local bookieHeap = self["bookie-heap"]; + local bookieDirect = self["bookie-direct-memory"]; + + local brokerMemLimit = self["broker-memory-limit"]; + local brokerMemReserv = self["broker-memory-reservation"]; + local brokerHeap = self["broker-heap"]; + local brokerDirect = self["broker-direct-memory"]; + + local initMemLimit = self["init-memory-limit"]; + local initMemReserv = self["init-memory-reservation"]; + local initHeap = self["init-heap"]; + local initDirect = self["init-direct-memory"]; + + // Zookeeper volume + local zkVolume = engine.volume("zookeeper").with_size("1G"); + + // Zookeeper container + local zkContainer = + engine.container("zookeeper") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "bin/apply-config-from-env.py conf/zookeeper.conf && bin/generate-zookeeper-config.sh conf/zookeeper.conf && exec bin/pulsar zookeeper" + ]) + .with_limits("1", zkMemLimit) + .with_reservations("0.05", zkMemReserv) + .with_user("0:1000") + .with_volume_mount(zkVolume, "/pulsar/data/zookeeper") + .with_environment({ + "metadataStoreUrl": "zk:zookeeper:2181", + "PULSAR_MEM": "-Xms%s -Xmx%s -XX:MaxDirectMemorySize=%s" % [ + zkHeap, zkHeap, zkDirect, + ], + }) + .with_port(2181, 2181, "zookeeper") + .with_port(2888, 2888, "zookeeper2") + .with_port(3888, 3888, "zookeeper3"); + + // Pulsar cluster init container + local initContainer = + engine.container("pulsar-init") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "sleep 10 && bin/pulsar initialize-cluster-metadata --cluster cluster-a --zookeeper zookeeper:2181 --configuration-store zookeeper:2181 --web-service-url http://pulsar:8080 --broker-service-url pulsar://pulsar:6650", + ]) + .with_limits("1", initMemLimit) + .with_reservations("0.05", initMemReserv) + .with_environment({ + "PULSAR_MEM": "-Xms%s -Xmx%s -XX:MaxDirectMemorySize=%s" % [ + initHeap, initHeap, initDirect, + ], + }); + + + // Bookkeeper volume + local bookieVolume = engine.volume("bookie").with_size("20G"); + + // Bookkeeper container + local bookieContainer = + engine.container("bookie") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "bin/apply-config-from-env.py conf/bookkeeper.conf && exec bin/pulsar bookie" + // false ^ causes this to be a 'failure' exit. + ]) + .with_limits("1", bookieMemLimit) + .with_reservations("0.1", bookieMemReserv) + .with_user("0:1000") + .with_volume_mount(bookieVolume, "/pulsar/data/bookkeeper") + .with_environment({ + "clusterName": "cluster-a", + "zkServers": "zookeeper:2181", + "bookieId": "bookie", + "metadataStoreUri": "metadata-store:zk:zookeeper:2181", + "advertisedAddress": "bookie", + "BOOKIE_MEM": "-Xms%s -Xmx%s -XX:MaxDirectMemorySize=%s" % [ + bookieHeap, bookieHeap, bookieDirect, + ], + }) + .with_port(3181, 3181, "bookie"); + + // Pulsar broker, stateless (uses ZK and Bookkeeper for state) + local brokerContainer = + engine.container("pulsar") + .with_image(images.pulsar) + .with_command([ + "bash", + "-c", + "bin/apply-config-from-env.py conf/broker.conf && exec bin/pulsar broker" + ]) + .with_limits("1", brokerMemLimit) + .with_reservations("0.1", brokerMemReserv) + .with_environment({ + "metadataStoreUrl": "zk:zookeeper:2181", + "zookeeperServers": "zookeeper:2181", + "clusterName": "cluster-a", + "managedLedgerDefaultEnsembleSize": "1", + "managedLedgerDefaultWriteQuorum": "1", + "managedLedgerDefaultAckQuorum": "1", + "advertisedAddress": "pulsar", + "advertisedListeners": "external:pulsar://pulsar:6650,localhost:pulsar://localhost:6650", + "PULSAR_MEM": "-Xms%s -Xmx%s -XX:MaxDirectMemorySize=%s" % [ + brokerHeap, brokerHeap, brokerDirect, + ], + }) + .with_port(6650, 6650, "pulsar") + .with_port(8080, 8080, "admin"); + + // Container sets + local zkContainerSet = engine.containers( + "zookeeper", + [ + zkContainer, + ] + ); + + local initContainerSet = engine.containers( + "init-pulsar", + [ + initContainer, + ] + ); + + local bookieContainerSet = engine.containers( + "bookie", + [ + bookieContainer, + ] + ); + + local brokerContainerSet = engine.containers( + "pulsar", + [ + brokerContainer, + ] + ); + + // Zookeeper service + local zkService = + engine.service(zkContainerSet) + .with_port(2181, 2181, "zookeeper") + .with_port(2888, 2888, "zookeeper2") + .with_port(3888, 3888, "zookeeper3"); + + // Bookkeeper service + local bookieService = + engine.service(bookieContainerSet) + .with_port(3181, 3181, "bookie"); + + // Pulsar broker service + local brokerService = + engine.service(brokerContainerSet) + .with_port(6650, 6650, "pulsar") + .with_port(8080, 8080, "admin"); + + engine.resources([ + zkVolume, + bookieVolume, + zkContainerSet, + initContainerSet, + bookieContainerSet, + brokerContainerSet, + zkService, + bookieService, + brokerService, + ]) + + } + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/pubsub/rabbitmq.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/pubsub/rabbitmq.jsonnet new file mode 100644 index 00000000..600b14fc --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/pubsub/rabbitmq.jsonnet @@ -0,0 +1,78 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; + +// RabbitMQ messaging fabric configuration. +// Replaces Pulsar as the message broker for TrustGraph. + +{ + + "pub-sub-args":: [ + "--pubsub-backend", + "rabbitmq", + "--rabbitmq-host", + "rabbitmq", + ], + + "pub-sub-init-args":: [ + "--pulsar-admin-url", + url.pulsar_admin, + ], + + "overview-dashboard":: + importstr "grafana/dashboards/overview-dashboard-rabbitmq.json", + + "prometheus-config":: + importstr "prometheus/prometheus-rabbitmq.yml", + + "rabbitmq" +: { + + // Memory settings (can be overridden by memory-profile) + "memory-limit":: "1024M", + "memory-reservation":: "1024M", + + // CPU settings + "cpu-limit":: "1", + "cpu-reservation":: "0.1", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + // RabbitMQ volume for persistent data + local volume = engine.volume("rabbitmq").with_size("10G"); + + // RabbitMQ container + local container = + engine.container("rabbitmq") + .with_image(images.rabbitmq) + .with_limits(self["cpu-limit"], memoryLimit) + .with_reservations(self["cpu-reservation"], memoryReservation) + .with_volume_mount(volume, "/var/lib/rabbitmq") + .with_environment({ + "RABBITMQ_DEFAULT_USER": "guest", + "RABBITMQ_DEFAULT_PASS": "guest", + }) + .with_port(5672, 5672, "amqp") + .with_port(15672, 15672, "management") + .with_port(15692, 15692, "metrics"); + + local containerSet = engine.containers( + "rabbitmq", [ container ] + ); + + // RabbitMQ service + local service = + engine.service(containerSet) + .with_port(5672, 5672, "amqp") + .with_port(15672, 15672, "management"); + + engine.resources([ + volume, + containerSet, + service, + ]) + + } + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-additionals.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-additionals.jsonnet new file mode 100644 index 00000000..3e9f3481 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-additionals.jsonnet @@ -0,0 +1,134 @@ +local decode = import "decode-config.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Custom engine that collects configVolume parts +local engine = { + + // Collection of all configVolume parts + configVolumes:: [], + + // Implement all required engine methods as no-ops + container:: function(name) { + with_image:: function(x) self, + with_command:: function(x) self, + with_environment:: function(x) self, + with_limits:: function(c, m) self, + with_reservations:: function(c, m) self, + with_port:: function(src, dest, name) self, + with_volume_mount:: function(vol, mnt) self, + with_user:: function(x) self, + with_runtime:: function(x) self, + with_privileged:: function(x) self, + with_ipc:: function(x) self, + with_capability:: function(x) self, + with_device:: function(hdev, cdev) self, + with_env_var_secrets:: function(vars) self, + }, + + volume:: function(name) { + with_size:: function(size) self, + }, + + // The key method - collects configVolume parts + configVolume:: function(name, dir, parts) + local collector = self + { + configVolumes: super.configVolumes + [ + { + dir: dir, + parts: parts, + } + ] + }; + { + // Return a dummy volume that has the collector in it + name: name, + with_size:: function(size) collector, + // Provide a way to get back to the collector + getCollector:: function() collector, + }, + + secretVolume:: function(name, dir, parts) { + with_size:: function(size) self, + }, + + envSecrets:: function(name) { + with_env_var:: function(name, key) self, + }, + + containers:: function(name, containers) self, + + service:: function(containers) { + with_port:: function(src, dest, name) self, + }, + + internalService:: function(containers) { + with_port:: function(src, dest, name) self, + }, + + resources:: function(res) + // Fold over resources and collect any configVolume state + local collected = std.foldl( + function(state, r) + if std.objectHasAll(r, 'getCollector') then + // Merge the configVolumes from the volume's collector into our state + local volumeCollector = r.getCollector(); + state + { + configVolumes: state.configVolumes + volumeCollector.configVolumes + } + else + state, + res, + self + ); + collected, +}; + +// Execute all component create() functions with our collecting engine +// Note: create:: is a hidden field, so we must use objectHasAll not objectHas +local result = std.foldl( + function(state, p) + if std.objectHasAll(p, 'create') then + // Pattern has create directly - call it + p.create(state) + else + state, + std.objectValues(patterns), + engine +); + +// Debug: show what we collected +local debug = { + numPatterns: std.length(std.objectValues(patterns)), + numConfigVolumes: std.length(result.configVolumes), +}; + +// Transform collected data into output format +local allFiles = std.flattenArrays([ + [ + { + // Remove trailing slash from dir to avoid double slashes + path: std.join("/", [std.rstripChars(cv.dir, "/"), filename]), + content: cv.parts[filename] + } + for filename in std.objectFields(cv.parts) + ] + for cv in result.configVolumes +]); + +// Deduplicate by path - use a map to keep only unique paths +local uniqueMap = std.foldl( + function(acc, item) acc + { [item.path]: item }, + allFiles, + {} +); + +// Convert back to array +local additionals = std.objectValues(uniqueMap); + +// Output the array +additionals diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-aks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-aks-k8s.jsonnet new file mode 100644 index 00000000..bcec1cbb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-aks-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "../engine/aks-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-docker-compose.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-docker-compose.jsonnet new file mode 100644 index 00000000..7f0dbabe --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-docker-compose.jsonnet @@ -0,0 +1,20 @@ + +local engine = import "../engine/docker-compose.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resources = std.foldl( + function(state, p) state + p.create(engine), + std.objectValues(patterns), + {} +); + +resources + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-eks-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-eks-k8s.jsonnet new file mode 100644 index 00000000..f9de59cb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-eks-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "../engine/eks-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-gcp-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-gcp-k8s.jsonnet new file mode 100644 index 00000000..50037a5c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-gcp-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "../engine/gcp-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-minikube-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-minikube-k8s.jsonnet new file mode 100644 index 00000000..6fa64706 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-minikube-k8s.jsonnet @@ -0,0 +1,26 @@ + +local engine = import "../engine/minikube-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +local ns = { + apiVersion: "v1", + kind: "Namespace", + metadata: { + name: "trustgraph", + }, + "spec": { + }, +}; + +// Extract resources using the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-noop.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-noop.jsonnet new file mode 100644 index 00000000..3ad735d9 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-noop.jsonnet @@ -0,0 +1,20 @@ + +local engine = import "../engine/noop.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resources = std.foldl( + function(state, p) state + p.create(engine), + std.objectValues(patterns), + {} +); + +resources + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-ovh-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-ovh-k8s.jsonnet new file mode 100644 index 00000000..92f61041 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-ovh-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "../engine/ovh-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-podman-compose.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-podman-compose.jsonnet new file mode 100644 index 00000000..7f0dbabe --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-podman-compose.jsonnet @@ -0,0 +1,20 @@ + +local engine = import "../engine/docker-compose.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resources = std.foldl( + function(state, p) state + p.create(engine), + std.objectValues(patterns), + {} +); + +resources + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-scw-k8s.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-scw-k8s.jsonnet new file mode 100644 index 00000000..4f6c1d7a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-scw-k8s.jsonnet @@ -0,0 +1,16 @@ + +local engine = import "../engine/scw-k8s.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract resources usnig the engine +local resourceList = engine.package(patterns); + +resourceList + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-tg-configuration.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-tg-configuration.jsonnet new file mode 100644 index 00000000..59b4732a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/config-to-tg-configuration.jsonnet @@ -0,0 +1,15 @@ + +local engine = import "../engine/noop.jsonnet"; +local decode = import "decode-config.jsonnet"; +local components = import "../components.jsonnet"; + +// Import config +local config = import "config.json"; + +// Produce patterns from config +local patterns = decode(config); + +// Extract configuration directly from patterns +patterns.configuration.configuration + + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/decode-config.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/decode-config.jsonnet new file mode 100644 index 00000000..759513c4 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/renderers/decode-config.jsonnet @@ -0,0 +1,31 @@ + +local components = import "../components.jsonnet"; + +local apply = function(p, components) + + local base = { + + with:: function(k, v) self + { + [k] +:: v + }, + + with_params:: function(pars) + self + std.foldl( + function(obj, par) obj.with(par.key, par.value), + std.objectKeysValues(pars), + self + ), + + }; + + local component = base + components[p.name]; + + component.with_params(p.parameters); + +local decode = function(config) + local add = function(state, c) state + apply(c, components); + local patterns = std.foldl(add, config, {}); + patterns; + +decode + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/row-store/cassandra.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/row-store/cassandra.jsonnet new file mode 100644 index 00000000..f1b7adb8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/row-store/cassandra.jsonnet @@ -0,0 +1,75 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local cassandra = import "backends/cassandra.jsonnet"; + +cassandra + { + + "store-rows" +: { + + create:: function(engine) + + local container = + engine.container("store-rows") + .with_image(images.trustgraph_flow) + .with_command([ + "rows-write-cassandra", +] + $["pub-sub-args"] + [ + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-rows", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-rows" +: { + + create:: function(engine) + + local container = + engine.container("query-rows") + .with_image(images.trustgraph_flow) + .with_command([ + "rows-query-cassandra", +] + $["pub-sub-args"] + [ + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "512M") + .with_reservations("0.1", "512M"); + + local containerSet = engine.containers( + "query-rows", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/runtime-config/agent-patterns.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/runtime-config/agent-patterns.jsonnet new file mode 100644 index 00000000..205e0cd8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/runtime-config/agent-patterns.jsonnet @@ -0,0 +1,20 @@ + +{ + "react": { + "name": "react", + "description": "Interleaved reasoning and action — the agent thinks, selects a tool, observes the result, and repeats until it has enough information to answer.", + "max_iterations": 10, + }, + "plan-then-execute": { + "name": "plan-then-execute", + "description": "The agent first produces a structured plan of steps, then executes each step in order. Supports re-planning on failure.", + "max_iterations": 15, + "max_replan_depth": 2, + }, + "supervisor": { + "name": "supervisor", + "description": "The agent decomposes the question into independent sub-goals, fans out subagent requests, waits for all to complete, then synthesises a final answer.", + "max_subagents": 5, + }, +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/runtime-config/agent-task-types.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/runtime-config/agent-task-types.jsonnet new file mode 100644 index 00000000..24fc3463 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/runtime-config/agent-task-types.jsonnet @@ -0,0 +1,27 @@ + +{ + "general": { + "name": "general", + "description": "No domain framing. All patterns are valid.", + "valid_patterns": ["react", "plan-then-execute", "supervisor"], + "framing": "", + }, + "research": { + "name": "research", + "description": "Research-oriented queries requiring information gathering.", + "valid_patterns": ["react", "plan-then-execute"], + "framing": "This is a research query. Focus on gathering accurate, well-sourced information.", + }, + "risk-assessment": { + "name": "risk-assessment", + "description": "Risk assessment queries requiring multi-dimensional analysis.", + "valid_patterns": ["supervisor", "plan-then-execute", "react"], + "framing": "This is a risk assessment query. Consider multiple risk dimensions and provide structured analysis.", + }, + "summarisation": { + "name": "summarisation", + "description": "Summarisation queries — straightforward information condensation.", + "valid_patterns": ["react"], + "framing": "This is a summarisation query. Focus on concise, accurate synthesis of the available information.", + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/runtime-config/config-composer.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/runtime-config/config-composer.jsonnet new file mode 100644 index 00000000..c1897e5b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/runtime-config/config-composer.jsonnet @@ -0,0 +1,101 @@ +// Configuration Composer Module +// Orchestrates the complete configuration building process +// Combines all components into the final TrustGraph configuration + +local flow_builder = import "flow-builder.jsonnet"; +local interface_builder = import "interface-builder.jsonnet"; + +{ + // Main function to build the complete configuration + build: function(config_spec) + // Extract configuration parameters + local flow_blueprints = config_spec.flow_blueprints; + local default_flow_blueprint = config_spec.default_flow_blueprint; + local default_flow_id = config_spec.default_flow_id; + local flow_init_parameters = config_spec.flow_init_parameters; + + // Build all processors for the default flow + local blueprint_processors = flow_builder.build_blueprint_processors( + flow_blueprints, + default_flow_blueprint, + flow_init_parameters + ); + + local flow_processors = flow_builder.build_flow_processors( + flow_blueprints, + default_flow_blueprint, + default_flow_id, + flow_init_parameters + ); + + // Combine processors into flow objects + local processor_array = blueprint_processors + flow_processors; + local flow_objects = flow_builder.build_flow_objects(processor_array); + local active_flows = flow_builder.merge_flow_objects(flow_objects); + + // Build interfaces for the default flow + local default_flow_interfaces = interface_builder.build_interfaces( + flow_blueprints, + default_flow_blueprint, + default_flow_id, + flow_init_parameters + ); + + // Return object with nested configuration (for backwards compatibility) + { + // Create function (for backwards compatibility) + create: function(engine) {}, + + // The actual configuration object + configuration: { + // Prompts configuration + prompt: { + "system": config_spec.prompts["system-template"], + "template-index": std.objectFieldsAll(config_spec.prompts.templates), + } + { + ["template." + template.key]: template.value + for template in std.objectKeysValuesAll(config_spec.prompts.templates) + }, + + // Tools configuration + tool: { + [tool.id]: tool + for tool in config_spec.tools + }, + + // MCP configuration + mcp: config_spec.mcp, + + // Agent orchestrator + "agent-pattern": config_spec.agent_patterns, + "agent-task-type": config_spec.agent_task_types, + + // Flow blueprints reference + "flow-blueprint": flow_blueprints, + + // Interface descriptions + "interface-description": config_spec.interface_descriptions, + + // Flow instances + "flow": { + [default_flow_id]: { + "description": "Default processing flow", + "blueprint-name": default_flow_blueprint, + "interfaces": default_flow_interfaces, + "parameters": flow_init_parameters, + }, + }, + + // Active flow processors + "active-flow": active_flows, + + // Token costs and parameter types + "token-cost": config_spec.token_costs, + "parameter-type": config_spec.parameter_types, + + // Collections configuration + "collection": config_spec.collection, + + }, + }, +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/runtime-config/flow-builder.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/runtime-config/flow-builder.jsonnet new file mode 100644 index 00000000..0b142750 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/runtime-config/flow-builder.jsonnet @@ -0,0 +1,72 @@ +// Flow Builder Module +// Processes flow blueprints and builds complete flow configurations +// Handles {blueprint}, {id}, and parameter substitutions + +local param_processor = import "parameter-processor.jsonnet"; + +{ + // Builds blueprint-level processors with parameter substitution + // Processes the 'blueprint' section of flow blueprints + build_blueprint_processors: function(flow_blueprints, blueprint_name, parameters) + [ + [ + // Replace {blueprint} in the processor key + local key = std.strReplace(processor.key, "{blueprint}", blueprint_name); + local parts = std.splitLimit(key, ":", 2); + parts, + { + // Process each field in the processor configuration + [field.key]: + // First replace {blueprint}, then substitute parameters + local blueprint_replaced = std.strReplace(field.value, "{blueprint}", blueprint_name); + param_processor.substitute_parameters(blueprint_replaced, parameters) + for field in std.objectKeysValuesAll(processor.value) + } + ] + for processor in std.objectKeysValuesAll(flow_blueprints[blueprint_name].blueprint) + ], + + // Builds flow-level processors with parameter substitution + // Processes the 'flow' section of flow blueprints + build_flow_processors: function(flow_blueprints, blueprint_name, flow_id, parameters) + [ + [ + // Replace both {blueprint} and {id} in the processor key + local key = std.strReplace( + std.strReplace(processor.key, "{blueprint}", blueprint_name), + "{id}", flow_id + ); + local parts = std.splitLimit(key, ":", 2); + parts, + { + // Process each field in the processor configuration + [field.key]: + // Replace {blueprint} and {id}, then substitute parameters + local blueprint_replaced = std.strReplace(field.value, "{blueprint}", blueprint_name); + local id_replaced = std.strReplace(blueprint_replaced, "{id}", flow_id); + param_processor.substitute_parameters(id_replaced, parameters) + for field in std.objectKeysValuesAll(processor.value) + } + ] + for processor in std.objectKeysValuesAll(flow_blueprints[blueprint_name].flow) + ], + + // Combines blueprint and flow processors into flow objects + build_flow_objects: function(processor_array) + std.map( + function(item) { + [item[0][0]] +: { + [item[0][1]]: item[1] + } + }, + processor_array + ), + + // Merges all flow objects into a single flows_active configuration + merge_flow_objects: function(flow_objects) + std.foldr( + function(a, b) a + b, + flow_objects, + {} + ), +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/runtime-config/interface-builder.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/runtime-config/interface-builder.jsonnet new file mode 100644 index 00000000..2143e600 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/runtime-config/interface-builder.jsonnet @@ -0,0 +1,30 @@ +// Interface Builder Module +// Processes flow class interfaces with parameter substitution +// Handles both string interfaces and nested object interfaces + +local param_processor = import "parameter-processor.jsonnet"; + +{ + // Builds interfaces for a specific flow class and instance + // Processes the 'interfaces' section of flow classes + build_interfaces: function(flow_classes, class_name, flow_id, parameters) + local interface_spec = flow_classes[class_name].interfaces; + { + [interface.key]: + if std.isString(interface.value) then + // Simple string interface - apply all substitutions + local class_replaced = std.strReplace(interface.value, "{class}", class_name); + local id_replaced = std.strReplace(class_replaced, "{id}", flow_id); + param_processor.substitute_parameters(id_replaced, parameters) + else + // Complex object interface - process nested fields + { + [field.key]: + local class_replaced = std.strReplace(field.value, "{class}", class_name); + local id_replaced = std.strReplace(class_replaced, "{id}", flow_id); + param_processor.substitute_parameters(id_replaced, parameters) + for field in std.objectKeysValuesAll(interface.value) + } + for interface in std.objectKeysValuesAll(interface_spec) + }, +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/runtime-config/interface-descriptions.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/runtime-config/interface-descriptions.jsonnet new file mode 100644 index 00000000..f9dda8b0 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/runtime-config/interface-descriptions.jsonnet @@ -0,0 +1,89 @@ +// Interface Descriptions Module +// Defines all external interfaces available in TrustGraph flows +// These are the 'endpoints' that external systems can interact with + +{ + // Document loading interfaces - for data ingestion + "document-load": { + "description": "Document loader", + "kind": "send", + "visible": true, + }, + "text-load": { + "description": "Text document loader", + "kind": "send", + "visible": true, + }, + + // Data storage interfaces - for processed data streams + "entity-contexts-load": { + "description": "Entity contexts loader", + "kind": "send", + }, + "triples-store": { + "description": "Triples loader", + "kind": "send", + }, + "graph-embeddings-store": { + "description": "Graph embeddings loader", + "kind": "send", + }, + "document-embeddings-store": { + "description": "Document embeddings loader", + "kind": "send", + }, + "objects-store": { + "description": "Object store", + "kind": "request-response", + }, + + // Query interfaces - for retrieving information + "graph-rag": { + "description": "GraphRAG service", + "kind": "request-response", + }, + "document-rag": { + "description": "ChunkRAG service", + "kind": "request-response", + }, + "triples": { + "description": "Triples query service", + "kind": "request-response", + }, + "graph-embeddings": { + "description": "Graph embeddings service", + "kind": "request-response", + }, + "document-embeddings": { + "description": "Document embeddings service", + "kind": "request-response", + }, + "objects": { + "description": "Object query service", + "kind": "request-response", + }, + + // Processing services - for text and data processing + "prompt": { + "description": "Prompt service", + "kind": "request-response", + }, + "agent": { + "description": "Agent service", + "kind": "request-response", + }, + "text-completion": { + "description": "Text completion service", + "kind": "request-response", + }, + + // Query translation services - for natural language queries + "nlp-query": { + "description": "NLP question to GraphQL service", + "kind": "request-response", + }, + "structured-query": { + "description": "Structured query service", + "kind": "request-response", + }, +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/runtime-config/parameter-processor.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/runtime-config/parameter-processor.jsonnet new file mode 100644 index 00000000..f317ae9f --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/runtime-config/parameter-processor.jsonnet @@ -0,0 +1,38 @@ +// Parameter Processing Module +// Handles dynamic parameter replacement in configuration values +// Replaces {parameter_name} placeholders with actual parameter values + +{ + // Applies parameter substitutions to string values + // Only processes strings - leaves other types unchanged + substitute_parameters: function(value, parameters) + if std.isString(value) then + std.foldl( + function(acc, param) + // Only do string replacement if param.value is a string + if std.isString(param.value) then + std.strReplace(acc, "{" + param.key + "}", param.value) + else + acc, // Skip replacement for non-string parameter values + std.objectKeysValuesAll(parameters), + value + ) + else + value, + + // Applies parameter substitutions to all values in an object + // Recursively processes nested objects and arrays + substitute_parameters_in_object: function(obj, parameters) + if std.isObject(obj) then + { + [key]: $.substitute_parameters_in_object(obj[key], parameters) + for key in std.objectFields(obj) + } + else if std.isArray(obj) then + [ + $.substitute_parameters_in_object(item, parameters) + for item in obj + ] + else + $.substitute_parameters(obj, parameters), +} \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/runtime-config/tools.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/runtime-config/tools.jsonnet new file mode 100644 index 00000000..4947279e --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/runtime-config/tools.jsonnet @@ -0,0 +1,36 @@ +// Tools Configuration Module +// Defines all available tools that can be used by agents and flows +// Each tool specifies its interface, arguments, and behavior + +[ + // Knowledge query tool - queries the knowledge base + { + id: "knowledge-query", + name: "Knowledge query", + description: "This tool queries a knowledge base that holds information about domain-specific information. The question should be a natural language question.", + type: "knowledge-query", + collection: "default", + arguments: [ + { + name: "question", + type: "string", + description: "A simple natural language question.", + } + ] + }, + + // LLM completion tool - general purpose text completion + { + id: "llm-completion", + name: "LLM text completion", + type: "text-completion", + description: "This tool queries an LLM for non-domain-specific information. The question should be a natural language question.", + arguments: [ + { + name: "question", + type: "string", + description: "The question which should be asked of the LLM.", + } + ] + } +] \ No newline at end of file diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/runtime-config/trustgraph-config.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/runtime-config/trustgraph-config.jsonnet new file mode 100644 index 00000000..9d899ceb --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/runtime-config/trustgraph-config.jsonnet @@ -0,0 +1,96 @@ +// TrustGraph Main Configuration +// Clean, modular composition of TrustGraph configuration +// Uses specialized modules for different aspects of config building + +// Import dependencies +local images = import "../values/images.jsonnet"; +local url = import "../values/url.jsonnet"; +local prompts = import "../prompts/mixtral.jsonnet"; +local default_prompts = import "../prompts/default-prompts.jsonnet"; +local token_costs = import "../values/token-costs.jsonnet"; +local flow_blueprints = import "../flows/flow-blueprints.jsonnet"; +local config_composer = import "config-composer.jsonnet"; +local interface_descriptions = import "interface-descriptions.jsonnet"; +local tools = import "tools.jsonnet"; +local temperature_params = import "../parameters/temperature-param-types.jsonnet"; +local chunking_params = import "../parameters/chunking-param-types.jsonnet"; + +// Main configuration object +local configuration = { + + // Prompt templates + prompts:: default_prompts, + + // Tool definitions + tools:: tools, + + // MCP configuration + mcp:: {}, + + // Flow classes reference + "flow-blueprints":: flow_blueprints, + + // LLM model parameters + "llm-models" +:: {}, + + // Embeddings model parameters + "embeddings-models" +:: {}, + + collections +:: { + "trustgraph:default": { + "user": "default-user", + "collection": "default", + "name": "Default Collection", + "description": "Default collection", + "tags": ["default"], + }, + }, + + // Default model and flow parameters + flow_init_parameters:: { + "llm-model": $["llm-models"].default, + "llm-rag-model": $["llm-models"].default, + "llm-temperature": "%0.3f" % $["parameter-types"]["llm-temperature"].default, + "llm-rag-temperature": "%0.3f" % $["parameter-types"]["llm-temperature"].default, + "chunk-size": std.toString($["parameter-types"]["chunk-size"].default), + "chunk-overlap": std.toString($["parameter-types"]["chunk-overlap"].default), + "embeddings-model": $["embeddings-models"].default, + }, + + // Interface descriptions for external endpoints + "interface-descriptions":: interface_descriptions, + + // Parameter type definitions + "parameter-types":: { + "llm-model": $["llm-models"], + "embeddings-model": $["embeddings-models"], + } + chunking_params + temperature_params, + + // Token costs + "token-costs":: token_costs, + + // Agent orchestator configuration + "agent-patterns":: import "agent-patterns.jsonnet", + "agent-task-types":: import "agent-task-types.jsonnet", + + // Build the complete configuration using the composer + configuration:: config_composer.build({ + flow_blueprints: $["flow-blueprints"], + default_flow_blueprint: "everything", + default_flow_id: "default", + flow_init_parameters: $["flow_init_parameters"], + prompts: $["prompts"], + tools: $["tools"], + mcp: $["mcp"], + interface_descriptions: $["interface-descriptions"], + parameter_types: $["parameter-types"], + token_costs: $["token-costs"], + collection: $["collections"], + agent_patterns: $["agent-patterns"], + agent_task_types: $["agent-task-types"], + }), + +} + default_prompts; + +// Export the final configuration +configuration diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/triple-store/cassandra.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/triple-store/cassandra.jsonnet new file mode 100644 index 00000000..d7a8460c --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/triple-store/cassandra.jsonnet @@ -0,0 +1,75 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local cassandra = import "backends/cassandra.jsonnet"; + +cassandra + { + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-cassandra", +] + $["pub-sub-args"] + [ + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "256M") + .with_reservations("0.1", "256M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-cassandra", +] + $["pub-sub-args"] + [ + "--cassandra-host", + cassandra_hosts, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "512M") + .with_reservations("0.1", "512M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/triple-store/falkordb.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/triple-store/falkordb.jsonnet new file mode 100644 index 00000000..7fac275e --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/triple-store/falkordb.jsonnet @@ -0,0 +1,77 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local falkordb = import "backends/falkordb.jsonnet"; + +falkordb + { + + "falkordb-url":: "falkor://falkordb:6379", + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-falkordb", +] + $["pub-sub-args"] + [ + "-g", + $["falkordb-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-falkordb", +] + $["pub-sub-args"] + [ + "-g", + $["falkordb-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/triple-store/memgraph.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/triple-store/memgraph.jsonnet new file mode 100644 index 00000000..06003fac --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/triple-store/memgraph.jsonnet @@ -0,0 +1,82 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local memgraph = import "backends/memgraph.jsonnet"; + +memgraph + { + + "memgraph-url":: "bolt://memgraph:7687", + "memgraph-database":: "memgraph", + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-memgraph", +] + $["pub-sub-args"] + [ + "-g", + $["memgraph-url"], + "--database", + $["memgraph-database"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-memgraph", +] + $["pub-sub-args"] + [ + "-g", + $["memgraph-url"], + "--database", + $["memgraph-database"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/triple-store/neo4j.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/triple-store/neo4j.jsonnet new file mode 100644 index 00000000..cca8500a --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/triple-store/neo4j.jsonnet @@ -0,0 +1,77 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local neo4j = import "backends/neo4j.jsonnet"; + +neo4j + { + + "neo4j-url":: "bolt://neo4j:7687", + + "store-triples" +: { + + create:: function(engine) + + local container = + engine.container("store-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-write-neo4j", +] + $["pub-sub-args"] + [ + "-g", + $["neo4j-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-triples" +: { + + create:: function(engine) + + local container = + engine.container("query-triples") + .with_image(images.trustgraph_flow) + .with_command([ + "triples-query-neo4j", +] + $["pub-sub-args"] + [ + "-g", + $["neo4j-url"], + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-triples", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/ui/workbench-ui.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/ui/workbench-ui.jsonnet new file mode 100644 index 00000000..f2048e47 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/ui/workbench-ui.jsonnet @@ -0,0 +1,32 @@ +local images = import "values/images.jsonnet"; + +{ + + "workbench-ui" +: { + + create:: function(engine) + + local container = + engine.container("workbench-ui") + .with_image(images["workbench-ui"]) + .with_limits("0.1", "256M") + .with_reservations("0.1", "256M") + .with_port(8888, 8888, "ui"); + + local containerSet = engine.containers( + "workbench-ui", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8888, 8888, "ui"); + + engine.resources([ + containerSet, + service, + ]) + + }, + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/values/images.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/values/images.jsonnet new file mode 100644 index 00000000..3b29db46 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/values/images.jsonnet @@ -0,0 +1,38 @@ +local version = import "version.jsonnet"; +{ + cassandra: "docker.io/cassandra:5.0.6", +// Not working +// ceph: "quay.io/ceph/daemon:latest-reef", + neo4j: "docker.io/neo4j:2025.08.0-community-bullseye", + pulsar: "docker.io/apachepulsar/pulsar:4.1.0", + rabbitmq: "docker.io/rabbitmq:4.1-management", + pulsar_manager: "docker.io/apachepulsar/pulsar-manager:v0.4.0", + etcd: "quay.io/coreos/etcd:v3.6.4", + minio: "docker.io/minio/minio:RELEASE.2025-09-07T16-13-09Z", + garage: "docker.io/dxflrs/garage:v2.1.0", + milvus: "docker.io/milvusdb/milvus:v2.5.17", + prometheus: "docker.io/prom/prometheus:v3.8.0", + grafana: "docker.io/grafana/grafana:12.3.0", + loki: "docker.io/grafana/loki:3.6.2", + trustgraph_base: "docker.io/trustgraph/trustgraph-base:" + version, + trustgraph_flow: "docker.io/trustgraph/trustgraph-flow:" + version, + trustgraph_ocr: "docker.io/trustgraph/trustgraph-ocr:" + version, + trustgraph_bedrock: "docker.io/trustgraph/trustgraph-bedrock:" + version, + trustgraph_vertexai: "docker.io/trustgraph/trustgraph-vertexai:" + version, + trustgraph_hf: "docker.io/trustgraph/trustgraph-hf:" + version, + trustgraph_mcp: "docker.io/trustgraph/trustgraph-mcp:" + version, + trustgraph_unstructured: "docker.io/trustgraph/trustgraph-unstructured:" + version, + qdrant: "docker.io/qdrant/qdrant:v1.15.4", + memgraph_mage: "docker.io/memgraph/memgraph-mage:3.5", + memgraph_lab: "docker.io/memgraph/lab:3.5.0", + falkordb: "docker.io/falkordb/falkordb:v4.12.5", + "workbench-ui": "docker.io/trustgraph/workbench-ui:1.7.2", + "ddg-mcp-server": "docker.io/trustgraph/ddg-mcp-server:0.1.0", + "tgi-service-intel-xpu": "ghcr.io/huggingface/text-generation-inference:3.3.1-intel-xpu", + "tgi-service-cpu": "ghcr.io/huggingface/text-generation-inference:3.3.1-intel-cpu", + "tgi-service-gaudi": "ghcr.io/huggingface/text-generation-inference:sha-f140440-gaudi", + "vllm-service-intel-xpu": "docker.io/intel/vllm:0.8.0-xpu", + "vllm-service-gaudi": "docker.io/trustgraph/vllm-hpu:027f5645", + "vllm-service-nvidia": "docker.io/vllm/vllm-openai:latest", + "vllm-service-intel-battlemage": "docker.io/intelanalytics/ipex-llm-serving-xpu:0.2.0-b6", +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/values/token-costs.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/values/token-costs.jsonnet new file mode 100644 index 00000000..84c70373 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/values/token-costs.jsonnet @@ -0,0 +1,102 @@ +{ + "mistral.mistral-large-2407-v1:0": { + "model_name": "mistral.mistral-large-2407-v1:0", + "input_price": 0.000004, + "output_price": 0.000012 + }, + "meta.llama3-1-405b-instruct-v1:0": { + "model_name": "meta.llama3-1-405b-instruct-v1:0", + "input_price": 0.00000532, + "output_price": 0.000016 + }, + "mistral.mixtral-8x7b-instruct-v0:1": { + "model_name": "mistral.mixtral-8x7b-instruct-v0:1", + "input_price": 0.00000045, + "output_price": 0.0000007 + }, + "meta.llama3-1-70b-instruct-v1:0": { + "model_name": "meta.llama3-1-70b-instruct-v1:0", + "input_price": 0.00000099, + "output_price": 0.00000099 + }, + "meta.llama3-1-8b-instruct-v1:0": { + "model_name": "meta.llama3-1-8b-instruct-v1:0", + "input_price": 0.00000022, + "output_price": 0.00000022 + }, + "anthropic.claude-3-haiku-20240307-v1:0": { + "model_name": "anthropic.claude-3-haiku-20240307-v1:0", + "input_price": 0.00000025, + "output_price": 0.00000125 + }, + "anthropic.claude-3-5-sonnet-20240620-v1:0": { + "model_name": "anthropic.claude-3-5-sonnet-20240620-v1:0", + "input_price": 0.000003, + "output_price": 0.000015 + }, + "cohere.command-r-plus-v1:0": { + "model_name": "cohere.command-r-plus-v1:0", + "input_price": 0.0000030, + "output_price": 0.0000150 + }, + "ollama": { + "model_name": "ollama", + "input_price": 0, + "output_price": 0 + }, + "claude-3-haiku-20240307": { + "model_name": "claude-3-haiku-20240307", + "input_price": 0.00000025, + "output_price": 0.00000125 + }, + "claude-3-5-sonnet-20240620": { + "model_name": "claude-3-5-sonnet-20240620", + "input_price": 0.000003, + "output_price": 0.000015 + }, + "claude-3-opus-20240229": { + "model_name": "claude-3-opus-20240229", + "input_price": 0.000015, + "output_price": 0.000075 + }, + "claude-3-sonnet-20240229": { + "model_name": "claude-3-sonnet-20240229", + "input_price": 0.000003, + "output_price": 0.000015 + }, + "command-r-08-202": { + "model_name": "command-r-08-202", + "input_price": 0.0000025, + "output_price": 0.000010 + }, + "c4ai-aya-23-8b": { + "model_name": "c4ai-aya-23-8b", + "input_price": 0, + "output_price": 0 + }, + "llama.cpp": { + "model_name": "llama.cpp", + "input_price": 0, + "output_price": 0 + }, + "gpt-4o": { + "model_name": "gpt-4o", + "input_price": 0.000005, + "output_price": 0.000015 + }, + "gpt-4o-2024-08-06": { + "model_name": "gpt-4o-2024-08-06", + "input_price": 0.0000025, + "output_price": 0.000010 + }, + "gpt-4o-2024-05-13": { + "model_name": "gpt-4o-2024-05-13", + "input_price": 0.000005, + "output_price": 0.000015 + }, + "gpt-4o-mini": { + "model_name": "gpt-4o-mini", + "input_price": 0.00000015, + "output_price": 0.0000006 + } +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/values/url.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/values/url.jsonnet new file mode 100644 index 00000000..68f8f706 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/values/url.jsonnet @@ -0,0 +1,8 @@ +{ + pulsar: "pulsar://pulsar:6650", + pulsar_admin: "http://pulsar:8080", + amqp: "amqp://guest:guest@rabbitmq:5672", + milvus: "http://milvus:19530", + qdrant: "http://qdrant:6333", + object_store: "garage:3900", +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/vector-store/milvus.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/vector-store/milvus.jsonnet new file mode 100644 index 00000000..81b3380b --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/vector-store/milvus.jsonnet @@ -0,0 +1,142 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local milvus = import "backends/milvus.jsonnet"; + +milvus + { + + "store-graph-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("store-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-write-milvus", +] + $["pub-sub-args"] + [ + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-graph-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("query-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-query-milvus", +] + $["pub-sub-args"] + [ + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "store-doc-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("store-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-write-milvus", +] + $["pub-sub-args"] + [ + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-doc-embeddings" +: { + + create:: function(engine) + + local container = + engine.container("query-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-query-milvus", +] + $["pub-sub-args"] + [ + "-t", + url.milvus, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/vector-store/pinecone.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/vector-store/pinecone.jsonnet new file mode 100644 index 00000000..ef26dfae --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/vector-store/pinecone.jsonnet @@ -0,0 +1,156 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; + +{ + + "pinecone-cloud":: "aws", + "pinecone-region":: "us-east-1", + + "store-graph-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("store-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-write-pinecone", +] + $["pub-sub-args"] + [ + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "query-graph-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("query-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "ge-query-pinecone", +] + $["pub-sub-args"] + [ + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "store-doc-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("store-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-write-pinecone", +] + $["pub-sub-args"] + [ + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "store-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + }, + + "query-doc-embeddings" +: { + + create:: function(engine) + + local envSecrets = engine.envSecrets("pinecone-api-key") + .with_env_var("PINECONE_API_KEY", "pinecone-api-key"); + + local container = + engine.container("query-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "de-query-pinecone", +] + $["pub-sub-args"] + [ + "--log-level", + $["log-level"], + ]) + .with_env_var_secrets(envSecrets) + .with_limits("0.5", "128M") + .with_reservations("0.1", "128M"); + + local containerSet = engine.containers( + "query-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + envSecrets, + containerSet, + service, + ]) + + + } + +} + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/vector-store/qdrant.jsonnet b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/vector-store/qdrant.jsonnet new file mode 100644 index 00000000..4a2b84f8 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/vector-store/qdrant.jsonnet @@ -0,0 +1,244 @@ +local images = import "values/images.jsonnet"; +local url = import "values/url.jsonnet"; +local cassandra_hosts = "cassandra"; +local qdrant = import "backends/qdrant.jsonnet"; + +qdrant + { + + "store-graph-embeddings" +: { + + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("store-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "graph-embeddings-write-qdrant", +] + $["pub-sub-args"] + [ + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", memoryLimit) + .with_reservations("0.1", memoryReservation); + + local containerSet = engine.containers( + "store-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-graph-embeddings" +: { + + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("query-graph-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "graph-embeddings-query-qdrant", +] + $["pub-sub-args"] + [ + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", memoryLimit) + .with_reservations("0.1", memoryReservation); + + local containerSet = engine.containers( + "query-graph-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "store-doc-embeddings" +: { + + "memory-limit":: "256M", + "memory-reservation":: "256M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("store-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "doc-embeddings-write-qdrant", +] + $["pub-sub-args"] + [ + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", memoryLimit) + .with_reservations("0.1", memoryReservation); + + local containerSet = engine.containers( + "store-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-doc-embeddings" +: { + + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("query-doc-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "doc-embeddings-query-qdrant", +] + $["pub-sub-args"] + [ + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", memoryLimit) + .with_reservations("0.1", memoryReservation); + + local containerSet = engine.containers( + "query-doc-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + }, + + "store-row-embeddings" +: { + + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("store-row-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "row-embeddings-write-qdrant", +] + $["pub-sub-args"] + [ + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", memoryLimit) + .with_reservations("0.1", memoryReservation); + + local containerSet = engine.containers( + "store-row-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + }, + + "query-row-embeddings" +: { + + "memory-limit":: "128M", + "memory-reservation":: "128M", + + create:: function(engine) + + local memoryLimit = self["memory-limit"]; + local memoryReservation = self["memory-reservation"]; + + local container = + engine.container("query-row-embeddings") + .with_image(images.trustgraph_flow) + .with_command([ + "row-embeddings-query-qdrant", +] + $["pub-sub-args"] + [ + "-t", + url.qdrant, + "--log-level", + $["log-level"], + ]) + .with_limits("0.5", memoryLimit) + .with_reservations("0.1", memoryReservation); + + local containerSet = engine.containers( + "query-row-embeddings", [ container ] + ); + + local service = + engine.internalService(containerSet) + .with_port(8000, 8000, "metrics"); + + engine.resources([ + containerSet, + service, + ]) + + + }, + +} diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/zip-readme.md b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/zip-readme.md new file mode 100644 index 00000000..0b117792 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/2.2/zip-readme.md @@ -0,0 +1,28 @@ + +Note! this is a subset of possible configurations, to generate your own +launch config use the config util... + +- Production: https://config-ui.demo.trustgraph.ai +- Early release: https://dev.config-ui.demo.trustgraph.ai + +The config util auto-generates deployment instructions for your +configuration, so that's the recommended way to deploy. + +---------------------------------------------------------------------------- + +These are launch configurations for TrustGraph. See https://trustgraph.ai for +the quickstart using docker compose. + +Hint for Linux: There are files here which get mounted as volumes inside +Docker Compose containers. This may trigger SELinux rules on your system, to +permit access insider the containers, use a command like this... + +chcon -Rt svirt_sandbox_file_t grafana/ prometheus/ + +The file vertexai/private.json is a placeholder for real GCP credentials if +you are using the VertexAI LLM. If you're using that in Docker Compose, +replace with your real credentials, and don't forget to permit access if you +are using Linux: + +chcon -Rt svirt_sandbox_file_t vertexai/ + diff --git a/ai-context/trustgraph-templates/trustgraph_configurator/templates/index.json b/ai-context/trustgraph-templates/trustgraph_configurator/templates/index.json new file mode 100644 index 00000000..dc94ed14 --- /dev/null +++ b/ai-context/trustgraph-templates/trustgraph_configurator/templates/index.json @@ -0,0 +1,80 @@ +{ + "templates": [ + { + "name": "1.8", + "description": "Pluggable messaging fabric, switched to Garage for object store", + "version": "1.8.21", + "status": "stable" + }, + { + "name": "1.9", + "description": "Ontology extraction update, JSONL output from prompts", + "version": "1.9.4", + "status": "beta" + }, + { + "name": "2.1", + "description": "End-to-end provenance tracking and explainability across the knowledge pipeline — from document ingestion through extraction to query-time retrieval - every answer traceable to its source evidence", + "version": "2.1.26", + "status": "stable" + }, + { + "name": "2.2", + "description": "New agent orchestration engine, extended document processing to handle more document types include MS Office types", + "version": "2.2.16", + "status": "pre-release" + } + ], + "platforms": [ + { + "name": "podman-compose", + "description": "Uses podman-compose to deploy a compose file to a local Podman environment" + }, + { + "name": "docker-compose", + "description": "Uses docker-compose to deploy a compose file to a local Docker installation" + }, + { + "name": "minikube-k8s", + "description": "Uses kubectl to deploy resources to a Minikube Kubernetes cluster" + }, + { + "name": "scw-k8s", + "description": "Uses kubectl to deploy resources to a Kubernetes cluster running in Scaleway cloud" + }, + { + "name": "ovh-k8s", + "description": "Uses kubectl to deploy resources to a Kubernetes cluster running in OVHCloud" + }, + { + "name": "gcp-k8s", + "description": "Uses kubectl to deploy resources to a Kubernetes cluster on Google Cloud (e.g. GKE)" + }, + { + "name": "aks-k8s", + "description": "Uses kubectl to deploy resources to a Kubernetes cluster in Azure (AKS)" + }, + { + "name": "eks-k8s", + "description": "Uses kubectl to deploy resources to an Kubernetes cluster running in AWS (e.g. EKS)" + } + ], + "statuses": [ + { + "name": "pre-release", + "description": "Pre-release" + }, + { + "name": "alpha", + "description": "Alpha" + }, + { + "name": "beta", + "description": "Beta" + }, + { + "name": "stable", + "description": "Stable for production use" + } + ] +} diff --git a/ai-context/trustgraph-templates/version.py b/ai-context/trustgraph-templates/version.py new file mode 100644 index 00000000..87fff4ab --- /dev/null +++ b/ai-context/trustgraph-templates/version.py @@ -0,0 +1,2 @@ +__version__ = "0.0.0" + diff --git a/ai-context/workbench-ui/.github/workflows/pull-request.yaml b/ai-context/workbench-ui/.github/workflows/pull-request.yaml new file mode 100644 index 00000000..b281c874 --- /dev/null +++ b/ai-context/workbench-ui/.github/workflows/pull-request.yaml @@ -0,0 +1,28 @@ + +name: Test pull request + +on: + pull_request: + +permissions: + contents: read + +jobs: + + container-push: + + name: Build + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install Typescript support + run: npm install + + - name: Run tests + run: npm test -- --run + + - name: Build + run: make service-package VERSION=0.0.0 diff --git a/ai-context/workbench-ui/.github/workflows/release.yaml b/ai-context/workbench-ui/.github/workflows/release.yaml new file mode 100644 index 00000000..d9cf463b --- /dev/null +++ b/ai-context/workbench-ui/.github/workflows/release.yaml @@ -0,0 +1,70 @@ + +name: Build + +on: + workflow_dispatch: + push: + tags: + - v* + +permissions: + contents: read + +jobs: + + deploy: + + name: Build everything + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + environment: + name: release + + steps: + + - name: Checkout + uses: actions/checkout@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a + with: + username: ${{ vars.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_SECRET }} + + - name: Get version + id: version + run: echo VERSION=$(git describe --exact-match --tags | sed 's/^v//') >> $GITHUB_OUTPUT + + - name: Install Typescript support + run: npm install + + - name: Run tests + run: npm test -- --run + + - name: Extract metadata for container + id: meta + uses: docker/metadata-action@v4 + with: + images: trustgraph/workbench-ui + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha + + - name: Build + run: make service-package VERSION=${{ steps.version.outputs.VERSION }} + + - name: Build and push Docker image + id: push + uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 + with: + context: . + file: ./Containerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + diff --git a/ai-context/workbench-ui/.gitignore b/ai-context/workbench-ui/.gitignore new file mode 100644 index 00000000..32183714 --- /dev/null +++ b/ai-context/workbench-ui/.gitignore @@ -0,0 +1,29 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +*~ +workbench-ui/workbench/__pycache__/ +workbench-ui/workbench/ui/ +workbench-ui/workbench/version.py +*.egg-info/ \ No newline at end of file diff --git a/ai-context/workbench-ui/.prettierignore b/ai-context/workbench-ui/.prettierignore new file mode 100644 index 00000000..eac61925 --- /dev/null +++ b/ai-context/workbench-ui/.prettierignore @@ -0,0 +1,7 @@ +*.json +*.yaml +*.md +pulumi +**/node_modules +env +dist diff --git a/ai-context/workbench-ui/.prettierrc b/ai-context/workbench-ui/.prettierrc new file mode 100644 index 00000000..8b6b8803 --- /dev/null +++ b/ai-context/workbench-ui/.prettierrc @@ -0,0 +1,3 @@ +{ + "printWidth": 79 +} diff --git a/ai-context/workbench-ui/CODEBOT-INSTRUCTIONS.md b/ai-context/workbench-ui/CODEBOT-INSTRUCTIONS.md new file mode 100644 index 00000000..24ce203a --- /dev/null +++ b/ai-context/workbench-ui/CODEBOT-INSTRUCTIONS.md @@ -0,0 +1,836 @@ +# UI Toolkits and Framework Notes + +## Change Management and API Stability + +**CRITICAL**: Components in `src/components/common/` are foundational to the entire application. DO NOT modify these components without explicit approval from the application design authority. Changes to common components have extensive downstream impact and can break multiple features across the application. + +### Change Impact Assessment +Before modifying any common component: +1. **Document all consumers** - Search the entire codebase for usage +2. **Assess breaking changes** - Any interface changes affect all consumers +3. **Test extensively** - Changes can break seemingly unrelated features +4. **Get approval** - Design authority must approve all common component changes + +### Lessons from SelectField Issues +Recent issues with SelectField demonstrate why common component changes are dangerous: +- **September 2025**: Changes to SelectField to support one feature (Ontology editor) broke document submission +- **Root cause**: Interface contract violations between array/string APIs +- **Impact**: Multiple components across different domains affected +- **Resolution required**: Systematic updates to 15+ components across the application + +**Key takeaway**: Changing common components to fix one feature often breaks others. Always prefer adapter patterns or feature-specific solutions over modifying shared infrastructure. + +## Directory Structure and Organization Rationale + +### Core Principles + +We follow a **domain-driven, flat structure** that avoids unnecessary nesting and keeps related code together: + +1. **Avoid generic aggregation directories** - No `src/hooks/`, `src/constants/`, `src/utils/` that become dumping grounds +2. **Colocate by domain** - Keep related code together in feature-specific directories +3. **Flat when possible** - Single files don't need their own subdirectories +4. **Clear separation of concerns** - Different types of logic go in appropriate places + +### Directory Layout + +``` +src/ +├── components/ # UI components organized by domain +│ ├── schemas/ # All schema-related UI components +│ │ ├── EditSchemaDialog.tsx # Main orchestrator component +│ │ ├── SchemaFieldEditor.tsx # Individual field editing +│ │ ├── SchemaFieldsList.tsx # Fields list management +│ │ ├── SchemaTableStates.tsx # Reusable table states +│ │ ├── useSchemaForm.ts # Form state logic (colocated) +│ │ └── ... +│ ├── taxonomies/ # All taxonomy-related UI components +│ └── common/ # Truly shared/generic components +├── model/ # Data models, types, and domain constants +│ ├── schemas-table.tsx # Schema data models +│ ├── schemaTypes.ts # Schema type constants +│ └── ... +├── state/ # Application state management +│ ├── schemas.ts # Schema API calls and state +│ └── ... +├── api/ # Direct API communication +└── utils/ # Pure utility functions (no React/UI) +``` + +### Rationale by Directory + +**`src/components/[domain]/`** +- Contains ALL UI components for a specific domain (schemas, taxonomies, etc.) +- Includes domain-specific hooks like `useSchemaForm.ts` +- **Why**: Keeps everything needed to work on a feature in one place +- **Avoid**: Generic `src/hooks/` that becomes a dumping ground + +**`src/model/`** +- Data types, interfaces, constants, and domain models +- **Why**: Centralized data definitions that can be imported anywhere +- **Example**: `schemaTypes.ts` contains `SCHEMA_TYPE_OPTIONS` and `DEFAULT_FIELD` + +**`src/state/`** +- High-level application state management +- React Query hooks for API calls and caching +- **Why**: Separates data fetching/caching from UI logic + +**`src/api/`** +- Direct API communication layer +- WebSocket management +- **Why**: Abstracts network concerns from business logic + +### Benefits of This Approach + +1. **Discoverability**: All schema-related code is in `src/components/schemas/` +2. **Maintainability**: Changes to schema features are localized +3. **Reusability**: Shared types in `src/model/` can be imported anywhere +4. **Scalability**: New domains get their own component directories +5. **Avoids Anti-patterns**: No generic directories that accumulate unrelated files + +### Example: Schema Feature Organization + +When working on schema-related features, everything you need is in one place: +- UI components: `src/components/schemas/` +- Data models: `src/model/schemas-table.tsx`, `src/model/schemaTypes.ts` +- API/state: `src/state/schemas.ts` + +This eliminates the need to hunt through multiple generic directories to understand or modify a feature. + +## Icon Library + +**CRITICAL**: Always use `lucide-react` for icons throughout the application. Do NOT use `react-icons` or any other icon library. + +```tsx +// ✅ Correct - Use lucide-react +import { Plus, Save, Trash2, Edit, Settings } from "lucide-react"; + +// ❌ Wrong - Don't use react-icons +import { FiPlus, FiSave } from "react-icons/fi"; +``` + +**Common icon mappings from react-icons to lucide-react:** +- `FiPlus` → `Plus` +- `FiX` → `X` +- `FiSave` → `Save` +- `FiTrash2` → `Trash2` +- `FiEdit/FiEdit3` → `Edit` +- `FiSettings` → `Settings` +- `FiDownload` → `Download` +- `FiUpload` → `Upload` +- `FiMove` → `Move` +- `FiMoreVertical` → `MoreVertical` +- `FiList` → `List` + +## Chakra UI Version + +**CRITICAL**: This project uses **Chakra UI v3**, NOT v2. Always check component APIs against v3 documentation. + +## Key Chakra v3 Migration Points + +### Modal → Dialog +```tsx +// ❌ Chakra v2 + + + + Title + Content + + + +// ✅ Chakra v3 + onOpenChange(x.open)}> + + + + + + Title + + Content + + + + +``` + +### Toast System +```tsx +// ❌ Chakra v2 +const toast = useToast(); +toast({ title: "Success", status: "success" }); + +// ✅ Chakra v3 +import { toaster } from "../ui/toaster"; +toaster.create({ title: "Success", status: "success" }); +``` + +### Form Components +```tsx +// ❌ Chakra v2 + + Label + + + +// ✅ Chakra v3 + + Label + + +``` + +### Tabs Structure +```tsx +// ❌ Chakra v2 + + + Tab 1 + + + Content + + + +// ✅ Chakra v3 + + + Tab 1 + + Content + +``` + +### Menu Components +```tsx +// ❌ Chakra v2 + + Button + + Item + + + +// ✅ Chakra v3 + + Button + + Item + + +``` + +### Props Changes +```tsx +// ❌ Chakra v2 props +colorScheme="blue" +isDisabled={true} + +// ✅ Chakra v3 props +colorPalette="blue" +disabled={true} +``` + +### Layout Components +```tsx +// ❌ Chakra v2 + + +// ✅ Chakra v3 + +``` + +### Spacing Props +```tsx +// ❌ Old pattern + + + +// ✅ Chakra v3 + + +``` + +### Button Icons +```tsx +// ❌ Old pattern + +} aria-label="Upload" /> + +// ✅ Chakra v3 + + +``` + +### Input Groups (Simplified) +```tsx +// ❌ Chakra v2 + + 🔍 + + + +// ✅ Chakra v3 (simplified approach) + +``` + +### Avatar Structure +```tsx +// ❌ Chakra v2 + + +// ✅ Chakra v3 + + + +``` + +### Alert Component +```tsx +// ❌ Chakra v2 + + + Error message + + +// ✅ Chakra v3 + + + + Error message + + +``` + +**Alert Status Options:** +- `status="error"` - Red error alerts +- `status="warning"` - Orange warning alerts +- `status="success"` - Green success alerts +- `status="info"` - Blue info alerts + +**Alert with Title:** +```tsx + + + + Warning Title + Warning description text + + +``` + +### Progress Component +```tsx +// ❌ Chakra v2 + + +// ✅ Chakra v3 + + + + + + + +``` + +**Progress with custom styling:** +```tsx + + + + + +``` + +## Layout Components Still Work + +**Important**: VStack, HStack, Box, Grid, GridItem, Text, Button, Input, etc. still work the same way in v3. The confusion around VStack/HStack causing "invalid component type" errors is usually due to **circular import dependencies**, not Chakra version issues. + +## Common Debugging Steps + +1. **Check imports**: Ensure all Chakra components are imported from `@chakra-ui/react` +2. **Verify component structure**: Use the v3 nested component patterns (Component.Root, Component.Trigger, etc.) +3. **Check props**: Use `colorPalette` instead of `colorScheme`, `disabled` instead of `isDisabled` +4. **Circular imports**: If getting "invalid component type" errors with basic components like VStack, check for circular import dependencies + +## Migration Verification Checklist + +When migrating components to Chakra v3: +- [ ] Replace `` with `` +- [ ] Replace `` with `` +- [ ] Wrap text in `...` +- [ ] Replace `` with `` +- [ ] Use `` structure for cards +- [ ] Replace `` with `` +- [ ] Replace `spacing` props with `gap` props +- [ ] Replace `colorScheme` with `colorPalette` +- [ ] Replace `isDisabled` with `disabled` +- [ ] Test build after changes +- [ ] Verify visual styling is preserved +- [ ] Be systematic: search for old patterns, document the fix, then apply consistently + +## Project-Specific Patterns + +### Notifications +**CRITICAL: NEVER use the toaster directly.** The `toaster` from `@chakra-ui/react` or `../ui/toaster` must NOT be imported or used directly. Always use the `useNotification` hook: + +```tsx +// ❌ NEVER do this - toaster is forbidden +import { toaster } from "../ui/toaster"; +import { toaster } from "@chakra-ui/react"; +toaster.create({ title: "Success", status: "success" }); + +// ✅ ALWAYS do this instead +import { useNotification } from "../../state/notify"; + +const notify = useNotification(); +notify.success("Operation completed successfully"); +notify.error("Something went wrong"); +notify.info("FYI: This is informational"); +``` + +**Why toaster is forbidden:** +- Direct toaster usage bypasses the project's notification standards +- The `useNotification` hook provides consistent error prefixing and styling +- It maintains a unified notification interface across the application +- Direct toaster usage can cause inconsistent user experience + +### Common Components +**ALWAYS** prefer using pre-built components from `src/components/common/` instead of raw Chakra components. These components handle Chakra v3 APIs correctly and reduce boilerplate: + +```tsx +// ❌ Don't use raw Chakra components + + Name + setValue(e.target.value)} /> + + +// ✅ Use common components instead + +``` + +**Available Common Components:** +- `TextField` - Text input with label and validation +- `TextAreaField` - Multi-line text input +- `SelectField` - Dropdown select with rich options +- `BasicTable` - Pre-configured Tanstack Table +- `Card` - Consistent card layout with title/description +- `ProgressSubmitButton` - Submit button with loading state +- `PageHeader` - Standard page header layout +- `StatusBadge` - Consistent status indicators +- `CenterSpinner` - Loading spinner +- `ChipInputField` - Tag/chip input field +- `NumberField` - Numeric input with validation +- `Slider` - Range slider component + +#### SelectField Usage +**CRITICAL**: SelectField expects array values for selection and MUST include description fields for dropdown display: + +```tsx +// ✅ Correct usage +import SelectField from "../common/SelectField"; +import SelectOptionText from "../common/SelectOptionText"; + + + Option 1 + + ) + }, + { + value: 'option2', + label: 'Option 2', + description: ( + + Option 2 + + ) + } + ]} + value={selectedValues} // array - current selection (empty array for no selection) + onValueChange={(values) => setSelectedValues(values)} // receives array +/> +``` + +**Important Notes:** +- Pass an empty array `[]` for no selection, not an empty string +- The `value` prop should always be an array +- The `onValueChange` callback receives an array +- For single selection, extract the first element: `values.length > 0 ? values[0] : null` +- **REQUIRED**: The `description` field MUST be provided using `SelectOptionText` or `SelectOption` components +- **Missing descriptions will result in empty dropdown options** + +**Example with single selection extraction:** +```tsx +const [selectedValues, setSelectedValues] = useState([]); + +// Get the selected value (for single select behavior) +const selectedValue = selectedValues.length > 0 ? selectedValues[0] : null; + +// Handle submission +const handleSubmit = () => { + if (selectedValue) { + onSubmit(selectedValue); + } +}; +``` + +**Common Mistake - Missing Descriptions:** +```tsx +// ❌ WRONG - Will show empty dropdown options +items={[ + {value: 'option1', label: 'Option 1'}, // Missing description! + {value: 'option2', label: 'Option 2'} // Missing description! +]} + +// ✅ CORRECT - Includes required descriptions +items={[ + { + value: 'option1', + label: 'Option 1', + description: Option 1 + }, + { + value: 'option2', + label: 'Option 2', + description: Option 2 + } +]} +``` + +### Theming and Colors +**ALWAYS** use semantic color tokens instead of direct color palettes. The theme provides semantic tokens that automatically handle light/dark mode: + +```tsx +// ❌ Don't use direct color palettes +colorPalette="blue" +bg="gray.100" +color="deepPlum.700" + +// ✅ Use semantic tokens instead +colorPalette="primary" +bg="bg.muted" +color="primary.fg" +``` + +**Available Semantic Color Palettes:** +- `primary` - Main brand color (airForceBlue) +- `accent` - Secondary brand color (deepPlum) +- `observing` - For observation callouts (warmNeutral) +- `thinking` - For thinking callouts (deepPlum variants) +- `insightful` - For answer callouts (neutralGreen) + +**Semantic Token Structure:** +Each palette has these variants: +- `.solid` - Strong, high contrast (buttons, badges) +- `.contrast` - Text on solid backgrounds +- `.fg` - Foreground text color +- `.muted` - Subtle backgrounds +- `.subtle` - Light backgrounds +- `.emphasized` - Medium emphasis backgrounds +- `.focusRing` - Focus indicators + +**Background/Text Tokens:** +- `background` - Main page background +- `text` - Main text color +- `bg.muted` - Subtle background areas +- `fg.muted` - Muted text + +### Page Structure +**ALWAYS** use consistent page structure with PageHeader: + +```tsx +// ❌ Don't embed headings in components +export const MyComponent = () => { + return ( + + My Page Title + + + ); +}; + +// ✅ Use PageHeader at the page level +// In pages/MyPage.tsx: +import PageHeader from "../components/common/PageHeader"; +import MyComponent from "../components/MyComponent"; + +const MyPage = () => { + return ( + <> + } + title="Page Title" + description="Brief description of what this page does" + /> + + + ); +}; + +// In components/MyComponent.tsx (no heading): +export const MyComponent = () => { + return ( + + + + ); +}; +``` + +**Page Structure Rules:** +1. Page components go in `src/pages/` directory +2. Always use `PageHeader` component for consistent headers +3. Page title and description should be at page level, not component level +4. Components should not contain their own page-level headings +5. Use appropriate lucide-react icons for the page icon + +### Progress Management and Loading States + +**CRITICAL**: Always use the `useActivity` hook for loading states instead of managing spinners manually. This provides consistent loading indicators across the application. + +```tsx +// ❌ Don't manage loading states manually +const [isLoading, setIsLoading] = useState(false); +const handleSubmit = async () => { + setIsLoading(true); + try { + await submitData(); + } finally { + setIsLoading(false); + } +}; + +// ✅ Use useActivity hook instead +import { useActivity } from "../../state/activity"; + +const submitMutation = useMutation({ + mutationFn: submitData, +}); + +// Automatically shows/hides loading indicator +useActivity(submitMutation.isPending, "Submitting data"); +``` + +**Progress System Components:** + +1. **`useProgressStateStore`** - Zustand store that manages global activity tracking + - `activity: Set` - Active operations being tracked + - `error: string` - Current error state + - `addActivity(name)` - Add a loading operation + - `removeActivity(name)` - Remove a loading operation + - `setError(message)` - Set/clear error state + +2. **`useActivity(isActive, description)`** - React hook for automatic activity management + - `isActive: boolean` - Whether the activity is currently running + - `description: string` - User-friendly description of the activity + - Automatically adds/removes activities based on the boolean condition + - Handles cleanup when component unmounts or dependencies change + +**Usage Patterns:** + +```tsx +// ✅ With React Query mutations +const updateMutation = useMutation({ mutationFn: updateData }); +useActivity(updateMutation.isPending, "Updating settings"); + +// ✅ With React Query queries +const dataQuery = useQuery({ queryKey: ['data'], queryFn: fetchData }); +useActivity(dataQuery.isLoading, "Loading data"); + +// ✅ Multiple activities for complex operations +useActivity(settingsQuery.isLoading, "Loading settings"); +useActivity(updateSettingsMutation.isPending, "Saving settings"); +useActivity(resetSettingsMutation.isPending, "Resetting settings"); + +// ✅ Manual activity management (when useActivity isn't sufficient) +const addActivity = useProgressStateStore((state) => state.addActivity); +const removeActivity = useProgressStateStore((state) => state.removeActivity); + +const handleComplexOperation = async () => { + const activityId = "Processing complex operation"; + addActivity(activityId); + try { + await step1(); + await step2(); + await step3(); + } finally { + removeActivity(activityId); + } +}; +``` + +**Benefits of the Progress System:** +- **Consistent UX**: All loading states are managed centrally +- **Automatic cleanup**: Activities are removed when operations complete or components unmount +- **Deduplication**: Multiple identical activity names are automatically deduplicated +- **Global visibility**: The UI can show a global loading indicator when any activities are active +- **Error handling**: Centralized error state management +- **Zero boilerplate**: Just call `useActivity()` with a boolean and description + +**Integration with TanStack Query:** +The progress system integrates perfectly with TanStack Query's loading states: + +```tsx +// All these patterns work seamlessly together +export const useSettings = () => { + const settingsQuery = useQuery({ + queryKey: ["settings"], + queryFn: fetchSettings, + }); + + const updateMutation = useMutation({ + mutationFn: updateSettings, + }); + + // Automatic activity tracking + useActivity(settingsQuery.isLoading, "Loading settings"); + useActivity(updateMutation.isPending, "Saving settings"); + + return { + settings: settingsQuery.data, + isLoading: settingsQuery.isLoading, + updateSettings: updateMutation.mutate, + isSaving: updateMutation.isPending, + }; +}; +``` + +### Table Components + +**CRITICAL**: Always use TanStack Table with our standardized table components instead of manually implementing Chakra Table structures. + +**Standard Table Pattern:** + +1. **Create Model File** (`src/model/[feature]-table.tsx`): +```tsx +import { createColumnHelper } from "@tanstack/react-table"; + +export type MyData = { + id: string; + name: string; + description: string; +}; + +export const columnHelper = createColumnHelper(); + +export const columns = [ + columnHelper.accessor("id", { + header: "ID", + cell: (info) => info.getValue(), + }), + columnHelper.accessor("name", { + header: "Name", + cell: (info) => info.getValue(), + }), + columnHelper.accessor("description", { + header: "Description", + cell: (info) => info.getValue(), + }), +]; +``` + +2. **Use Common Table Components**: +```tsx +// ❌ Don't manually implement table structure + + + + Name + + + + {data.map(row => ( + + {row.name} + + ))} + + + +// ✅ Use standardized components and models +import { BasicTable } from "../common/BasicTable"; +import { columns, MyData } from "../../model/my-data-table"; + +const table = useReactTable({ + data: myData as MyData[], + columns, + getCoreRowModel: getCoreRowModel(), + getSortedRowModel: getSortedRowModel(), +}); + +return ; +``` + +**Available Table Components:** +- `BasicTable` - Standard table display +- `ClickableTable` - Table with row click handlers +- `SelectableTable` - Table with row selection checkboxes + +**Standard Column Patterns:** +```tsx +// Selection column (for SelectableTable) +columnHelper.display({ + id: "select", + header: ({ table }) => ( + + + + + ), + cell: ({ row }) => ( + + + + + ), +}); + +// Data columns with custom formatting +columnHelper.accessor("timestamp", { + header: "Created", + cell: (info) => timeString(info.getValue()), +}); + +// Tags/badges column +columnHelper.accessor("tags", { + header: "Tags", + cell: (info) => + info.getValue()?.map((tag) => ( + + {tag} + + )), +}); +``` + +**Benefits of Standardized Tables:** +- ✅ Consistent behavior and styling across the application +- ✅ Built-in sorting, selection, and interaction patterns +- ✅ Type safety with column definitions +- ✅ Easier testing and maintenance +- ✅ Better performance with TanStack optimizations +- ✅ Automatic loading state integration with progress system + +### Other Patterns +- Use Tanstack Query for state management with existing socket-based config API +- Follow kebab-case naming conventions for IDs and URLs +- Always prefer TanStack Table models over manual Chakra Table implementation \ No newline at end of file diff --git a/ai-context/workbench-ui/Containerfile b/ai-context/workbench-ui/Containerfile new file mode 100644 index 00000000..90b3b6e1 --- /dev/null +++ b/ai-context/workbench-ui/Containerfile @@ -0,0 +1,30 @@ + +FROM alpine:3.21 AS build + +RUN apk add --update --no-cache --no-progress make g++ gcc linux-headers + +RUN apk add --update --no-cache --no-progress python3 py3-pip py3-wheel \ + python3-dev + +RUN mkdir /root/wheels + +COPY workbench-ui /root/workbench-ui/ + +RUN (cd /root/workbench-ui && pip wheel -w /root/wheels --no-deps .) + +FROM alpine:3.21 + +ENV PIP_BREAK_SYSTEM_PACKAGES=1 + +COPY --from=build /root/wheels /root/wheels + +RUN apk add --update --no-cache --no-progress python3 py3-pip \ + py3-aiohttp + +RUN pip install /root/wheels/* && \ + pip cache purge && \ + rm -rf /root/wheels + +CMD service +EXPOSE 8888 + diff --git a/ai-context/workbench-ui/FLOW-CLASS-NOTES.md b/ai-context/workbench-ui/FLOW-CLASS-NOTES.md new file mode 100644 index 00000000..85df7c1b --- /dev/null +++ b/ai-context/workbench-ui/FLOW-CLASS-NOTES.md @@ -0,0 +1,132 @@ +# Flow Class Architecture Notes + +## Overview + +Flow Classes define distributed service mesh architectures for TrustGraph dataflow processing. They specify how processors connect through message queues to form complete data processing pipelines. + +## Core Concepts + +### Service Mesh Graph + +Flow Classes describe a **service mesh graph** where: +- **Processors** are nodes that provide and consume services +- **Queues** are the edges that connect processors +- **Queue names** determine connectivity - matching names create connections +- **Template variables** control queue multiplexing strategy + +### Service Providers vs Consumers + +**Service Providers** implement services by listening to special queue names: +- `input` - receives work/data to process +- `request` - handles request/response patterns (receives requests) +- `response` - sends back responses in request/response patterns + +**Service Consumers** use services through all other queue names: +- Any queue name NOT `input`/`request`/`response` +- Represents dependencies on external services +- Send messages TO these queues as clients + +### Queue Multiplexing Strategy + +**Class Processors** (`{class}` template): +- Use **shared queues** across all flow instances of that class +- One `service-name-{class}` queue serves ALL flows +- Higher throughput, shared resources +- Example: `user-auth-{class}` becomes `user-auth-nlp-chat` + +**Flow Processors** (`{id}` template): +- Use **dedicated queues** per individual flow instance +- Each flow gets its own `service-name-{id}` queue +- Isolated processing, per-flow state +- Example: `document-store-{id}` becomes `document-store-flow123` + +## Flow Class Structure + +```typescript +interface FlowClassDefinition { + id: string; + class: { + [processorName: string]: { + [queueName: string]: string; // Queue pattern with templates + }; + }; + flow: { + [processorName: string]: { + [queueName: string]: string; // Queue pattern with templates + }; + }; + interfaces: { + [interfaceName: string]: string | { + request: string; + response: string; + }; + }; + description?: string; + tags?: string[]; +} +``` + +## Service Graph Connections + +Connections form when: +1. **Processor A** has queue "service-x" (as consumer) +2. **Processor B** implements "service-x" via `input`/`request`/`response` (as provider) +3. This creates edge: A → B + +Example: +``` +nlp-processor: + input: "nlp-service-{class}" # Provides nlp-service + +chat-handler: + nlp: "nlp-service-{class}" # Consumes nlp-service +``` +Result: `chat-handler` → `nlp-processor` + +## External API Layer + +**Interfaces** define the **external API contract** - how external clients access internal services: + +- **Public Service Contract**: External clients call interface endpoints +- **Internal Routing**: Interfaces map to internal queue services +- **Implementation Hiding**: Internal processor topology is hidden from clients + +Interface Types: +- **Simple**: `"api-endpoint": "internal-service-name"` +- **Request/Response**: + ```json + { + "user-api": { + "request": "user-request-{class}", + "response": "user-response-{class}" + } + } + ``` + +## Architecture Layers + +``` +┌─────────────────────────────────┐ +│ External Clients │ +│ (REST, GraphQL, etc.) │ +└─────────────────┬───────────────┘ + │ +┌─────────────────▼───────────────┐ +│ Interfaces │ +│ (Public API Contract) │ +└─────────────────┬───────────────┘ + │ +┌─────────────────▼───────────────┐ +│ Internal Service Mesh │ +│ (Class + Flow Processors) │ +│ Connected via Queue Names │ +└─────────────────────────────────┘ +``` + +## Key Insights + +1. **Class vs Flow is about queue sharing**, not different processor types +2. **Queue names create the service graph** - they're the connection points +3. **Interfaces expose internal services** to external clients +4. **Template variables control multiplexing** - shared vs dedicated queues +5. **Service mesh is unified** - class and flow processors all participate in the same graph \ No newline at end of file diff --git a/ai-context/workbench-ui/LICENSE b/ai-context/workbench-ui/LICENSE new file mode 100644 index 00000000..6b0b1270 --- /dev/null +++ b/ai-context/workbench-ui/LICENSE @@ -0,0 +1,203 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/ai-context/workbench-ui/Makefile b/ai-context/workbench-ui/Makefile new file mode 100644 index 00000000..487c7e12 --- /dev/null +++ b/ai-context/workbench-ui/Makefile @@ -0,0 +1,33 @@ + +PACKAGE_VERSION=0.0.0 +VERSION=0.0.8 + +all: service-package container + +ui: + npm run build + rm -rf workbench-ui/workbench/ui/ + cp -r dist/ workbench-ui/workbench/ui/ +# cp public/*.png workbench-ui/workbench/ui/ + cp public/*.svg workbench-ui/workbench/ui/ + +service-package: ui update-package-versions + cd workbench-ui && python3 setup.py sdist --dist-dir ../pkgs/ + +update-package-versions: + echo __version__ = \"${PACKAGE_VERSION}\" > workbench-ui/workbench/version.py + +CONTAINER=docker.io/trustgraph/workbench-ui +DOCKER=podman + +container: + ${DOCKER} build -f Containerfile -t ${CONTAINER}:${VERSION} \ + --format docker + +push: + ${DOCKER} push ${CONTAINER}:${VERSION} + +docker-hub-login: + cat docker-token.txt | \ + docker login -u trustgraph --password-stdin registry-1.docker.io + diff --git a/ai-context/workbench-ui/README.criteria b/ai-context/workbench-ui/README.criteria new file mode 100644 index 00000000..07b9550c --- /dev/null +++ b/ai-context/workbench-ui/README.criteria @@ -0,0 +1,31 @@ +● Service connections: Same connection type (kind=service) where consumer's {connection-name}-request/{connection-name}-response + queues = provider's request/response queues + + Flow connections: Same connection type (kind=flow) where they share the exact same queue value + + Passive connections: Same connection type (kind=passive) where consumer's single queue value = provider's response queue value + + + + +Interfaces... + + 1. Look up the interface name in the service map's interfaces definitions + 2. Check the kind: + + 2. If kind = "flow": + - The flow class specifies a single queue string + - Example: "entity-contexts-load": "persistent://tg/flow/entity-contexts-load:{id}" + - To connect: find any processor that produces/consumes this exact queue + + If kind = "service": + - The flow class specifies request/response queues + - Example: "agent": {"request": "non-persistent://tg/request/agent:{id}", "response": "non-persistent://tg/response/agent:{id}"} + - To connect: find any processor whose request/response queues match these + 3. The interface definition provides: + - Documentation (description) + - Validation (ensuring correct structure based on kind) + - Visibility hints (for UI purposes) + + So the interfaces are essentially the "public API" of a flow class - they declare which standard connection points this flow class + exposes, and other components can connect to these standardized interfaces without knowing the internal implementation details. diff --git a/ai-context/workbench-ui/README.md b/ai-context/workbench-ui/README.md new file mode 100644 index 00000000..44fde108 --- /dev/null +++ b/ai-context/workbench-ui/README.md @@ -0,0 +1,66 @@ + +# Test Suite for TrustGraph + +## Setup for Python + +``` +pip3 -m venv env +. env/bin/activate +pip3 install -r requirements.txt +``` + +## Dev mode + +``` +npm install +npm run dev +``` + +This runs the application in Vite at http://localhost:5173. +Note that UI bit works, but the generation part isn't running. + +## Run it all locally + +This builds the UI and the Python package: +``` +make service-package +``` + +Then run the Python package which serves the generator and UI: + +``` +export PYTHONPATH=workbench-ui +workbench-ui/scripts/service +``` + +Generation should work + +## Run it in a container + +Build the container: +``` +make service-package VERSION=0.0.0 +``` + +and run it + +``` +podman run -i -t -p 8080:8080 localhost/workbench-ui:0.0.0 +``` + +Go to http://localhost:8080 + +## Release it + +Deployment is Github actions, automatic to Docker Hub. Deployment kicks in +automatically on anything with a version tag. Version tags should be of +form v1.2.3. Convention is to have a branch name something like +`release/vX.Y` for version tags of the form `vX.Y.Z`. So, +version `v0.1.10` would be release on branch `release/v0.1`. + +On release, container images are pushed to docker hub. + +To release with TrustGraph, change the version number of the container +in the trustgraph repo, `templates/values/images.jsonnet` and also +in the config portal repo, same filename, `templates/values/images.jsonnet`. + diff --git a/ai-context/workbench-ui/TEST_STRATEGY.md b/ai-context/workbench-ui/TEST_STRATEGY.md new file mode 100644 index 00000000..66cd4afb --- /dev/null +++ b/ai-context/workbench-ui/TEST_STRATEGY.md @@ -0,0 +1,590 @@ +# Test Strategy for TrustGraph UI + +## Overview + +This document outlines the comprehensive testing strategy for the TrustGraph UI application, a React-based knowledge graph visualization and chat interface built with TypeScript, Vite, and Chakra UI. + +## Technology Stack + +- **Frontend**: React 18, TypeScript, Vite +- **UI Framework**: Chakra UI v3 +- **State Management**: Zustand +- **Data Fetching**: TanStack Query (React Query) +- **Routing**: React Router v7 +- **Visualization**: React Force Graph (3D), Three.js +- **WebSocket**: Custom socket implementation for real-time communication +- **Build Tools**: Vite, ESLint, Prettier + +## Testing Approach + +### 1. Unit Testing + +#### Framework Recommendation +- **Jest** + **React Testing Library** for component testing +- **Vitest** (recommended for Vite projects) as Jest alternative +- **@testing-library/jest-dom** for DOM assertions + +#### What to Test +- **State Management (Zustand stores)**: + - `src/state/chat.ts` - Chat state management + - `src/state/session.ts` - Session state + - `src/state/workbench.ts` - Workbench state + - `src/state/graph-query.ts` - Graph query state + - All other state modules in `src/state/` + +- **Utility Functions**: + - `src/utils/knowledge-graph.ts` - Graph manipulation utilities + - `src/utils/document-encoding.ts` - Document encoding/decoding + - `src/utils/vector-search.ts` - Vector search utilities + - `src/utils/time-string.ts` - Time formatting utilities + +- **API Layer**: + - `src/api/trustgraph/socket.ts` - WebSocket connection + - `src/api/trustgraph/service-call.ts` - Service call utilities + - `src/api/trustgraph/messages.ts` - Message handling + +#### Test Structure +``` +tests/ +├── unit/ +│ ├── components/ +│ ├── state/ +│ ├── utils/ +│ ├── api/ +│ └── model/ +├── integration/ +├── e2e/ +└── fixtures/ +``` + +### 2. Component Testing + +#### Core Components to Test +- **Chat Components**: + - `src/components/chat/ChatConversation.tsx` + - `src/components/chat/ChatMessage.tsx` + - `src/components/chat/InputArea.tsx` + - `src/components/chat/ChatModeSelector.tsx` + +- **Graph Components**: + - `src/components/graph/Graph.tsx` + - `src/components/entity/EntityDetail.tsx` + - `src/components/entity/EntityNode.tsx` + +- **Common Components**: + - `src/components/common/BasicTable.tsx` + - `src/components/common/SelectableTable.tsx` + - `src/components/common/TextField.tsx` + - `src/components/common/Card.tsx` + +- **Layout Components**: + - `src/components/Layout.tsx` + - `src/components/Sidebar.tsx` + +#### Testing Strategies +- **Snapshot Testing** for UI consistency +- **User Interaction Testing** (click, input, navigation) +- **Props Testing** and component behavior +- **Error Boundary Testing** +- **Accessibility Testing** (screen reader, keyboard navigation) + +### 3. Integration Testing + +#### Areas to Test +- **WebSocket Integration**: Test real-time communication with backend +- **State Persistence**: Test Zustand store persistence +- **Route Navigation**: Test React Router integration +- **API Integration**: Test service calls and data flow +- **Component Interaction**: Test parent-child component communication + +#### Mock Strategy +- Mock WebSocket connections for consistent testing +- Mock external APIs and services +- Mock file upload/download operations +- Mock 3D graph rendering for performance + +### 4. End-to-End Testing + +#### Framework Recommendation +- **Playwright** or **Cypress** for cross-browser testing + +#### Test Scenarios +1. **User Authentication Flow** +2. **Chat Interface**: + - Send messages in different modes (graph-rag, agent, basic-llm) + - Receive and display responses + - Chat history persistence + +3. **Knowledge Graph Visualization**: + - Load and display graph data + - Node interaction and navigation + - Graph filtering and search + +4. **Document Management**: + - Upload documents + - Process and index documents + - Search through document library + +5. **Flow Management**: + - Create and edit processing flows + - Execute flows with different parameters + +6. **Agent Tools**: + - Configure MCP tools + - Test agent interactions + - Tool execution and results + +### 5. Performance Testing + +#### Areas to Monitor +- **Bundle Size**: Monitor JavaScript bundle size +- **3D Graph Rendering**: Test performance with large graphs +- **Memory Usage**: Monitor memory leaks in long-running sessions +- **WebSocket Performance**: Test real-time communication under load + +#### Tools +- **Lighthouse** for web performance metrics +- **Bundle Analyzer** for bundle size optimization +- **React DevTools Profiler** for component performance + +### 6. Accessibility Testing + +#### Requirements +- **WCAG 2.1 AA** compliance +- **Screen Reader** compatibility +- **Keyboard Navigation** support +- **Color Contrast** validation + +#### Tools +- **axe-core** for automated accessibility testing +- **React Testing Library** accessibility queries +- **Manual testing** with screen readers + +## Testing Infrastructure + +### Setup Requirements +```bash +# Install testing dependencies +npm install --save-dev \ + vitest \ + @testing-library/react \ + @testing-library/jest-dom \ + @testing-library/user-event \ + jsdom \ + @vitest/ui \ + playwright +``` + +### Configuration Files +- `vitest.config.ts` - Vitest configuration +- `playwright.config.ts` - E2E test configuration +- `test-setup.ts` - Global test setup + +### CI/CD Integration +- **GitHub Actions** workflow for automated testing +- **Pre-commit hooks** for running tests before commits +- **Coverage reporting** with minimum thresholds +- **Visual regression testing** for UI components + +## Test Data Management + +### Mock Data Strategy +- **Fixtures**: Static test data for consistent testing +- **Factory Functions**: Generate test data programmatically +- **MSW (Mock Service Worker)**: Mock API responses +- **WebSocket Mocking**: Mock real-time communication + +### Data Categories +- **Graph Data**: Nodes, edges, and relationships +- **Chat Messages**: Various message types and formats +- **User Sessions**: Authentication and session data +- **Document Metadata**: File information and processing status + +## Coverage Goals + +### Minimum Coverage Targets +- **Unit Tests**: 80% line coverage +- **Integration Tests**: Cover all major user flows +- **E2E Tests**: Cover critical business paths +- **Component Tests**: 90% of UI components + +### Exclusions +- Third-party library code +- Generated type definitions +- Development-only code + +## Testing Best Practices + +### General Guidelines +1. **Test behavior, not implementation** +2. **Use descriptive test names** +3. **Keep tests independent and isolated** +4. **Test error conditions and edge cases** +5. **Maintain test data consistency** + +### React-Specific Guidelines +1. **Test user interactions, not internal state** +2. **Use React Testing Library queries effectively** +3. **Test accessibility features** +4. **Mock external dependencies** +5. **Test component composition** + +## Monitoring and Maintenance + +### Test Health +- **Flaky Test Detection**: Monitor and fix unstable tests +- **Test Performance**: Keep test execution time reasonable +- **Coverage Trends**: Monitor coverage over time +- **Test Maintenance**: Regular review and cleanup + +### Quality Gates +- **All tests must pass** before merging +- **Coverage thresholds** must be maintained +- **Performance budgets** must not be exceeded +- **Accessibility standards** must be met + +## Domain-Specific Testing Strategy + +### Agents Module (`src/components/agents/`) + +#### Components to Test: +- **EditDialog.tsx**: Agent configuration dialog + - Form validation for agent settings + - Tool selection and configuration + - Modal open/close behavior +- **ToolsTable.tsx**: Display agent tools + - Table rendering with tool data + - Tool selection/filtering + - Action buttons (edit, delete, enable/disable) + +#### Testing Approach: +```tsx +// Example test structure +describe('Agent EditDialog', () => { + it('validates required fields', () => { + // Test form validation + }); + + it('handles tool selection', () => { + // Test multi-select tool interface + }); + + it('saves agent configuration', () => { + // Test API calls and state updates + }); +}); +``` + +#### Mock Requirements: +- Agent configuration API calls +- Available tools data +- Agent execution results + +--- + +### Graph Module (`src/components/graph/`) + +#### Components to Test: +- **Graph.tsx**: 3D force graph visualization + - Graph rendering with mock data + - Node selection and highlighting + - Performance with large datasets (>1000 nodes) +- **NodeDetailsDrawer.tsx**: Entity detail panel + - Property display formatting + - Relationship navigation + - Edit mode functionality + +#### Testing Challenges: +- **3D Rendering**: Mock Three.js and react-force-graph +- **Performance**: Test with large graph datasets +- **Interactions**: Node selection, drag, zoom behaviors + +#### Testing Approach: +```tsx +describe('Graph Component', () => { + beforeEach(() => { + // Mock 3D rendering libraries + vi.mock('react-force-graph-3d'); + }); + + it('renders nodes and links', () => { + // Test graph data rendering + }); + + it('handles node selection', () => { + // Test node click events + }); + + it('performs well with large datasets', () => { + // Performance testing with 1000+ nodes + }); +}); +``` + +--- + +### MCP Tools Module (`src/components/mcp-tools/`) + +#### Components to Test: +- **EditDialog.tsx**: MCP tool configuration + - Tool parameter validation + - API endpoint configuration + - Authentication settings +- **McpToolsTable.tsx**: Tools management interface + - Tool status indicators + - Bulk operations (enable/disable multiple tools) + - Tool execution history + +#### Test Data Requirements: +```tsx +// Mock MCP tool configurations +const mockMcpTool = { + id: 'test-tool-1', + name: 'File System Tool', + endpoint: 'http://localhost:3001', + enabled: true, + parameters: { + path: '/tmp', + permissions: 'read-write' + } +}; +``` + +--- + +### Prompts Module (`src/components/prompts/`) + +#### Components to Test: +- **EditDialog.tsx**: Prompt template editor + - Template syntax validation + - Variable substitution preview + - JSON schema validation for structured outputs +- **PromptsTable.tsx**: Prompt management + - Template versioning + - Usage statistics + - Import/export functionality + +#### Key Test Scenarios: +- Template variable validation: `{{variable}}` syntax +- JSON schema validation for structured prompts +- Prompt execution with different input types + +--- + +### Schemas Module (`src/components/schemas/`) - Recently Modularized + +#### Components to Test (New Modular Structure): +- **SchemaFieldEditor.tsx**: Individual field configuration + - Field type selection and validation + - Enum value management + - Required/optional field toggles +- **useSchemaForm.ts**: Form state management hook + - Field addition/removal + - Form validation logic + - Form reset functionality +- **EnumValueManager.tsx**: Enum value editing + - Add/remove enum values + - Duplicate value prevention + - Input validation + +#### Testing Approach for Modular Components: +```tsx +describe('Schema Field Editor', () => { + it('handles field type changes', () => { + // Test type dropdown and dependent field updates + }); + + it('manages enum values correctly', () => { + // Test enum value addition/removal + }); + + it('validates field configurations', () => { + // Test field validation rules + }); +}); + +describe('useSchemaForm hook', () => { + it('manages form state correctly', () => { + // Test hook state management + }); + + it('handles field operations', () => { + // Test add/remove/update field operations + }); +}); +``` + +--- + +### Taxonomies Module (`src/components/taxonomies/`) - Extensive Functionality + +#### Priority Components to Test: +- **TaxonomyManager.tsx**: Main taxonomy editor + - SKOS concept hierarchy management + - Concept relationships (broader/narrower/related) + - Bulk concept operations +- **ConceptEditor.tsx**: Individual concept editing + - Concept metadata validation + - Relationship consistency checking + - Auto-save functionality +- **TaxonomyValidationTab.tsx**: SKOS validation + - Validation rule execution + - Error reporting and suggestions + - Auto-fix functionality +- **SKOSDialog.tsx**: Import/export functionality + - SKOS RDF/XML parsing + - Format conversion (RDF ↔ Turtle ↔ JSON) + - File upload/download + +#### Complex Test Scenarios: +```tsx +describe('Taxonomy Validation', () => { + it('detects circular references', () => { + const invalidTaxonomy = { + concepts: { + 'A': { broader: 'B' }, + 'B': { broader: 'A' } // Circular reference + } + }; + // Test validation catches this error + }); + + it('suggests auto-fixes', () => { + // Test quality improvement suggestions + }); +}); + +describe('SKOS Import/Export', () => { + it('parses valid SKOS RDF/XML', () => { + // Test XML parsing and conversion + }); + + it('handles parsing errors gracefully', () => { + // Test error handling for invalid SKOS + }); +}); +``` + +#### Mock Requirements: +- SKOS validation rules +- File upload/download operations +- Large taxonomy datasets (100+ concepts) + +--- + +## Implementation Roadmap (Updated) + +### Phase 1: Foundation & Utilities (Weeks 1-2) +- **Complete**: Set up testing framework (Vitest already configured) +- **Complete**: Utility function tests (SKOS, time-string, encoding) +- **Complete**: Basic common component tests +- **New**: Test modularized schema components + +### Phase 2: Domain-Specific Components (Weeks 3-4) +- **Agents Module**: Test agent configuration and tools management +- **Prompts Module**: Test prompt editing and template validation +- **MCP Tools Module**: Test tool configuration and execution +- **Graph Module**: Test visualization components (with mocked 3D) + +### Phase 3: Complex Domain Logic (Weeks 5-6) +- **Taxonomies Module**: Test SKOS validation, concept editing, import/export +- **Schemas Module**: Test advanced schema validation and form logic +- **Integration Tests**: Test cross-module interactions + +### Phase 4: Advanced Testing (Weeks 7-8) +- **Performance Testing**: Large dataset handling (1000+ graph nodes, 100+ taxonomy concepts) +- **E2E Workflows**: Complete user journeys across modules +- **Accessibility**: WCAG compliance for all form components +- **Visual Regression**: Ensure UI consistency across refactoring + +--- + +## Testing Priority Matrix + +### High Priority (Week 3-4) +1. **Taxonomies**: Complex SKOS logic, validation, import/export +2. **Schemas**: Recently modularized, needs comprehensive coverage +3. **Graph**: Core visualization functionality +4. **Agents**: Critical for AI functionality + +### Medium Priority (Week 5-6) +1. **Prompts**: Template management and validation +2. **MCP Tools**: Tool configuration and execution +3. **Integration**: Cross-module data flow + +### Lower Priority (Week 7-8) +1. **Performance**: Large dataset handling +2. **Accessibility**: WCAG compliance +3. **Visual**: UI consistency and regression testing + +--- + +## Mock Data Strategy (Updated) + +### Taxonomy Test Data: +```tsx +const mockTaxonomy = { + concepts: { + 'animals': { + prefLabel: 'Animals', + narrower: ['mammals', 'birds'], + topConcept: true + }, + 'mammals': { + prefLabel: 'Mammals', + broader: 'animals', + narrower: ['cats', 'dogs'] + } + }, + scheme: { + uri: 'http://example.org/taxonomy', + hasTopConcept: ['animals'] + } +}; +``` + +### Schema Test Data: +```tsx +const mockSchema = { + name: 'Customer Record', + fields: [ + { + name: 'customer_id', + type: 'string', + required: true, + primary_key: true + }, + { + name: 'status', + type: 'enum', + enum: ['active', 'inactive', 'pending'] + } + ] +}; +``` + +### Agent Test Data: +```tsx +const mockAgent = { + id: 'research-agent', + name: 'Research Assistant', + description: 'Helps with research tasks', + tools: ['web-search', 'document-reader'], + model: 'gpt-4', + temperature: 0.7 +}; +``` + +## Success Metrics + +- **Test Coverage**: Achieve and maintain 80%+ coverage +- **Test Reliability**: < 1% flaky test rate +- **Test Performance**: Test suite completes in < 5 minutes +- **Bug Detection**: Catch 90%+ of bugs before production +- **Developer Experience**: Tests provide clear feedback and are easy to maintain + +## Conclusion + +This testing strategy provides comprehensive coverage for the TrustGraph UI application, ensuring reliability, performance, and maintainability. The phased implementation approach allows for gradual adoption while maintaining development velocity. + +Regular review and updates of this strategy will ensure it remains effective as the application evolves and new features are added. \ No newline at end of file diff --git a/ai-context/workbench-ui/docs/tech-specs/collections.md b/ai-context/workbench-ui/docs/tech-specs/collections.md new file mode 100644 index 00000000..9c82a794 --- /dev/null +++ b/ai-context/workbench-ui/docs/tech-specs/collections.md @@ -0,0 +1,734 @@ +# Collections Support for TrustGraph UI + +## Overview + +This document specifies the implementation of collections support in the TrustGraph UI. Collections provide a way to organize and manage groups of documents with metadata including name, description, and tags. The feature adds collection management capabilities to the Library page through a tabbed interface. + +## API Integration + +### Backend API Summary + +The TrustGraph API provides three collection operations via the `collection-management` endpoint: + +#### 1. List Collections +- **Operation**: `list-collections` +- **Request**: + ```json + { + "operation": "list-collections", + "user": "username", + "tag_filter": ["tag1", "tag2"] // optional + } + ``` +- **Response**: Array of collection metadata objects +- **Fields**: user, collection, name, description, tags, created_at, updated_at + +#### 2. Update Collection (also creates) +- **Operation**: `update-collection` +- **Request**: + ```json + { + "operation": "update-collection", + "user": "username", + "collection": "collection-id", + "name": "Display Name", // optional + "description": "Description", // optional + "tags": ["tag1", "tag2"] // optional + } + ``` +- **Response**: Single collection metadata object in `collections` array +- **Note**: Creates collection if it doesn't exist; updates if it does + +#### 3. Delete Collection +- **Operation**: `delete-collection` +- **Request**: + ```json + { + "operation": "delete-collection", + "user": "username", + "collection": "collection-id" + } + ``` +- **Response**: Empty object `{}` + +### Collection Metadata Structure + +```typescript +interface CollectionMetadata { + user: string; + collection: string; // Collection ID (unique identifier) + name: string; // Display name + description: string; // Description text + tags: string[]; // Array of tags + created_at: string; // ISO timestamp + updated_at: string; // ISO timestamp +} +``` + +## Implementation Plan + +### Phase 1: Socket Layer Integration + +**File**: `src/api/trustgraph/trustgraph-socket.ts` + +Add collection management methods to the socket interface: + +```typescript +// Add to Socket interface +export interface Socket { + // ... existing methods ... + + // Collection management + collectionManagement: () => CollectionManagement; +} + +// New CollectionManagement interface +export interface CollectionManagement { + listCollections: ( + user: string, + tagFilter?: string[] + ) => Promise; + + updateCollection: ( + user: string, + collection: string, + name?: string, + description?: string, + tags?: string[] + ) => Promise; + + deleteCollection: ( + user: string, + collection: string + ) => Promise; +} + +// Implementation in BaseApi class +class BaseApi { + // ... existing methods ... + + collectionManagement(): CollectionManagement { + return { + listCollections: async (user, tagFilter) => { + const request = { + operation: "list-collections", + user, + ...(tagFilter && { tag_filter: tagFilter }), + }; + const response = await this.request("collection-management", request); + return response.collections || []; + }, + + updateCollection: async (user, collection, name, description, tags) => { + const request = { + operation: "update-collection", + user, + collection, + ...(name !== undefined && { name }), + ...(description !== undefined && { description }), + ...(tags !== undefined && { tags }), + }; + const response = await this.request("collection-management", request); + return response.collections[0]; + }, + + deleteCollection: async (user, collection) => { + const request = { + operation: "delete-collection", + user, + collection, + }; + await this.request("collection-management", request); + }, + }; + } +} +``` + +### Phase 2: State Management Hook + +**File**: `src/state/collections.ts` (new file) + +Create a React Query-based state management hook following the library.ts pattern: + +```typescript +import { useQueryClient, useQuery, useMutation } from "@tanstack/react-query"; +import { useSocket, useConnectionState } from "../api/trustgraph/socket"; +import { useNotification } from "./notify"; +import { useActivity } from "./activity"; +import { useSettings } from "./settings"; + +export interface CollectionMetadata { + user: string; + collection: string; + name: string; + description: string; + tags: string[]; + created_at: string; + updated_at: string; +} + +export const useCollections = () => { + const socket = useSocket(); + const connectionState = useConnectionState(); + const queryClient = useQueryClient(); + const notify = useNotification(); + const { settings } = useSettings(); + + const isSocketReady = + connectionState?.status === "authenticated" || + connectionState?.status === "unauthenticated"; + + // Query for fetching all collections + const collectionsQuery = useQuery({ + queryKey: ["collections", settings.user], + enabled: isSocketReady && !!settings.user, + queryFn: () => { + return socket.collectionManagement().listCollections(settings.user); + }, + }); + + // Mutation for creating/updating a collection + const updateCollectionMutation = useMutation({ + mutationFn: ({ collection, name, description, tags, onSuccess }) => { + return socket + .collectionManagement() + .updateCollection( + settings.user, + collection, + name, + description, + tags + ) + .then(() => { + if (onSuccess) onSuccess(); + }); + }, + onError: (err) => { + console.log("Error:", err); + notify.error(err.toString()); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["collections"] }); + notify.success("Collection saved successfully"); + }, + }); + + // Mutation for deleting collections + const deleteCollectionsMutation = useMutation({ + mutationFn: ({ collections, onSuccess }) => { + return Promise.all( + collections.map((collection) => + socket + .collectionManagement() + .deleteCollection(settings.user, collection) + ) + ).then(() => { + if (onSuccess) onSuccess(); + }); + }, + onError: (err) => { + console.log("Error:", err); + notify.error(err.toString()); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["collections"] }); + notify.success("Collections deleted successfully"); + }, + }); + + // Activity indicators + useActivity(collectionsQuery.isLoading, "Loading collections"); + useActivity(updateCollectionMutation.isPending, "Saving collection"); + useActivity(deleteCollectionsMutation.isPending, "Deleting collections"); + + return { + // Collection data and query state + collections: collectionsQuery.data || [], + isLoading: collectionsQuery.isLoading, + isError: collectionsQuery.isError, + error: collectionsQuery.error, + + // Update/create collection operations + updateCollection: updateCollectionMutation.mutate, + isUpdating: updateCollectionMutation.isPending, + updateError: updateCollectionMutation.error, + + // Delete collection operations + deleteCollections: deleteCollectionsMutation.mutate, + isDeleting: deleteCollectionsMutation.isPending, + deleteError: deleteCollectionsMutation.error, + + // Manual refetch + refetch: collectionsQuery.refetch, + }; +}; +``` + +### Phase 3: Data Model + +**File**: `src/model/collection-table.tsx` (new file) + +Define table columns for collections display: + +```typescript +import { createColumnHelper } from "@tanstack/react-table"; +import { Tag } from "@chakra-ui/react"; +import { Checkbox } from "../components/ui/checkbox"; +import { selectionState } from "../components/common/SelectableTable"; +import { CollectionMetadata } from "../state/collections"; + +export const columnHelper = createColumnHelper(); + +export const columns = [ + // Selection column + columnHelper.display({ + id: "select", + header: ({ table }) => ( + + + + + ), + cell: ({ row }) => ( + + + + + ), + }), + + // Collection ID column + columnHelper.accessor("collection", { + header: "Collection ID", + cell: (info) => info.getValue(), + }), + + // Name column + columnHelper.accessor("name", { + header: "Name", + cell: (info) => info.getValue(), + }), + + // Description column + columnHelper.accessor("description", { + header: "Description", + cell: (info) => info.getValue(), + }), + + // Tags column + columnHelper.accessor("tags", { + header: "Tags", + cell: (info) => + info.getValue()?.map((tag) => ( + + {tag} + + )), + }), + + // Created column + columnHelper.accessor("created_at", { + header: "Created", + cell: (info) => new Date(info.getValue()).toLocaleString(), + }), + + // Updated column + columnHelper.accessor("updated_at", { + header: "Updated", + cell: (info) => new Date(info.getValue()).toLocaleString(), + }), +]; +``` + +### Phase 4: UI Components + +#### 4.1 Collections Component + +**File**: `src/components/library/Collections.tsx` (new file) + +Main collections management component following the Documents.tsx pattern: + +```typescript +import React, { useState } from "react"; +import { getCoreRowModel, useReactTable } from "@tanstack/react-table"; + +import { columns } from "../../model/collection-table"; +import { useCollections } from "../../state/collections"; +import { useNotification } from "../../state/notify"; + +import CollectionActions from "./CollectionActions"; +import CollectionDialog from "./CollectionDialog"; +import SelectableTable from "../common/SelectableTable"; +import CollectionControls from "./CollectionControls"; + +const Collections = () => { + const [dialogOpen, setDialogOpen] = useState(false); + const [editingCollection, setEditingCollection] = useState(null); + + const notify = useNotification(); + const collectionsState = useCollections(); + + const collections = collectionsState.collections || []; + + const table = useReactTable({ + data: collections, + columns: columns, + getCoreRowModel: getCoreRowModel(), + }); + + const selected = table.getSelectedRowModel().rows.map((x) => x.original.collection); + + const onCreateNew = () => { + setEditingCollection(null); + setDialogOpen(true); + }; + + const onEdit = () => { + if (selected.length !== 1) { + notify.info("Please select exactly one collection to edit"); + return; + } + const collection = collections.find(c => c.collection === selected[0]); + setEditingCollection(collection); + setDialogOpen(true); + }; + + const onDelete = () => { + collectionsState.deleteCollections({ + collections: selected, + onSuccess: () => { + table.setRowSelection({}); + }, + }); + }; + + const onSaveCollection = (collection, name, description, tags) => { + collectionsState.updateCollection({ + collection, + name, + description, + tags, + onSuccess: () => { + setDialogOpen(false); + table.setRowSelection({}); + }, + }); + }; + + return ( + <> + + + + + + + + + ); +}; + +export default Collections; +``` + +#### 4.2 Collection Actions Bar + +**File**: `src/components/library/CollectionActions.tsx` (new file) + +Action buttons for bulk operations on selected collections: + +```typescript +import React from "react"; +import { HStack, Button, Text } from "@chakra-ui/react"; +import { Edit, Trash2 } from "lucide-react"; + +interface CollectionActionsProps { + selectedCount: number; + onEdit: () => void; + onDelete: () => void; +} + +const CollectionActions = ({ selectedCount, onEdit, onDelete }: CollectionActionsProps) => { + if (selectedCount === 0) return null; + + return ( + + + {selectedCount} collection{selectedCount !== 1 ? "s" : ""} selected + + + + + ); +}; + +export default CollectionActions; +``` + +#### 4.3 Collection Dialog + +**File**: `src/components/library/CollectionDialog.tsx` (new file) + +Dialog for creating/editing collections: + +```typescript +import React, { useState, useEffect } from "react"; +import { Dialog } from "@chakra-ui/react"; +import { Portal } from "@chakra-ui/react"; + +import TextField from "../common/TextField"; +import TextAreaField from "../common/TextAreaField"; +import ChipInputField from "../common/ChipInputField"; +import ProgressSubmitButton from "../common/ProgressSubmitButton"; + +interface CollectionDialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; + onSave: (collection: string, name: string, description: string, tags: string[]) => void; + editingCollection?: any; +} + +const CollectionDialog = ({ + open, + onOpenChange, + onSave, + editingCollection, +}: CollectionDialogProps) => { + const [collection, setCollection] = useState(""); + const [name, setName] = useState(""); + const [description, setDescription] = useState(""); + const [tags, setTags] = useState([]); + + useEffect(() => { + if (editingCollection) { + setCollection(editingCollection.collection); + setName(editingCollection.name); + setDescription(editingCollection.description); + setTags(editingCollection.tags || []); + } else { + setCollection(""); + setName(""); + setDescription(""); + setTags([]); + } + }, [editingCollection, open]); + + const handleSubmit = () => { + onSave(collection, name, description, tags); + }; + + const isValid = collection.trim() !== "" && name.trim() !== ""; + + return ( + onOpenChange(e.open)}> + + + + + + + {editingCollection ? "Edit Collection" : "Create Collection"} + + + + + + + + + + + + {editingCollection ? "Update" : "Create"} + + + + + + + ); +}; + +export default CollectionDialog; +``` + +#### 4.4 Collection Controls + +**File**: `src/components/library/CollectionControls.tsx` (new file) + +Control buttons for collection operations: + +```typescript +import React from "react"; +import { HStack, Button } from "@chakra-ui/react"; +import { Plus } from "lucide-react"; + +interface CollectionControlsProps { + onCreate: () => void; +} + +const CollectionControls = ({ onCreate }: CollectionControlsProps) => { + return ( + + + + ); +}; + +export default CollectionControls; +``` + +### Phase 5: Update Library Page with Tabs + +**File**: `src/pages/LibraryPage.tsx` + +Update the library page to use tabs for Documents and Collections: + +```typescript +import React from "react"; +import { LibraryBig } from "lucide-react"; +import { Tabs } from "@chakra-ui/react"; + +import PageHeader from "../components/common/PageHeader"; +import Documents from "../components/library/Documents"; +import Collections from "../components/library/Collections"; + +const LibraryPage = () => { + return ( + <> + } + title="Library" + description="Managing documents and collections" + /> + + + Documents + Collections + + + + + + + + + + ); +}; + +export default LibraryPage; +``` + +## Type Definitions + +**File**: `src/api/trustgraph/messages.ts` + +Add collection-related message types: + +```typescript +// Collection management request +export interface CollectionRequest extends RequestMessage { + operation: "list-collections" | "update-collection" | "delete-collection"; + user: string; + collection?: string; + name?: string; + description?: string; + tags?: string[]; + tag_filter?: string[]; +} + +// Collection management response +export interface CollectionResponse { + collections?: Array<{ + user: string; + collection: string; + name: string; + description: string; + tags: string[]; + created_at: string; + updated_at: string; + }>; +} +``` + +## Testing Checklist + +- [ ] List collections displays all collections for the user +- [ ] Tag filter works when listing collections +- [ ] Create new collection with ID, name, description, and tags +- [ ] Edit existing collection (ID is disabled, other fields editable) +- [ ] Delete single collection +- [ ] Delete multiple collections +- [ ] Selection state persists correctly +- [ ] Loading indicators show during operations +- [ ] Error notifications display for failures +- [ ] Success notifications display for completed operations +- [ ] Tab switching preserves state +- [ ] Collections table sorts correctly +- [ ] Empty state displays when no collections exist +- [ ] Validation prevents creating collections with empty ID or name + +## Future Enhancements + +1. **Collection Assignment**: Allow assigning documents to collections from the Documents tab +2. **Collection Filtering**: Filter documents by collection +3. **Bulk Collection Operations**: Move multiple documents between collections +4. **Collection Statistics**: Show document count per collection +5. **Collection Search**: Search collections by name, description, or tags +6. **Collection Export**: Export collection metadata + +## Migration Notes + +- No breaking changes to existing functionality +- Documents tab functionality remains unchanged +- New collections functionality is additive +- Follows established patterns from library.ts and Documents.tsx +- Uses consistent Chakra v3 components and patterns diff --git a/ai-context/workbench-ui/docs/tech-specs/flow-class-definition.md b/ai-context/workbench-ui/docs/tech-specs/flow-class-definition.md new file mode 100644 index 00000000..5469144e --- /dev/null +++ b/ai-context/workbench-ui/docs/tech-specs/flow-class-definition.md @@ -0,0 +1,156 @@ +# Flow Class Definition Specification + +## Overview + +A flow class defines a complete dataflow pattern template in the TrustGraph system. When instantiated, it creates an interconnected network of processors that handle data ingestion, processing, storage, and querying as a unified system. + +## Structure + +A flow class definition consists of four main sections: + +### 1. Class Section +Defines shared service processors that are instantiated once per flow class. These processors handle requests from all flow instances of this class. + +```json +"class": { + "service-name:{class}": { + "request": "queue-pattern:{class}", + "response": "queue-pattern:{class}" + } +} +``` + +**Characteristics:** +- Shared across all flow instances of the same class +- Typically expensive or stateless services (LLMs, embedding models) +- Use `{class}` template variable for queue naming +- Examples: `embeddings:{class}`, `text-completion:{class}`, `graph-rag:{class}` + +### 2. Flow Section +Defines flow-specific processors that are instantiated for each individual flow instance. Each flow gets its own isolated set of these processors. + +```json +"flow": { + "processor-name:{id}": { + "input": "queue-pattern:{id}", + "output": "queue-pattern:{id}" + } +} +``` + +**Characteristics:** +- Unique instance per flow +- Handle flow-specific data and state +- Use `{id}` template variable for queue naming +- Examples: `chunker:{id}`, `pdf-decoder:{id}`, `kg-extract-relationships:{id}` + +### 3. Interfaces Section +Defines the entry points and interaction contracts for the flow. These form the API surface for external systems and internal component communication. + +Interfaces can take two forms: + +**Fire-and-Forget Pattern** (single queue): +```json +"interfaces": { + "document-load": "persistent://tg/flow/document-load:{id}", + "triples-store": "persistent://tg/flow/triples-store:{id}" +} +``` + +**Request/Response Pattern** (object with request/response fields): +```json +"interfaces": { + "embeddings": { + "request": "non-persistent://tg/request/embeddings:{class}", + "response": "non-persistent://tg/response/embeddings:{class}" + } +} +``` + +**Types of Interfaces:** +- **Entry Points**: Where external systems inject data (`document-load`, `agent`) +- **Service Interfaces**: Request/response patterns for services (`embeddings`, `text-completion`) +- **Data Interfaces**: Fire-and-forget data flow connection points (`triples-store`, `entity-contexts-load`) + +### 4. Metadata +Additional information about the flow class: + +```json +"description": "Human-readable description", +"tags": ["capability-1", "capability-2"] +``` + +## Template Variables + +### {id} +- Replaced with the unique flow instance identifier +- Creates isolated resources for each flow +- Example: `flow-123`, `customer-A-flow` + +### {class} +- Replaced with the flow class name +- Creates shared resources across flows of the same class +- Example: `standard-rag`, `enterprise-rag` + +## Queue Patterns (Pulsar) + +Flow classes use Apache Pulsar for messaging. Queue names follow the Pulsar format: +``` +://// +``` + +### Components: +- **persistence**: `persistent` or `non-persistent` (Pulsar persistence mode) +- **tenant**: `tg` for TrustGraph-supplied flow class definitions +- **namespace**: Indicates the messaging pattern + - `flow`: Fire-and-forget services + - `request`: Request portion of request/response services + - `response`: Response portion of request/response services +- **topic**: The specific queue/topic name with template variables + +### Persistent Queues +- Pattern: `persistent://tg/flow/:{id}` +- Used for fire-and-forget services and durable data flow +- Data persists in Pulsar storage across restarts +- Example: `persistent://tg/flow/chunk-load:{id}` + +### Non-Persistent Queues +- Pattern: `non-persistent://tg/request/:{class}` or `non-persistent://tg/response/:{class}` +- Used for request/response messaging patterns +- Ephemeral, not persisted to disk by Pulsar +- Lower latency, suitable for RPC-style communication +- Example: `non-persistent://tg/request/embeddings:{class}` + +## Dataflow Architecture + +The flow class creates a unified dataflow where: + +1. **Document Processing Pipeline**: Flows from ingestion through transformation to storage +2. **Query Services**: Integrated processors that query the same data stores and services +3. **Shared Services**: Centralized processors that all flows can utilize +4. **Storage Writers**: Persist processed data to appropriate stores + +All processors (both `{id}` and `{class}`) work together as a cohesive dataflow graph, not as separate systems. + +## Example Flow Instantiation + +Given: +- Flow Instance ID: `customer-A-flow` +- Flow Class: `standard-rag` + +Template expansions: +- `persistent://tg/flow/chunk-load:{id}` → `persistent://tg/flow/chunk-load:customer-A-flow` +- `non-persistent://tg/request/embeddings:{class}` → `non-persistent://tg/request/embeddings:standard-rag` + +This creates: +- Isolated document processing pipeline for `customer-A-flow` +- Shared embedding service for all `standard-rag` flows +- Complete dataflow from document ingestion through querying + +## Benefits + +1. **Resource Efficiency**: Expensive services are shared across flows +2. **Flow Isolation**: Each flow has its own data processing pipeline +3. **Scalability**: Can instantiate multiple flows from the same template +4. **Modularity**: Clear separation between shared and flow-specific components +5. **Unified Architecture**: Query and processing are part of the same dataflow \ No newline at end of file diff --git a/ai-context/workbench-ui/docs/tech-specs/flow-class-editor.md b/ai-context/workbench-ui/docs/tech-specs/flow-class-editor.md new file mode 100644 index 00000000..1238432a --- /dev/null +++ b/ai-context/workbench-ui/docs/tech-specs/flow-class-editor.md @@ -0,0 +1,910 @@ +# Flow Class Visual Editor Technical Specification + +## Overview + +A React-based visual editor for creating and modifying TrustGraph flow class definitions using a node-and-edge graph interface. Built with React Flow, this component allows users to visually design dataflow patterns by dragging processors onto a canvas and connecting them with queues. + +## Core Technologies + +- **React Flow** - Node-based editor framework +- **TypeScript** - Type safety for flow definitions +- **Chakra UI v3** - UI components and theming +- **Zustand** - Editor state management +- **Zod** - Schema validation for flow class structure + +## Component Architecture + +### Directory Structure +``` +src/components/flow-editor/ +├── FlowClassEditor.tsx # Main editor component +├── nodes/ # Custom node components +│ ├── ClassProcessorNode.tsx # Shared service nodes +│ ├── FlowProcessorNode.tsx # Flow-specific nodes +│ └── InterfaceNode.tsx # Entry/exit point nodes +├── edges/ # Custom edge components +│ ├── PersistentQueueEdge.tsx # Persistent queue connections +│ └── RequestResponseEdge.tsx # Request/response pairs +├── panels/ # Editor UI panels +│ ├── NodePalette.tsx # Drag-and-drop processor library +│ ├── PropertiesPanel.tsx # Node/edge configuration +│ └── ValidationPanel.tsx # Real-time validation feedback +├── hooks/ +│ ├── useFlowValidation.ts # Validation logic +│ ├── useFlowExport.ts # JSON export/import +│ └── useAutoLayout.ts # Automatic graph layout +└── types/ + └── flowEditorTypes.ts # TypeScript definitions +``` + +## Visual Design + +### Node Types + +#### 1. Class Processor Node ({class}) +```tsx +{ + type: 'classProcessor', + data: { + processorName: string, // e.g., "embeddings" + queues: { + request?: string, // Queue pattern + response?: string, // Queue pattern + [key: string]: string // Additional queues + } + }, + style: { + background: 'accent.subtle', // Shared service color + border: '2px solid accent.solid', + icon: // Lucide icon indicating shared + } +} +``` + +#### 2. Flow Processor Node ({id}) +```tsx +{ + type: 'flowProcessor', + data: { + processorName: string, // e.g., "chunker" + queues: { + input?: string, // Input queue + output?: string, // Output queue + [key: string]: string // Additional queues + } + }, + style: { + background: 'primary.subtle', // Flow-specific color + border: '2px solid primary.solid', + icon: // Lucide icon for isolated + } +} +``` + +#### 3. Interface Node +```tsx +{ + type: 'interfaceNode', + data: { + interfaceName: string, // e.g., "document-load" + interfaceType: 'fire-and-forget' | 'request-response', + queuePattern?: string, // For fire-and-forget + request?: string, // For request-response + response?: string // For request-response + }, + style: { + background: 'bg.muted', + border: '2px dashed border.muted', + icon: // Entry/exit point indicator + } +} +``` + +### Edge Types + +#### 1. Persistent Queue Edge +- **Visual**: Solid line with arrow +- **Color**: Based on namespace (flow: green, request: blue, response: purple) +- **Label**: Queue name displayed on hover +- **Validation**: Source/target compatibility checking + +#### 2. Non-Persistent Queue Edge +- **Visual**: Dashed line with arrow +- **Color**: Lighter variant of namespace colors +- **Label**: Queue name with non-persistent indicator +- **Validation**: Request/response pairing validation + +## User Edit Operations + +### 1. Processor Management + +#### Add Processor +- **Drag from palette**: Drag processor type from categorized library +- **Double-click canvas**: Quick-add with processor type selector +- **Context menu**: Right-click → Add Processor → Select type +- **Keyboard shortcut**: `A` key opens add processor dialog + +#### Configure Processor +- **Rename**: Click processor name to edit inline +- **Change type**: Toggle between `{class}` and `{id}` via properties panel +- **Queue management**: + ```tsx + // Add new queue to processor + addQueue(processorId, { + name: "custom-queue", + direction: "input" | "output" | "bidirectional", + pattern: "persistent://tg/flow/custom:{id}" + }); + + // Remove queue + removeQueue(processorId, queueName); + + // Edit queue pattern + updateQueue(processorId, queueName, newPattern); + ``` + +#### Delete Processor +- **Single**: Select + Delete key +- **Multiple**: Multi-select + Delete key +- **Context menu**: Right-click → Delete +- **Validation**: Warn if processor has connections + +### 2. Connection Management + +#### Create Connection +- **Drag connection**: From output handle to input handle +- **Validation rules**: + - Persistence compatibility (persistent ↔ persistent preferred) + - Namespace compatibility (flow/request/response) + - Template variable consistency ({class} ↔ {class}, {id} ↔ {id}) + - No self-connections + - No duplicate connections + +#### Configure Connection +- **Auto-naming**: Generate queue name from source/target processors +- **Custom naming**: Override auto-generated queue name +- **Persistence mode**: Toggle persistent/non-persistent +- **Queue pattern template**: + ```tsx + generateQueuePattern({ + persistence: "persistent" | "non-persistent", + tenant: "tg", + namespace: "flow" | "request" | "response", + topic: "document-embeddings", + template: "{id}" | "{class}" + }); + // Result: "persistent://tg/flow/document-embeddings:{id}" + ``` + +#### Delete Connection +- **Click to select** + Delete key +- **Context menu** on edge +- **Disconnect handle**: Drag connection away from handle + +### 3. Interface Operations + +#### Add Interface +- **Entry points**: Document load, text input, etc. +- **Exit points**: Response outputs, storage endpoints +- **Service interfaces**: Request/response pairs + +#### Configure Interface Type +```tsx +// Fire-and-forget pattern +interface FireAndForgetInterface { + type: "fire-and-forget"; + queue: string; // Single queue pattern +} + +// Request/response pattern +interface RequestResponseInterface { + type: "request-response"; + request: string; // Request queue pattern + response: string; // Response queue pattern +} +``` + +### 4. Bulk Operations + +#### Multi-select Actions +- **Box select**: Click and drag to select multiple nodes +- **Shift-click**: Add to selection +- **Cmd-click**: Toggle selection +- **Select all**: Cmd+A + +#### Group Operations +- **Move together**: Drag any selected node moves all +- **Delete together**: Delete key removes all selected +- **Duplicate**: Cmd+D duplicates selection +- **Copy/paste**: Cmd+C/Cmd+V for cross-flow copying + +### 5. Layout Operations + +#### Auto-layout +```tsx +const layoutStrategies = { + hierarchical: { + direction: "LR" | "TB", // Left-right or top-bottom + nodeSpacing: 150, + levelSpacing: 200 + }, + force: { + strength: -1000, + distance: 150 + }, + circular: { + radius: 300, + startAngle: 0 + } +}; +``` + +#### Manual Arrangement +- **Snap to grid**: Optional grid snapping (toggle with G key) +- **Alignment tools**: Align selected nodes (top/bottom/left/right/center) +- **Distribution**: Distribute nodes evenly (horizontal/vertical) + +### 6. Template Operations + +#### Apply Template +```tsx +const templates = { + "document-rag": { + description: "Document processing with RAG", + processors: [ + { type: "pdf-decoder", id: "{id}" }, + { type: "chunker", id: "{id}" }, + { type: "embeddings", id: "{class}" }, + { type: "de-write", id: "{id}" } + ], + connections: [ + { from: "pdf-decoder.output", to: "chunker.input" }, + { from: "chunker.output", to: "embeddings.input" }, + { from: "embeddings.output", to: "de-write.input" } + ] + } +}; +``` + +#### Create Template +- Select nodes/edges → Right-click → "Save as template" +- Provide template name and description +- Template saved to library for reuse + +### 7. Validation Operations + +#### Real-time Validation +```tsx +interface ValidationRule { + id: string; + severity: "error" | "warning" | "info"; + check: (flowClass: FlowClass) => ValidationResult; +} + +const validationRules = [ + { + id: "no-orphans", + severity: "warning", + check: (flow) => findOrphanedNodes(flow) + }, + { + id: "queue-consistency", + severity: "error", + check: (flow) => validateQueuePatterns(flow) + }, + { + id: "template-consistency", + severity: "error", + check: (flow) => validateTemplateVariables(flow) + } +]; +``` + +#### Fix Suggestions +- **Auto-fix**: One-click fixes for common issues +- **Quick actions**: Context-aware suggestions +- **Validation overlay**: Visual indicators on invalid elements + +### 8. History Management + +#### Undo/Redo Stack +```tsx +interface HistoryAction { + type: "add" | "delete" | "update" | "connect" | "disconnect"; + before: FlowState; + after: FlowState; + timestamp: number; +} + +const historyStack: HistoryAction[] = []; +const redoStack: HistoryAction[] = []; + +// Track all operations +const executeOperation = (operation: Operation) => { + const before = getCurrentState(); + performOperation(operation); + const after = getCurrentState(); + + historyStack.push({ + type: operation.type, + before, + after, + timestamp: Date.now() + }); + + redoStack.length = 0; // Clear redo on new operation +}; +``` + +### 9. Import/Export Operations + +#### Import Flow Class +- **From JSON file**: Upload or paste JSON +- **From Config API**: Select from existing flow classes +- **Validation**: Verify structure before import +- **Merge options**: Replace or merge with existing + +#### Export Flow Class +- **To JSON**: Download as .json file +- **To Config API**: Save directly to backend +- **To clipboard**: Copy JSON for sharing +- **Format options**: Minified or pretty-printed + +### 10. Metadata Operations + +#### Edit Flow Properties +```tsx +interface FlowMetadata { + id: string; // Kebab-case identifier + name: string; // Human-readable name + description: string; // Detailed description + tags: string[]; // Categorization tags + version: string; // Semantic version + author: string; // Creator identity + created: Date; // Creation timestamp + modified: Date; // Last modification +} +``` + +#### Tag Management +- **Add tags**: Type or select from existing +- **Remove tags**: Click X on tag chips +- **Tag suggestions**: Based on processors used +- **Tag categories**: System tags vs user tags + +## Core Features + +### 1. Auto-Layout +```tsx +const handleAutoLayout = () => { + const layoutedElements = getLayoutedElements(nodes, edges, { + direction: 'LR', // Left to right + nodeSpacing: 150, + levelSpacing: 200, + animate: true + }); + setNodes(layoutedElements.nodes); + setEdges(layoutedElements.edges); +}; +``` + +### 2. Import/Export +```tsx +// Export to flow class JSON +const exportFlowClass = () => { + const flowClass = { + class: extractClassProcessors(nodes), + flow: extractFlowProcessors(nodes), + interfaces: extractInterfaces(nodes), + description: metadata.description, + tags: metadata.tags + }; + return JSON.stringify(flowClass, null, 2); +}; + +// Import from JSON +const importFlowClass = (json: string) => { + const flowClass = JSON.parse(json); + const { nodes, edges } = convertToReactFlow(flowClass); + setNodes(nodes); + setEdges(edges); +}; +``` + +### 3. Connection Validation +```tsx +const isValidConnection = (connection: Connection) => { + const sourceNode = getNode(connection.source); + const targetNode = getNode(connection.target); + + // Validate queue compatibility + if (!areQueuesCompatible(sourceNode, targetNode)) { + return false; + } + + // Prevent circular dependencies + if (createsCircularDependency(connection)) { + return false; + } + + return true; +}; +``` + +### 4. Smart Templates +Pre-built flow patterns users can instantiate: +- **Document RAG Pipeline**: PDF → Chunker → Embeddings → Storage +- **Graph RAG Pipeline**: Knowledge extraction → Graph embeddings → Query +- **Simple Q&A**: Prompt → LLM → Response +- **Custom Template**: User-defined reusable patterns + +## State Management + +```tsx +interface FlowEditorState { + // React Flow state + nodes: Node[]; + edges: Edge[]; + + // Editor state + selectedElement: Node | Edge | null; + validationErrors: ValidationError[]; + isDirty: boolean; + + // Metadata + flowClassName: string; + description: string; + tags: string[]; + + // Actions + addNode: (type: NodeType, position: XYPosition) => void; + updateNode: (nodeId: string, data: NodeData) => void; + deleteNode: (nodeId: string) => void; + addEdge: (edge: Edge) => void; + deleteEdge: (edgeId: string) => void; + validateFlow: () => ValidationResult; + exportFlow: () => FlowClassDefinition; + importFlow: (definition: FlowClassDefinition) => void; +} +``` + +## Visual Indicators + +### Node States +- **Normal**: Default appearance +- **Selected**: Blue glow/border +- **Invalid**: Red border with error icon +- **Connecting**: Pulse animation on handles +- **Hover**: Slight scale increase + +### Edge States +- **Normal**: Default appearance +- **Selected**: Highlighted with thicker stroke +- **Invalid**: Red dashed line +- **Animated**: Flow animation for active connections + +### Queue Handle Types +- **Input**: Left side of node, inward arrow +- **Output**: Right side of node, outward arrow +- **Bidirectional**: Both sides, for request/response + +## Keyboard Shortcuts + +- `Delete` - Delete selected elements +- `Cmd+Z` - Undo +- `Cmd+Shift+Z` - Redo +- `Cmd+S` - Save flow class +- `Cmd+O` - Open flow class +- `Cmd+E` - Export to JSON +- `Space` - Pan mode +- `Cmd+A` - Select all nodes +- `Cmd+D` - Duplicate selected nodes + +## Integration Points + +### Config API Integration + +The flow class editor uses the existing Config API for all flow class operations: + +#### State Management Hook +```tsx +// src/state/flow-classes.ts +import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; +import { useSocket } from "./socket"; + +export const useFlowClasses = () => { + const socket = useSocket(); + + return useQuery({ + queryKey: ["flow-classes"], + queryFn: async () => { + const response = await socket.request({ + operation: "get-config", + path: "flow-classes" + }); + return response.configuration as FlowClassDefinition[]; + } + }); +}; + +export const useFlowClass = (flowClassId: string) => { + const socket = useSocket(); + + return useQuery({ + queryKey: ["flow-class", flowClassId], + queryFn: async () => { + const response = await socket.request({ + operation: "get-config", + path: `flow-classes/${flowClassId}` + }); + return response.configuration as FlowClassDefinition; + } + }); +}; + +export const useUpdateFlowClass = () => { + const socket = useSocket(); + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async ({ id, flowClass }: { + id: string; + flowClass: FlowClassDefinition + }) => { + return await socket.request({ + operation: "set-config", + path: `flow-classes/${id}`, + configuration: flowClass + }); + }, + onSuccess: (_, variables) => { + queryClient.invalidateQueries(["flow-class", variables.id]); + queryClient.invalidateQueries(["flow-classes"]); + } + }); +}; + +export const useDeleteFlowClass = () => { + const socket = useSocket(); + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (id: string) => { + return await socket.request({ + operation: "delete-config", + path: `flow-classes/${id}` + }); + }, + onSuccess: () => { + queryClient.invalidateQueries(["flow-classes"]); + } + }); +}; +``` + +#### Editor Component Integration +```tsx +// src/components/flow-editor/FlowClassEditor.tsx +import { useFlowClass, useUpdateFlowClass } from "../../state/flow-classes"; +import { useActivity } from "../../state/activity"; +import { useNotification } from "../../state/notify"; + +export const FlowClassEditor = ({ flowClassId }: { flowClassId?: string }) => { + const notify = useNotification(); + + // Load existing flow class if ID provided + const { data: flowClass, isLoading } = useFlowClass(flowClassId); + const updateMutation = useUpdateFlowClass(); + + // Track loading state + useActivity(isLoading, "Loading flow class"); + useActivity(updateMutation.isPending, "Saving flow class"); + + // Initialize React Flow with loaded data + useEffect(() => { + if (flowClass) { + const { nodes, edges } = convertFlowClassToReactFlow(flowClass); + setNodes(nodes); + setEdges(edges); + setMetadata({ + description: flowClass.description, + tags: flowClass.tags + }); + } + }, [flowClass]); + + // Save handler + const handleSave = async () => { + const flowClassData = exportFlowClass(); + + try { + await updateMutation.mutateAsync({ + id: flowClassId || generateFlowClassId(), + flowClass: flowClassData + }); + notify.success("Flow class saved successfully"); + } catch (error) { + notify.error("Failed to save flow class"); + } + }; + + return ( + + ); +}; +``` + +#### Flow Class List Integration +```tsx +// src/components/flow-editor/FlowClassList.tsx +import { useFlowClasses, useDeleteFlowClass } from "../../state/flow-classes"; + +export const FlowClassList = () => { + const { data: flowClasses, isLoading } = useFlowClasses(); + const deleteMutation = useDeleteFlowClass(); + + useActivity(isLoading, "Loading flow classes"); + useActivity(deleteMutation.isPending, "Deleting flow class"); + + return ( + + {flowClasses?.map(flowClass => ( + + + {flowClass.description} + + + + + ))} + + ); +}; +``` + +#### Config API Request/Response Format +```typescript +// Request to get all flow classes +{ + operation: "get-config", + path: "flow-classes" +} + +// Response +{ + configuration: [ + { + id: "document-rag-flow", + class: { /* class processors */ }, + flow: { /* flow processors */ }, + interfaces: { /* interfaces */ }, + description: "Document RAG pipeline", + tags: ["rag", "documents"] + } + ] +} + +// Request to update flow class +{ + operation: "set-config", + path: "flow-classes/document-rag-flow", + configuration: { + class: { /* updated class processors */ }, + flow: { /* updated flow processors */ }, + interfaces: { /* updated interfaces */ }, + description: "Updated description", + tags: ["rag", "documents", "v2"] + } +} +``` + +### Real-time Updates via WebSocket + +The editor subscribes to configuration changes to handle external updates: + +```tsx +useEffect(() => { + const subscription = socket.subscribe( + `config/flow-classes/${flowClassId}`, + (update) => { + // Handle external updates to the flow class + if (update.source !== currentSessionId) { + notify.warning("Flow class updated externally. Refreshing..."); + queryClient.invalidateQueries(["flow-class", flowClassId]); + } + } + ); + + return () => subscription.unsubscribe(); +}, [flowClassId]); +``` + +### With Existing UI + +#### Page Integration +The Flow Class Editor is a separate page in the workbench, controlled by a feature toggle: + +```tsx +// src/components/settings/FeatureSwitchesSection.tsx +// Add to existing feature switches: + + + Flow Class Editor + + + Enable the visual flow class editor for creating and modifying dataflow patterns + + + experimental + + + + + onFlowClassEditorChange(details.checked) + } + > + + + + + + +``` + +#### Sidebar Navigation +```tsx +// src/components/Sidebar.tsx +// Add conditional menu item based on feature switch: +{settings.featureSwitches.flowClassEditor && ( + } + label="Flow Class Editor" + path="/flow-class-editor" + isActive={location.pathname === "/flow-class-editor"} + /> +)} +``` + +#### Route Configuration +```tsx +// src/App.tsx +// Add route for the editor page: +{settings.featureSwitches.flowClassEditor && ( + } /> +)} +``` + +#### Page Component +```tsx +// src/pages/FlowClassEditorPage.tsx +import React from "react"; +import PageHeader from "../components/common/PageHeader"; +import FlowClassEditor from "../components/flow-editor/FlowClassEditor"; +import { GitBranch } from "lucide-react"; + +const FlowClassEditorPage: React.FC = () => { + return ( + <> + } + title="Flow Class Editor" + description="Visual editor for creating and modifying TrustGraph dataflow patterns" + /> + + + ); +}; + +export default FlowClassEditorPage; +``` + +#### Settings State Update +```tsx +// src/state/settings.ts +interface FeatureSwitches { + ontologyEditor: boolean; + submissions: boolean; + agentTools: boolean; + mcpTools: boolean; + schemas: boolean; + tokenCost: boolean; + flowClasses: boolean; // Existing flow classes management + flowClassEditor: boolean; // New visual editor + structuredQuery: boolean; +} +``` + +#### Integration Features +- Uses consistent Chakra UI theming +- Integrates with notification system via `useNotification` +- Progress indicators via `useActivity` +- Follows existing Config API patterns +- Respects user's feature toggle preferences + +## Responsive Design + +### Desktop (Primary) +- Full editor with all panels visible +- Optimal canvas size for complex flows +- Properties panel as sidebar + +### Tablet +- Collapsible panels to maximize canvas +- Touch-friendly node manipulation +- Simplified toolbar + +### Mobile (View-only) +- Read-only flow visualization +- Pan and zoom only +- Export functionality retained + +## Performance Considerations + +### Optimizations +- **Virtualization** for large flows (100+ nodes) +- **Debounced validation** during editing +- **Memoized node/edge components** +- **Lazy loading** of processor templates +- **Web Workers** for layout calculations + +### Limits +- Max 500 nodes per flow class +- Max 1000 edges per flow class +- Auto-layout for flows under 100 nodes +- Real-time validation for flows under 50 nodes + +## Error Handling + +### Validation Errors +- Inline error indicators on invalid nodes/edges +- Validation panel with detailed error list +- Prevent export/save when errors exist + +### Runtime Errors +- Connection rejection with toast notification +- Import failure with detailed parsing errors +- Auto-save recovery for browser crashes + +## Future Enhancements + +### Phase 2 +- **Processor library management** - Add custom processors +- **Collaborative editing** - Real-time multi-user support +- **Version control** - Flow class versioning and diff view +- **Simulation mode** - Visualize data flow through the graph + +### Phase 3 +- **AI assistance** - Suggest connections and optimizations +- **Performance profiling** - Visualize bottlenecks +- **Template marketplace** - Share flow patterns +- **Code generation** - Generate processor stubs from flow + +## Testing Strategy + +### Unit Tests +- Node/edge component rendering +- Validation logic +- Import/export transformations +- State management actions + +### Integration Tests +- Full editor workflow +- Save/load operations +- Template instantiation +- Keyboard shortcuts + +### E2E Tests +- Create flow from scratch +- Import and modify existing flow +- Export and validate JSON +- Deploy flow instance \ No newline at end of file diff --git a/ai-context/workbench-ui/docs/tech-specs/flow-configurable-parameters-client.md b/ai-context/workbench-ui/docs/tech-specs/flow-configurable-parameters-client.md new file mode 100644 index 00000000..6f00100b --- /dev/null +++ b/ai-context/workbench-ui/docs/tech-specs/flow-configurable-parameters-client.md @@ -0,0 +1,1048 @@ +# Flow Configurable Parameters - Client-Side Technical Specification + +## Overview + +This specification describes the client-side implementation of configurable parameters for flow classes in TrustGraph UI. This complements the server-side implementation by providing dynamic form generation, parameter validation, and user-friendly parameter input interfaces for flow creation. + +The client-side implementation enables users to: +- View available parameters when selecting a flow class +- Input parameter values through dynamically generated forms +- Validate parameters according to their schema definitions +- Launch flows with custom parameter configurations +- Save and reuse parameter presets for common configurations + +## Goals + +- **Dynamic Form Generation**: Automatically create parameter input forms based on flow class parameter schemas +- **Type-Safe Validation**: Validate parameter inputs according to schema definitions (string, number, boolean, enum) +- **Intuitive User Experience**: Provide clear parameter descriptions, validation feedback, and sensible defaults +- **Integration with Existing UI**: Seamlessly integrate with the current CreateDialog and flow management system +- **Parameter Presets**: Allow users to save and reuse common parameter configurations +- **Real-time Feedback**: Show validation errors and hints as users input parameters +- **Accessibility**: Ensure parameter forms are accessible and follow project UI patterns + +## Architecture + +### Component Structure + +``` +src/components/flows/ +├── CreateDialog.tsx # Enhanced with parameter support +├── ParameterInputs.tsx # Dynamic parameter form component +├── ParameterPresets.tsx # Parameter preset management +├── ParameterValidation.tsx # Validation logic and error display +└── __tests__/ + ├── ParameterInputs.test.tsx + └── ParameterPresets.test.tsx + +src/state/ +├── flows.ts # Enhanced with parameter support +└── flow-parameters.ts # Parameter definition fetching and caching + +src/model/ +└── flow-parameters.ts # Parameter type definitions and utilities +``` + +### Data Flow + +1. **Parameter Schema Fetching**: + ``` + User selects flow class → Fetch parameter definitions → Parse schema → Generate form + ``` + +2. **Parameter Input**: + ``` + User inputs values → Validate against schema → Update form state → Enable/disable submit + ``` + +3. **Flow Creation**: + ``` + User submits → Validate all parameters → Send to API → Create flow with parameters + ``` + +## Technical Design + +### Core Types + +```typescript +// Parameter schema definition (from server) +interface ParameterSchema { + type: 'string' | 'number' | 'integer' | 'boolean'; + description?: string; + default?: any; + enum?: EnumOption[] | string[]; // Can be rich objects or simple strings + minimum?: number; + maximum?: number; + pattern?: string; + required?: boolean; + helper?: string; // Custom helper text + placeholder?: string; // Custom placeholder text +} + +// Rich enum option structure +interface EnumOption { + id: string; // The actual value + description: string; // Display text +} + +// Flow class structure (from getFlowClass API) +interface FlowClass { + class: { [processorName: string]: any }; // Processor definitions + description: string; + flow: { [stepName: string]: any }; // Flow step definitions + interfaces: { [interfaceName: string]: any }; // Interface definitions + parameters?: { [flowParamName: string]: FlowParameterMetadata }; // Maps flow param names to parameter metadata + tags?: string[]; +} + +// Flow parameter metadata structure +interface FlowParameterMetadata { + type: string; // Reference to parameter-type definition name + description: string; // Human-readable description for UI display + order: number; // Display order for parameter forms (lower numbers appear first) +} + +// Parameter definitions fetched from config +interface ParameterDefinitions { + [definitionName: string]: ParameterSchema; +} + +// User parameter values +interface ParameterValues { + [flowParamName: string]: any; +} + +// Parameter validation result +interface ValidationResult { + isValid: boolean; + errors: { [paramName: string]: string }; +} +``` + +### Component Implementation + +#### 1. Enhanced CreateDialog + +```typescript +// Key enhancements to existing CreateDialog +const CreateDialog = ({ open, onOpenChange }) => { + const [flowClass, setFlowClass] = useState(); + const [id, setId] = useState(""); + const [description, setDescription] = useState(""); + const [parameterValues, setParameterValues] = useState({}); + + // Fetch parameter definitions when flow class is selected + const { + parameterDefinitions, + isLoadingParameters + } = useFlowParameters(flowClass); + + // Validate form including parameters + const { isValid, errors } = useParameterValidation( + flowClass, + parameterDefinitions, + parameterValues + ); + + const onSubmit = () => { + if (!isValid) return; + + flowState.startFlow({ + id, + flowClass, + description, + parameters: parameterValues, // Include parameters + onSuccess: () => { + setParameterValues({}); // Clear parameters on success + // ... rest of success logic + }, + }); + }; + + return ( + + {/* Existing dialog structure */} + + {/* Enhanced with parameter inputs */} + + + {/* Parameter presets */} + + + ); +}; +``` + +#### 2. ParameterInputs Component + +**Following Project Patterns**: +- Uses common components (TextField, SelectField) instead of raw Chakra +- Implements Chakra v3 patterns (Field.Root, etc.) +- Provides proper SelectField descriptions with SelectOptionText + +```typescript +interface ParameterInputsProps { + flowClass?: string; + parameterDefinitions: ParameterDefinitions; + parameterValues: ParameterValues; + onParameterChange: (values: ParameterValues) => void; + validationErrors: { [key: string]: string }; +} + +const ParameterInputs: React.FC = ({ + parameterDefinitions, + parameterValues, + onParameterChange, + validationErrors, +}) => { + const contentRef = useRef(null); + + const handleParameterChange = (paramName: string, value: any) => { + onParameterChange({ + ...parameterValues, + [paramName]: value, + }); + }; + + const renderParameterInput = (paramName: string, schema: ParameterSchema) => { + const defaultValue = schema.default; + const value = parameterValues[paramName] ?? defaultValue ?? ""; + const error = validationErrors[paramName]; + const label = (schema.description || paramName) + (schema.required ? " *" : ""); + + // Helper text priority: schema.helper -> type-based fallback + const getHelperText = () => { + if (schema.helper) return schema.helper; + + switch (schema.type) { + case 'integer': return 'Enter a whole number'; + case 'number': return 'Enter a number (decimals allowed)'; + case 'boolean': return 'Select true or false'; + case 'string': return schema.enum ? undefined : 'Enter text'; + default: return undefined; + } + }; + + const helperText = getHelperText(); + const placeholder = schema.placeholder || ""; + + // Enum parameters - handle both rich {id, description} and simple string arrays + if (schema.enum && schema.enum.length > 0) { + const options = schema.enum.map(option => { + // Handle both rich {id, description} and simple string enums + const optionId = typeof option === 'object' ? option.id : option; + const optionDesc = typeof option === 'object' ? option.description : option; + + return { + value: optionId, + label: optionDesc, + description: ( + + {optionId} + + ), + }; + }); + + return ( + + { + const selectedValue = values.length > 0 ? values[0] : ""; + handleParameterChange(paramName, selectedValue); + }} + contentRef={contentRef} + /> + {error && {error}} + {helperText && ( + + {helperText} + + )} + + ); + } + + // Boolean parameters - use Checkbox + if (schema.type === 'boolean') { + return ( + + + handleParameterChange(paramName, e.target.checked)} + > + {label} + + {helperText && {helperText}} + {error && {error}} + + + ); + } + + // Number/Integer parameters - use TextField with type="number" + if (schema.type === 'number' || schema.type === 'integer') { + let enhancedHelperText = helperText; + if (schema.minimum !== undefined || schema.maximum !== undefined) { + const rangeText = []; + if (schema.minimum !== undefined) rangeText.push(`min: ${schema.minimum}`); + if (schema.maximum !== undefined) rangeText.push(`max: ${schema.maximum}`); + const rangeInfo = rangeText.join(", "); + enhancedHelperText = enhancedHelperText + ? `${enhancedHelperText} (${rangeInfo})` + : rangeInfo; + } + + return ( + + { + const numValue = schema.type === 'integer' + ? parseInt(val, 10) + : parseFloat(val); + if (!isNaN(numValue)) { + handleParameterChange(paramName, numValue); + } else if (val === "") { + handleParameterChange(paramName, ""); + } + }} + type="number" + required={schema.required} + /> + {error && {error}} + + ); + } + + // String parameters - use TextField + return ( + + handleParameterChange(paramName, val)} + required={schema.required} + /> + {error && {error}} + + ); + }; + + return ( + + {Object.entries(parameterDefinitions).map(([paramName, schema]) => + renderParameterInput(paramName, schema) + )} + + ); +}; +``` + +#### 3. State Management + +**Following Project Patterns**: +- Uses TanStack Query for API calls and caching +- Uses useActivity hook for loading states +- Uses useNotification hook for error/success messages + +```typescript +// Enhanced flows.ts state +export const useFlows = () => { + // ... existing code + + /** + * Enhanced mutation for starting flows with parameters + */ + const startFlowMutation = useMutation({ + mutationFn: ({ id, flowClass, description, parameters, onSuccess }) => { + return socket + .flows() + .startFlow(id, flowClass, description, parameters) + .then(() => { + if (onSuccess) onSuccess(); + }); + }, + onError: (err) => { + notify.error(err.message); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["flows"] }); + notify.success("Flow started successfully"); + }, + }); + + // ... rest of existing code +}; + +// New flow-parameters.ts state +export const useFlowParameters = (flowClassName?: string) => { + const socket = useSocket(); + const connectionState = useConnectionState(); + const notify = useNotification(); + + const isSocketReady = + connectionState?.status === "authenticated" || + connectionState?.status === "unauthenticated"; + + /** + * Query for fetching parameter definitions for a flow class + */ + const parametersQuery = useQuery({ + queryKey: ["flow-parameters", flowClassName], + enabled: isSocketReady && !!flowClassName, + queryFn: async () => { + if (!flowClassName) return null; + + // Get flow class definition first + const flowClass = await socket.flows().getFlowClass(flowClassName); + + // Extract parameter metadata + const parameterMetadata = flowClass.parameters || {}; + if (Object.keys(parameterMetadata).length === 0) { + return { parameterDefinitions: {}, parameterMapping: {}, parameterMetadata: {} }; + } + + // Extract unique parameter types for fetching definitions + const parameterTypes = [...new Set(Object.values(parameterMetadata).map(meta => meta.type))]; + const configKeys = parameterTypes.map(type => ({ type: "parameter-types", key: type })); + + const configResponse = await socket.config().getConfig(configKeys); + const parameterDefinitions = {}; + + // Parse config response to get parameter definitions + configResponse.values?.forEach(item => { + if (item.type === "parameter-types") { + parameterDefinitions[item.key] = JSON.parse(item.value); + } + }); + + // Create mapping for backwards compatibility + const parameterMapping = {}; + Object.entries(parameterMetadata).forEach(([paramName, meta]) => { + parameterMapping[paramName] = meta.type; + }); + + return { + parameterDefinitions, + parameterMapping, // Maps flow param names to definition names (backwards compatibility) + parameterMetadata, // Full metadata with description, order, and type + }; + }, + }); + + useActivity(parametersQuery.isLoading, "Loading flow parameters"); + + return { + parameterDefinitions: parametersQuery.data?.parameterDefinitions || {}, + parameterMapping: parametersQuery.data?.parameterMapping || {}, + parameterMetadata: parametersQuery.data?.parameterMetadata || {}, + isLoading: parametersQuery.isLoading, + isError: parametersQuery.isError, + error: parametersQuery.error, + }; +}; +``` + +#### 4. Parameter Validation + +```typescript +// Custom hook for parameter validation +export const useParameterValidation = ( + flowClass: string, + parameterDefinitions: ParameterDefinitions, + parameterValues: ParameterValues +) => { + return useMemo(() => { + const errors: { [key: string]: string } = {}; + let isValid = true; + + Object.entries(parameterDefinitions).forEach(([paramName, schema]) => { + const value = parameterValues[paramName]; + + // Check required fields + if (schema.required && (value === undefined || value === "")) { + errors[paramName] = `${paramName} is required`; + isValid = false; + return; + } + + // Skip validation for empty optional fields + if (value === undefined || value === "") { + return; + } + + // Type validation + if (schema.type === 'number' || schema.type === 'integer') { + const numValue = typeof value === 'string' ? parseFloat(value) : value; + if (isNaN(numValue)) { + errors[paramName] = `${paramName} must be a valid number`; + isValid = false; + return; + } + + if (schema.type === 'integer' && !Number.isInteger(numValue)) { + errors[paramName] = `${paramName} must be an integer`; + isValid = false; + return; + } + + // Range validation + if (schema.minimum !== undefined && numValue < schema.minimum) { + errors[paramName] = `${paramName} must be at least ${schema.minimum}`; + isValid = false; + } + if (schema.maximum !== undefined && numValue > schema.maximum) { + errors[paramName] = `${paramName} must be at most ${schema.maximum}`; + isValid = false; + } + } + + // Enum validation + if (schema.enum && schema.enum.length > 0) { + if (!schema.enum.includes(value)) { + errors[paramName] = `${paramName} must be one of: ${schema.enum.join(', ')}`; + isValid = false; + } + } + + // Pattern validation for strings + if (schema.pattern && schema.type === 'string') { + const regex = new RegExp(schema.pattern); + if (!regex.test(value.toString())) { + errors[paramName] = `${paramName} format is invalid`; + isValid = false; + } + } + }); + + return { isValid, errors }; + }, [parameterDefinitions, parameterValues]); +}; +``` + +### API Integration + +#### Enhanced Socket API + +The socket API has been enhanced to support parameters (already implemented): + +```typescript +// In trustgraph-socket.ts - already updated +startFlow(id: string, class_name: string, description: string, parameters?: { [key: string]: any }) { + return this.api.makeRequest( + "flow", + { + operation: "start-flow", + "flow-id": id, + "class-name": class_name, + description: description, + parameters: parameters, + }, + 30000, + ).then((response) => { + if (response.error) { + const errorMessage = typeof response.error === 'object' && response.error.message + ? response.error.message + : typeof response.error === 'string' + ? response.error + : "Flow start failed"; + throw new Error(errorMessage); + } + return response; + }); +} +``` + +#### Config API Integration + +```typescript +// Fetching parameter definitions from config system +const fetchParameterDefinitions = async (definitionNames: string[]) => { + const configKeys = definitionNames.map(name => ({ + type: "parameter-types", + key: name + })); + + const response = await socket.config().getConfig(configKeys); + const definitions = {}; + + response.values?.forEach(item => { + if (item.type === "parameter-types") { + definitions[item.key] = JSON.parse(item.value); + } + }); + + return definitions; +}; +``` + +## Real-World Examples + +### Flow Class Example +```json +{ + "class": { + "text-completion:{id}": { + "model": "{llm-model}", + "request": "non-persistent://tg/request/text-completion:{id}", + "response": "non-persistent://tg/response/text-completion:{id}" + } + }, + "description": "GraphRAG, DocumentRAG, structured data + knowledge cores", + "flow": { + "text-completion:{id}": { + "model": "{llm-model}", + "temperature": "{llm-temperature}", + "request": "non-persistent://tg/request/text-completion:{id}", + "response": "non-persistent://tg/response/text-completion:{id}" + } + }, + "interfaces": { /* ... */ }, + "parameters": { + "llm-model": { + "description": "LLM model", + "order": 1, + "type": "llm-model" + }, + "llm-rag-model": { + "description": "LLM model for RAG", + "order": 2, + "type": "llm-model" + }, + "llm-rag-temperature": { + "description": "LLM temperature", + "order": 3, + "type": "llm-temperature" + }, + "llm-temperature": { + "description": "LLM temperature", + "order": 3, + "type": "llm-temperature" + } + }, + "tags": ["document-rag", "graph-rag"] +} +``` + +### Parameter Definition Examples + +#### Rich Enum Parameter (LLM Model) +```json +{ + "default": "gemini-2.5-flash-lite", + "description": "LLM model to use", + "enum": [ + { + "description": "Gemini 2.5 Pro", + "id": "gemini-2.5-pro" + }, + { + "description": "Claude 3.5 Sonnet (via VertexAI)", + "id": "claude-3-5-sonnet@20241022" + } + ], + "required": true, + "type": "string" +} +``` + +#### String Parameter (Free-form) +```json +{ + "default": "gemini-2.5-flash-lite", + "description": "LLM model to use", + "required": true, + "type": "string", + "helper": "Enter the model identifier", + "placeholder": "e.g. gpt-4, claude-3" +} +``` + +#### Number Parameter Example +```json +{ + "default": 0.7, + "description": "Temperature for model generation", + "type": "number", + "minimum": 0.0, + "maximum": 2.0, + "required": false, + "helper": "Controls randomness of model output" +} +``` + +#### Boolean Parameter Example +```json +{ + "default": false, + "description": "Enable streaming responses", + "type": "boolean", + "required": false, + "helper": "Stream responses as they are generated" +} +``` + +### Config API Response Example +```json +{ + "values": [ + { + "type": "parameter-types", + "key": "llm-model", + "value": "{\"default\": \"gemini-2.5-flash-lite\", \"description\": \"LLM model to use\", \"enum\": [{\"description\": \"Gemini 2.5 Pro\", \"id\": \"gemini-2.5-pro\"}], \"required\": true, \"type\": \"string\"}" + } + ] +} +``` + +## User Experience Design + +### Form Behavior + +1. **Initial State**: When CreateDialog opens, no parameters are shown +2. **Flow Class Selection**: When user selects a flow class: + - Show loading indicator while fetching parameters + - Display parameter form sections if parameters exist + - Show "No additional parameters required" if none exist +3. **Parameter Input**: + - Show validation errors in real-time + - Disable submit button until all required parameters are valid + - Provide clear descriptions and hints for each parameter +4. **Form Submission**: + - Validate all parameters before submission + - Show progress indicator during submission + - Clear form on successful submission + - Retain values on error for correction + +### Parameter Input Types + +1. **Enum Parameters (with rich options)**: + - SelectField dropdown with `{id, description}` structure + - Display user-friendly descriptions, store technical IDs + - Example: "Gemini 2.5 Pro" displays, "gemini-2.5-pro" is the value + - Supports both rich objects and simple string arrays + +2. **String Parameters (no enum)**: + - TextField for free-form text input + - Uses `description` field as label + - Uses `helper` field or falls back to "Enter text" + - Uses `placeholder` field if provided + +3. **Integer Parameters**: + - TextField with `type="number"` + - Helper text: "Enter a whole number" + - Validates and converts to integer + +4. **Number/Float Parameters**: + - TextField with `type="number"` + - Helper text: "Enter a number (decimals allowed)" + - Validates and converts to float + +5. **Boolean Parameters**: + - Checkbox component + - Helper text: "Select true or false" + - Direct true/false values + +6. **Required Parameters**: + - Marked with asterisk (*) in label + - Red error styling for validation failures + - Cannot submit form without valid values + +7. **Default Values**: + - Pre-populated when form loads + - Shows current value or default from schema + +### Parameter Presets (Future Enhancement) + +```typescript +// Parameter preset management component +const ParameterPresets: React.FC<{ + flowClass: string; + onPresetLoad: (values: ParameterValues) => void; + currentValues: ParameterValues; +}> = ({ flowClass, onPresetLoad, currentValues }) => { + const [presets, setPresets] = useState([]); + const [presetName, setPresetName] = useState(""); + + const savePreset = () => { + const preset: ParameterPreset = { + id: generateId(), + name: presetName, + flowClass, + values: currentValues, + createdAt: new Date(), + }; + // Save to local storage or backend + saveParameterPreset(preset); + setPresets([...presets, preset]); + }; + + return ( + + Parameter Presets + + {/* Preset selection */} + {presets.length > 0 && ( + ({ + value: preset.id, + label: preset.name, + description: ( + + {preset.name} - {formatDate(preset.createdAt)} + + ), + }))} + value={[]} + onValueChange={(values) => { + const presetId = values[0]; + const preset = presets.find(p => p.id === presetId); + if (preset) { + onPresetLoad(preset.values); + } + }} + /> + )} + + {/* Save current values as preset */} + + + + + + ); +}; +``` + +## Testing Strategy + +### Unit Tests + +1. **ParameterInputs Component Tests**: +```typescript +describe('ParameterInputs', () => { + it('renders string parameters with TextField', () => { + const schema = { type: 'string', description: 'Test string param' }; + render(); + expect(screen.getByRole('textbox')).toBeInTheDocument(); + }); + + it('renders enum parameters with SelectField', () => { + const schema = { type: 'string', enum: ['option1', 'option2'] }; + render(); + expect(screen.getByRole('combobox')).toBeInTheDocument(); + }); + + it('validates required parameters', () => { + const schema = { type: 'string', required: true }; + const validationErrors = { param1: 'param1 is required' }; + render(); + expect(screen.getByText('param1 is required')).toBeInTheDocument(); + }); + + it('calls onParameterChange when value changes', () => { + const mockOnChange = jest.fn(); + const schema = { type: 'string' }; + render(); + + const input = screen.getByRole('textbox'); + fireEvent.change(input, { target: { value: 'test value' } }); + + expect(mockOnChange).toHaveBeenCalledWith({ param1: 'test value' }); + }); +}); +``` + +2. **Parameter Validation Tests**: +```typescript +describe('useParameterValidation', () => { + it('validates required parameters', () => { + const { result } = renderHook(() => useParameterValidation( + 'test-flow', + { param1: { type: 'string', required: true } }, + {} + )); + + expect(result.current.isValid).toBe(false); + expect(result.current.errors.param1).toBe('param1 is required'); + }); + + it('validates number ranges', () => { + const { result } = renderHook(() => useParameterValidation( + 'test-flow', + { param1: { type: 'number', minimum: 5, maximum: 10 } }, + { param1: 15 } + )); + + expect(result.current.isValid).toBe(false); + expect(result.current.errors.param1).toContain('must be at most 10'); + }); + + it('validates enum values', () => { + const { result } = renderHook(() => useParameterValidation( + 'test-flow', + { param1: { type: 'string', enum: ['opt1', 'opt2'] } }, + { param1: 'invalid' } + )); + + expect(result.current.isValid).toBe(false); + expect(result.current.errors.param1).toContain('must be one of: opt1, opt2'); + }); +}); +``` + +### Integration Tests + +1. **CreateDialog with Parameters**: +```typescript +describe('CreateDialog with Parameters', () => { + it('fetches and displays parameters when flow class is selected', async () => { + const mockFlowClass = { + parameters: { 'model': 'llm-model', 'temp': 'temperature' } + }; + const mockParameterDefs = { + 'llm-model': { type: 'string', enum: ['gpt-4', 'claude-3'] }, + 'temperature': { type: 'number', minimum: 0, maximum: 2 } + }; + + mockSocket.flows().getFlowClass.mockResolvedValue(mockFlowClass); + mockSocket.config().getConfig.mockResolvedValue({ + values: [ + { type: 'parameters', key: 'llm-model', value: JSON.stringify(mockParameterDefs['llm-model']) }, + { type: 'parameters', key: 'temperature', value: JSON.stringify(mockParameterDefs.temperature) } + ] + }); + + render(); + + // Select flow class + const flowClassSelect = screen.getByLabelText('Flow class'); + fireEvent.change(flowClassSelect, { target: { value: 'test-flow' } }); + + // Wait for parameters to load and display + await waitFor(() => { + expect(screen.getByLabelText('model')).toBeInTheDocument(); + expect(screen.getByLabelText('temp')).toBeInTheDocument(); + }); + }); + + it('submits flow with parameter values', async () => { + // Set up mocks and render + // Fill in form including parameters + // Submit form + // Verify startFlow called with correct parameters + }); + + it('prevents submission with invalid parameters', () => { + // Set up form with required parameters + // Leave parameters empty + // Verify submit button is disabled + // Verify validation errors are shown + }); +}); +``` + +### End-to-End Tests + +1. **Complete Flow Creation with Parameters**: + - Navigate to Flows page + - Click Create button + - Select flow class with parameters + - Fill in all required fields and parameters + - Submit form + - Verify flow appears in list + - Verify flow has correct parameter values + +2. **Parameter Validation Scenarios**: + - Test all parameter types (string, number, boolean, enum) + - Test required field validation + - Test range validation for numbers + - Test enum value validation + - Test form reset after successful submission + +## Migration Plan + +### Phase 1: Core Parameter Support +1. ✅ Update FlowRequest/FlowResponse types (completed) +2. ✅ Update startFlow API method (completed) +3. ✅ Create ParameterInputs component (completed) +4. Integrate ParameterInputs into CreateDialog +5. Implement parameter fetching from config API +6. Add parameter validation logic + +### Phase 2: Enhanced User Experience +1. Add loading states during parameter fetching +2. Improve validation error display +3. Add parameter descriptions and help text +4. Implement form reset on successful submission + +### Phase 3: Advanced Features (Future) +1. Parameter presets and templates +2. Parameter value suggestions based on history +3. Bulk parameter import/export +4. Parameter dependency validation +5. Real-time parameter preview + +## Security Considerations + +1. **Parameter Validation**: All parameter validation occurs on both client and server +2. **Type Safety**: TypeScript ensures type safety for parameter values +3. **Sanitization**: Parameter values are properly sanitized before API calls +4. **Schema Validation**: Parameter schemas are validated against expected formats +5. **Error Handling**: Sensitive information is not exposed in validation errors + +## Performance Considerations + +1. **Lazy Loading**: Parameter definitions are only fetched when needed +2. **Caching**: Parameter definitions are cached using TanStack Query +3. **Validation Debouncing**: Real-time validation is debounced to avoid excessive computation +4. **Component Optimization**: ParameterInputs uses React.memo for performance +5. **Bundle Size**: Components are tree-shakeable and imported dynamically where possible + +## Backwards Compatibility + +1. **Flow Classes Without Parameters**: Existing flow classes continue to work without changes +2. **API Compatibility**: Parameter field is optional in API calls +3. **UI Graceful Degradation**: UI gracefully handles flows with no parameters +4. **Existing Flows**: Existing flows without parameters continue to function normally + +## Future Enhancements + +1. **Parameter Templates**: Pre-defined parameter sets for common use cases +2. **Conditional Parameters**: Parameters that appear based on other parameter values +3. **Parameter Groups**: Organize related parameters into collapsible sections +4. **Import/Export**: Bulk parameter configuration via JSON/YAML files +5. **Parameter History**: Track and suggest parameter values based on usage patterns +6. **Advanced Validation**: Custom validation rules and cross-parameter validation +7. **Parameter Documentation**: Rich documentation with examples and best practices \ No newline at end of file diff --git a/ai-context/workbench-ui/docs/tech-specs/gateway-auth.md b/ai-context/workbench-ui/docs/tech-specs/gateway-auth.md new file mode 100644 index 00000000..19f5c9cf --- /dev/null +++ b/ai-context/workbench-ui/docs/tech-specs/gateway-auth.md @@ -0,0 +1,373 @@ +# Gateway Authentication for TrustGraph UI + +## Overview + +This document specifies the implementation of gateway authentication for the TrustGraph UI application. The gateway authentication system will provide secure communication between the UI and the TrustGraph backend services through an authentication token mechanism. + +## Requirements + +The gateway authentication system should provide: + +1. **Authentication Modes** + - **Unauthenticated Mode**: When no API key is entered (empty string), the system operates without authentication + - **Authenticated Mode**: When an API key is specified in settings, all communications include the authentication token + +2. **Token Management** + - Secure storage and retrieval of authentication credentials via settings system + - Authentication is determined by presence/absence of API key + - Token persists across sessions (stored in localStorage via settings) + - Token should be masked in UI displays + +3. **Integration Points** + - **WebSocket Connections**: Append `?token=` to WebSocket connection URL + - **REST API Calls**: Include token as Bearer token in Authorization header + - Settings page for token configuration + - Error handling for 401/403 responses + +## Implementation Details + +### Settings Integration + +**Authentication Settings** (already exists in settings-types.ts): +```typescript +authentication: { + apiKey: string; // Gateway authentication token/secret +} +``` + +### Socket Layer Integration + +**WebSocket Authentication**: +```typescript +// In createTrustGraphSocket or SocketProvider +const wsUrl = settings.authentication.apiKey + ? `/api/socket?token=${settings.authentication.apiKey}` + : `/api/socket`; +``` + +**REST API Authentication**: +```typescript +// For any REST endpoints (if used) +const headers = settings.authentication.apiKey + ? { 'Authorization': `Bearer ${settings.authentication.apiKey}` } + : {}; +``` + +**Current State**: +- `useSocket()` hook now has access to settings via `useSettings()` +- Socket context created once at app initialization +- Settings changes require socket reconnection for auth updates + +**Required Changes**: +1. Modify `createTrustGraphSocket` to accept optional token parameter +2. Update socket initialization to append token to WebSocket URL when present +3. Add Bearer token to any REST API calls (if applicable) +4. Handle authentication errors (401/403) gracefully +5. Consider socket reconnection when authentication settings change + +### User Interface + +**Settings Page**: +- Password input field for gateway secret +- Show/hide toggle for secret visibility +- Clear button to remove authentication +- Save confirmation with success/error feedback + +**Authentication Status**: +- Optional status indicator in header/sidebar +- Error notifications for auth failures +- Redirect to settings on 401/403 errors + +## Socket Initialization Timing + +### Critical Requirements +1. **The socket MUST NOT be created until settings have been loaded from localStorage/backend**. Creating the socket too early will result in incorrect authentication state. +2. **The socket MUST reconnect when the API key changes**. This ensures authentication state stays synchronized with user settings. + +### Initialization Scenarios + +1. **Scenario 1: Token Already Configured** + - User has previously saved an API key in settings + - Settings load from localStorage → contains `apiKey: "token123"` + - Socket creation MUST wait for settings load + - Socket connects with `?token=token123` appended to URL + - **Risk if socket created early**: Connects without auth, requires reconnection + +2. **Scenario 2: Explicitly Unauthenticated** + - User has explicitly chosen no authentication (saved empty token) + - Settings load from localStorage → contains `apiKey: ""` + - Socket creation MUST wait for settings load + - Socket connects WITHOUT token parameter + - **Risk if socket created early**: Might use stale token from previous session + +3. **Scenario 3: First-Time User / No Settings** + - No settings have been saved yet + - Settings system returns defaults (empty apiKey) + - **Options**: + a. Wait for settings to initialize with defaults, then create socket (safest) + b. Create socket immediately without auth (assumes unauthenticated default) + c. Show setup wizard requiring auth decision before socket creation + - **Recommendation**: Option (a) - always wait for settings initialization + +### Socket Reconnection Requirements + +When the API key changes (user updates settings), the socket must: + +1. **Detect the Change** + - Monitor `settings.authentication.apiKey` for changes + - Triggered when user saves new API key in settings + - Also triggered when user clears API key (switches to unauthenticated) + +2. **Clean Disconnect** + - Close existing WebSocket connection gracefully + - Cancel any pending requests/subscriptions + - Clear any auth-related state + +3. **Reconnect with New Auth** + - Create new socket with updated token (or no token) + - Re-establish WebSocket connection + - Show brief loading/reconnecting state to user + +4. **Handle Edge Cases** + - API key changes from `""` to `"token123"` (unauthenticated → authenticated) + - API key changes from `"token123"` to `"token456"` (change tokens) + - API key changes from `"token123"` to `""` (authenticated → unauthenticated) + - Rapid API key changes (debounce or queue reconnections) + +### Implementation Strategy + +```typescript +// BAD - Socket created immediately, no reconnection +const socket = createTrustGraphSocket(); // ❌ No access to settings yet +export const SocketContext = createContext(socket); + +// GOOD - Socket created after settings load, reconnects on auth change +const SocketProvider = ({ children }) => { + const { settings, isLoaded } = useSettings(); + const [socket, setSocket] = useState(null); + const [isReconnecting, setIsReconnecting] = useState(false); + + useEffect(() => { + if (!isLoaded) return; // Wait for settings + + // Show reconnecting state during transitions + setIsReconnecting(true); + + // Clean up old socket if it exists + if (socket) { + console.log("Closing existing socket for reconnection..."); + socket.close(); + } + + // Create new socket with current auth settings + const newSocket = createTrustGraphSocket(settings.authentication.apiKey); + + // Wait for connection to establish + newSocket.addEventListener('open', () => { + console.log("Socket connected with auth:", + settings.authentication.apiKey ? 'enabled' : 'disabled'); + setIsReconnecting(false); + }); + + setSocket(newSocket); + + return () => newSocket?.close(); + }, [isLoaded, settings.authentication.apiKey]); // Re-run when apiKey changes + + if (!socket || isReconnecting) { + return ( + + + {isReconnecting ? 'Reconnecting...' : 'Initializing...'} + + ); + } + + return ( + + {children} + + ); +}; +``` + +## Technical Approach + +### Phase 1: Deferred Socket Initialization +1. Convert static socket creation to dynamic SocketProvider +2. Wait for settings to load before creating socket +3. Show loading state while settings/socket initialize +4. Pass loaded auth token to socket creation + +### Phase 2: Basic Authentication +1. Append `?token=` to WebSocket URL if token exists +2. Add Bearer token to REST API headers if token exists +3. Handle basic auth success/failure + +### Phase 3: Socket Reconnection on Auth Change +1. Detect when authentication settings change +2. Close existing socket connection gracefully +3. Create new socket with updated authentication +4. Handle in-flight requests during reconnection +5. Restore any active subscriptions/state (if needed) + +### Phase 4: Enhanced Features (Future) +1. Token validation endpoint +2. Authentication status indicator +3. Auto-retry with exponential backoff on auth failures +4. Better error messages for authentication issues + +## Security Considerations + +1. **Token Storage**: + - Stored in localStorage via settings system + - Never logged to console in production + - Masked in UI displays + +2. **Token Transmission**: + - Sent via secure headers + - HTTPS required in production + - No token in URL parameters + +3. **Error Handling**: + - Generic error messages to users + - Detailed errors only in development mode + - Rate limiting on failed attempts + +## Testing Strategy + +1. **Unit Tests**: + - Settings storage and retrieval + - Header injection logic + - Error handling paths + +2. **Integration Tests**: + - Full authentication flow + - Token persistence across sessions + - Error recovery scenarios + +3. **Manual Testing**: + - UI interaction flows + - Network failure scenarios + - Token expiration handling + +## Migration Path + +1. **Backwards Compatibility**: + - Support unauthenticated mode (empty token) + - Graceful degradation for older backends + - Feature detection for auth requirements + +2. **Rollout Strategy**: + - Deploy with auth disabled by default + - Enable per-user via settings + - Monitor error rates during rollout + +## Implementation Example + +```typescript +// In trustgraph-socket.ts +export const createTrustGraphSocket = (token?: string) => { + // Use relative URL for WebSocket connection + const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; + const host = window.location.host; + const baseUrl = `${protocol}//${host}/api/socket`; + const wsUrl = token ? `${baseUrl}?token=${token}` : baseUrl; + + console.log(`Creating socket with auth: ${token ? 'enabled' : 'disabled'}`); + + // Create WebSocket connection with authentication + const socket = new WebSocket(wsUrl); + + // ... rest of socket implementation +}; + +// In SocketProvider.tsx (NEW FILE) +export const SocketProvider = ({ children }) => { + const { settings, isLoaded } = useSettings(); + const [socket, setSocket] = useState(null); + const [isSocketReady, setIsSocketReady] = useState(false); + + useEffect(() => { + // CRITICAL: Wait for settings to load + if (!isLoaded) { + console.log("Waiting for settings to load before creating socket..."); + return; + } + + console.log("Settings loaded, creating socket with auth:", + settings.authentication.apiKey ? 'enabled' : 'disabled'); + + // Clean up existing socket before creating new one + if (socket) { + console.log("API key changed, closing existing socket..."); + socket.close(); + setIsSocketReady(false); + } + + // Create socket with current auth settings + const newSocket = createTrustGraphSocket(settings.authentication.apiKey); + + // Mark socket as ready when connection opens + newSocket.addEventListener('open', () => { + console.log("Socket connected successfully"); + setIsSocketReady(true); + }); + + setSocket(newSocket); + + return () => { + newSocket?.close(); + setIsSocketReady(false); + }; + }, [isLoaded, settings.authentication.apiKey]); // Reconnects when API key changes + + // Show loading state until both settings and socket are ready + if (!isSocketReady) { + return ( + + + Initializing connection... + + ); + } + + return ( + + {children} + + ); +}; + +// In App.tsx or index.tsx + + {/* Now waits for settings before creating socket */} + + + + + +``` + +## Open Questions + +1. **Socket Reconnection Strategy**: + - Should socket reconnect automatically when auth settings change? + - How to handle in-flight requests during reconnection? + - Should we show a loading state during reconnection? + +2. **Error Handling**: + - How does the backend communicate auth failures (401 vs 403)? + - Should we automatically redirect to settings on auth failure? + - How to differentiate between network errors and auth errors? + +3. **Token Security**: + - Should we support token rotation/refresh? + - How long should tokens be valid? + - Should we add CSRF protection for REST calls? + +## References + +- [Settings System Documentation](./SETTINGS.md) +- [Socket Implementation](../../src/api/trustgraph/socket.ts) +- [TrustGraph Socket API](../../src/api/trustgraph/trustgraph-socket.ts) \ No newline at end of file diff --git a/ai-context/workbench-ui/docs/tech-specs/llm-models-editor.md b/ai-context/workbench-ui/docs/tech-specs/llm-models-editor.md new file mode 100644 index 00000000..eedae62c --- /dev/null +++ b/ai-context/workbench-ui/docs/tech-specs/llm-models-editor.md @@ -0,0 +1,185 @@ +# LLM Models Editor Technical Specification + +## Overview + +This specification describes the LLM Models Editor in the TrustGraph UI. This feature allows administrators to manage the `llm-model` parameter type - the list of available LLM models that appear in dropdown menus when launching flows. + +The LLM model list is stored as a parameter type definition in the configuration system with type `"parameter-types"` and key `"llm-model"`. The editor provides a simple table interface for managing model options (ID, Description, Default). + +## Background + +The `llm-model` parameter controls which models are available when configuring flows. It's stored as a parameter type definition with an `enum` field containing model options. + +### Current State + +- The llm-model parameter can be modified through direct config API calls or CLI commands +- The parameter type with its `enum` array renders as a dropdown in flow dialogs +- The default value determines which model is pre-selected + +### Feature Switch + +This feature is controlled by a feature switch in Settings: +- **Setting Name**: `llmModels` +- **Display Label**: "LLM Models" +- **Default**: `false` (off by default) +- **Location**: Settings page → Feature Switches section + +## Goals + +- **Simple Table Editor**: Editable table with ID, Description, and Default columns +- **Direct Editing**: Edit model options directly in table cells +- **Add/Delete Rows**: Add new models or delete existing ones +- **Default Selection**: Radio button to mark one model as default +- **Save Changes**: Manual save with "Save Changes" button +- **Auto-defaults**: First model automatically selected as default when adding to empty table + +## Technical Design + +### Architecture + +Following CODEBOT-INSTRUCTIONS.md patterns: + +**Component Structure:** +``` +src/ +├── pages/ +│ └── LLMModelsPage.tsx # Main page with PageHeader +├── components/ +│ └── llm-models/ # Domain-specific directory +│ ├── LLMModels.tsx # Container component +│ ├── ParameterTypeSelector.tsx # (unused - kept for future) +│ └── ModelsTable.tsx # Editable table with save +├── state/ +│ └── llm-models.ts # API hooks +└── model/ + └── llm-models.ts # TypeScript types +``` + +### Data Models + +#### EnumOption (Model Option) + +```typescript +interface EnumOption { + id: string; // Model ID (e.g., "gemini-2.5-flash") + description: string; // Display text (e.g., "Gemini 2.5 Flash") +} +``` + +#### LLMModelParameter + +```typescript +interface LLMModelParameter { + name: string; // Parameter type key (always "llm-model") + type: string; // Always "string" + description: string; // Read-only (e.g., "LLM model to use") + default: string; // Default model ID + enum: EnumOption[]; // List of models + required: boolean; // Read-only +} +``` + +### Implementation Details + +**Key Behavior:** +1. Page only handles the single `llm-model` parameter type +2. Table edits are local until "Save Changes" is clicked +3. Radio buttons use native HTML inputs (Chakra RadioGroup had issues in tables) +4. When adding first model to empty table, it's auto-selected as default +5. When editing ID of default model, default value updates to track changes +6. When deleting default model, first remaining model becomes default +7. Empty ID fields are allowed but disabled for default selection + +**State Management:** +- Uses `getConfig([{type: "parameter-types", key: "llm-model"}])` to fetch single param +- Uses `putConfig()` to save changes, preserving read-only fields +- React Query handles caching and invalidation + +### Routing and Navigation + +#### Route (`src/App.tsx`) +```typescript +} /> +``` + +#### Sidebar Navigation (`src/components/Sidebar.tsx`) +```typescript +{settings.featureSwitches.llmModels && ( + +)} +``` + +### Feature Switch Integration + +#### Settings Types (`src/model/settings-types.ts`) +```typescript +featureSwitches: { + llmModels: boolean; // Default: false +} +``` + +#### Feature Switches Section (`src/components/settings/FeatureSwitchesSection.tsx`) +Adds toggle UI with prop `llmModels` and handler `onLlmModelsChange` + +## User Workflows + +### Editing Model Options + +1. Enable feature in Settings → Feature Switches → LLM Models +2. Navigate to LLM Models page from sidebar +3. View current models in table +4. Edit ID or Description fields directly +5. Click "Save Changes" to persist +6. Notification confirms success + +### Setting Default Model + +1. View models table +2. Click radio button in "Default" column for desired model +3. Click "Save Changes" to persist + +### Adding New Model + +1. Click "Add Model" button +2. New empty row appears +3. Enter Model ID and Description +4. If it's the only model, radio button is auto-selected +5. Click "Save Changes" to persist + +### Deleting Model + +1. Click trash icon next to model +2. Row is removed from local state +3. If deleted model was default, first remaining model becomes default +4. Click "Save Changes" to persist + +## Implementation Checklist + +- [x] Update `src/model/settings-types.ts` - Add `llmModels` feature switch +- [x] Update `src/components/settings/FeatureSwitchesSection.tsx` - Add LLM Models toggle +- [x] Update `src/components/settings/Settings.tsx` - Wire up llmModels prop +- [x] Create `src/model/llm-models.ts` - Type definitions +- [x] Create `src/state/llm-models.ts` - useLLMModels hook +- [x] Create `src/components/llm-models/LLMModels.tsx` - Container +- [x] Create `src/components/llm-models/ParameterTypeSelector.tsx` - (Created but unused) +- [x] Create `src/components/llm-models/ModelsTable.tsx` - Editable table with save +- [x] Create `src/pages/LLMModelsPage.tsx` - Main page with PageHeader +- [x] Update `src/App.tsx` - Add route +- [x] Update `src/components/Sidebar.tsx` - Add navigation item with Bot icon +- [x] Test CRUD operations +- [x] Test feature switch toggle + +## Future Enhancements + +1. **Multiple Parameter Types**: Support editing other parameter types with enum arrays (llm-rag-model, etc.) +2. **Import/Export**: Bulk import/export model lists from JSON +3. **Templates**: Pre-configured model lists for common providers +4. **Model Metadata**: Additional fields like context length, cost per token +5. **Reordering**: Drag-and-drop or up/down arrows to reorder models + +## References + +- Flow Configurable Parameters: `docs/tech-specs/flow-configurable-parameters.md` +- Parameter Inputs Component: `src/components/flows/ParameterInputs.tsx` +- Settings Feature Switches: `src/components/settings/FeatureSwitchesSection.tsx` +- CODEBOT Instructions: `CODEBOT-INSTRUCTIONS.md` diff --git a/ai-context/workbench-ui/docs/tech-specs/ontology.md b/ai-context/workbench-ui/docs/tech-specs/ontology.md new file mode 100644 index 00000000..c3357cb0 --- /dev/null +++ b/ai-context/workbench-ui/docs/tech-specs/ontology.md @@ -0,0 +1,1029 @@ +# OWL Ontology Management UI Technical Specification + +## Overview + +This specification describes a user interface component for managing OWL ontologies within the existing TrustGraph system. The UI enables data architects to create, edit, and maintain formal ontologies with classes, properties, and complex relationships, supporting multiple concurrent ontologies stored as configuration items in the backend. + +**Naming Convention**: This project uses kebab-case for all identifiers (configuration keys, API endpoints, module names, etc.) rather than snake_case. + +The system supports four primary use cases: + +1. **Ontology Creation and Management**: Create new ontologies and manage existing ones through a structured interface +2. **Class and Property Definition**: Define OWL classes, object properties, and datatype properties with domains and ranges +3. **Rich Metadata and Constraints**: Add labels, descriptions, cardinality constraints, and type specifications +4. **AI-Assisted Content Generation**: Optional wizard/getting started flow using LLM helpers to generate or enhance ontology content + +## Goals + +- **Class and Property Management**: Define and manage OWL classes with properties, domains, ranges, and type constraints +- **Rich Semantic Support**: Enable comprehensive editing of RDFS/OWL properties including labels, multi-language support, and formal constraints +- **Multi-Ontology Support**: Allow data architects to work with multiple ontologies simultaneously +- **AI-Enhanced Productivity**: Provide optional LLM assistance for content generation and ontology enhancement +- **Configuration Integration**: Seamlessly store and retrieve ontologies from the existing configuration backend +- **Data Architect Focus**: Design interfaces that expose technical details and provide precise control over ontology structure +- **Validation and Reasoning**: Ensure ontologies conform to OWL standards with consistency checking and inference support +- **Import/Export Capabilities**: Support standard formats (Turtle, RDF/XML, OWL/XML) for ontology interchange + +## Background + +TrustGraph currently stores configuration data in a flexible key-value system that can accommodate various data types. OWL ontologies represent formal knowledge models that need to be managed through dedicated tooling rather than raw configuration editing. + +Data architects working with knowledge graphs need ontologies to: +- Define formal object types and their properties +- Specify property domains, ranges, and type constraints +- Enable logical reasoning and inference +- Support complex relationships and cardinality constraints +- Provide unique identifiers and external reference capabilities + +Current limitations include: +- No dedicated UI for ontology management +- Manual editing of complex class and property definitions +- Difficulty maintaining OWL consistency +- No automated assistance for content generation +- Limited validation and reasoning capabilities + +This specification addresses these gaps by providing a specialized interface that understands OWL/RDFS semantics while integrating with the existing configuration infrastructure. + +## Technical Design + +### Architecture + +The taxonomy management UI requires the following components: + +1. **Ontology Manager Component** + - React-based interface component for the main ontology management workspace + - Integrates with existing UI framework and design system + - Provides tree view, detail panels, and editing interfaces + - Supports undo/redo operations and change tracking + + Module: workbench-ui/src/components/OntologyManager + +2. **Configuration API Integration** ✅ **[EXISTING]** + - Extends existing configuration API to handle ontology-specific operations + - Type: `ontology` for all OWL ontology configurations + - Key: Unique ontology identifier (e.g., `domain-model`, `process-ontology`) + - Value: Complete ontology in JSON-LD, Turtle, or OWL/XML format + +3. **OWL Parser/Serializer Module** + - Converts between internal JSON representation and standard OWL formats + - Supports Turtle, RDF/XML, and OWL/XML import/export + - Validates OWL compliance and logical consistency + - Handles namespace management and URI generation + + Module: workbench-ui/src/utils/owl + +4. **LLM Assistant Integration (Optional)** + - Provides an optional wizard/getting started flow for new ontologies + - Connects to existing LLM services for content generation + - Offers context-aware suggestions for classes, properties, and constraints + - Supports bulk content enhancement and ontology expansion + - Can be bypassed entirely for manual ontology creation + - Maintains logical consistency with existing definitions + + Module: workbench-ui/src/services/ontologyAssistant + +5. **Class Hierarchy Visualization Component** + - Tree-based visual representation of class hierarchies and property relationships + - Drag-and-drop support for restructuring class inheritance + - Visual indicators for class completeness, property assignments, and validation status + - Search and filtering capabilities for classes and properties + + Module: workbench-ui/src/components/OntologyTree + +6. **Class/Property Editor Component** + - Form-based interface for editing classes and properties + - Dedicated sections for domains, ranges, type constraints, and cardinality + - Real-time validation and consistency checking + - Preview of generated OWL/Turtle output + + Module: workbench-ui/src/components/OntologyEditor + +7. **Ontology Import/Export Service** + - Handles file uploads and downloads + - Supports multiple formats (Turtle, RDF/XML, OWL/XML, JSON-LD) + - Provides format conversion and validation + - Manages conflict resolution during imports + + Module: workbench-ui/src/services/ontologyIO + +### Data Models + +#### Internal Representation + +Ontologies are stored as configuration items with the following structure: + +**Configuration Schema:** +``` +Type: ontology +Key: [ontology_identifier] +Value: { + "metadata": { + "name": "Domain Ontology", + "description": "Comprehensive domain model ontology", + "version": "1.2", + "created": "2025-01-15T10:30:00Z", + "modified": "2025-08-15T14:22:00Z", + "creator": "data-architect-001", + "namespace": "http://example.org/domain/", + "imports": ["http://www.w3.org/2002/07/owl#"] + }, + "classes": { + "Document": { + "uri": "http://example.org/domain/Document", + "type": "owl:Class", + "rdfs:label": [{"value": "Document", "lang": "en"}, {"value": "Documento", "lang": "es"}], + "rdfs:comment": "A document in the system", + "rdfs:subClassOf": "Resource", + "owl:disjointWith": ["Person", "Organization"], + "dcterms:identifier": "DOC-001" + }, + "Resource": { + "uri": "http://example.org/domain/Resource", + "type": "owl:Class", + "rdfs:label": [{"value": "Resource", "lang": "en"}], + "rdfs:comment": "Base class for all resources" + } + }, + "objectProperties": { + "hasAuthor": { + "uri": "http://example.org/domain/hasAuthor", + "type": "owl:ObjectProperty", + "rdfs:label": [{"value": "has author", "lang": "en"}], + "rdfs:domain": "Document", + "rdfs:range": "Person", + "owl:inverseOf": "authorOf", + "rdfs:comment": "Links a document to its author" + } + }, + "datatypeProperties": { + "title": { + "uri": "http://example.org/domain/title", + "type": "owl:DatatypeProperty", + "rdfs:label": [{"value": "title", "lang": "en"}], + "rdfs:domain": "Document", + "rdfs:range": "xsd:string", + "owl:functionalProperty": true, + "rdfs:comment": "The title of a document" + }, + "pageCount": { + "uri": "http://example.org/domain/pageCount", + "type": "owl:DatatypeProperty", + "rdfs:label": [{"value": "page count", "lang": "en"}], + "rdfs:domain": "Document", + "rdfs:range": "xsd:integer", + "rdfs:comment": "Number of pages in the document" + } + } +} +``` + +#### OWL/RDFS Property Mapping + +The UI supports all essential OWL and RDFS properties: + +**Class Definition Properties:** +- `owl:Class` - Define object types +- `rdfs:subClassOf` - Class hierarchy and inheritance +- `owl:equivalentClass` - Class equivalence relationships +- `owl:disjointWith` - Disjoint class declarations +- `rdfs:label` - Human-readable labels with language tags +- `rdfs:comment` - Class descriptions and documentation +- `dcterms:identifier` - External reference IDs + +**Property Definition:** +- `owl:ObjectProperty` - Properties linking to other classes +- `owl:DatatypeProperty` - Properties with literal values +- `rdfs:domain` - Specifies which classes have the property +- `rdfs:range` - Specifies property value types (classes or XSD datatypes) +- `rdfs:subPropertyOf` - Property hierarchy +- `owl:inverseOf` - Inverse property relationships + +**Cardinality and Constraints:** +- `owl:functionalProperty` - At most one value +- `owl:inverseFunctionalProperty` - Unique identifying property +- `owl:minCardinality` - Minimum number of values +- `owl:maxCardinality` - Maximum number of values +- `owl:cardinality` - Exact number of values + +**Datatype Support (XSD):** +- `xsd:string` - Text values +- `xsd:integer` - Integer numbers +- `xsd:float` - Floating point numbers +- `xsd:boolean` - True/false values +- `xsd:dateTime` - Date and time values +- `xsd:anyURI` - URI references + +### User Interface Design + +#### Main Workspace Layout + +``` ++----------------------------------------------------------+ +| Ontology Manager [?] [⚙] | ++----------------------------------------------------------+ +| [Ontologies ▼] [+ New] [Import] [Export] [Reasoner] | ++------------------+---------------------------------------+ +| Ontology Tree | Element Details | +| +--------------+ | +-----------------------------------+ | +| | ▼ Classes | | | Class: Document | | +| | ├─ Resource| | | ┌─ Basic Info ──────────────────┐ | | +| | │ └─ Doc. | | | │ URI: http://example.org/Doc │ | | +| | └─ Person | | | │ Labels: [en: Document_____] │ | | +| | ▼ Properties | | | │ [+ Add language] │ | | +| | ├─ Object | | | │ Parent: [Resource_____▼] │ | | +| | │ └─ has..| | | │ Disjoint: [Person, Org.] │ | | +| | └─ Datatype| | | │ External ID: [DOC-001____] │ | | +| | [+ Add Class]| | | └─────────────────────────────────┘ | | +| | [🔍 Search_] | | | ┌─ Properties ───────────────────┐ | | +| +--------------+ | | │ Domain Properties: │ | | +| | | │ • title (string) │ | | +| | | │ • hasAuthor → Person │ | | +| | | │ [+ Add Property] │ | | +| | | └─────────────────────────────────┘ | | ++------------------+---------------------------------------+ +``` + +#### Core UI Components + +**1. Ontology Selector** +- Dropdown showing all available ontologies +- Quick actions: New, Clone, Delete +- Status indicators: Modified, Validated, Consistent + +**2. Ontology Tree** +- Dual-section tree: Classes and Properties +- Expandable class hierarchy with inheritance visualization +- Property organization by type (Object/Datatype) +- Visual indicators: ✓ Complete, ⚠ Missing constraints, ❌ Inconsistencies +- Context menu: Add Subclass, Add Property, Edit, Delete, Move +- Search with highlighting and filtering + +**3. Element Detail Panel** +- Context-sensitive form based on selection (Class or Property): + - **For Classes**: + - URI, labels (multi-language), parent class + - Equivalent/disjoint classes + - Properties with this class as domain + - External identifiers + - **For Properties**: + - Type (Object/Datatype) + - Domain and range specifications + - Cardinality constraints + - Inverse relationships + - Functional property settings + +**4. AI Assistant Panel (Optional)** +- Toggle on/off for manual-only workflow +- When enabled, provides: + - Context-aware class and property suggestions + - Bulk operations: "Generate properties for all classes" + - Smart templates: "Create subclasses for [parent]" + - Consistency checking: "Find logical conflicts" + - Domain/range recommendations +- Can be hidden completely for users preferring manual creation + +### AI Assistant Features (Optional Wizard) + +The AI Assistant is designed as an optional "getting started" wizard that helps users bootstrap their ontologies. Users can: +- Choose to start with the AI wizard or create ontologies manually from scratch +- Toggle AI assistance on/off at any point during editing +- Use AI for specific tasks (e.g., generating properties) while doing everything else manually +- Skip the wizard entirely and build ontologies using traditional manual methods + +#### Content Generation Capabilities + +**Class Definition Assistant** +- Analyzes class names and inheritance structure +- Suggests appropriate parent classes +- Recommends disjoint class relationships +- Generates class descriptions and documentation + +**Property Generator** +- Creates relevant object and datatype properties for classes +- Suggests appropriate domains and ranges +- Recommends cardinality constraints +- Identifies potential inverse relationships + +**Type and Constraint Suggestions** +- Recommends appropriate XSD datatypes for properties +- Suggests functional property declarations +- Identifies candidates for inverse functional properties +- Detects potential consistency issues + +**Bulk Enhancement** +- Processes multiple classes simultaneously +- Maintains consistency across the ontology +- Fills in missing domains/ranges systematically +- Provides logical consistency checking + +#### Implementation Details + +**Assistant Integration:** +```javascript +const assistantService = { + generateClassProperties: async (className, context) => { + // Analyze class name and context to suggest properties + // Return object and datatype properties with domains/ranges + }, + + suggestDomainRange: async (propertyName, ontologyContext) => { + // Recommend appropriate domain classes and range types + // Consider existing class hierarchy and property patterns + }, + + expandClassHierarchy: async (parentClass, depth = 2) => { + // Suggest logical subclasses + // Maintain naming consistency and inheritance logic + }, + + validateConsistency: async (ontology) => { + // Check for logical inconsistencies + // Identify missing constraints or conflicting definitions + } +}; +``` + +### APIs + +#### Using the Existing Config API + +The ontology management system uses the existing configuration API with type `ontology`. No new API endpoints are needed - all operations go through the existing socket-based config API. + +**React Hook Implementation (following the established pattern):** + +```typescript +// src/state/ontologies.ts +import { useQueryClient, useQuery, useMutation } from "@tanstack/react-query"; +import { useSocket } from "../api/trustgraph/socket"; +import { useNotification } from "./notify"; +import { useActivity } from "./activity"; + +export const useOntologies = () => { + const socket = useSocket(); + const queryClient = useQueryClient(); + const notify = useNotification(); + + // Fetch all ontologies using getValues + const ontologiesQuery = useQuery({ + queryKey: ["ontologies"], + queryFn: () => { + return socket + .config() + .getValues("ontology") + .then((values) => { + // Returns array of [id, ontologyData] tuples + return values.map((item) => [item.key, JSON.parse(item.value)]); + }) + .catch((err) => { + console.log("Error:", err); + throw err; + }); + }, + }); + + // Create/Update ontology mutation + const updateOntologyMutation = useMutation({ + mutationFn: ({ id, ontology, onSuccess }) => { + return socket + .config() + .putConfig([ + { + type: "ontology", + key: id, + value: JSON.stringify(ontology), + }, + ]) + .then((x) => { + if (x["error"]) { + console.log("Error:", x); + throw x.error.message; + } + if (onSuccess) onSuccess(); + }); + }, + onError: (err) => { + console.log("Error:", err); + notify.error(err.toString()); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["ontologies"] }); + notify.success("Ontology updated"); + }, + }); + + // Delete ontology mutation + const deleteOntologyMutation = useMutation({ + mutationFn: ({ id, onSuccess }) => { + return socket + .config() + .deleteConfig([ + { + type: "ontology", + key: id, + }, + ]) + .then((x) => { + if (x["error"]) { + console.log("Error:", x); + throw x.error.message; + } + if (onSuccess) onSuccess(); + }); + }, + onError: (err) => { + console.log("Error:", err); + notify.error(err.toString()); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["ontologies"] }); + notify.success("Ontology deleted"); + }, + }); + + // Track loading states + useActivity(ontologiesQuery.isLoading, "Loading ontologies"); + useActivity(updateOntologyMutation.isPending, "Updating ontology"); + useActivity(deleteOntologyMutation.isPending, "Deleting ontology"); + + return { + ontologies: ontologiesQuery.data || [], + ontologiesLoading: ontologiesQuery.isLoading, + ontologiesError: ontologiesQuery.error, + + updateOntology: updateOntologyMutation.mutate, + isUpdatingOntology: updateOntologyMutation.isPending, + + createOntology: updateOntologyMutation.mutate, // Same as update + isCreatingOntology: updateOntologyMutation.isPending, + + deleteOntology: deleteOntologyMutation.mutate, + isDeletingOntology: deleteOntologyMutation.isPending, + + refetch: () => ontologiesQuery.refetch(), + }; +}; +``` + +**AI Assistant APIs:** + +The AI assistant functionality will use the existing LLM integration through the socket API. No new endpoints needed - the assistant will use the existing prompt/completion mechanisms. + +### Table Component Implementation + +Following the established pattern using Tanstack Table: + +**Table Column Definition:** + +```typescript +// src/model/ontologies-table.tsx +import { createColumnHelper } from "@tanstack/react-table"; + +export type OntologyTableRow = [string, { + metadata: { + name: string; + description: string; + version: string; + modified: string; + creator: string; + namespace: string; + }; + classes: Record; + objectProperties: Record; + datatypeProperties: Record; +}]; + +const columnHelper = createColumnHelper(); + +export const ontologyColumns = [ + columnHelper.accessor((row) => row[0], { + id: "id", + header: "ID", + cell: (info) => info.getValue(), + }), + columnHelper.accessor((row) => row[1].metadata.name, { + id: "name", + header: "Name", + cell: (info) => info.getValue(), + }), + columnHelper.accessor((row) => row[1].metadata.description, { + id: "description", + header: "Description", + cell: (info) => info.getValue() || "-", + }), + columnHelper.accessor((row) => Object.keys(row[1].classes || {}).length, { + id: "classCount", + header: "Classes", + cell: (info) => info.getValue(), + }), + columnHelper.accessor((row) => + Object.keys(row[1].objectProperties || {}).length + + Object.keys(row[1].datatypeProperties || {}).length, { + id: "propertyCount", + header: "Properties", + cell: (info) => info.getValue(), + }), + columnHelper.accessor((row) => row[1].metadata.modified, { + id: "modified", + header: "Last Modified", + cell: (info) => new Date(info.getValue()).toLocaleDateString(), + }), +]; +``` + +**Table Component:** + +```typescript +// src/components/ontologies/OntologiesTable.tsx +import React from "react"; +import { Box, Table, Text, Spinner, Center } from "@chakra-ui/react"; +import { + useReactTable, + getCoreRowModel, + getSortedRowModel, + flexRender, +} from "@tanstack/react-table"; +import { useOntologies } from "../../state/ontologies"; +import { OntologyTableRow, ontologyColumns } from "../../model/ontologies-table"; +import { EditOntologyDialog } from "./EditOntologyDialog"; + +export const OntologiesTable: React.FC = () => { + const { ontologies, ontologiesLoading, ontologiesError } = useOntologies(); + const [isOpen, setIsOpen] = React.useState(false); + const [selectedOntology, setSelectedOntology] = + React.useState(null); + + const table = useReactTable({ + data: ontologies as OntologyTableRow[], + columns: ontologyColumns, + getCoreRowModel: getCoreRowModel(), + getSortedRowModel: getSortedRowModel(), + }); + + const handleRowClick = (row: OntologyTableRow) => { + setSelectedOntology(row); + setIsOpen(true); + }; + + if (ontologiesLoading) { + return ( +
+ +
+ ); + } + + if (ontologiesError) { + return ( + + + Error loading ontologies: {ontologiesError.toString()} + + + ); + } + + if (ontologies.length === 0) { + return ( +
+ + No ontologies found. Create one to get started. + +
+ ); + } + + return ( + <> + + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} + + ))} + + ))} + + + {table.getRowModel().rows.map((row) => ( + handleRowClick(row.original)} + style={{ cursor: "pointer" }} + > + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext(), + )} + + ))} + + ))} + + + + + {selectedOntology && ( + setIsOpen(false)} + mode="edit" + ontologyId={selectedOntology[0]} + initialOntology={selectedOntology[1]} + /> + )} + + ); +}; +``` + +### Validation and Quality Assurance + +#### OWL Compliance and Reasoning + +**Structural Validation:** +- Verify proper ontology structure and namespace declarations +- Check for circular class inheritance +- Validate URI consistency and uniqueness +- Ensure required OWL/RDFS properties are present + +**Type and Constraint Checking:** +- Validate domain and range specifications +- Check cardinality constraint consistency +- Verify datatype property ranges against XSD types +- Detect conflicting functional property declarations + +**Logical Consistency:** +- Check for unsatisfiable classes +- Validate disjoint class declarations +- Ensure property inverse relationships are bidirectional +- Detect inconsistent equivalence relationships + +**Reasoning and Inference:** +- Support for OWL reasoners (HermiT, Pellet) +- Automatic classification of inferred relationships +- Detection of implicit contradictions +- Validation of transitivity and symmetry + +#### Real-time Validation + +```javascript +const validator = { + validateClass: (owlClass, ontology) => { + return { + isValid: boolean, + errors: [{ field, message, severity }], + warnings: [{ field, message, suggestion }], + missingProperties: [propertyNames], + completeness: percentage + }; + }, + + validateProperty: (property, ontology) => { + return { + isValid: boolean, + domainIssues: [issues], + rangeIssues: [issues], + cardinalityConflicts: [conflicts] + }; + }, + + validateOntology: (ontology) => { + return { + isConsistent: boolean, + unsatisfiableClasses: [classIds], + logicalErrors: [{ type, message, elements }], + inferredRelationships: [relationships], + qualityScore: percentage + }; + }, + + runReasoner: async (ontology) => { + // Integrate with OWL reasoner service + return { + inferences: [inferredFacts], + inconsistencies: [conflicts], + classification: hierarchyUpdates + }; + } +}; +``` + +### Import/Export Functionality + +#### Supported Formats + +**Turtle (.ttl)** +- Standard OWL/RDF representation +- Full property and constraint support +- Namespace management +- Human-readable format + +**RDF/XML (.rdf, .owl)** +- W3C standard OWL serialization +- Complete OWL feature support +- Tool compatibility (Protégé, etc.) +- Verbose but comprehensive + +**OWL/XML (.owx)** +- OWL-specific XML format +- Structured representation +- Easy parsing and validation +- Direct OWL construct mapping + +**JSON-LD (.jsonld)** +- Web-friendly JSON format +- Linked data compatibility +- API integration ready +- Compact representation + +#### Import Process + +1. **File Upload**: Drag-and-drop or file picker +2. **Format Detection**: Automatic format recognition +3. **Parsing**: Extract classes, properties, and relationships +4. **Preview**: Show ontology structure before import +5. **Conflict Resolution**: Handle duplicate URIs and conflicting definitions +6. **Validation**: Check OWL consistency and run reasoner +7. **Import Execution**: Create configuration entries with full ontology + +### Security Considerations + +**Access Control:** +- Integrate with existing user authentication +- Role-based permissions for ontology editing +- Audit trail for all modifications +- Version control for ontology changes + +**Data Validation:** +- Sanitize all user inputs +- Validate file uploads for malicious content +- Limit ontology size and complexity +- Rate limiting for AI assistant and reasoner requests + +### Performance Considerations + +**Frontend Optimization:** +- Virtual scrolling for large ontologies +- Lazy loading of class and property details +- Debounced search and filtering +- Optimistic UI updates with rollback +- Incremental reasoning for real-time validation + +**Backend Efficiency:** +- Efficient configuration storage queries +- Caching for frequently accessed ontologies +- Batch operations for bulk modifications +- Async processing for large imports +- Background reasoning for complex ontologies + +### Testing Strategy + +**Unit Testing:** +- Component testing for UI elements +- OWL parser/serializer validation +- AI assistant response handling +- Import/export functionality +- Property constraint validation + +**Integration Testing:** +- Configuration API integration +- Full ontology CRUD operations +- Import/export round-trip testing +- AI assistant service integration +- Reasoner integration testing + +**User Acceptance Testing:** +- Data architect workflow validation +- Ontology creation scenarios +- Class and property definition workflows +- Constraint specification and validation +- Import/export with real OWL files + +## UX Refactoring Plan + +### Problem Statement + +The current schema creation flow has significant duplication between the creation dialog and the main editor: +- The creation dialog allows entering concepts/fields which duplicates the editor functionality +- Users are forced to work in a constrained dialog when they could be using the full editor +- The transition from creation to editing is jarring and requires mental context switching + +### Solution: Streamlined Creation Flow + +#### 1. Minimal Creation Dialog + +The "Create New Ontology" dialog should only capture the absolute minimum needed to create an ontology: + +``` ++---------------------------------------------+ +| Create New Ontology [X] | ++---------------------------------------------+ +| Ontology ID: [___________________] | +| | +| Name: [___________________] | +| | +| Description: [___________________] | +| [___________________] | +| | +| Namespace URI: [http://example.org/____] | +| | +| ○ Start with blank ontology | +| ○ Use AI assistant to bootstrap | +| ○ Import from file | +| | +| [Cancel] [Create & Open Editor] | ++---------------------------------------------+ +``` + +**Key Features:** +- Only 4 required fields: ID, name, description, namespace +- Three clear starting options +- Single action: "Create & Open Editor" +- No concept/class creation in dialog + +#### 2. Main Ontology Editor Workflow + +After creation, immediately open the full ontology editor: + +``` ++----------------------------------------------------------+ +| Ontology Editor: Domain Ontology [Save] | ++----------------------------------------------------------+ +| [Classes] [Properties] [Imports] [Prefixes] [Settings] | ++----------------------------------------------------------+ +| Welcome Panel (shown for new ontologies): | +| ┌────────────────────────────────────────────────────┐ | +| │ Getting Started with Your Ontology │ | +| │ │ | +| │ Your ontology is ready! Here's what to do next: │ | +| │ │ | +| │ [+ Add First Class] Start by defining object types │ | +| │ [⚡ Use AI Assistant] Let AI suggest initial classes │ | +| │ [📁 Import Classes] Load from existing ontology │ | +| │ │ | +| │ [Dismiss] [Show me around 🎯] │ | +| └────────────────────────────────────────────────────┘ | ++----------------------------------------------------------+ +``` + +**Key Benefits:** +- Full editor context immediately available +- Welcome panel provides guidance without constraining +- Can be dismissed to start working immediately +- All tools accessible from the start + +#### 3. Editor-First Design Principles + +**Immediate Context:** +- User lands in the full editor, not a dialog +- All ontology management happens in one place +- No artificial separation between creation and editing + +**Progressive Disclosure:** +- Start simple with empty ontology +- Reveal complexity as user adds elements +- Contextual help and suggestions based on current state + +**Persistent Workspace:** +- Auto-save drafts during editing +- Can leave and return without losing work +- Version history for rollback + +### Implementation Phases + +#### Phase 1: Simplify Creation Dialog +- Remove all field/concept creation from dialog +- Reduce to just core metadata fields +- Add starting mode selection (blank/AI/import) + +#### Phase 2: Enhance Editor Landing +- Create welcome panel component +- Implement guided first steps +- Add contextual empty states + +#### Phase 3: Improve Editor Navigation +- Tab-based navigation for different aspects +- Breadcrumb trail for deep navigation +- Quick access toolbar for common actions + +#### Phase 4: AI Integration Points +- AI assistant panel (collapsible) +- Inline suggestions in editors +- Bulk generation workflows + +### Detailed Component Changes + +#### CreateOntologyDialog Component + +```typescript +// Simplified creation dialog +interface CreateOntologyDialogProps { + isOpen: boolean; + onClose: () => void; + onCreated: (ontologyId: string) => void; // Callback to open editor +} + +// Only captures: +// - id: string (kebab-case, unique) +// - name: string (display name) +// - description: string (brief description) +// - namespace: string (base URI) +// - startMode: 'blank' | 'ai-assisted' | 'import' +``` + +#### OntologyEditor Component + +```typescript +// Full-featured editor +interface OntologyEditorProps { + ontologyId: string; + showWelcome?: boolean; // Show welcome panel for new ontologies +} + +// Features: +// - Multi-tab interface (Classes, Properties, etc.) +// - Split-pane layout (tree + details) +// - Integrated AI assistant (optional) +// - Real-time validation +// - Import/Export toolbar +``` + +### Benefits of This Approach + +1. **Reduced Cognitive Load**: Users only think about metadata during creation +2. **Faster Time to Value**: Get into the editor immediately +3. **Consistent Context**: All ontology work happens in the editor +4. **Better Discoverability**: Full UI reveals all capabilities +5. **Natural Workflow**: Create → Edit → Refine (no back-and-forth) + +### Migration from Current System + +1. **Preserve Existing Schemas**: Convert to OWL format behind the scenes +2. **Dual Mode Support**: Keep old schema editor while building new ontology editor +3. **Gradual Transition**: Users can opt-in to new editor +4. **Data Migration Tools**: Automated conversion utilities + +### Success Metrics + +- Time from "Create" click to first class/property added +- Number of back-navigations during creation flow +- User feedback on creation experience +- Completion rate of ontology creation +- Feature discovery and usage + +### Migration Plan + +**Phase 1: Core UI** +- Basic ontology management interface +- Configuration API integration +- Class and property CRUD operations + +**Phase 2: Advanced Features** +- Class hierarchy visualization +- Property domain/range specification +- Cardinality and constraint management +- Multi-language label support + +**Phase 3: AI Assistant Wizard** +- Optional LLM integration +- "Getting started" wizard flow +- Property generation features +- Consistency checking tools +- Manual-only mode support + +**Phase 4: Reasoning & Import/Export** +- OWL reasoner integration +- File format support (Turtle, RDF/XML, OWL/XML) +- Import/export workflows +- Inference visualization + +### Open Questions + +- Do we need integration with external ontology repositories (e.g., BioPortal, schema.org)? Not needed currently. +- Should the system support ontology versioning and branching? No. +- How should we handle very large ontologies (1000+ classes)? No explicit support needed at the moment. +- Do we need offline editing capabilities? No. +- Which OWL profiles should we support (OWL Lite, DL, Full)? Start with OWL DL for decidable reasoning. + +### References + +- [OWL 2 Web Ontology Language Reference](https://www.w3.org/TR/owl2-overview/) +- [RDF Schema 1.1](https://www.w3.org/TR/rdf-schema/) +- [RDF/XML Syntax Specification](https://www.w3.org/TR/rdf-syntax-grammar/) +- [OWL 2 XML Serialization](https://www.w3.org/TR/owl2-xml-serialization/) +- [JSON-LD 1.1 Specification](https://www.w3.org/TR/json-ld11/) +- [Turtle RDF Syntax](https://www.w3.org/TR/turtle/) +- [Dublin Core Metadata Terms](https://www.dublincore.org/specifications/dublin-core/dcmi-terms/) +- [XML Schema Datatypes](https://www.w3.org/TR/xmlschema-2/) + diff --git a/ai-context/workbench-ui/docs/tech-specs/schema.md b/ai-context/workbench-ui/docs/tech-specs/schema.md new file mode 100644 index 00000000..e902185d --- /dev/null +++ b/ai-context/workbench-ui/docs/tech-specs/schema.md @@ -0,0 +1,150 @@ +# Schema Support for TrustGraph UI + +## Overview + +This document specifies the UI work needed to support structured data schemas in TrustGraph. Schemas enable the system to work with structured data (rows in tables/objects) alongside unstructured data processing. + +## Schema Representation + +Based on the STRUCTURED_DATA.md specification, schemas are stored in TrustGraph's configuration system with: + +- **Type**: `schema` (fixed value for all structured data schemas) +- **Key**: Unique schema identifier (e.g., `customer_records`, `transaction_log`) +- **Value**: JSON schema definition + +### Schema Structure Example: +```json +{ + "name": "customer_records", + "description": "Customer information table", + "fields": [ + { + "name": "customer_id", + "type": "string", + "primary_key": true + }, + { + "name": "name", + "type": "string", + "required": true + }, + { + "name": "email", + "type": "string", + "required": true + }, + { + "name": "registration_date", + "type": "timestamp" + }, + { + "name": "status", + "type": "string", + "enum": ["active", "inactive", "suspended"] + } + ], + "indexes": ["email", "registration_date"] +} +``` + +### Field Types Supported: +- `string` +- `integer` +- `float` +- `boolean` +- `timestamp` +- `enum` (with predefined values) + +### Field Properties: +- `name`: Field identifier +- `type`: Data type +- `primary_key`: Boolean flag for primary key fields +- `required`: Boolean flag for required fields +- `enum`: Array of allowed values for enum types + +## Requirements + +Based on the Prompts page implementation pattern, the Schema UI should provide: + +1. **Schema Management Page** + - List all schemas in a table view + - Create new schemas via modal dialog + - Edit existing schemas + - Delete schemas with confirmation + - View schema details in a readable format + +2. **UI Components Needed** + - Main schemas page with table listing + - Create/Edit schema dialog with form validation + - Schema field editor (add/remove/edit fields) + - Field type selector with appropriate options + - Primary key and index configuration + - Schema preview/viewer component + +3. **State Management** + - Use React Query for data fetching and mutations + - Implement CRUD operations following the prompts pattern + - Handle loading states and error notifications + - Cache management and invalidation + +## Implementation Details + +### API Integration Pattern (from Prompts example) + +1. **Configuration Keys** + - Individual schemas: `{ type: "schema", key: "{schema_id}" }` + - List all schemas by querying all keys with `type: "schema"` + +2. **State Management Hook** (`useSchemas`) + - `getValues("schema")` to list all schemas (returns array of {key, value} objects) + - `putConfig()` to create/update schemas + - `deleteConfig()` to remove schemas + - No need for separate index management + +3. **Component Structure** + - `SchemasPage.tsx` - Main page component + - `components/schemas/Schemas.tsx` - Container component + - `components/schemas/SchemasTable.tsx` - List view + - `components/schemas/SchemaControls.tsx` - Action buttons + - `components/schemas/EditSchemaDialog.tsx` - Create/Edit form + - `components/schemas/SchemaViewer.tsx` - Read-only schema display + - `state/schemas.ts` - React Query hooks + - `model/schemas-table.tsx` - TypeScript definitions + +4. **Field Editor Requirements** + - Dynamic field list with add/remove capabilities + - Field property editors: + - Name (text input) + - Type (dropdown: string, integer, float, boolean, timestamp, enum) + - Primary key (checkbox) + - Required (checkbox) + - Enum values (list editor, shown only for enum type) + - Index configuration (multi-select from available fields) + +5. **Validation Rules** + - Schema name: Required, unique + - At least one field required + - At least one primary key field + - Field names must be unique within schema + - Enum type requires at least one enum value + +## Tasks + +1. Create schema state management hook (`useSchemas`) +2. Implement SchemasPage and routing +3. Build SchemasTable component with sorting/filtering +4. Create EditSchemaDialog with field editor +5. Add schema validation logic +6. Implement schema viewer component +7. Add TypeScript models and table configurations +8. Integration testing with backend API + +## Notes + +- Follow the existing Prompts page pattern for consistency +- Use Chakra UI components matching current design system +- Implement proper error handling and user feedback +- Consider adding import/export functionality for schemas +- May need to handle schema versioning in the future +- Implementation is simpler than prompts since we use `getValues("schema")` instead of maintaining a separate index +- Reference the agent-tools implementation pattern which also uses `getValues()` directly diff --git a/ai-context/workbench-ui/docs/tech-specs/settings.md b/ai-context/workbench-ui/docs/tech-specs/settings.md new file mode 100644 index 00000000..1a1e5b24 --- /dev/null +++ b/ai-context/workbench-ui/docs/tech-specs/settings.md @@ -0,0 +1,232 @@ +# Settings Page for TrustGraph UI + +## Overview + +This document specifies the implementation of a Settings page for the TrustGraph UI application. The Settings page will provide a centralized interface for configuring application preferences, user settings, and system-wide configurations. + +## Requirements + +The Settings page should provide: + +1. **Settings Management Interface** + - Centralized location for all user and system settings + - Organized into logical sections/categories using visual grouping + - Real-time save functionality with visual feedback + - Reset to defaults capability + - Import/export settings configuration + +2. **Settings Categories** + - **Authentication**: API key configuration for TrustGraph socket authentication + - **GraphRAG Configuration**: Entity limits, triple limits, and graph traversal settings + - **Feature Switches**: Toggle switches for advanced/experimental functionality + +3. **Specific Settings** + + **Authentication Section**: + - **API Key**: Text input field (password type for security) + - Default: empty string (no authentication) + - When set: used for TrustGraph socket authentication + - Should mask the key value when displayed + + **GraphRAG Settings Section**: + - **Entity Limit**: Number input (default: 50) + - **Triple Limit**: Number input (default: 30) + - **Max Subgraph Size**: Number input (default: 1000) + - **Path Length**: Number input (default: 2) + + **Feature Switches Section**: + - **Taxonomy Editor**: Boolean toggle (default: false) + - **Submissions**: Boolean toggle (default: false) + +3. **Navigation Integration** + - Add settings route at the end of the sidebar navigation + - Use Settings icon from lucide-react + - Standard page structure with PageHeader + +## Implementation Details + +### Routing Integration + +**Sidebar Addition** (src/components/Sidebar.tsx): +- Add import: `Settings` from lucide-react +- Add NavItem at end of VStack: `` + +**Route Configuration**: +- Add route in main router configuration +- Path: `/settings` +- Component: `SettingsPage` + +### Component Structure + +Following the established patterns from UI-TOOLKITS.md: + +``` +src/ +├── pages/ +│ └── SettingsPage.tsx # Main page with PageHeader +├── components/ +│ └── settings/ +│ ├── Settings.tsx # Main container component +│ ├── SettingsForm.tsx # Settings form management +│ ├── AuthenticationSection.tsx # API key configuration +│ ├── GraphRagSection.tsx # GraphRAG settings +│ ├── FeatureSwitchesSection.tsx # Feature toggles +│ └── SettingsControls.tsx # Action buttons (save, reset, import/export) +├── state/ +│ └── settings.ts # Settings state management with localStorage +└── model/ + └── settings-types.ts # TypeScript definitions for settings +``` + +### UI Framework Considerations + +Based on UI-TOOLKITS.md guidelines: + +**Chakra UI v3 Components**: +- Use `Field.Root` and `Field.Label` for form inputs +- Use common components: `TextField`, `SelectField`, `Card` +- Use `Alert.Root` for validation feedback +- Follow semantic color tokens (`primary`, `accent`, etc.) + +**Icons**: +- Use `Settings` from lucide-react (already established pattern) +- Other icons as needed: `Save`, `RotateCcw`, `Download`, `Upload` + +**Notifications**: +- Use `useNotification` hook (NOT direct toaster) +- Provide success/error feedback for save operations + +### State Management Pattern + +Following the established React Query pattern: + +**Settings State Hook** (`useSettings`): +- `getSettings()` to retrieve current settings from localStorage +- `updateSetting()` to modify individual settings and persist to localStorage +- `resetSettings()` to restore defaults and clear localStorage +- `exportSettings()` and `importSettings()` for configuration management +- Handle localStorage serialization/deserialization +- Provide default values when localStorage is empty + +**Data Storage**: +- **Browser localStorage**: All settings stored in browser's localStorage +- Settings persist across browser sessions +- Settings are client-side only (no server synchronization) +- Use structured key naming for organized storage + +### Testing Strategy + +Based on TEST_STRATEGY.md: + +**Component Tests**: +- SettingsForm validation and state management +- Settings section rendering and interaction +- Import/export functionality +- Reset to defaults behavior + +**Integration Tests**: +- Settings persistence across sessions +- Settings application to other components +- Route navigation and sidebar integration + +**Test Data**: +```tsx +const mockSettings = { + authentication: { + apiKey: '' // Empty by default + }, + graphrag: { + entityLimit: 50, + tripleLimit: 30, + maxSubgraphSize: 1000, + pathLength: 2 + }, + featureSwitches: { + taxonomyEditor: false, + submissions: false + } +}; +``` + +## Tasks + +1. **Foundation Setup** + - Create SettingsPage component with PageHeader + - Add routing integration and sidebar navigation + - Set up basic component structure + +2. **State Management** + - Implement settings state hook with localStorage integration + - Define settings data model with typed interfaces + - Create default settings configuration + - Handle localStorage persistence and retrieval + +3. **UI Implementation** + - Build AuthenticationSection with masked API key input + - Create GraphRagSection with NumberField components for limits + - Implement FeatureSwitchesSection with toggle switches + - Add visual grouping with Card components for each section + - Implement form validation and submission + - Add import/export functionality + - Create reset to defaults mechanism + +4. **Integration & Testing** + - Add route configuration + - Implement component tests + - Add integration tests for settings persistence + - Verify UI consistency with design system + +## Data Model + +### Settings Structure +```tsx +interface Settings { + authentication: { + apiKey: string; // Default: '' + }; + graphrag: { + entityLimit: number; // Default: 50 + tripleLimit: number; // Default: 30 + maxSubgraphSize: number; // Default: 1000 + pathLength: number; // Default: 2 + }; + featureSwitches: { + taxonomyEditor: boolean; // Default: false + submissions: boolean; // Default: false + }; +} +``` + +### LocalStorage Keys +- Main settings: `trustgraph-settings` +- Backup/versioning: Consider `trustgraph-settings-backup` for import/export + +## Integration Points + +### API Key Integration +- Settings API key should be used by TrustGraph socket authentication +- When API key is empty, no authentication is used +- When API key has value, it's passed to socket connection for authentication + +### Feature Switches Integration +- **Taxonomy Editor**: Controls visibility of taxonomy-related routes/components +- **Submissions**: Controls visibility of submissions/processing routes/components +- Features should be conditionally rendered based on these settings + +## Notes + +- **Security**: API key should be masked in UI but stored as plaintext in localStorage +- **Visual Grouping**: Use Card components to separate the three main sections +- **Real-time Updates**: Settings changes should be immediately persisted to localStorage +- **Validation**: Number inputs should have min/max constraints and validation +- **Accessibility**: Ensure full keyboard navigation and screen reader support +- **Responsive**: Settings should work well on mobile and desktop layouts + +## Future Considerations + +- User-specific vs. system-wide settings +- Settings synchronization across devices +- Advanced settings with warnings/confirmations +- Settings search/filter capability +- Bulk settings operations +- Settings versioning and migration \ No newline at end of file diff --git a/ai-context/workbench-ui/docs/tech-specs/socket-reliability.md b/ai-context/workbench-ui/docs/tech-specs/socket-reliability.md new file mode 100644 index 00000000..1081dea8 --- /dev/null +++ b/ai-context/workbench-ui/docs/tech-specs/socket-reliability.md @@ -0,0 +1,289 @@ +# Socket Reliability Refactor + +## Overview + +This document outlines a comprehensive refactor to address critical issues in the TrustGraph UI WebSocket connection handling that are causing exponential retry storms and excessive logging. + +## Current Problems + +### Issue #1: Dual Retry System Conflict ⚠️ CRITICAL + +**Problem**: Two independent retry mechanisms create multiplicative retry storms: + +1. **BaseApi Socket-Level Reconnection** (`trustgraph-socket.ts`) + - Triggers on `onClose()` events + - 10 attempts with exponential backoff (2-60 seconds) + - Handles socket-level connection failures + +2. **ServiceCall Request-Level Retries** (`service-call.ts`) + - Triggers on send failures and timeouts + - 3 retries per request with backoff + - **Calls `socket.reopen()` which triggers BaseApi reconnection** + +**Result**: Single connection failure → 3 request retries × 10 socket reconnections = **30+ retry attempts** + +```typescript +// service-call.ts:160, 174 - PROBLEM LINES +console.log("Reopen..."); +this.socket.reopen(); // ← Triggers BaseApi reconnection +``` + +### Issue #2: SocketProvider Dependency Loop ✅ FIXED +**Status**: Resolved by removing `socket` from dependency array + +### Issue #3: Inconsistent Request Retry Backoff ⚠️ MEDIUM +**Location**: `service-call.ts:170` + +```typescript +// Inconsistent retry strategies: +setTimeout(this.attempt.bind(this), backoffDelay); // Exponential backoff ✅ +setTimeout(this.attempt.bind(this), 500); // Fixed 500ms ❌ (spams) +setTimeout(this.attempt.bind(this), backoffDelay); // Exponential backoff ✅ +``` + +### Issue #4: Concurrent Socket Reopen Calls ⚠️ MEDIUM +**Problem**: Multiple failed requests simultaneously call `socket.reopen()`: +- No coordination between ServiceCalls +- Redundant reconnection attempts +- Race conditions in connection state + +## Proposed Solution: Centralized Retry Strategy + +### Architectural Decision + +**Adopt Option A: Let BaseApi handle ALL reconnection logic** + +**Rationale**: +- ✅ Single source of truth for connection state +- ✅ BaseApi already has robust exponential backoff +- ✅ Eliminates retry system conflicts +- ✅ Cleaner separation of concerns +- ✅ Minimal code changes required + +### Implementation Plan + +#### Phase 1: Remove ServiceCall Reconnection Triggers + +**File**: `src/api/trustgraph/service-call.ts` + +**Changes**: +1. Remove `this.socket.reopen()` calls (lines 160, 174) +2. Replace with passive waiting for socket reconnection +3. Standardize backoff for all retry paths + +```typescript +// BEFORE (service-call.ts:156-161) +console.log("Reopen..."); +this.socket.reopen(); // ← REMOVE THIS + +// AFTER +console.log("Message send failure, waiting for socket reconnection..."); +// Let BaseApi handle reconnection, just retry the request +``` + +#### Phase 2: Improve Request Queueing Strategy + +**Current Behavior**: ServiceCall attempts fail when socket is not ready + +**New Behavior**: ServiceCall waits for socket to become available + +```typescript +// Enhanced attempt() method logic +attempt() { + if (this.complete) return; + + this.retries--; + if (this.retries < 0) { + // Give up after retries exhausted + this.error("Ran out of retries"); + return; + } + + if (this.socket.ws && this.socket.ws.readyState === WebSocket.OPEN) { + // Socket ready - send message + try { + this.socket.ws.send(JSON.stringify(this.msg)); + this.timeoutId = setTimeout(this.onTimeout.bind(this), this.timeout); + } catch (e) { + // Send failed - wait and retry (no socket reopen) + setTimeout(this.attempt.bind(this), this.calculateBackoff()); + } + } else { + // Socket not ready - wait for BaseApi to reconnect + console.log("Request", this.mid, "waiting for socket reconnection..."); + setTimeout(this.attempt.bind(this), this.calculateBackoff()); + } +} + +calculateBackoff() { + return Math.min( + SOCKET_RECONNECTION_TIMEOUT * Math.pow(2, 3 - this.retries) + Math.random() * 1000, + 30000 + ); +} +``` + +#### Phase 3: Enhanced BaseApi Connection Management + +**File**: `src/api/trustgraph/trustgraph-socket.ts` + +**Improvements**: +1. Add connection state tracking +2. Prevent redundant reconnection attempts +3. Improve logging for debugging + +```typescript +class BaseApi { + reconnectionState: 'idle' | 'reconnecting' | 'failed' = 'idle'; + + scheduleReconnect() { + // Prevent concurrent reconnection attempts + if (this.reconnectionState === 'reconnecting') { + console.log("[socket] Reconnection already in progress, skipping"); + return; + } + + if (this.reconnectTimer) return; + + this.reconnectionState = 'reconnecting'; + // ... existing logic + } + + onOpen() { + console.log("[socket open]"); + this.reconnectAttempts = 0; + this.reconnectionState = 'idle'; // Reset state + + // Clear any pending reconnect timer + if (this.reconnectTimer) { + clearTimeout(this.reconnectTimer); + this.reconnectTimer = undefined; + } + } +} +``` + +## Expected Benefits + +### Immediate Impact +- **80-90% reduction in retry attempts** - eliminates dual retry system +- **Cleaner logs** - single source of reconnection messages +- **Predictable behavior** - one retry algorithm instead of two + +### Log Message Changes +``` +// BEFORE: Chaotic dual retry messages +[socket] Reconnecting in 2000ms (attempt 1) +Request test-123 timed out +Message send failure, retry... +Reopen... +[socket] Reconnecting in 4000ms (attempt 2) +Request test-123 ran out of retries +Request test-456 timed out +Reopen... +[socket] Reconnecting in 8000ms (attempt 3) + +// AFTER: Clean, coordinated messages +[socket] Reconnecting in 2000ms (attempt 1) +Request test-123 waiting for socket reconnection... +Request test-456 waiting for socket reconnection... +[socket open] +Request test-123 sent successfully +Request test-456 sent successfully +``` + +### Performance Improvements +- **Reduced CPU usage** - fewer concurrent timers and retry loops +- **Less network spam** - coordinated reconnection attempts +- **Better user experience** - faster recovery from connection issues + +## Risk Assessment + +### Low Risk Changes +- ✅ Removing `socket.reopen()` calls from ServiceCall +- ✅ Standardizing backoff calculations +- ✅ Adding connection state tracking + +### Potential Issues +- ⚠️ **Request timeout behavior may change** - requests may take longer to fail +- ⚠️ **Need to test edge cases** - rapid API key changes, server restarts +- ⚠️ **Verify inflight request cleanup** - ensure requests don't hang indefinitely + +### Mitigation Strategies +1. **Preserve existing timeout behavior** - requests should still timeout appropriately +2. **Add circuit breaker** - stop retrying after socket reconnection gives up +3. **Comprehensive testing** - test connection failure scenarios + +## Testing Strategy + +### Unit Tests +- Mock WebSocket state transitions +- Verify ServiceCall doesn't trigger socket reopens +- Test backoff calculations are consistent + +### Integration Tests +- Test connection failure and recovery scenarios +- Verify request queueing during reconnection +- Test concurrent request handling + +### Manual Testing Scenarios +1. **Server shutdown** - verify clean reconnection behavior +2. **Network interruption** - test mobile/wifi scenarios +3. **API key changes** - ensure proper socket recreation +4. **High load** - multiple concurrent requests during connection issues + +## Implementation Timeline + +### Phase 1: Core Fixes (1-2 hours) +- Remove `socket.reopen()` calls from ServiceCall +- Standardize ServiceCall backoff calculations +- Add basic connection state tracking + +### Phase 2: Enhanced Reliability (2-3 hours) +- Implement request queueing improvements +- Add comprehensive logging +- Enhanced error handling + +### Phase 3: Testing & Validation (2-4 hours) +- Unit test coverage +- Integration testing +- Performance validation + +**Total Estimated Effort**: 5-9 hours + +## Success Metrics + +### Quantitative Goals +- **Reduce retry attempts by 80%+** during connection failures +- **Eliminate concurrent socket reopen calls** +- **Standardize all retry backoff to exponential** + +### Qualitative Goals +- **Cleaner, more understandable logs** +- **Predictable connection recovery behavior** +- **Better separation of concerns in codebase** + +## Future Enhancements + +### Potential Improvements (Out of Scope) +1. **Request prioritization** - critical requests retry faster +2. **Connection health monitoring** - proactive reconnection +3. **Metrics collection** - track connection reliability +4. **Advanced queueing** - persist important requests across sessions + +### Monitoring Additions +```typescript +// Connection reliability metrics +interface SocketMetrics { + connectionAttempts: number; + successfulConnections: number; + averageReconnectionTime: number; + requestsLostDuringReconnection: number; +} +``` + +## Conclusion + +This refactor addresses the root cause of socket retry storms by establishing BaseApi as the single authority for connection management. The changes are surgical and low-risk, focusing on removing the problematic dual retry system while preserving all existing functionality. + +**Next Steps**: Implement Phase 1 changes and validate that retry storms are eliminated before proceeding with enhanced features. \ No newline at end of file diff --git a/ai-context/workbench-ui/eslint.config.js b/ai-context/workbench-ui/eslint.config.js new file mode 100644 index 00000000..7a981f2d --- /dev/null +++ b/ai-context/workbench-ui/eslint.config.js @@ -0,0 +1,29 @@ +import js from "@eslint/js"; +import globals from "globals"; +import reactHooks from "eslint-plugin-react-hooks"; +import reactRefresh from "eslint-plugin-react-refresh"; +import tseslint from "typescript-eslint"; + +export default tseslint.config( + { ignores: ["dist"] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ["**/*.{ts,tsx}"], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + "react-hooks": reactHooks, + "react-refresh": reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + "react-refresh/only-export-components": "off", + "@typescript-eslint/no-unused-vars": [ + "error", + { argsIgnorePattern: "^_" }, + ], + }, + }, +); diff --git a/ai-context/workbench-ui/index.html b/ai-context/workbench-ui/index.html new file mode 100644 index 00000000..ce7b96af --- /dev/null +++ b/ai-context/workbench-ui/index.html @@ -0,0 +1,16 @@ + + + + + + + TrustGraph + + + +
+ + + + + diff --git a/ai-context/workbench-ui/package-lock.json b/ai-context/workbench-ui/package-lock.json new file mode 100644 index 00000000..d90ce585 --- /dev/null +++ b/ai-context/workbench-ui/package-lock.json @@ -0,0 +1,9485 @@ +{ + "name": "vite-ts", + "version": "1.0.2", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "vite-ts", + "version": "1.0.2", + "dependencies": { + "@chakra-ui/react": "^3.19.1", + "@emotion/styled": "^11.13.0", + "@msgpack/msgpack": "^3.1.1", + "@tanstack/react-query": "^5.80.3", + "@tanstack/react-table": "^8.21.3", + "@trustgraph/client": "github:trustgraph-ai/trustgraph-client#master", + "@trustgraph/react-provider": "github:trustgraph-ai/trustgraph-react-provider#master", + "@trustgraph/react-state": "github:trustgraph-ai/trustgraph-react-state#d24cdec0", + "@types/dagre": "^0.7.53", + "compute-cosine-similarity": "^1.1.0", + "dagre": "^0.8.5", + "jszip": "^3.10.1", + "lucide-react": "^0.511.0", + "n3": "^1.26.0", + "next-themes": "^0.4.6", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-force-graph": ">=1.47.0", + "react-hotkeys-hook": "^5.1.0", + "react-icons": "^5.5.0", + "react-markdown-it": "^1.0.2", + "react-resize-detector": "^12.0.2", + "react-router": "^7.6.0", + "reactflow": "^11.11.4", + "streamsaver": "^2.0.6", + "three-spritetext": "^1.9.3", + "uuid": "^11.0.3", + "zustand": "^5.0.0-rc.2" + }, + "devDependencies": { + "@eslint/js": "^9.9.0", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/react": "^16.3.0", + "@testing-library/user-event": "^14.6.1", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react": "^4.3.1", + "eslint": "^9.9.0", + "eslint-plugin-react-hooks": "^5.1.0-rc.0", + "eslint-plugin-react-refresh": "^0.4.9", + "globals": "^15.9.0", + "jsdom": "^26.1.0", + "prettier": "3.5.3", + "sass-embedded": "^1.77.8", + "typescript": "^5.5.3", + "typescript-eslint": "^8.0.1", + "vite": "^6.3.4", + "vitest": "^3.2.4" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", + "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@ark-ui/react": { + "version": "5.26.2", + "resolved": "https://registry.npmjs.org/@ark-ui/react/-/react-5.26.2.tgz", + "integrity": "sha512-qB2i9AoxhXbADTo+LEphrf/mOnxJJP18ya/0lmoZ4LZ4/K7rY4gStPaj79gKJzIkLYpOO1G4sS5ycPk/Btmiig==", + "license": "MIT", + "dependencies": { + "@internationalized/date": "3.10.0", + "@zag-js/accordion": "1.26.3", + "@zag-js/anatomy": "1.26.3", + "@zag-js/angle-slider": "1.26.3", + "@zag-js/async-list": "1.26.3", + "@zag-js/auto-resize": "1.26.3", + "@zag-js/avatar": "1.26.3", + "@zag-js/bottom-sheet": "1.26.3", + "@zag-js/carousel": "1.26.3", + "@zag-js/checkbox": "1.26.3", + "@zag-js/clipboard": "1.26.3", + "@zag-js/collapsible": "1.26.3", + "@zag-js/collection": "1.26.3", + "@zag-js/color-picker": "1.26.3", + "@zag-js/color-utils": "1.26.3", + "@zag-js/combobox": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/date-picker": "1.26.3", + "@zag-js/date-utils": "1.26.3", + "@zag-js/dialog": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/editable": "1.26.3", + "@zag-js/file-upload": "1.26.3", + "@zag-js/file-utils": "1.26.3", + "@zag-js/floating-panel": "1.26.3", + "@zag-js/focus-trap": "1.26.3", + "@zag-js/highlight-word": "1.26.3", + "@zag-js/hover-card": "1.26.3", + "@zag-js/i18n-utils": "1.26.3", + "@zag-js/json-tree-utils": "1.26.3", + "@zag-js/listbox": "1.26.3", + "@zag-js/menu": "1.26.3", + "@zag-js/number-input": "1.26.3", + "@zag-js/pagination": "1.26.3", + "@zag-js/password-input": "1.26.3", + "@zag-js/pin-input": "1.26.3", + "@zag-js/popover": "1.26.3", + "@zag-js/presence": "1.26.3", + "@zag-js/progress": "1.26.3", + "@zag-js/qr-code": "1.26.3", + "@zag-js/radio-group": "1.26.3", + "@zag-js/rating-group": "1.26.3", + "@zag-js/react": "1.26.3", + "@zag-js/scroll-area": "1.26.3", + "@zag-js/select": "1.26.3", + "@zag-js/signature-pad": "1.26.3", + "@zag-js/slider": "1.26.3", + "@zag-js/splitter": "1.26.3", + "@zag-js/steps": "1.26.3", + "@zag-js/switch": "1.26.3", + "@zag-js/tabs": "1.26.3", + "@zag-js/tags-input": "1.26.3", + "@zag-js/timer": "1.26.3", + "@zag-js/toast": "1.26.3", + "@zag-js/toggle": "1.26.3", + "@zag-js/toggle-group": "1.26.3", + "@zag-js/tooltip": "1.26.3", + "@zag-js/tour": "1.26.3", + "@zag-js/tree-view": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@asamuzakjp/css-color": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", + "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.3", + "@csstools/css-color-parser": "^3.0.9", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", + "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", + "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/generator": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bufbuild/protobuf": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.10.0.tgz", + "integrity": "sha512-fdRs9PSrBF7QUntpZpq6BTw58fhgGJojgg39m9oFOJGZT+nip9b0so5cYY1oWl5pvemDLr0cPPsH46vwThEbpQ==", + "dev": true, + "license": "(Apache-2.0 AND BSD-3-Clause)" + }, + "node_modules/@chakra-ui/react": { + "version": "3.28.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react/-/react-3.28.0.tgz", + "integrity": "sha512-Rm9Fppqmc4NleAhNhbvE/fiA6IfSYpzRE5IQcQ39v27yFmWwSC8M1q7E5CYF36JJp1jmMG3CBPv4JRox/WESBg==", + "license": "MIT", + "dependencies": { + "@ark-ui/react": "^5.26.2", + "@emotion/is-prop-valid": "^1.4.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@pandacss/is-valid-prop": "^1.4.2", + "csstype": "^3.1.3", + "fast-safe-stringify": "^2.1.1" + }, + "peerDependencies": { + "@emotion/react": ">=11", + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz", + "integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT", + "peer": true + }, + "node_modules/@emotion/styled": { + "version": "11.14.1", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", + "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT", + "peer": true + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz", + "integrity": "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.11.tgz", + "integrity": "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.11.tgz", + "integrity": "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.11.tgz", + "integrity": "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.11.tgz", + "integrity": "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.11.tgz", + "integrity": "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.11.tgz", + "integrity": "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.11.tgz", + "integrity": "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.11.tgz", + "integrity": "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.11.tgz", + "integrity": "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.11.tgz", + "integrity": "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.11.tgz", + "integrity": "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.11.tgz", + "integrity": "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.11.tgz", + "integrity": "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.11.tgz", + "integrity": "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.11.tgz", + "integrity": "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.11.tgz", + "integrity": "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.11.tgz", + "integrity": "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.11.tgz", + "integrity": "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.11.tgz", + "integrity": "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.11.tgz", + "integrity": "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.11.tgz", + "integrity": "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.11.tgz", + "integrity": "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.11.tgz", + "integrity": "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.11.tgz", + "integrity": "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.11.tgz", + "integrity": "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.1.tgz", + "integrity": "sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.16.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz", + "integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.38.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.38.0.tgz", + "integrity": "sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz", + "integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.16.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", + "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@internationalized/date": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.10.0.tgz", + "integrity": "sha512-oxDR/NTEJ1k+UFVQElaNIk65E/Z83HK1z1WI3lQyhTtnNg4R5oVXaPzK3jcpKG8UHKDVuDQHzn+wsxSz8RP3aw==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, + "node_modules/@internationalized/number": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.5.tgz", + "integrity": "sha512-6hY4Kl4HPBvtfS62asS/R22JzNNy8vi/Ssev7x6EobfCp+9QIB2hKvI2EtbdJ0VSQacxVNtqhE/NmF/NZ0gm6g==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@msgpack/msgpack": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-3.1.2.tgz", + "integrity": "sha512-JEW4DEtBzfe8HvUYecLU9e6+XJnKDlUAIve8FvPzF3Kzs6Xo/KuZkZJsDH0wJXl/qEZbeeE7edxDNY3kMs39hQ==", + "license": "ISC", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pandacss/is-valid-prop": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@pandacss/is-valid-prop/-/is-valid-prop-1.4.3.tgz", + "integrity": "sha512-9xHAFaRGoXikp0SKUyTHdO97mjIRuGFLDqQ+zphaVeNjTIsyac+fZjOS/2jJNuTS1eybU0/ny1FZ3BpS6SkEqw==", + "license": "MIT" + }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@reactflow/background": { + "version": "11.3.14", + "resolved": "https://registry.npmjs.org/@reactflow/background/-/background-11.3.14.tgz", + "integrity": "sha512-Gewd7blEVT5Lh6jqrvOgd4G6Qk17eGKQfsDXgyRSqM+CTwDqRldG2LsWN4sNeno6sbqVIC2fZ+rAUBFA9ZEUDA==", + "license": "MIT", + "dependencies": { + "@reactflow/core": "11.11.4", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/background/node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/@reactflow/controls": { + "version": "11.2.14", + "resolved": "https://registry.npmjs.org/@reactflow/controls/-/controls-11.2.14.tgz", + "integrity": "sha512-MiJp5VldFD7FrqaBNIrQ85dxChrG6ivuZ+dcFhPQUwOK3HfYgX2RHdBua+gx+40p5Vw5It3dVNp/my4Z3jF0dw==", + "license": "MIT", + "dependencies": { + "@reactflow/core": "11.11.4", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/controls/node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/@reactflow/core": { + "version": "11.11.4", + "resolved": "https://registry.npmjs.org/@reactflow/core/-/core-11.11.4.tgz", + "integrity": "sha512-H4vODklsjAq3AMq6Np4LE12i1I4Ta9PrDHuBR9GmL8uzTt2l2jh4CiQbEMpvMDcp7xi4be0hgXj+Ysodde/i7Q==", + "license": "MIT", + "dependencies": { + "@types/d3": "^7.4.0", + "@types/d3-drag": "^3.0.1", + "@types/d3-selection": "^3.0.3", + "@types/d3-zoom": "^3.0.1", + "classcat": "^5.0.3", + "d3-drag": "^3.0.0", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/core/node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/@reactflow/minimap": { + "version": "11.7.14", + "resolved": "https://registry.npmjs.org/@reactflow/minimap/-/minimap-11.7.14.tgz", + "integrity": "sha512-mpwLKKrEAofgFJdkhwR5UQ1JYWlcAAL/ZU/bctBkuNTT1yqV+y0buoNVImsRehVYhJwffSWeSHaBR5/GJjlCSQ==", + "license": "MIT", + "dependencies": { + "@reactflow/core": "11.11.4", + "@types/d3-selection": "^3.0.3", + "@types/d3-zoom": "^3.0.1", + "classcat": "^5.0.3", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/minimap/node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/@reactflow/node-resizer": { + "version": "2.2.14", + "resolved": "https://registry.npmjs.org/@reactflow/node-resizer/-/node-resizer-2.2.14.tgz", + "integrity": "sha512-fwqnks83jUlYr6OHcdFEedumWKChTHRGw/kbCxj0oqBd+ekfs+SIp4ddyNU0pdx96JIm5iNFS0oNrmEiJbbSaA==", + "license": "MIT", + "dependencies": { + "@reactflow/core": "11.11.4", + "classcat": "^5.0.4", + "d3-drag": "^3.0.0", + "d3-selection": "^3.0.0", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/node-resizer/node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/@reactflow/node-toolbar": { + "version": "1.3.14", + "resolved": "https://registry.npmjs.org/@reactflow/node-toolbar/-/node-toolbar-1.3.14.tgz", + "integrity": "sha512-rbynXQnH/xFNu4P9H+hVqlEUafDCkEoCy0Dg9mG22Sg+rY/0ck6KkrAQrYrTgXusd+cEJOMK0uOOFCK2/5rSGQ==", + "license": "MIT", + "dependencies": { + "@reactflow/core": "11.11.4", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/node-toolbar/node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz", + "integrity": "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.5.tgz", + "integrity": "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.5.tgz", + "integrity": "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.5.tgz", + "integrity": "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.5.tgz", + "integrity": "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.5.tgz", + "integrity": "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.5.tgz", + "integrity": "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.5.tgz", + "integrity": "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.5.tgz", + "integrity": "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.5.tgz", + "integrity": "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.5.tgz", + "integrity": "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.5.tgz", + "integrity": "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.5.tgz", + "integrity": "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.5.tgz", + "integrity": "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.5.tgz", + "integrity": "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.5.tgz", + "integrity": "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.5.tgz", + "integrity": "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.5.tgz", + "integrity": "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.5.tgz", + "integrity": "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.5.tgz", + "integrity": "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.5.tgz", + "integrity": "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.5.tgz", + "integrity": "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", + "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "license": "MIT", + "peer": true, + "dependencies": { + "defer-to-connect": "^1.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.90.5", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.5.tgz", + "integrity": "sha512-wLamYp7FaDq6ZnNehypKI5fNvxHPfTYylE0m/ZpuuzJfJqhR5Pxg9gvGBHZx4n7J+V5Rg5mZxHHTlv25Zt5u+w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.90.5", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.5.tgz", + "integrity": "sha512-pN+8UWpxZkEJ/Rnnj2v2Sxpx1WFlaa9L6a4UO89p6tTQbeo+m0MS8oYDjbggrR8QcTyjKoYWKS3xJQGr3ExT8Q==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.90.5" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@tanstack/react-table": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz", + "integrity": "sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==", + "license": "MIT", + "dependencies": { + "@tanstack/table-core": "8.21.3" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/@tanstack/table-core": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz", + "integrity": "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", + "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "picocolors": "^1.1.1", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/react": { + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.0.tgz", + "integrity": "sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@testing-library/user-event": { + "version": "14.6.1", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", + "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, + "node_modules/@trustgraph/client": { + "version": "0.2.0", + "resolved": "git+ssh://git@github.com/trustgraph-ai/trustgraph-client.git#972d944b66c020c5d55ed6667a4ed8b0ee30f408", + "license": "Apache-2.0" + }, + "node_modules/@trustgraph/react-provider": { + "version": "0.2.0", + "resolved": "git+ssh://git@github.com/trustgraph-ai/trustgraph-react-provider.git#e0ea931a7f6184b218039401b09988f09f017572", + "license": "Apache-2.0", + "peerDependencies": { + "@trustgraph/client": "^0.2.0", + "react": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/@trustgraph/react-state": { + "version": "0.2.0", + "resolved": "git+ssh://git@github.com/trustgraph-ai/trustgraph-react-state.git#d24cdec00075b2797a9c417485d2cb6f6858f523", + "license": "MIT", + "dependencies": { + "@trustgraph/client": "github:trustgraph-ai/trustgraph-client#master", + "@trustgraph/react-provider": "github:trustgraph-ai/trustgraph-react-provider#master", + "compute-cosine-similarity": "^1.1.0", + "uuid": "^11.0.3" + }, + "peerDependencies": { + "@tanstack/react-query": "^5.0.0", + "react": "^18.0.0", + "zustand": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/@tweenjs/tween.js": { + "version": "25.0.0", + "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-25.0.0.tgz", + "integrity": "sha512-XKLA6syeBUaPzx4j3qwMqzzq+V4uo72BnlbOjmuljLrRqdsd3qnzvZZoxvMHZ23ndsRS4aufU6JOZYpCbU6T1A==", + "license": "MIT" + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", + "license": "MIT" + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz", + "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==", + "license": "MIT" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "license": "MIT", + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", + "license": "MIT" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", + "license": "MIT" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", + "license": "MIT" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", + "license": "MIT" + }, + "node_modules/@types/d3-random": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", + "license": "MIT" + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", + "license": "MIT" + }, + "node_modules/@types/d3-shape": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", + "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "license": "MIT", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "node_modules/@types/dagre": { + "version": "0.7.53", + "resolved": "https://registry.npmjs.org/@types/dagre/-/dagre-0.7.53.tgz", + "integrity": "sha512-f4gkWqzPZvYmKhOsDnhq/R8mO4UMcKdxZo+i5SCkOU1wvGeHJeUXGIHeE9pnwGyPMDof1Vx5ZQo4nxpeg2TTVQ==", + "license": "MIT" + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.26", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.26.tgz", + "integrity": "sha512-RFA/bURkcKzx/X9oumPG9Vp3D3JUgus/d0b67KB0t5S/raciymilkOa66olh78MUI92QLbEJevO7rvqU/kjwKA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.2.tgz", + "integrity": "sha512-ZGBMToy857/NIPaaCucIUQgqueOiq7HeAKkhlvqVV4lm089zUFW6ikRySx2v+cAhKeUCPuWVHeimyk6Dw1iY3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.46.2", + "@typescript-eslint/type-utils": "8.46.2", + "@typescript-eslint/utils": "8.46.2", + "@typescript-eslint/visitor-keys": "8.46.2", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.46.2", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.2.tgz", + "integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.46.2", + "@typescript-eslint/types": "8.46.2", + "@typescript-eslint/typescript-estree": "8.46.2", + "@typescript-eslint/visitor-keys": "8.46.2", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.2.tgz", + "integrity": "sha512-PULOLZ9iqwI7hXcmL4fVfIsBi6AN9YxRc0frbvmg8f+4hQAjQ5GYNKK0DIArNo+rOKmR/iBYwkpBmnIwin4wBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.46.2", + "@typescript-eslint/types": "^8.46.2", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.2.tgz", + "integrity": "sha512-LF4b/NmGvdWEHD2H4MsHD8ny6JpiVNDzrSZr3CsckEgCbAGZbYM4Cqxvi9L+WqDMT+51Ozy7lt2M+d0JLEuBqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.46.2", + "@typescript-eslint/visitor-keys": "8.46.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.2.tgz", + "integrity": "sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.2.tgz", + "integrity": "sha512-HbPM4LbaAAt/DjxXaG9yiS9brOOz6fabal4uvUmaUYe6l3K1phQDMQKBRUrr06BQkxkvIZVVHttqiybM9nJsLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.46.2", + "@typescript-eslint/typescript-estree": "8.46.2", + "@typescript-eslint/utils": "8.46.2", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.2.tgz", + "integrity": "sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.2.tgz", + "integrity": "sha512-f7rW7LJ2b7Uh2EiQ+7sza6RDZnajbNbemn54Ob6fRwQbgcIn+GWfyuHDHRYgRoZu1P4AayVScrRW+YfbTvPQoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.46.2", + "@typescript-eslint/tsconfig-utils": "8.46.2", + "@typescript-eslint/types": "8.46.2", + "@typescript-eslint/visitor-keys": "8.46.2", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.2.tgz", + "integrity": "sha512-sExxzucx0Tud5tE0XqR0lT0psBQvEpnpiul9XbGUB1QwpWJJAps1O/Z7hJxLGiZLBKMCutjTzDgmd1muEhBnVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.46.2", + "@typescript-eslint/types": "8.46.2", + "@typescript-eslint/typescript-estree": "8.46.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.2.tgz", + "integrity": "sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.46.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/@vitest/expect": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.2.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.2.4", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@zag-js/accordion": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/accordion/-/accordion-1.26.3.tgz", + "integrity": "sha512-xZgXTc4AhH0vha5zLjjSj3GZml2LFMhJnZ/F9TU0KEci7xML1U0n2ay4KydgtiEG31/A3j3LLE+vON+/N/0jAg==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/anatomy": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/anatomy/-/anatomy-1.26.3.tgz", + "integrity": "sha512-2HL+BDX05zRtctKwrSlYEtxeLdiJZIV5SbEpqTNvbNJ9aZNxHKgg0ciEi0bYZe8qJ70TqfNfwWb2xwRamzedhg==", + "license": "MIT" + }, + "node_modules/@zag-js/angle-slider": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/angle-slider/-/angle-slider-1.26.3.tgz", + "integrity": "sha512-tmOcOLiKN+5enp2YobuZkhVCWY8Q78SnGVO4tJgj+IjZ1PU/EJwipAiAXRM1Adl/6MPIwIMe2ag5g+Qi9xIDuw==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/rect-utils": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/aria-hidden": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/aria-hidden/-/aria-hidden-1.26.3.tgz", + "integrity": "sha512-F6P7Ff4iqly4VmxBRZaJoG09QonOIlYDmMRgHH8d8vca7aBmR5InTxy3iOjCZvX3U4xFFednEARRPjncy4Vl6Q==", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.26.3" + } + }, + "node_modules/@zag-js/async-list": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/async-list/-/async-list-1.26.3.tgz", + "integrity": "sha512-rcM4E8ITSpTANSgk6QX7TKbLyewE1iHOYIJ0NKvSLqfLqSXZgjYTVbWnOmt5q3UOhR3paCbxkEtSdX2/ZCNO/A==", + "license": "MIT", + "dependencies": { + "@zag-js/core": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/auto-resize": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/auto-resize/-/auto-resize-1.26.3.tgz", + "integrity": "sha512-/hIJGEHjknBnrbGjz7NqZbRGCldklJEvePMbMi5JnELZIFRCcCKnvaXwq7DS1mYODjsLqxBZRSL4y0hNxExk4Q==", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.26.3" + } + }, + "node_modules/@zag-js/avatar": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/avatar/-/avatar-1.26.3.tgz", + "integrity": "sha512-CGQunbcfHzDi1p1B51TN+sGLGaBgLTsgHpqKM8YbR385hMuULcUguOwcnx7rFBq/w79QsdrACwhz3LFlZOsyUQ==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/bottom-sheet": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/bottom-sheet/-/bottom-sheet-1.26.3.tgz", + "integrity": "sha512-qhlkSmBz/Ch2piku5h7itWNO0Uit4pTlhICVb8lLf0Q17X7SNxGYW+pMKAQMlL2M4EgA9tV1z/xE1pW4R05F6A==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/aria-hidden": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dismissable": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/focus-trap": "1.26.3", + "@zag-js/remove-scroll": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/carousel": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/carousel/-/carousel-1.26.3.tgz", + "integrity": "sha512-zF7RD7SR/nzl2CDPIp06J3eMXbY2Vk4auMzo9R4puuGN3XSh4+KYmKRt6DH5XmkOd/fFknT3eHH2gNRlpxBaMQ==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/scroll-snap": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/checkbox": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/checkbox/-/checkbox-1.26.3.tgz", + "integrity": "sha512-7WcJChF9jKLr3e5aJfLNiMxmAA8iYt2ZEjEmmaL1KRDUyONpig9JMDX5mg4u3iXQvZdmsxCptIYHXwdYjZjr3Q==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/focus-visible": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/clipboard": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/clipboard/-/clipboard-1.26.3.tgz", + "integrity": "sha512-Cx8oafXtujYRlBU0XA2aXb78bs3JgNVZ4ikR6cm7Qo1AMUxh7FejIBNVaWGzHxUc5XrfliKGvQzJotyrGS3GDA==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/collapsible": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/collapsible/-/collapsible-1.26.3.tgz", + "integrity": "sha512-vPnWRHolQXdXhP0GkNMADZsBuxhLqGzv/krrJitslYOwsKebtrYz3DrIvtLwpRPsED4igzNLv1ihHPa89kaOAA==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/collection": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/collection/-/collection-1.26.3.tgz", + "integrity": "sha512-AYiIuZZ3Fr8eVH657uKGobf6WU5t7a4f/HfFiekwfvG0U0vsySBP7eOxukMvQJDCRNEKxARW9JyDj9pl4d7T8Q==", + "license": "MIT", + "dependencies": { + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/color-picker": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/color-picker/-/color-picker-1.26.3.tgz", + "integrity": "sha512-qsmgl1vHIavY+wH9jHLXlVnbSn+HBbIJy9ysC1vyM1nAM8Qm1P+h1PhojxgPkVujI6QcxSBzA+YcY56ZuSUHdw==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/color-utils": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dismissable": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/popper": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/color-utils": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/color-utils/-/color-utils-1.26.3.tgz", + "integrity": "sha512-II2mfISJc0xLupXHmfc5AABG0pZy0cYwn741yjlbe7orubWnFLZLmVCzLF4b0qpLzYM+c+MnRVAb/YV6t7Mw6A==", + "license": "MIT", + "dependencies": { + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/combobox": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/combobox/-/combobox-1.26.3.tgz", + "integrity": "sha512-CejmsLlKhxfSg0FkGMMS8JPGnCa3sCHv4hZrqRrBuhUJaLjiH5/VazV+WFlHEDUrSzVWUNeoYiZuqQlBWuIeCA==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/aria-hidden": "1.26.3", + "@zag-js/collection": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dismissable": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/popper": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/core": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/core/-/core-1.26.3.tgz", + "integrity": "sha512-NU/uBGEFE5kUDJfvayZuPom/mcvJjjIytBclIO4/dCt1IBo+C1ETbpQjG/RgpfmSjfAs2G/9pQgdqobakyWU2A==", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/date-picker": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/date-picker/-/date-picker-1.26.3.tgz", + "integrity": "sha512-3JZckrr1EI01bEYfKeB4nAgI4f+bWZLQnfOq8LtiL4HnS0a31Z3DYFxk3r49X4dxg3B62j1tlb6DzdQJARaElg==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/date-utils": "1.26.3", + "@zag-js/dismissable": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/live-region": "1.26.3", + "@zag-js/popper": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + }, + "peerDependencies": { + "@internationalized/date": ">=3.0.0" + } + }, + "node_modules/@zag-js/date-utils": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/date-utils/-/date-utils-1.26.3.tgz", + "integrity": "sha512-eoWJZzFY3fkTJU7rzFcRLGCecAnJiu1/xHrB317jgHd0eCGUrgJVY2m0h+agWUCIFtt1VdvEbcf9R9rZmQm7LQ==", + "license": "MIT", + "peerDependencies": { + "@internationalized/date": ">=3.0.0" + } + }, + "node_modules/@zag-js/dialog": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/dialog/-/dialog-1.26.3.tgz", + "integrity": "sha512-8fRmRdTrmX5o9IRCo9kpldlM+L4boesyU3mDYCGoCxymzUImg8Crr+8y3uUHEKFWdxnlisYfBGbC586kWaaCSQ==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/aria-hidden": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dismissable": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/focus-trap": "1.26.3", + "@zag-js/remove-scroll": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/dismissable": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/dismissable/-/dismissable-1.26.3.tgz", + "integrity": "sha512-THAzkq2KLYq7px+pdIZ4QQoRjF02nxa5WXXVyw+cv7yb+CNcemcNaPtAAIORJhKsQC+1XF1JNiT5TKrvhSprhw==", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.26.3", + "@zag-js/interact-outside": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/dom-query": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.26.3.tgz", + "integrity": "sha512-ZI/EzpWRDFjeSuXiHuCHwFUtFXX7ZNiBiOnsqR5AnbQSTEvYv+Oo803RRuQ+zHDpGSFyYluSnT00/zt4vskXDQ==", + "license": "MIT", + "dependencies": { + "@zag-js/types": "1.26.3" + } + }, + "node_modules/@zag-js/editable": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/editable/-/editable-1.26.3.tgz", + "integrity": "sha512-DdlBwOqoXbF+X0uM7k5mYxiKI4eDkX+BYar/DnEifnEgpsTkYMtl3J5B9WmuWmFtmSoTauScHQsVe3OmVCTqTA==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/interact-outside": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/file-upload": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/file-upload/-/file-upload-1.26.3.tgz", + "integrity": "sha512-SAifHG71HmRyAUHlqj50xjMr0Ua0uDp8qOucgr7kP2xQvxp8ETfJ5LXrvf+IWv67bF4GmXgIH5Tn7OVEY6ETWQ==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/file-utils": "1.26.3", + "@zag-js/i18n-utils": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/file-utils": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/file-utils/-/file-utils-1.26.3.tgz", + "integrity": "sha512-chXv4dL4SeBdGuNvHlPRZDDsIqRZ1YHAX7kHTZhQbp8iqXSZR/XCnwnQg93b7pb6rG9P8oN+f/pYAuKfrulXbA==", + "license": "MIT", + "dependencies": { + "@zag-js/i18n-utils": "1.26.3" + } + }, + "node_modules/@zag-js/floating-panel": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/floating-panel/-/floating-panel-1.26.3.tgz", + "integrity": "sha512-Xue020m4gb9K/zboeW+r6bhSzqqwQpuGqfeFqgekP1XZTxGcEBDHO330gngi6hu7gO9DEMVr1sKinJKZ5kgQ2Q==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/popper": "1.26.3", + "@zag-js/rect-utils": "1.26.3", + "@zag-js/store": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/focus-trap": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/focus-trap/-/focus-trap-1.26.3.tgz", + "integrity": "sha512-mzvt9pzZ80T7O3TDcpUc7kybuWcOEO6yWvDUbJybJsMCKMvLG3HpH14gcPGOL41YZ7RTOh5PixdML8/Xb7/5Iw==", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.26.3" + } + }, + "node_modules/@zag-js/focus-visible": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-1.26.3.tgz", + "integrity": "sha512-gjcOCZeVlqDsCes3z5bmVLmVJQXhs+aMFs3+1JCmOUOT9zfLQ4HLlGfoesG3Zf2h/VUiIV6oZIJDLu3t/iUSig==", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.26.3" + } + }, + "node_modules/@zag-js/highlight-word": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/highlight-word/-/highlight-word-1.26.3.tgz", + "integrity": "sha512-AylY/vZMPzN4FfZeeURlMmyvS4ytQDvAYFOGLPwJjK9zajIuP7CuIa6DtkAIvhf1hfb4oHtH4MwHA2TA2hfUAQ==", + "license": "MIT" + }, + "node_modules/@zag-js/hover-card": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/hover-card/-/hover-card-1.26.3.tgz", + "integrity": "sha512-M6hEQHZnwx4PeGIl3GsMdGFtKvp2f+dYiDttqkhi5DCcWN4S3173mExK7MNFTgqFCGLTqX86w4eIyWRGBWrZ0Q==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dismissable": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/popper": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/i18n-utils": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/i18n-utils/-/i18n-utils-1.26.3.tgz", + "integrity": "sha512-wFCVuUK3/GjOryQYPbsQMKlE7LX2sFgU0JccbzrvvKLe1fBCznESWrYjPd93hA+Dm6myQYQa9maxnJ2HiVB4vA==", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.26.3" + } + }, + "node_modules/@zag-js/interact-outside": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/interact-outside/-/interact-outside-1.26.3.tgz", + "integrity": "sha512-CBTagsGQ6QXaPISc8SS1wwjdpZNgMJt3ecYCIA0FP7QwUXNSnNVq7izxXwyoSzkO6xLkXS+kiqwd9guKTUBQ0Q==", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/json-tree-utils": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/json-tree-utils/-/json-tree-utils-1.26.3.tgz", + "integrity": "sha512-SubdKHCnAqZAFd4H0YOCBLFS2ZKP9+eq6+sBlj/2M8hmFPltjSDQWHko5u+/RMo3nKae/FpxLu2yo4CsswKpHA==", + "license": "MIT" + }, + "node_modules/@zag-js/listbox": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/listbox/-/listbox-1.26.3.tgz", + "integrity": "sha512-oeD5s8R37xTo+sXIEAb4uNzu+RxTfSdojchg7WGBQMQfbgiTyyzSpZeQ1WpvdiRNiI7RREKfCBwNDEE+EWwwHA==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/collection": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/focus-visible": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/live-region": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/live-region/-/live-region-1.26.3.tgz", + "integrity": "sha512-of7L/R3sdDmsRU3JhZ0azKWqveCBSsTU6yT9xDocY+m7g4MRyUtNfzaqpVDQeJI1S7BD6rGhL7podYYuRhJUvg==", + "license": "MIT" + }, + "node_modules/@zag-js/menu": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/menu/-/menu-1.26.3.tgz", + "integrity": "sha512-92GHkFKW6PdG8DUnXlZWJskW+rjWtHUgdPUVOI94EN/+Bz7IqJ0AtVTiR9Sc/tuvxP6RRcBelD1pj+8+wVucfg==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dismissable": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/popper": "1.26.3", + "@zag-js/rect-utils": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/number-input": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/number-input/-/number-input-1.26.3.tgz", + "integrity": "sha512-Ft/Ot4jJpkaTGS1wK7z9YzdKxqAAKOM8EiGFHcMufPpyGwLIxNmScXNVGTjDqdWIIW3rPPrxLtIlA4ovUe4ZsQ==", + "license": "MIT", + "dependencies": { + "@internationalized/number": "3.6.5", + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/pagination": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/pagination/-/pagination-1.26.3.tgz", + "integrity": "sha512-dTuDXWx833UUzVfn0HLoTtj0SIfKr5sJl/CEyLaLLS4+GAhDYsPkkPereLSjAL3ffUoKzqkqACh17wI/IibmgQ==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/password-input": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/password-input/-/password-input-1.26.3.tgz", + "integrity": "sha512-rdItTM+TnhW6Wpzt0fPhqLug0BfHUZYSK3+77gny9ZhrBH9XrPXBbHb3ORzmGZfBFuVG+bmF1shI+wg6YS/6rA==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/pin-input": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/pin-input/-/pin-input-1.26.3.tgz", + "integrity": "sha512-4e5h3T9UirAd8+StrHJ180PComxXofg2Pc5NxYs1D73dIKRhbB03Gbbt8ONJg0Pys3HCCcMe5IMzhmmwIdTBCw==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/popover": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/popover/-/popover-1.26.3.tgz", + "integrity": "sha512-9eohN1s5ha0mwF6AIht9SE6D0Nl40IL4BY8gL4QWiaLZPPuILqzFby7yug+USdQuMNV2sk7ow6khaU2f7DTQEQ==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/aria-hidden": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dismissable": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/focus-trap": "1.26.3", + "@zag-js/popper": "1.26.3", + "@zag-js/remove-scroll": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/popper": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/popper/-/popper-1.26.3.tgz", + "integrity": "sha512-rCBhh+yfMUmBKJkzFvOdeeS3z/uHgbts1lXQ9SZqHBXQ8zQNjSREdQt9E1Ge2lR+ZPtwLpS8adrUtLzLRFc7Uw==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "1.7.4", + "@zag-js/dom-query": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/presence": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/presence/-/presence-1.26.3.tgz", + "integrity": "sha512-K3jKOcqimLOGRGcywY128NRzUHeUKXVKYALA5yi/gn6EfJRbX/lV/CQGG9dRiVpI+KnpuCIgJ5zr0ojU0d27yw==", + "license": "MIT", + "dependencies": { + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/types": "1.26.3" + } + }, + "node_modules/@zag-js/progress": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/progress/-/progress-1.26.3.tgz", + "integrity": "sha512-NwCjePJLEhtNypBFvHg74dtzx6TsK4+1lr/2jwDWO7/vTCLzMRa6TzHD/Tt6KJl5SlLKRSPeCRRc3pnOmKC4LA==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/qr-code": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/qr-code/-/qr-code-1.26.3.tgz", + "integrity": "sha512-Tq3TWpECOlUmYiC+8svYaUaocBYH4/psAAl2tq4f2qQdCYsB3c20DAG/x6GaDZPf6EFPC/jSgIjCDeBZGw2g4A==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3", + "proxy-memoize": "3.0.1", + "uqr": "0.1.2" + } + }, + "node_modules/@zag-js/radio-group": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/radio-group/-/radio-group-1.26.3.tgz", + "integrity": "sha512-3JLamlpsaa5qW2BzQ6qjoSS70DV2E5YBJWdFAYIHKttQMBzxhRux0U3GR7SCVXp6C7xinR+/xxYEt4uexMK2MQ==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/focus-visible": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/rating-group": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/rating-group/-/rating-group-1.26.3.tgz", + "integrity": "sha512-4Ki6GtCY5+su2V6080NlYtOwt+DTjvWcJ0SVPqc/TDe6FgefkHhqAlTClKWVR8hocxk5Mq0z3mgZZ/Y2Yzss9Q==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/react": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/react/-/react-1.26.3.tgz", + "integrity": "sha512-Z+O89rAmpMmNTMmdLAambnnwTwsRYMAiUZe7RF5TjlLuWMtV6Nc5TpBii3fqxZ/5S9vaUMtDMb9gsHPEfOR7Kw==", + "license": "MIT", + "dependencies": { + "@zag-js/core": "1.26.3", + "@zag-js/store": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@zag-js/rect-utils": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/rect-utils/-/rect-utils-1.26.3.tgz", + "integrity": "sha512-+ayBRgNO0xo+sJmnGfZDTKxHyWj6acAUjELwlcHbT0LIxMULTPxbA+Sf1d8Zz3x4fah9v2UN6/AaIrYnldFxTQ==", + "license": "MIT" + }, + "node_modules/@zag-js/remove-scroll": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/remove-scroll/-/remove-scroll-1.26.3.tgz", + "integrity": "sha512-vofKmcJqN9AQqH6BXOii6cIQ5wj6w4cMj0psGeMUE5Rh1Xifg6chvp6ZMX0EqlTz5pYuE9e3VLPpuWUEuY+8Hg==", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.26.3" + } + }, + "node_modules/@zag-js/scroll-area": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/scroll-area/-/scroll-area-1.26.3.tgz", + "integrity": "sha512-C35ad+q5tZkZQwgoiUwyB/HoGLiin+DBPKvKcXQjztGMTB8fv/7n0vzKdpbo5BEFV626ys8vKGV/WY9Zkjdj6w==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/scroll-snap": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/scroll-snap/-/scroll-snap-1.26.3.tgz", + "integrity": "sha512-Pzy2p2xAYILg18Z2h8xtVZqxgcITf7igXIJEngR8Tmvg+97NBYHPgl9sp86mNdoVKkQ5FB4ohirYvHrHONIurw==", + "license": "MIT", + "dependencies": { + "@zag-js/dom-query": "1.26.3" + } + }, + "node_modules/@zag-js/select": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/select/-/select-1.26.3.tgz", + "integrity": "sha512-4T7Y6fd8FZiTuPQWjuBNG6MWDZbVPO74BlEpBJdbDK9LEJnR2yPnDmNS855BAk0ERzw4UWBwX/HbbDDaYwiRiQ==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/collection": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dismissable": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/popper": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/signature-pad": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/signature-pad/-/signature-pad-1.26.3.tgz", + "integrity": "sha512-2oO06kTt96POPkQ1VvXcfSKihHJAdGSKhIQHpi5pqPl5OWCyD7DlqVeeP+IHsoTSOIyR2w1EzEG9PCQFc9F50Q==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3", + "perfect-freehand": "^1.2.2" + } + }, + "node_modules/@zag-js/slider": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/slider/-/slider-1.26.3.tgz", + "integrity": "sha512-EIZcljXslieIz6uWe4YOUjrZbLiW5w6LFhlSfszDs4OGnyKxOImiFWEhuArPCGW7CyuyISFy1GWFW+vi5+6Y1w==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/splitter": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/splitter/-/splitter-1.26.3.tgz", + "integrity": "sha512-dN4ZbOXv83bBVsDyZxBFmPB/o/0OMcOTVIdZbt7aOe1y2z/As/8omzg6zzhqrIV+ASVJwFl6R+QHMngS2NZFhQ==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/steps": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/steps/-/steps-1.26.3.tgz", + "integrity": "sha512-eM4ytff6eo93J6AlG8OJcKZ7t5XF8SLLHDJFKIGG59GDYtK8oPSdIVkjy6Ud4XvKXhKufZJUqT7qDT8UDOHxww==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/store": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/store/-/store-1.26.3.tgz", + "integrity": "sha512-mDBylMkKKobJTUCJmuLDRzZsDRBkwQFcjlyCFQa9fLAjVhfCkF2Y3lAp3MuyqeJa+c7X3BbkXDD/kL2UoiTFMg==", + "license": "MIT", + "dependencies": { + "proxy-compare": "3.0.1" + } + }, + "node_modules/@zag-js/switch": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/switch/-/switch-1.26.3.tgz", + "integrity": "sha512-Fc56yuh0cIf0/8AdRtBS9YVoxIIESQiYf61QMQS/pfO8hBeSFQQmbjeAf8MyOd2QadWwoNKABCiUArP2nBl1Rg==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/focus-visible": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/tabs": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/tabs/-/tabs-1.26.3.tgz", + "integrity": "sha512-3Iqb+TAw49WldJaMczyzhOQl6F/x9QBgMSp7NhE6AIMaityr0ZE3jCU0qYS+F2DPAu7ng87SOCVA0dOaO/J4eg==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/tags-input": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/tags-input/-/tags-input-1.26.3.tgz", + "integrity": "sha512-BSzQtfx72zhs4mfBsWReY30HhnKg1KSTzCVThm8oHmkcDGvpDuUbylM2KMsQdgYWTGYHI4FiIzUsUd+RVDOAgw==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/auto-resize": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/interact-outside": "1.26.3", + "@zag-js/live-region": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/timer": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/timer/-/timer-1.26.3.tgz", + "integrity": "sha512-VwRvtY8OJSsSii3Is+8iqf48eY2tc5bKEgCOCQ3JKWhlDtj+Z9toXEQzR2aEiCk64M9AnzKFhFjDS+l7GmWIGg==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/toast": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/toast/-/toast-1.26.3.tgz", + "integrity": "sha512-JOLvSHXC4hKlzNb2dDU7tvXIY6U4M+z8ewnHSAOO8Sl8OKTE3pbRU3Q9A0B2qW/3qteQA/P7411e5WrWdxn+9Q==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dismissable": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/toggle": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/toggle/-/toggle-1.26.3.tgz", + "integrity": "sha512-nxsVxkQCTzeOCJcOwqrsIccSf+jjAKmVrmFD0l5IjvZWrypukKPmUFd9BgM7QYTtd3STaQPMMjkYzfFQ/SljzA==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/toggle-group": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/toggle-group/-/toggle-group-1.26.3.tgz", + "integrity": "sha512-CL6oOih/7R8r2NAq7U9HuR5MYlDmAPmS2q9VZINb415bKYuFEJyGMClFB3B+NFaLy6KG/voYahOBJ1NRsgMnxQ==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/tooltip": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/tooltip/-/tooltip-1.26.3.tgz", + "integrity": "sha512-givMhlRGWt9PD9JMrA+GBceD2ViQT8MUgb5r/ovDdaahW8xlMkosWIBnjJBafilrg3tw2Oemw1gPwctcPjAlMQ==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/focus-visible": "1.26.3", + "@zag-js/popper": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/tour": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/tour/-/tour-1.26.3.tgz", + "integrity": "sha512-2WqYTpCTo46LWsF/arI+kuCexDDbgyKwE0hYjsK5NKf1BFAsAWpwmZ1Ne1RGDYl52sXMGfGu7tCQhDzGTzWU9w==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dismissable": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/focus-trap": "1.26.3", + "@zag-js/interact-outside": "1.26.3", + "@zag-js/popper": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/tree-view": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/tree-view/-/tree-view-1.26.3.tgz", + "integrity": "sha512-3P376SKf/poaUUjeobm/qenxaO8ApB/P/rpplkUE3fZVhnwKJbzKoUeNZiTpVX36FaJBb3AwBavmyQxMuYoxiQ==", + "license": "MIT", + "dependencies": { + "@zag-js/anatomy": "1.26.3", + "@zag-js/collection": "1.26.3", + "@zag-js/core": "1.26.3", + "@zag-js/dom-query": "1.26.3", + "@zag-js/types": "1.26.3", + "@zag-js/utils": "1.26.3" + } + }, + "node_modules/@zag-js/types": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/types/-/types-1.26.3.tgz", + "integrity": "sha512-fJf2CgNLQuaFCRZzwGP69vWdFPc1bd1sPngzrYxIfT9SpIRFcBUrBa3p8hXlXg3EScx4O8qC0PrMe9NasUXV1Q==", + "license": "MIT", + "dependencies": { + "csstype": "3.1.3" + } + }, + "node_modules/@zag-js/utils": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@zag-js/utils/-/utils-1.26.3.tgz", + "integrity": "sha512-C5PlGTVfuMYc/GydvyIyxjSoHib9ZNcinLoucZaRjXF4l6ClDPIlujXc11//XZ0EajpxOKNhfjP9m9stj5Vk0A==", + "license": "MIT" + }, + "node_modules/3d-force-graph": { + "version": "1.79.0", + "resolved": "https://registry.npmjs.org/3d-force-graph/-/3d-force-graph-1.79.0.tgz", + "integrity": "sha512-0RUNcfiH12f93loY/iS4wShzhXzdLLN4futvFnintF7eP30DjX+nAdLDAGOZwSflhijQyVwnGtpczNjFrDLUzQ==", + "license": "MIT", + "dependencies": { + "accessor-fn": "1", + "kapsule": "^1.16", + "three": ">=0.118 <1", + "three-forcegraph": "1", + "three-render-objects": "^1.35" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/3d-force-graph-ar": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/3d-force-graph-ar/-/3d-force-graph-ar-1.10.0.tgz", + "integrity": "sha512-I93bRB+PY7RGPGEPa/+MyC17UYXUBkGu8i71VjRVJCSDtk23XOJ3RlcyVWHKzifTktQ7Oy0YRnqWwbxkHV8m4g==", + "license": "MIT", + "dependencies": { + "aframe-forcegraph-component": "3", + "kapsule": "^1.16" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/3d-force-graph-vr": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/3d-force-graph-vr/-/3d-force-graph-vr-3.1.1.tgz", + "integrity": "sha512-q/NhHQyAkGMHkf9Irvt6350yIYnCK77XuEc/09HrRqYbaENqwraPPzahr9BG5wkd2//KtzEEp9AYhPU2GQ0KHg==", + "license": "MIT", + "dependencies": { + "accessor-fn": "1", + "aframe-extras": "^7.2", + "aframe-forcegraph-component": "3", + "kapsule": "^1.16", + "polished": "4" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "aframe": "^1.5" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accessor-fn": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/accessor-fn/-/accessor-fn-1.5.3.tgz", + "integrity": "sha512-rkAofCwe/FvYFUlMB0v0gWmhqtfAtV1IUkdPbfhTUyYniu5LrC0A0UJkTH0Jv3S8SvwkmfuAlY+mQIJATdocMA==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/aframe": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/aframe/-/aframe-1.7.1.tgz", + "integrity": "sha512-dcc7PWI5z8pyJ0s2W0mUd8d83339frgMXhUvWr1yxkdgg6zSExkuQwsSJjiNn7XWKMUUqKYDvV/WzQQRA+OBXA==", + "license": "MIT", + "peer": true, + "dependencies": { + "buffer": "^6.0.3", + "debug": "^4.3.4", + "deep-assign": "^2.0.0", + "load-bmfont": "^1.2.3", + "super-animejs": "^3.1.0", + "three": "npm:super-three@0.173.5", + "three-bmfont-text": "github:dmarcos/three-bmfont-text#eed4878795be9b3e38cf6aec6b903f56acd1f695" + }, + "engines": { + "node": ">= 4.6.0", + "npm": ">= 2.15.9" + } + }, + "node_modules/aframe-extras": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/aframe-extras/-/aframe-extras-7.6.0.tgz", + "integrity": "sha512-IKRMWsU1DgxIYPDatFpB0JMErIGSh1tWoLx01rodOM5mgzqyEumlhphh5ouCwz0aFKdWQ8KxznECaXf4B+xwAw==", + "license": "MIT", + "dependencies": { + "nipplejs": "^0.10.2", + "three": "^0.164.0", + "three-pathfinding": "^1.3.0" + } + }, + "node_modules/aframe-extras/node_modules/three": { + "version": "0.164.1", + "resolved": "https://registry.npmjs.org/three/-/three-0.164.1.tgz", + "integrity": "sha512-iC/hUBbl1vzFny7f5GtqzVXYjMJKaTPxiCxXfrvVdBi1Sf+jhd1CAkitiFwC7mIBFCo3MrDLJG97yisoaWig0w==", + "license": "MIT" + }, + "node_modules/aframe-forcegraph-component": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/aframe-forcegraph-component/-/aframe-forcegraph-component-3.3.0.tgz", + "integrity": "sha512-rwVk1t93YGOQ9+qaeFdcYgY8RZfd3NHbrIBhT9Ju7gxXP8QDtE6fIi409OiPZEhMx9/36zFQ/etxdpQNsWmHPQ==", + "license": "MIT", + "dependencies": { + "three-forcegraph": "1" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "aframe": "*" + } + }, + "node_modules/aframe/node_modules/three": { + "name": "super-three", + "version": "0.173.5", + "resolved": "https://registry.npmjs.org/super-three/-/super-three-0.173.5.tgz", + "integrity": "sha512-ecjojbhUg/5QrixwqF4s6gvtJap9XQz7TcnFUX/J8Yosgb2eE2ZUqsyqr/JczFG//6hwIaZPeAa9M5DNaM1dmQ==", + "license": "MIT", + "peer": true + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/an-array": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/an-array/-/an-array-1.0.0.tgz", + "integrity": "sha512-M175GYI7RmsYu24Ok383yZQa3eveDfNnmhTe3OQ3bm70bEovz2gWenH+ST/n32M8lrwLWk74hcPds5CDRPe2wg==", + "license": "MIT", + "peer": true + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/array-shuffle": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-shuffle/-/array-shuffle-1.0.1.tgz", + "integrity": "sha512-PBqgo1Y2XWSksBzq3GFPEb798ZrW2snAcmr4drbVeF/6MT/5aBlkGJEvu5A/CzXHf4EjbHOj/ZowatjlIiVidA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/as-number": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/as-number/-/as-number-1.0.0.tgz", + "integrity": "sha512-HkI/zLo2AbSRO4fqVkmyf3hms0bJDs3iboHqTrNuwTiCRvdYXM7HFhfhB6Dk51anV2LM/IMB83mtK9mHw4FlAg==", + "license": "MIT", + "peer": true + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.19.tgz", + "integrity": "sha512-zoKGUdu6vb2jd3YOq0nnhEDQVbPcHhco3UImJrv5dSkvxTc2pl2WjOPsjZXDwPDSl5eghIMuY3R6J9NDKF3KcQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/bezier-js": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/bezier-js/-/bezier-js-6.1.4.tgz", + "integrity": "sha512-PA0FW9ZpcHbojUCMu28z9Vg/fNkwTj5YhusSAjHHDfHDGLxJ6YUKrAN2vk1fP2MMOxVw4Oko16FMlRGVBGqLKg==", + "license": "MIT", + "funding": { + "type": "individual", + "url": "https://github.com/Pomax/bezierjs/blob/master/FUNDING.md" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz", + "integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.8.19", + "caniuse-lite": "^1.0.30001751", + "electron-to-chromium": "^1.5.238", + "node-releases": "^2.0.26", + "update-browserslist-db": "^1.1.4" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-builder": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/buffer-builder/-/buffer-builder-0.2.0.tgz", + "integrity": "sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg==", + "dev": true, + "license": "MIT/X11" + }, + "node_modules/buffer-equal": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", + "integrity": "sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "license": "MIT", + "peer": true, + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "license": "MIT", + "peer": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cacheable-request/node_modules/json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==", + "license": "MIT", + "peer": true + }, + "node_modules/cacheable-request/node_modules/keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "license": "MIT", + "peer": true, + "dependencies": { + "json-buffer": "3.0.0" + } + }, + "node_modules/cacheable-request/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001751", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001751.tgz", + "integrity": "sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/canvas-color-tracker": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/canvas-color-tracker/-/canvas-color-tracker-1.3.2.tgz", + "integrity": "sha512-ryQkDX26yJ3CXzb3hxUVNlg1NKE4REc5crLBq661Nxzr8TNd236SaEf2ffYLXyI5tSABSeguHLqcVq4vf9L3Zg==", + "license": "MIT", + "dependencies": { + "tinycolor2": "^1.6.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/centra": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/centra/-/centra-2.7.0.tgz", + "integrity": "sha512-PbFMgMSrmgx6uxCdm57RUos9Tc3fclMvhLSATYN39XsDV29B89zZ3KA89jmY0vwSGazyU+uerqwa6t+KaodPcg==", + "license": "MIT", + "peer": true, + "dependencies": { + "follow-redirects": "^1.15.6" + } + }, + "node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/classcat": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz", + "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==", + "license": "MIT" + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "license": "MIT", + "peer": true, + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/colorjs.io": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.5.2.tgz", + "integrity": "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/compute-cosine-similarity": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/compute-cosine-similarity/-/compute-cosine-similarity-1.1.0.tgz", + "integrity": "sha512-FXhNx0ILLjGi9Z9+lglLzM12+0uoTnYkHm7GiadXDAr0HGVLm25OivUS1B/LPkbzzvlcXz/1EvWg9ZYyJSdhTw==", + "dependencies": { + "compute-dot": "^1.1.0", + "compute-l2norm": "^1.1.0", + "validate.io-array": "^1.0.5", + "validate.io-function": "^1.0.2" + } + }, + "node_modules/compute-dot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/compute-dot/-/compute-dot-1.1.0.tgz", + "integrity": "sha512-L5Ocet4DdMrXboss13K59OK23GXjiSia7+7Ukc7q4Bl+RVpIXK2W9IHMbWDZkh+JUEvJAwOKRaJDiFUa1LTnJg==", + "dependencies": { + "validate.io-array": "^1.0.3", + "validate.io-function": "^1.0.2" + } + }, + "node_modules/compute-l2norm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/compute-l2norm/-/compute-l2norm-1.1.0.tgz", + "integrity": "sha512-6EHh1Elj90eU28SXi+h2PLnTQvZmkkHWySpoFz+WOlVNLz3DQoC4ISUHSV9n5jMxPHtKGJ01F4uu2PsXBB8sSg==", + "dependencies": { + "validate.io-array": "^1.0.3", + "validate.io-function": "^1.0.2" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cssstyle": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", + "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^3.2.0", + "rrweb-cssom": "^0.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-binarytree": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d3-binarytree/-/d3-binarytree-1.0.2.tgz", + "integrity": "sha512-cElUNH+sHu95L04m92pG73t2MEJXKu+GeKUN1TJkFsu93E5W8E9Sc3kHEGJKgenGvj19m6upSn2EunvMgMD2Yw==", + "license": "MIT" + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force-3d": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/d3-force-3d/-/d3-force-3d-3.0.6.tgz", + "integrity": "sha512-4tsKHUPLOVkyfEffZo1v6sFHvGFwAIIjt/W8IThbp08DYAsXZck+2pSHEG5W1+gQgEvFLdZkYvmJAbRM2EzMnA==", + "license": "MIT", + "dependencies": { + "d3-binarytree": "1", + "d3-dispatch": "1 - 3", + "d3-octree": "1", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-octree": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/d3-octree/-/d3-octree-1.1.0.tgz", + "integrity": "sha512-F8gPlqpP+HwRPMO/8uOu5wjH110+6q4cgJvgJT6vlpy3BEaDIKlTZrgHKZSp/i1InRpVfh4puY/kvL6MxK930A==", + "license": "MIT" + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dagre": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/dagre/-/dagre-0.8.5.tgz", + "integrity": "sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==", + "license": "MIT", + "dependencies": { + "graphlib": "^2.1.8", + "lodash": "^4.17.15" + } + }, + "node_modules/data-bind-mapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/data-bind-mapper/-/data-bind-mapper-1.0.3.tgz", + "integrity": "sha512-QmU3lyEnbENQPo0M1F9BMu4s6cqNNp8iJA+b/HP2sSb7pf3dxwF3+EP1eO69rwBfH9kFJ1apmzrtogAmVt2/Xw==", + "license": "MIT", + "dependencies": { + "accessor-fn": "1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==", + "license": "MIT", + "peer": true, + "dependencies": { + "mimic-response": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/deep-assign": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/deep-assign/-/deep-assign-2.0.0.tgz", + "integrity": "sha512-2QhG3Kxulu4XIF3WL5C5x0sc/S17JLgm1SfvDfIRsR/5m7ZGmcejII7fZ2RyWhN0UWIJm0TNM/eKow6LAn3evQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "license": "MIT", + "peer": true + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/dom-walk": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==", + "peer": true + }, + "node_modules/dtype": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dtype/-/dtype-2.0.0.tgz", + "integrity": "sha512-s2YVcLKdFGS0hpFqJaTwscsyt0E8nNFdmo73Ocd81xNPj4URI4rj6D60A+vFMIw7BXWlb4yRkEwfBqcZzPGiZg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/duplexer3": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", + "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/electron-to-chromium": { + "version": "1.5.239", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.239.tgz", + "integrity": "sha512-1y5w0Zsq39MSPmEjHjbizvhYoTaulVtivpxkp5q5kaPmQtsK6/2nvAzGRxNMS9DoYySp9PkW0MAQDwU1m764mg==", + "dev": true, + "license": "ISC" + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "peer": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/end-of-stream/node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "peer": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-toolkit": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.40.0.tgz", + "integrity": "sha512-8o6w0KFmU0CiIl0/Q/BCEOabF2IJaELM1T2PWj6e8KqzHv1gdx+7JtFnDwOx1kJH/isJ5NwlDG1nCr1HrRF94Q==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks" + ] + }, + "node_modules/esbuild": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.11.tgz", + "integrity": "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.11", + "@esbuild/android-arm": "0.25.11", + "@esbuild/android-arm64": "0.25.11", + "@esbuild/android-x64": "0.25.11", + "@esbuild/darwin-arm64": "0.25.11", + "@esbuild/darwin-x64": "0.25.11", + "@esbuild/freebsd-arm64": "0.25.11", + "@esbuild/freebsd-x64": "0.25.11", + "@esbuild/linux-arm": "0.25.11", + "@esbuild/linux-arm64": "0.25.11", + "@esbuild/linux-ia32": "0.25.11", + "@esbuild/linux-loong64": "0.25.11", + "@esbuild/linux-mips64el": "0.25.11", + "@esbuild/linux-ppc64": "0.25.11", + "@esbuild/linux-riscv64": "0.25.11", + "@esbuild/linux-s390x": "0.25.11", + "@esbuild/linux-x64": "0.25.11", + "@esbuild/netbsd-arm64": "0.25.11", + "@esbuild/netbsd-x64": "0.25.11", + "@esbuild/openbsd-arm64": "0.25.11", + "@esbuild/openbsd-x64": "0.25.11", + "@esbuild/openharmony-arm64": "0.25.11", + "@esbuild/sunos-x64": "0.25.11", + "@esbuild/win32-arm64": "0.25.11", + "@esbuild/win32-ia32": "0.25.11", + "@esbuild/win32-x64": "0.25.11" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.38.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.38.0.tgz", + "integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.1", + "@eslint/core": "^0.16.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.38.0", + "@eslint/plugin-kit": "^0.4.0", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.24.tgz", + "integrity": "sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/expect-type": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", + "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/float-tooltip": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/float-tooltip/-/float-tooltip-1.7.5.tgz", + "integrity": "sha512-/kXzuDnnBqyyWyhDMH7+PfP8J/oXiAavGzcRxASOMRHFuReDtofizLLJsf7nnDLAfEaMW4pVWaXrAjtnglpEkg==", + "license": "MIT", + "dependencies": { + "d3-selection": "2 - 3", + "kapsule": "^1.16", + "preact": "10" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/force-graph": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/force-graph/-/force-graph-1.51.0.tgz", + "integrity": "sha512-aTnihCmiMA0ItLJLCbrQYS9mzriopW24goFPgUnKAAmAlPogTSmFWqoBPMXzIfPb7bs04Hur5zEI4WYgLW3Sig==", + "license": "MIT", + "dependencies": { + "@tweenjs/tween.js": "18 - 25", + "accessor-fn": "1", + "bezier-js": "3 - 6", + "canvas-color-tracker": "^1.3", + "d3-array": "1 - 3", + "d3-drag": "2 - 3", + "d3-force-3d": "2 - 3", + "d3-scale": "1 - 4", + "d3-scale-chromatic": "1 - 3", + "d3-selection": "2 - 3", + "d3-zoom": "2 - 3", + "float-tooltip": "^1.7", + "index-array-by": "1", + "kapsule": "^1.16", + "lodash-es": "4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "license": "MIT", + "peer": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/global": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", + "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", + "license": "MIT", + "peer": true, + "dependencies": { + "min-document": "^2.19.0", + "process": "^0.11.10" + } + }, + "node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/graphlib": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz", + "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.15" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "license": "BSD-2-Clause", + "peer": true + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, + "node_modules/immutable": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", + "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==", + "dev": true, + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/index-array-by": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/index-array-by/-/index-array-by-1.4.2.tgz", + "integrity": "sha512-SP23P27OUKzXWEC/TOyWlwLviofQkCSCKONnc62eItjp69yCZZPqDQtr3Pw5gJDnPeUMqExmKydNZaJO0FU9pw==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "license": "MIT", + "peer": true + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", + "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==", + "license": "MIT", + "peer": true + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jerrypick": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/jerrypick/-/jerrypick-1.1.2.tgz", + "integrity": "sha512-YKnxXEekXKzhpf7CLYA0A+oDP8V0OhICNCr5lv96FvSsDEmrb0GKM776JgQvHTMjr7DTTPEVv/1Ciaw0uEWzBA==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", + "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssstyle": "^4.2.1", + "data-urls": "^5.0.0", + "decimal.js": "^10.5.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.16", + "parse5": "^7.2.1", + "rrweb-cssom": "^0.8.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.1.1", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.1.1", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/kapsule": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/kapsule/-/kapsule-1.16.3.tgz", + "integrity": "sha512-4+5mNNf4vZDSwPhKprKwz3330iisPrb08JyMgbsdFrimBCKNHecua/WBwvVg3n7vwx0C1ARjfhwIpbrbd9n5wg==", + "license": "MIT", + "dependencies": { + "lodash-es": "4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/layout-bmfont-text": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/layout-bmfont-text/-/layout-bmfont-text-1.3.4.tgz", + "integrity": "sha512-mceomHZ8W7pSKQhTdLvOe1Im4n37u8xa5Gr0J3KPCHRMO/9o7+goWIOzZcUUd+Xgzy3+22bvoIQ0OaN3LRtgaw==", + "license": "MIT", + "peer": true, + "dependencies": { + "as-number": "^1.0.0", + "word-wrapper": "^1.0.7", + "xtend": "^4.0.0" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/linkify-it": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-1.2.4.tgz", + "integrity": "sha512-eGHwtlABkp1NOJSiKUNqBf3SYAS5jPHtvRXPAgNaQwTqmkTahjtiLH9NtxdR5IOPhNvwNMN/diswSfZKzUkhGg==", + "license": "MIT", + "dependencies": { + "uc.micro": "^1.0.1" + } + }, + "node_modules/load-bmfont": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.2.tgz", + "integrity": "sha512-qElWkmjW9Oq1F9EI5Gt7aD9zcdHb9spJCW1L/dmPf7KzCCEJxq8nhHz5eCgI9aMf7vrG/wyaCqdsI+Iy9ZTlog==", + "license": "MIT", + "peer": true, + "dependencies": { + "buffer-equal": "0.0.1", + "mime": "^1.3.4", + "parse-bmfont-ascii": "^1.0.3", + "parse-bmfont-binary": "^1.0.5", + "parse-bmfont-xml": "^1.1.4", + "phin": "^3.7.1", + "xhr": "^2.0.1", + "xtend": "^4.0.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.511.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.511.0.tgz", + "integrity": "sha512-VK5a2ydJ7xm8GvBeKLS9mu1pVK6ucef9780JVUjw6bAjJL/QXnd4Y0p7SPeOUMC27YhzNCZvm5d/QX0Tp3rc0w==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/map-limit": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/map-limit/-/map-limit-0.0.1.tgz", + "integrity": "sha512-pJpcfLPnIF/Sk3taPW21G/RQsEEirGaFpCW3oXRwH9dnFHPHNGjNyvh++rdmC2fNqEaTw2MhYJraoJWAHx8kEg==", + "license": "MIT", + "peer": true, + "dependencies": { + "once": "~1.3.0" + } + }, + "node_modules/markdown-it": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-4.4.0.tgz", + "integrity": "sha512-Rl8dHHeLuAh3E72OPY0tY7CLvlxgHiLhlshIYswAAabAg4YDBLa6e/LTgNkkxBO2K61ESzoquPQFMw/iMrT1PA==", + "license": "MIT", + "dependencies": { + "argparse": "~1.0.2", + "entities": "~1.1.1", + "linkify-it": "~1.2.0", + "mdurl": "~1.0.0", + "uc.micro": "^1.0.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/markdown-it/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/markdown-it/node_modules/entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "license": "BSD-2-Clause" + }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "peer": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==", + "peer": true, + "dependencies": { + "dom-walk": "^0.1.0" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/n3": { + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/n3/-/n3-1.26.0.tgz", + "integrity": "sha512-SQknS0ua90rN+3RHuk8BeIqeYyqIH/+ecViZxX08jR4j6MugqWRjtONl3uANG/crWXnOM2WIqBJtjIhVYFha+w==", + "license": "MIT", + "dependencies": { + "buffer": "^6.0.3", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">=12.0" + } + }, + "node_modules/n3/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/n3/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/n3/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/new-array": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/new-array/-/new-array-1.0.0.tgz", + "integrity": "sha512-K5AyFYbuHZ4e/ti52y7k18q8UHsS78FlRd85w2Fmsd6AkuLipDihPflKC0p3PN5i8II7+uHxo+CtkLiJDfmS5A==", + "license": "MIT", + "peer": true + }, + "node_modules/next-themes": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", + "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" + } + }, + "node_modules/ngraph.events": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ngraph.events/-/ngraph.events-1.4.0.tgz", + "integrity": "sha512-NeDGI4DSyjBNBRtA86222JoYietsmCXbs8CEB0dZ51Xeh4lhVl1y3wpWLumczvnha8sFQIW4E0vvVWwgmX2mGw==", + "license": "BSD-3-Clause" + }, + "node_modules/ngraph.forcelayout": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/ngraph.forcelayout/-/ngraph.forcelayout-3.3.1.tgz", + "integrity": "sha512-MKBuEh1wujyQHFTW57y5vd/uuEOK0XfXYxm3lC7kktjJLRdt/KEKEknyOlc6tjXflqBKEuYBBcu7Ax5VY+S6aw==", + "license": "BSD-3-Clause", + "dependencies": { + "ngraph.events": "^1.0.0", + "ngraph.merge": "^1.0.0", + "ngraph.random": "^1.0.0" + } + }, + "node_modules/ngraph.graph": { + "version": "20.1.0", + "resolved": "https://registry.npmjs.org/ngraph.graph/-/ngraph.graph-20.1.0.tgz", + "integrity": "sha512-1jorNgIc0Kg0L9bTNN4+RCrVvbZ+4pqGVMrbhX3LLyqYcRdLvAQRRnxddmfj9l5f6Eq59SUTfbYZEm8cktiE7Q==", + "license": "BSD-3-Clause", + "dependencies": { + "ngraph.events": "^1.2.1" + } + }, + "node_modules/ngraph.merge": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ngraph.merge/-/ngraph.merge-1.0.0.tgz", + "integrity": "sha512-5J8YjGITUJeapsomtTALYsw7rFveYkM+lBj3QiYZ79EymQcuri65Nw3knQtFxQBU1r5iOaVRXrSwMENUPK62Vg==", + "license": "MIT" + }, + "node_modules/ngraph.random": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ngraph.random/-/ngraph.random-1.2.0.tgz", + "integrity": "sha512-4EUeAGbB2HWX9njd6bP6tciN6ByJfoaAvmVL9QTaZSeXrW46eNGA9GajiXiPBbvFqxUWFkEbyo6x5qsACUuVfA==", + "license": "BSD-3-Clause" + }, + "node_modules/nice-color-palettes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/nice-color-palettes/-/nice-color-palettes-3.0.0.tgz", + "integrity": "sha512-lL4AjabAAFi313tjrtmgm/bxCRzp4l3vCshojfV/ij3IPdtnRqv6Chcw+SqJUhbe7g3o3BecaqCJYUNLswGBhQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "got": "^9.2.2", + "map-limit": "0.0.1", + "minimist": "^1.2.0", + "new-array": "^1.0.0" + }, + "bin": { + "nice-color-palettes": "bin/index.js" + } + }, + "node_modules/nipplejs": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/nipplejs/-/nipplejs-0.10.2.tgz", + "integrity": "sha512-XGxFY8C2DOtobf1fK+MXINTzkkXJLjZDDpfQhOUZf4TSytbc9s4bmA0lB9eKKM8iDivdr9NQkO7DpIQfsST+9g==", + "license": "MIT" + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-releases": { + "version": "2.0.26", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.26.tgz", + "integrity": "sha512-S2M9YimhSjBSvYnlr5/+umAnPHE++ODwt5e2Ij6FoX45HA/s4vHdkDx1eax2pAPeAOqu4s9b7ppahsyEFdVqQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/nwsapi": { + "version": "2.2.22", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.22.tgz", + "integrity": "sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "integrity": "sha512-6vaNInhu+CHxtONf3zw3vq4SP2DOQhjBvIa3rNcG0+P7eKWlYH6Peu7rHizSloRU2EwMz6GraLieis9Ac9+p1w==", + "license": "ISC", + "peer": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-bmfont-ascii": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz", + "integrity": "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA==", + "license": "MIT", + "peer": true + }, + "node_modules/parse-bmfont-binary": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz", + "integrity": "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA==", + "license": "MIT", + "peer": true + }, + "node_modules/parse-bmfont-xml": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.6.tgz", + "integrity": "sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==", + "license": "MIT", + "peer": true, + "dependencies": { + "xml-parse-from-string": "^1.0.0", + "xml2js": "^0.5.0" + } + }, + "node_modules/parse-headers": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.6.tgz", + "integrity": "sha512-Tz11t3uKztEW5FEVZnj1ox8GKblWn+PvHY9TmJV5Mll2uHEwRdR/5Li1OlXoECjLYkApdhWy44ocONwXLiKO5A==", + "license": "MIT", + "peer": true + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/perfect-freehand": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/perfect-freehand/-/perfect-freehand-1.2.2.tgz", + "integrity": "sha512-eh31l019WICQ03pkF3FSzHxB8n07ItqIQ++G5UV8JX0zVOXzgTGCqnRR0jJ2h9U8/2uW4W4mtGJELt9kEV0CFQ==", + "license": "MIT" + }, + "node_modules/phin": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/phin/-/phin-3.7.1.tgz", + "integrity": "sha512-GEazpTWwTZaEQ9RhL7Nyz0WwqilbqgLahDM3D0hxWwmVDI52nXEybHqiN6/elwpkJBhcuj+WbBu+QfT0uhPGfQ==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "MIT", + "peer": true, + "dependencies": { + "centra": "^2.7.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/polished": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/polished/-/polished-4.3.1.tgz", + "integrity": "sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.17.8" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/preact": { + "version": "10.27.2", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.27.2.tgz", + "integrity": "sha512-5SYSgFKSyhCbk6SrXyMpqjb5+MQBgfvEKE/OC+PujcY34sOpqtr+0AZQtPYx5IA6VxynQ7rUPCtKzyovpj9Bpg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/proxy-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/proxy-compare/-/proxy-compare-3.0.1.tgz", + "integrity": "sha512-V9plBAt3qjMlS1+nC8771KNf6oJ12gExvaxnNzN/9yVRLdTv/lc+oJlnSzrdYDAvBfTStPCoiaCOTmTs0adv7Q==", + "license": "MIT" + }, + "node_modules/proxy-memoize": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/proxy-memoize/-/proxy-memoize-3.0.1.tgz", + "integrity": "sha512-VDdG/VYtOgdGkWJx7y0o7p+zArSf2383Isci8C+BP3YXgMYDoPd3cCBjw0JdWb6YBb9sFiOPbAADDVTPJnh+9g==", + "license": "MIT", + "dependencies": { + "proxy-compare": "^3.0.0" + } + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "license": "MIT", + "peer": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/quad-indices": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/quad-indices/-/quad-indices-2.0.1.tgz", + "integrity": "sha512-6jtmCsEbGAh5npThXrBaubbTjPcF0rMbn57XCJVI7LkW8PUT56V+uIrRCCWCn85PSgJC9v8Pm5tnJDwmOBewvA==", + "license": "MIT", + "peer": true, + "dependencies": { + "an-array": "^1.0.0", + "dtype": "^2.0.0", + "is-buffer": "^1.0.2" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-force-graph": { + "version": "1.48.1", + "resolved": "https://registry.npmjs.org/react-force-graph/-/react-force-graph-1.48.1.tgz", + "integrity": "sha512-9TdtUM5GNacpurCj2UdSw5h/QO47tDCrU1pHJ7z7xmdPCbtVtsXCftxinQoX9n0eHR3UocxaQiwUNbSEZ3vxnQ==", + "license": "MIT", + "dependencies": { + "3d-force-graph": "^1.79", + "3d-force-graph-ar": "^1.10", + "3d-force-graph-vr": "^3.1", + "force-graph": "^1.51", + "prop-types": "15", + "react-kapsule": "^2.5" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-hotkeys-hook": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/react-hotkeys-hook/-/react-hotkeys-hook-5.2.1.tgz", + "integrity": "sha512-xbKh6zJxd/vJHT4Bw4+0pBD662Fk20V+VFhLqciCg+manTVO4qlqRqiwFOYelfHN9dBvWj9vxaPkSS26ZSIJGg==", + "license": "MIT", + "workspaces": [ + "packages/*" + ], + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/react-icons": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", + "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/react-kapsule": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/react-kapsule/-/react-kapsule-2.5.7.tgz", + "integrity": "sha512-kifAF4ZPD77qZKc4CKLmozq6GY1sBzPEJTIJb0wWFK6HsePJatK3jXplZn2eeAt3x67CDozgi7/rO8fNQ/AL7A==", + "license": "MIT", + "dependencies": { + "jerrypick": "^1.1.1" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "react": ">=16.13.1" + } + }, + "node_modules/react-markdown-it": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/react-markdown-it/-/react-markdown-it-1.0.2.tgz", + "integrity": "sha512-Bzo/9UCCxlL2D7rYiVlxEqiOU66mqmLTzjxN0JLlioEhZhp7amzSq1YNS0+Jf0YKQmpBb5rfI9nh5s3wBsKnww==", + "license": "MIT", + "dependencies": { + "markdown-it": "^4.4.0", + "strip-indent": "^1.0.1" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-resize-detector": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-12.3.0.tgz", + "integrity": "sha512-mIDOVrTHKGnKe6qEUWi8dFdfHM5CPyTOpqoHctdMQf89Ljm/0qqDIzkP3vTRZZJi9/raaMiRxDEOqO4you5x+A==", + "license": "MIT", + "dependencies": { + "es-toolkit": "^1.39.6" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-router": { + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.4.tgz", + "integrity": "sha512-SD3G8HKviFHg9xj7dNODUKDFgpG4xqD5nhyd0mYoB5iISepuZAvzSr8ywxgxKJ52yRzf/HWtVHc9AWwoTbljvA==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/reactflow": { + "version": "11.11.4", + "resolved": "https://registry.npmjs.org/reactflow/-/reactflow-11.11.4.tgz", + "integrity": "sha512-70FOtJkUWH3BAOsN+LU9lCrKoKbtOPnz2uq0CV2PLdNSwxTXOhCbsZr50GmZ+Rtw3jx8Uv7/vBFtCGixLfd4Og==", + "license": "MIT", + "dependencies": { + "@reactflow/background": "11.3.14", + "@reactflow/controls": "11.2.14", + "@reactflow/core": "11.11.4", + "@reactflow/minimap": "11.7.14", + "@reactflow/node-resizer": "2.2.14", + "@reactflow/node-toolbar": "1.3.14" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/redent/node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "lowercase-keys": "^1.0.0" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz", + "integrity": "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.52.5", + "@rollup/rollup-android-arm64": "4.52.5", + "@rollup/rollup-darwin-arm64": "4.52.5", + "@rollup/rollup-darwin-x64": "4.52.5", + "@rollup/rollup-freebsd-arm64": "4.52.5", + "@rollup/rollup-freebsd-x64": "4.52.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", + "@rollup/rollup-linux-arm-musleabihf": "4.52.5", + "@rollup/rollup-linux-arm64-gnu": "4.52.5", + "@rollup/rollup-linux-arm64-musl": "4.52.5", + "@rollup/rollup-linux-loong64-gnu": "4.52.5", + "@rollup/rollup-linux-ppc64-gnu": "4.52.5", + "@rollup/rollup-linux-riscv64-gnu": "4.52.5", + "@rollup/rollup-linux-riscv64-musl": "4.52.5", + "@rollup/rollup-linux-s390x-gnu": "4.52.5", + "@rollup/rollup-linux-x64-gnu": "4.52.5", + "@rollup/rollup-linux-x64-musl": "4.52.5", + "@rollup/rollup-openharmony-arm64": "4.52.5", + "@rollup/rollup-win32-arm64-msvc": "4.52.5", + "@rollup/rollup-win32-ia32-msvc": "4.52.5", + "@rollup/rollup-win32-x64-gnu": "4.52.5", + "@rollup/rollup-win32-x64-msvc": "4.52.5", + "fsevents": "~2.3.2" + } + }, + "node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sass": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.93.2.tgz", + "integrity": "sha512-t+YPtOQHpGW1QWsh1CHQ5cPIr9lbbGZLZnbihP/D/qZj/yuV68m8qarcV17nvkOX81BCrvzAlq2klCQFZghyTg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/sass-embedded": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.93.2.tgz", + "integrity": "sha512-FvQdkn2dZ8DGiLgi0Uf4zsj7r/BsiLImNa5QJ10eZalY6NfZyjrmWGFcuCN5jNwlDlXFJnftauv+UtvBKLvepQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bufbuild/protobuf": "^2.5.0", + "buffer-builder": "^0.2.0", + "colorjs.io": "^0.5.0", + "immutable": "^5.0.2", + "rxjs": "^7.4.0", + "supports-color": "^8.1.1", + "sync-child-process": "^1.0.2", + "varint": "^6.0.0" + }, + "bin": { + "sass": "dist/bin/sass.js" + }, + "engines": { + "node": ">=16.0.0" + }, + "optionalDependencies": { + "sass-embedded-all-unknown": "1.93.2", + "sass-embedded-android-arm": "1.93.2", + "sass-embedded-android-arm64": "1.93.2", + "sass-embedded-android-riscv64": "1.93.2", + "sass-embedded-android-x64": "1.93.2", + "sass-embedded-darwin-arm64": "1.93.2", + "sass-embedded-darwin-x64": "1.93.2", + "sass-embedded-linux-arm": "1.93.2", + "sass-embedded-linux-arm64": "1.93.2", + "sass-embedded-linux-musl-arm": "1.93.2", + "sass-embedded-linux-musl-arm64": "1.93.2", + "sass-embedded-linux-musl-riscv64": "1.93.2", + "sass-embedded-linux-musl-x64": "1.93.2", + "sass-embedded-linux-riscv64": "1.93.2", + "sass-embedded-linux-x64": "1.93.2", + "sass-embedded-unknown-all": "1.93.2", + "sass-embedded-win32-arm64": "1.93.2", + "sass-embedded-win32-x64": "1.93.2" + } + }, + "node_modules/sass-embedded-all-unknown": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-all-unknown/-/sass-embedded-all-unknown-1.93.2.tgz", + "integrity": "sha512-GdEuPXIzmhRS5J7UKAwEvtk8YyHQuFZRcpnEnkA3rwRUI27kwjyXkNeIj38XjUQ3DzrfMe8HcKFaqWGHvblS7Q==", + "cpu": [ + "!arm", + "!arm64", + "!riscv64", + "!x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "sass": "1.93.2" + } + }, + "node_modules/sass-embedded-android-arm": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-android-arm/-/sass-embedded-android-arm-1.93.2.tgz", + "integrity": "sha512-I8bpO8meZNo5FvFx5FIiE7DGPVOYft0WjuwcCCdeJ6duwfkl6tZdatex1GrSigvTsuz9L0m4ngDcX/Tj/8yMow==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-android-arm64": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.93.2.tgz", + "integrity": "sha512-346f4iVGAPGcNP6V6IOOFkN5qnArAoXNTPr5eA/rmNpeGwomdb7kJyQ717r9rbJXxOG8OAAUado6J0qLsjnjXQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-android-riscv64": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-android-riscv64/-/sass-embedded-android-riscv64-1.93.2.tgz", + "integrity": "sha512-hSMW1s4yJf5guT9mrdkumluqrwh7BjbZ4MbBW9tmi1DRDdlw1Wh9Oy1HnnmOG8x9XcI1qkojtPL6LUuEJmsiDg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-android-x64": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-android-x64/-/sass-embedded-android-x64-1.93.2.tgz", + "integrity": "sha512-JqktiHZduvn+ldGBosE40ALgQ//tGCVNAObgcQ6UIZznEJbsHegqStqhRo8UW3x2cgOO2XYJcrInH6cc7wdKbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-darwin-arm64": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.93.2.tgz", + "integrity": "sha512-qI1X16qKNeBJp+M/5BNW7v/JHCDYWr1/mdoJ7+UMHmP0b5AVudIZtimtK0hnjrLnBECURifd6IkulybR+h+4UA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-darwin-x64": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.93.2.tgz", + "integrity": "sha512-4KeAvlkQ0m0enKUnDGQJZwpovYw99iiMb8CTZRSsQm8Eh7halbJZVmx67f4heFY/zISgVOCcxNg19GrM5NTwtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-arm": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.93.2.tgz", + "integrity": "sha512-N3+D/ToHtzwLDO+lSH05Wo6/KRxFBPnbjVHASOlHzqJnK+g5cqex7IFAp6ozzlRStySk61Rp6d+YGrqZ6/P0PA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-arm64": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.93.2.tgz", + "integrity": "sha512-9ftX6nd5CsShJqJ2WRg+ptaYvUW+spqZfJ88FbcKQBNFQm6L87luj3UI1rB6cP5EWrLwHA754OKxRJyzWiaN6g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-musl-arm": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.93.2.tgz", + "integrity": "sha512-XBTvx66yRenvEsp3VaJCb3HQSyqCsUh7R+pbxcN5TuzueybZi0LXvn9zneksdXcmjACMlMpIVXi6LyHPQkYc8A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-musl-arm64": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.93.2.tgz", + "integrity": "sha512-+3EHuDPkMiAX5kytsjEC1bKZCawB9J6pm2eBIzzLMPWbf5xdx++vO1DpT7hD4bm4ZGn0eVHgSOKIfP6CVz6tVg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-musl-riscv64": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-riscv64/-/sass-embedded-linux-musl-riscv64-1.93.2.tgz", + "integrity": "sha512-0sB5kmVZDKTYzmCSlTUnjh6mzOhzmQiW/NNI5g8JS4JiHw2sDNTvt1dsFTuqFkUHyEOY3ESTsfHHBQV8Ip4bEA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-musl-x64": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.93.2.tgz", + "integrity": "sha512-t3ejQ+1LEVuHy7JHBI2tWHhoMfhedUNDjGJR2FKaLgrtJntGnyD1RyX0xb3nuqL/UXiEAtmTmZY+Uh3SLUe1Hg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-riscv64": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-riscv64/-/sass-embedded-linux-riscv64-1.93.2.tgz", + "integrity": "sha512-e7AndEwAbFtXaLy6on4BfNGTr3wtGZQmypUgYpSNVcYDO+CWxatKVY4cxbehMPhxG9g5ru+eaMfynvhZt7fLaA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-x64": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.93.2.tgz", + "integrity": "sha512-U3EIUZQL11DU0xDDHXexd4PYPHQaSQa2hzc4EzmhHqrAj+TyfYO94htjWOd+DdTPtSwmLp+9cTWwPZBODzC96w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-unknown-all": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-unknown-all/-/sass-embedded-unknown-all-1.93.2.tgz", + "integrity": "sha512-7VnaOmyewcXohiuoFagJ3SK5ddP9yXpU0rzz+pZQmS1/+5O6vzyFCUoEt3HDRaLctH4GT3nUGoK1jg0ae62IfQ==", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "!android", + "!darwin", + "!linux", + "!win32" + ], + "dependencies": { + "sass": "1.93.2" + } + }, + "node_modules/sass-embedded-win32-arm64": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.93.2.tgz", + "integrity": "sha512-Y90DZDbQvtv4Bt0GTXKlcT9pn4pz8AObEjFF8eyul+/boXwyptPZ/A1EyziAeNaIEIfxyy87z78PUgCeGHsx3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-win32-x64": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.93.2.tgz", + "integrity": "sha512-BbSucRP6PVRZGIwlEBkp+6VQl2GWdkWFMN+9EuOTPrLxCJZoq+yhzmbjspd3PeM8+7WJ7AdFu/uRYdO8tor1iQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "license": "ISC", + "peer": true + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/streamsaver": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/streamsaver/-/streamsaver-2.0.6.tgz", + "integrity": "sha512-LK4e7TfCV8HzuM0PKXuVUfKyCB1FtT9L0EGxsFk5Up8njj0bXK8pJM9+Wq2Nya7/jslmCQwRK39LFm55h7NBTw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + } + ], + "license": "MIT" + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha512-I5iQq6aFMM62fBEAIB/hXzwJD6EEZ0xEGCX2t7oXqaKPIRgt4WruAQ285BISgdkP+HLGWyeGmNJcpIwFeRYRUA==", + "license": "MIT", + "dependencies": { + "get-stdin": "^4.0.1" + }, + "bin": { + "strip-indent": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-literal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, + "node_modules/super-animejs": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/super-animejs/-/super-animejs-3.1.0.tgz", + "integrity": "sha512-6MFAFJDRuvwkovxQZPruuyHinTa4rgj4hNLOndjcYYhZLckoXtVRY9rJPuq8p6c/tgZJrFYEAYAfJ2/hhNtUCA==", + "license": "MIT", + "peer": true + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/sync-child-process": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/sync-child-process/-/sync-child-process-1.0.2.tgz", + "integrity": "sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sync-message-port": "^1.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/sync-message-port": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sync-message-port/-/sync-message-port-1.1.3.tgz", + "integrity": "sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/three": { + "version": "0.180.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.180.0.tgz", + "integrity": "sha512-o+qycAMZrh+TsE01GqWUxUIKR1AL0S8pq7zDkYOQw8GqfX8b8VoCKYUoHbhiX5j+7hr8XsuHDVU6+gkQJQKg9w==", + "license": "MIT" + }, + "node_modules/three-bmfont-text": { + "version": "3.0.0", + "resolved": "git+ssh://git@github.com/dmarcos/three-bmfont-text.git#eed4878795be9b3e38cf6aec6b903f56acd1f695", + "integrity": "sha512-JkvSn4FoichG1tsUJ9SRN452+jvf7RDf/M/Y4j5G7LfaAWmrhGQQbjlCJLJft6sSbjqqB96R1OKvyZmVZff7ew==", + "license": "MIT", + "peer": true, + "dependencies": { + "array-shuffle": "^1.0.1", + "layout-bmfont-text": "^1.2.0", + "nice-color-palettes": "^3.0.0", + "quad-indices": "^2.0.1" + } + }, + "node_modules/three-forcegraph": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/three-forcegraph/-/three-forcegraph-1.43.0.tgz", + "integrity": "sha512-1AqLmTCjjjwcuccObG96fCxiRnNJjCLdA5Mozl7XK+ROwTJ6QEJPo2XJ6uxWeuAmPE7ukMhgv4lj28oZSfE4wg==", + "license": "MIT", + "dependencies": { + "accessor-fn": "1", + "d3-array": "1 - 3", + "d3-force-3d": "2 - 3", + "d3-scale": "1 - 4", + "d3-scale-chromatic": "1 - 3", + "data-bind-mapper": "1", + "kapsule": "^1.16", + "ngraph.forcelayout": "3", + "ngraph.graph": "20", + "tinycolor2": "1" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "three": ">=0.118.3" + } + }, + "node_modules/three-pathfinding": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/three-pathfinding/-/three-pathfinding-1.3.0.tgz", + "integrity": "sha512-LKxMI3/YqdMYvt6AdE2vB6s5ueDFczt/DWoxhtPNgRsH6E0D8LMYQxz+eIrmKo0MQpDvMVzXYUMBk+b86+k97w==", + "license": "MIT", + "peerDependencies": { + "three": "0.x.x" + } + }, + "node_modules/three-render-objects": { + "version": "1.40.4", + "resolved": "https://registry.npmjs.org/three-render-objects/-/three-render-objects-1.40.4.tgz", + "integrity": "sha512-Ukpu1pei3L5r809izvjsZxwuRcYLiyn6Uvy3lZ9bpMTdvj3i6PeX6w++/hs2ZS3KnEzGjb6YvTvh4UQuwHTDJg==", + "license": "MIT", + "dependencies": { + "@tweenjs/tween.js": "18 - 25", + "accessor-fn": "1", + "float-tooltip": "^1.7", + "kapsule": "^1.16", + "polished": "4" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "three": ">=0.168" + } + }, + "node_modules/three-spritetext": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/three-spritetext/-/three-spritetext-1.10.0.tgz", + "integrity": "sha512-t08iP1FCU1lQh8T5MmCpdijKgas8GDHJE0LqMGBuVu3xqMMpFnEZhTlih7FlxLPQizHIGoumUSpfOlY1GO/Tgg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "three": ">=0.86.0" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", + "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.86" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.2.tgz", + "integrity": "sha512-vbw8bOmiuYNdzzV3lsiWv6sRwjyuKJMQqWulBOU7M0RrxedXledX8G8kBbQeiOYDnTfiXz0Y4081E1QMNB6iQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.46.2", + "@typescript-eslint/parser": "8.46.2", + "@typescript-eslint/typescript-estree": "8.46.2", + "@typescript-eslint/utils": "8.46.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", + "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uqr": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/uqr/-/uqr-0.1.2.tgz", + "integrity": "sha512-MJu7ypHq6QasgF5YRTjqscSzQp/W11zoUk6kvmlH+fmWEs63Y0Eib13hYFwAzagRJcVY8WVnlV+eBDUGMJ5IbA==", + "license": "MIT" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "prepend-http": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/validate.io-array": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/validate.io-array/-/validate.io-array-1.0.6.tgz", + "integrity": "sha512-DeOy7CnPEziggrOO5CZhVKJw6S3Yi7e9e65R1Nl/RTN1vTQKnzjfvks0/8kQ40FP/dsjRAOd4hxmJ7uLa6vxkg==", + "license": "MIT" + }, + "node_modules/validate.io-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/validate.io-function/-/validate.io-function-1.0.2.tgz", + "integrity": "sha512-LlFybRJEriSuBnUhQyG5bwglhh50EpTL2ul23MPIuR1odjO7XaMLFV8vHGwp7AZciFxtYOeiSCT5st+XSPONiQ==" + }, + "node_modules/varint": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz", + "integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vitest": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/pretty-format": "^3.2.4", + "@vitest/runner": "3.2.4", + "@vitest/snapshot": "3.2.4", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.4", + "@vitest/ui": "3.2.4", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/word-wrapper": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/word-wrapper/-/word-wrapper-1.0.7.tgz", + "integrity": "sha512-VOPBFCm9b6FyYKQYfn9AVn2dQvdR/YOVFV6IBRA1TBMJWKffvhEX1af6FMGrttILs2Q9ikCRhLqkbY2weW6dOQ==", + "license": "MIT", + "peer": true + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC", + "peer": true + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xhr": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz", + "integrity": "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==", + "license": "MIT", + "peer": true, + "dependencies": { + "global": "~4.4.0", + "is-function": "^1.0.1", + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xml-parse-from-string": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz", + "integrity": "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==", + "license": "MIT", + "peer": true + }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "license": "MIT", + "peer": true, + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zustand": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.8.tgz", + "integrity": "sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + } + } +} diff --git a/ai-context/workbench-ui/package.json b/ai-context/workbench-ui/package.json new file mode 100644 index 00000000..61399b26 --- /dev/null +++ b/ai-context/workbench-ui/package.json @@ -0,0 +1,67 @@ +{ + "name": "vite-ts", + "private": true, + "version": "1.0.2", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "lint": "eslint .", + "prettify": "prettier src --write", + "test": "vitest run", + "test:ui": "vitest --ui", + "test:run": "vitest", + "test:coverage": "vitest run --coverage" + }, + "dependencies": { + "@chakra-ui/react": "^3.19.1", + "@emotion/styled": "^11.13.0", + "@msgpack/msgpack": "^3.1.1", + "@tanstack/react-query": "^5.80.3", + "@tanstack/react-table": "^8.21.3", + "@trustgraph/client": "github:trustgraph-ai/trustgraph-client#master", + "@trustgraph/react-provider": "github:trustgraph-ai/trustgraph-react-provider#master", + "@trustgraph/react-state": "github:trustgraph-ai/trustgraph-react-state#d24cdec0", + "@types/dagre": "^0.7.53", + "compute-cosine-similarity": "^1.1.0", + "dagre": "^0.8.5", + "jszip": "^3.10.1", + "lucide-react": "^0.511.0", + "n3": "^1.26.0", + "next-themes": "^0.4.6", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-force-graph": ">=1.47.0", + "react-hotkeys-hook": "^5.1.0", + "react-icons": "^5.5.0", + "react-markdown-it": "^1.0.2", + "react-resize-detector": "^12.0.2", + "react-router": "^7.6.0", + "reactflow": "^11.11.4", + "streamsaver": "^2.0.6", + "three-spritetext": "^1.9.3", + "uuid": "^11.0.3", + "zustand": "^5.0.0-rc.2" + }, + "devDependencies": { + "@eslint/js": "^9.9.0", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/react": "^16.3.0", + "@testing-library/user-event": "^14.6.1", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react": "^4.3.1", + "eslint": "^9.9.0", + "eslint-plugin-react-hooks": "^5.1.0-rc.0", + "eslint-plugin-react-refresh": "^0.4.9", + "globals": "^15.9.0", + "jsdom": "^26.1.0", + "prettier": "3.5.3", + "sass-embedded": "^1.77.8", + "typescript": "^5.5.3", + "typescript-eslint": "^8.0.1", + "vite": "^6.3.4", + "vitest": "^3.2.4" + } +} diff --git a/ai-context/workbench-ui/palette.svg b/ai-context/workbench-ui/palette.svg new file mode 100644 index 00000000..dc1b87e8 --- /dev/null +++ b/ai-context/workbench-ui/palette.svg @@ -0,0 +1,8 @@ + + + + ,,,, + Exported from Coolors.co + https://coolors.co/250219-83b7ce-417094-3f1d44-dac0ec + + \ No newline at end of file diff --git a/ai-context/workbench-ui/public/tg.svg b/ai-context/workbench-ui/public/tg.svg new file mode 100644 index 00000000..7123ae45 --- /dev/null +++ b/ai-context/workbench-ui/public/tg.svg @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/ai-context/workbench-ui/pulumi/Pulumi.dev.yaml b/ai-context/workbench-ui/pulumi/Pulumi.dev.yaml new file mode 100644 index 00000000..431ff4c1 --- /dev/null +++ b/ai-context/workbench-ui/pulumi/Pulumi.dev.yaml @@ -0,0 +1,14 @@ +encryptionsalt: v1:vQGk98eEeYI=:v1:tHg+f1b66tEydgA9:J1RGVNI0FssyjSXVhcKU7bfBofNFTg== +config: + config-ui:artifact-name: config-ui-dev + config-ui:artifact-repo: us-central1-docker.pkg.dev/trustgraph-demo/config-ui-dev + config-ui:artifact-repo-region: us-central1 + config-ui:cloud-run-region: us-central1 + config-ui:domain: demo.trustgraph.ai + config-ui:environment: dev + config-ui:gcp-project: trustgraph-demo + config-ui:gcp-region: us-central1 + config-ui:hostname: dev.config-ui.demo.trustgraph.ai + config-ui:managed-zone: demo + config-ui:max-scale: "2" + config-ui:min-scale: "0" diff --git a/ai-context/workbench-ui/pulumi/Pulumi.prod.yaml b/ai-context/workbench-ui/pulumi/Pulumi.prod.yaml new file mode 100644 index 00000000..96dfcf00 --- /dev/null +++ b/ai-context/workbench-ui/pulumi/Pulumi.prod.yaml @@ -0,0 +1,14 @@ +encryptionsalt: v1:vQGk98eEeYI=:v1:tHg+f1b66tEydgA9:J1RGVNI0FssyjSXVhcKU7bfBofNFTg== +config: + config-ui:artifact-name: config-ui-prod + config-ui:artifact-repo: us-central1-docker.pkg.dev/trustgraph-demo/config-ui-prod + config-ui:artifact-repo-region: us-central1 + config-ui:cloud-run-region: us-central1 + config-ui:domain: demo.trustgraph.ai + config-ui:environment: prod + config-ui:gcp-project: trustgraph-demo + config-ui:gcp-region: us-central1 + config-ui:hostname: config-ui.demo.trustgraph.ai + config-ui:managed-zone: demo + config-ui:max-scale: "2" + config-ui:min-scale: "0" diff --git a/ai-context/workbench-ui/pulumi/Pulumi.yaml b/ai-context/workbench-ui/pulumi/Pulumi.yaml new file mode 100644 index 00000000..53285728 --- /dev/null +++ b/ai-context/workbench-ui/pulumi/Pulumi.yaml @@ -0,0 +1,3 @@ +name: config-ui +runtime: nodejs +description: Config UI diff --git a/ai-context/workbench-ui/pulumi/index.ts b/ai-context/workbench-ui/pulumi/index.ts new file mode 100644 index 00000000..e393802a --- /dev/null +++ b/ai-context/workbench-ui/pulumi/index.ts @@ -0,0 +1,348 @@ + +import * as pulumi from "@pulumi/pulumi"; +import * as gcp from "@pulumi/gcp"; +import { local } from "@pulumi/command"; +//import * as fs from 'fs'; + +const cfg = new pulumi.Config(); + +function get(tag : string) { + + const val = cfg.get(tag); + + if (!val) { + console.log("ERROR: The '" + tag + "' config is mandatory"); + throw "The '" + tag + "' config is mandatory"; + } + + return val; + +} + +const imageVersion = process.env.IMAGE_VERSION; +if (!imageVersion) + throw Error("IMAGE_VERSION not defined"); + +const repo = get("artifact-repo"); +const artifactRepoRegion = get("artifact-repo-region"); +const artifactName = get("artifact-name"); +const hostname = get("hostname"); +const managedZone = get("managed-zone"); +const project = get("gcp-project"); +const region = get("gcp-region"); +const cloudRunRegion = get("cloud-run-region"); +const environment = get("environment"); +//const domain = get("domain"); +const minScale = get("min-scale"); +const maxScale = get("max-scale"); + +const provider = new gcp.Provider( + "gcp", + { + project: project, + region: region, + } +); + +const artifactRepo = new gcp.artifactregistry.Repository( + "artifact-repo", + { + description: "repository for " + environment, + format: "DOCKER", + location: artifactRepoRegion, + repositoryId: artifactName, + cleanupPolicies: [ + { + id: "keep-minimum-versions", + action: "KEEP", + mostRecentVersions: { + keepCount: 5, + }, + } + ], + }, + { + provider: provider, + } +); + +const localImageName = "localhost/config-ui:" + imageVersion; + +const imageName = repo + "/config-ui:" + imageVersion; + +const taggedImage = new local.Command( + "podman-tag-command", + { + create: "podman tag " + localImageName + " " + imageName, + } +); + +const image = new local.Command( + "podman-push-command", + { + create: "podman push " + imageName, + }, + { + dependsOn: [taggedImage, artifactRepo], + } +); + +const svcAccount = new gcp.serviceaccount.Account( + "service-account", + { + accountId: "config-ui-" + environment, + displayName: "Config UI", + description: "Config UI", + }, + { + provider: provider, + } +); + +/* + +const vertexAiUserMember = new gcp.projects.IAMMember( + "vertexai-user-role", + { + member: svcAccount.email.apply(x => "serviceAccount:" + x), + project: project, + role: "roles/aiplatform.admin", + }, + { + provider: provider, + } +); + +*/ + +const service = new gcp.cloudrun.Service( + "service", + { + name: "config-ui-" + environment, + location: cloudRunRegion, + template: { + metadata: { + labels: { + version: "v" + imageVersion.replace(/\./g, "-"), + }, + annotations: { + + // Scale attributes + "autoscaling.knative.dev/minScale": minScale, + "autoscaling.knative.dev/maxScale": maxScale, + + // 2nd generation. Need to specify at least 512MB RAM. + // Going back to gen1 because faster cold starts + "run.googleapis.com/execution-environment": "gen1", + + } + }, + spec: { + containerConcurrency: 100, + timeoutSeconds: 300, + serviceAccountName: svcAccount.email, + containers: [ + { + image: imageName, +// commands: [ +// "config-ui" +// ], + ports: [ + { + "name": "http1", // Must be http1 or h2c. + "containerPort": 8080, + } + ], + resources: { + limits: { + cpu: "1000m", + memory: "512Mi", + } + }, + } + ], + }, + }, + }, + { + provider: provider, + dependsOn: [image], + } +); + +const allUsersPolicy = gcp.organizations.getIAMPolicy( + { + bindings: [{ + role: "roles/run.invoker", + members: ["allUsers"], + }], + }, + { + provider: provider, + } +); + +/*const _noAuthPolicy =*/ new gcp.cloudrun.IamPolicy( + "no-auth-policy", + { + location: service.location, + project: service.project, + service: service.name, + policyData: allUsersPolicy.then(pol => pol.policyData), + }, + { + provider: provider, + } +); + +//////////////////////////////////////////////////////////////////////////// + +const domainMapping = new gcp.cloudrun.DomainMapping( + "domain-mapping", + { + name: hostname, + location: cloudRunRegion, + metadata: { + namespace: project, + }, + spec: { + routeName: service.name, + } + }, + { + provider: provider + } +); + +const zone = gcp.dns.getManagedZoneOutput( + { + name: managedZone, + }, + { + provider: provider, + } +); + +//////////////////////////////////////////////////////////////////////////// + +domainMapping.statuses.apply( + ss => ss[0].resourceRecords +).apply( + rrs => { + if (rrs) { + + let mapping : { [k : string] : string[] } = {}; + + for(let i = 0; i < rrs.length; i++) { + if (rrs[i].rrdata) { + + const rr = rrs[i].rrdata; + const tp = rrs[i].type; + + if (!rr || !tp) continue; + + if (mapping[tp]) + mapping = { + ...mapping, + [tp]: [...mapping[tp], rr], + }; + else + mapping = { + ...mapping, + [tp]: [rr], + }; + + } + } + + for (const tp in mapping) { + + new gcp.dns.RecordSet( + "resource-record-" + tp, + { + name: hostname + ".", + managedZone: zone.name, + type: tp, + ttl: 300, + rrdatas: mapping[tp], + }, + { + provider: provider, + } + ); + + } + + } + + } +); + +//////////////////////////////////////////////////////////////////////////// + +const serviceMon = new gcp.monitoring.GenericService( + "service-monitoring", + { + basicService: { + serviceLabels: { + service_name: service.name, + location: cloudRunRegion, + }, + serviceType: "CLOUD_RUN", + }, + displayName: "Config UI service (" + environment + ")", + serviceId: "config-ui-service-" + environment + "-mon", + userLabels: { + "service": service.name, + "application": "config-ui", + "environment": environment, + }, + }, + { + provider: provider, + } +); + +new gcp.monitoring.Slo( + "latency-slo", + { + service: serviceMon.serviceId, + sloId: "config-ui-service-" + environment + "-latency-slo", + displayName: "Config UI latency (" + environment + ")", + goal: 0.95, + rollingPeriodDays: 5, + basicSli: { + latency: { + threshold: "2s" + } + }, + }, + { + provider: provider, + } +); + +new gcp.monitoring.Slo( + "availability-slo", + { + service: serviceMon.serviceId, + sloId: "config-ui-service-" + environment + "-availability-slo", + displayName: "Config UI availability (" + environment + ")", + goal: 0.95, + rollingPeriodDays: 5, + windowsBasedSli: { + windowPeriod: "3600s", + goodTotalRatioThreshold: { + basicSliPerformance: { + availability: { + } + }, + threshold: 0.9, + } + } + }, + { + provider: provider, + } +); + diff --git a/ai-context/workbench-ui/pulumi/package-lock.json b/ai-context/workbench-ui/pulumi/package-lock.json new file mode 100644 index 00000000..410840f8 --- /dev/null +++ b/ai-context/workbench-ui/pulumi/package-lock.json @@ -0,0 +1,3625 @@ +{ + "name": "safety-ai", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "safety-ai", + "dependencies": { + "@pulumi/command": "^0.7.2", + "@pulumi/gcp": "^6.58.0", + "@pulumi/pulumi": "^3.0.0" + }, + "devDependencies": { + "@types/node": "^16" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.12.0.tgz", + "integrity": "sha512-eWdP97A6xKtZXVP/ze9y8zYRB2t6ugQAuLXFuZXAsyqmyltaAjl4yPkmIfc0wuTFJMOUF1AdvIFQCL7fMtaX6g==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.7.13", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/string-locale-compare": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz", + "integrity": "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==", + "license": "ISC" + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@logdna/tail-file": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@logdna/tail-file/-/tail-file-2.2.0.tgz", + "integrity": "sha512-XGSsWDweP80Fks16lwkAUIr54ICyBs6PsI4mpfTLQaWgEJRtY9xEV+PeyDpJ+sJEGZxqINlpmAwe/6tS1pP8Ng==", + "license": "SEE LICENSE IN LICENSE", + "engines": { + "node": ">=10.3.0" + } + }, + "node_modules/@npmcli/agent": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz", + "integrity": "sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==", + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-7.5.4.tgz", + "integrity": "sha512-nWtIc6QwwoUORCRNzKx4ypHqCk3drI+5aeYdMTQQiRCcn4lOOgfQh7WyZobGYTxXPSq1VwV53lkpN/BRlRk08g==", + "license": "ISC", + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/fs": "^3.1.1", + "@npmcli/installed-package-contents": "^2.1.0", + "@npmcli/map-workspaces": "^3.0.2", + "@npmcli/metavuln-calculator": "^7.1.1", + "@npmcli/name-from-folder": "^2.0.0", + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/package-json": "^5.1.0", + "@npmcli/query": "^3.1.0", + "@npmcli/redact": "^2.0.0", + "@npmcli/run-script": "^8.1.0", + "bin-links": "^4.0.4", + "cacache": "^18.0.3", + "common-ancestor-path": "^1.0.1", + "hosted-git-info": "^7.0.2", + "json-parse-even-better-errors": "^3.0.2", + "json-stringify-nice": "^1.1.4", + "lru-cache": "^10.2.2", + "minimatch": "^9.0.4", + "nopt": "^7.2.1", + "npm-install-checks": "^6.2.0", + "npm-package-arg": "^11.0.2", + "npm-pick-manifest": "^9.0.1", + "npm-registry-fetch": "^17.0.1", + "pacote": "^18.0.6", + "parse-conflict-json": "^3.0.0", + "proc-log": "^4.2.0", + "proggy": "^2.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^3.0.1", + "read-package-json-fast": "^3.0.2", + "semver": "^7.3.7", + "ssri": "^10.0.6", + "treeverse": "^3.0.0", + "walk-up-path": "^3.0.1" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/fs": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", + "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/git": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-5.0.8.tgz", + "integrity": "sha512-liASfw5cqhjNW9UFd+ruwwdEf/lbOAQjLL2XY2dFW/bkJheXDYZgOyul/4gVvEV4BWkTXjYGmDqMw9uegdbJNQ==", + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^7.0.0", + "ini": "^4.1.3", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^9.0.0", + "proc-log": "^4.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/git/node_modules/ini": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/installed-package-contents": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.1.0.tgz", + "integrity": "sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==", + "license": "ISC", + "dependencies": { + "npm-bundled": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/map-workspaces": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@npmcli/map-workspaces/-/map-workspaces-3.0.6.tgz", + "integrity": "sha512-tkYs0OYnzQm6iIRdfy+LcLBjcKuQCeE5YLb8KnrIlutJfheNaPvPpgoFEyEFgbjzl5PLZ3IA/BWAwRU0eHuQDA==", + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^2.0.0", + "glob": "^10.2.2", + "minimatch": "^9.0.0", + "read-package-json-fast": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/metavuln-calculator/-/metavuln-calculator-7.1.1.tgz", + "integrity": "sha512-Nkxf96V0lAx3HCpVda7Vw4P23RILgdi/5K1fmj2tZkWIYLpXAN8k2UVVOsW16TsS5F8Ws2I7Cm+PU1/rsVF47g==", + "license": "ISC", + "dependencies": { + "cacache": "^18.0.0", + "json-parse-even-better-errors": "^3.0.0", + "pacote": "^18.0.0", + "proc-log": "^4.1.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/name-from-folder": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/name-from-folder/-/name-from-folder-2.0.0.tgz", + "integrity": "sha512-pwK+BfEBZJbKdNYpHHRTNBwBoqrN/iIMO0AiGvYsp3Hoaq0WbgGSWQR6SCldZovoDpY3yje5lkFUe6gsDgJ2vg==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/node-gyp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz", + "integrity": "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/package-json": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-5.2.1.tgz", + "integrity": "sha512-f7zYC6kQautXHvNbLEWgD/uGu1+xCn9izgqBfgItWSx22U0ZDekxN08A1vM8cTxj/cRVe0Q94Ode+tdoYmIOOQ==", + "license": "ISC", + "dependencies": { + "@npmcli/git": "^5.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^7.0.0", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^6.0.0", + "proc-log": "^4.0.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/promise-spawn": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-7.0.2.tgz", + "integrity": "sha512-xhfYPXoV5Dy4UkY0D+v2KkwvnDfiA/8Mt3sWCGI/hM03NsYIH8ZaG6QzS9x7pje5vHZBZJ2v6VRFVTWACnqcmQ==", + "license": "ISC", + "dependencies": { + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/query": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/query/-/query-3.1.0.tgz", + "integrity": "sha512-C/iR0tk7KSKGldibYIB9x8GtO/0Bd0I2mhOaDb8ucQL/bQVTmGoeREaFj64Z5+iCBRf3dQfed0CjJL7I8iTkiQ==", + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/redact": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-2.0.1.tgz", + "integrity": "sha512-YgsR5jCQZhVmTJvjduTOIHph0L73pK8xwMVaDY0PatySqVM9AZj93jpoXYSJqfHFxFkN9dmqTw6OiqExsS3LPw==", + "license": "ISC", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/run-script": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-8.1.0.tgz", + "integrity": "sha512-y7efHHwghQfk28G2z3tlZ67pLG0XdfYbcVG26r7YIXALRsrVQcTq4/tdenSmdOrEsNahIYA/eh8aEVROWGFUDg==", + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/package-json": "^5.0.0", + "@npmcli/promise-spawn": "^7.0.0", + "node-gyp": "^10.0.0", + "proc-log": "^4.0.0", + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.52.1.tgz", + "integrity": "sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.26.0.tgz", + "integrity": "sha512-HedpXXYzzbaoutw6DFLWLDket2FwLkLpil4hGCZ1xYEIMTcivdfwEOISgdbLEWyG3HW52gTq2V9mOVJrONgiwg==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.26.0.tgz", + "integrity": "sha512-1iKxXXE8415Cdv0yjG3G6hQnB5eVEsJce3QaawX8SjDn0mAS0ZM8fAbZZJD4ajvhC15cePvosSCut404KrIIvQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin": { + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.26.0.tgz", + "integrity": "sha512-PW5R34n3SJHO4t0UetyHKiXL6LixIqWN6lWncg3eRXhKuT30x+b7m5sDJS0kEWRfHeS+kG7uCw2vBzmB2lk3Dw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.26.0", + "@opentelemetry/resources": "1.26.0", + "@opentelemetry/sdk-trace-base": "1.26.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.52.1.tgz", + "integrity": "sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@types/shimmer": "^1.0.2", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-grpc": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.52.1.tgz", + "integrity": "sha512-EdSDiDSAO+XRXk/ZN128qQpBo1I51+Uay/LUPcPQhSRGf7fBPIEUBeOLQiItguGsug5MGOYjql2w/1wCQF3fdQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "0.52.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-grpc/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/propagator-b3": { + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.26.0.tgz", + "integrity": "sha512-vvVkQLQ/lGGyEy9GT8uFnI047pajSOVnZI2poJqVGD3nJ+B9sFGdlHNnQKophE3lHfnIH0pw2ubrCTjZCgIj+Q==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.26.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger": { + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.26.0.tgz", + "integrity": "sha512-DelFGkCdaxA1C/QA0Xilszfr0t4YbGd3DjxiCDPh34lfnFr+VkkrjV9S8ZTJvAzfdKERXhfOxIKBoGPJwoSz7Q==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.26.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.26.0.tgz", + "integrity": "sha512-CPNYchBE7MBecCSVy0HKpUISEeJOniWqcHaAHpmasZ3j9o6V3AyBzhRc90jdmemq0HOxDr6ylhUbDhBqqPpeNw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.26.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.26.0.tgz", + "integrity": "sha512-olWQldtvbK4v22ymrKLbIcBi9L2SpMO84sCPY54IVsJhP9fRsxJT194C/AVaAuJzLE30EdhhM1VmvVYR7az+cw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.26.0", + "@opentelemetry/resources": "1.26.0", + "@opentelemetry/semantic-conventions": "1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.26.0.tgz", + "integrity": "sha512-Fj5IVKrj0yeUwlewCRwzOVcr5avTuNnMHWf7GPc1t6WaT78J6CJyF3saZ/0RkZfdeNO8IcBl/bNcWMVZBMRW8Q==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/context-async-hooks": "1.26.0", + "@opentelemetry/core": "1.26.0", + "@opentelemetry/propagator-b3": "1.26.0", + "@opentelemetry/propagator-jaeger": "1.26.0", + "@opentelemetry/sdk-trace-base": "1.26.0", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz", + "integrity": "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, + "node_modules/@pulumi/command": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@pulumi/command/-/command-0.7.2.tgz", + "integrity": "sha512-R/eAIXIywSkn2cHIaNqs0EXcXduNeDuS+wom6oadB5KEP+6elgJbo4WMDP+OuDYGkkxEWIKjk0bPPO7570aXFQ==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@pulumi/pulumi": "^3.0.0" + } + }, + "node_modules/@pulumi/gcp": { + "version": "6.67.1", + "resolved": "https://registry.npmjs.org/@pulumi/gcp/-/gcp-6.67.1.tgz", + "integrity": "sha512-/4BQ4j0Yz5VobPRe96o34SZoV1JlJSRlChI0uNx36uq17GHpzSFnQkKTb+nNvK7ujqSoQFIVuy5Ez9pY5uyFiA==", + "license": "Apache-2.0", + "dependencies": { + "@pulumi/pulumi": "^3.0.0", + "@types/express": "^4.16.0", + "read-package-json": "^2.0.13" + } + }, + "node_modules/@pulumi/pulumi": { + "version": "3.135.1", + "resolved": "https://registry.npmjs.org/@pulumi/pulumi/-/pulumi-3.135.1.tgz", + "integrity": "sha512-uSAewnD7KgkR0oEzfBlbYbLVJ3qEOXA/Q/NU127DXSOvZi//AMd7MHrIxMd2DIFQnho3cjgtc12r+XZCjkePJQ==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.10.1", + "@logdna/tail-file": "^2.0.6", + "@npmcli/arborist": "^7.3.1", + "@opentelemetry/api": "^1.9", + "@opentelemetry/exporter-zipkin": "^1.25", + "@opentelemetry/instrumentation": "^0.52", + "@opentelemetry/instrumentation-grpc": "^0.52", + "@opentelemetry/resources": "^1.25", + "@opentelemetry/sdk-trace-base": "^1.25", + "@opentelemetry/sdk-trace-node": "^1.25", + "@opentelemetry/semantic-conventions": "^1.25", + "@pulumi/query": "^0.3.0", + "@types/google-protobuf": "^3.15.5", + "@types/semver": "^7.5.6", + "@types/tmp": "^0.2.6", + "execa": "^5.1.0", + "fdir": "^6.1.1", + "google-protobuf": "^3.5.0", + "got": "^11.8.6", + "ini": "^2.0.0", + "js-yaml": "^3.14.0", + "minimist": "^1.2.6", + "normalize-package-data": "^6.0.0", + "picomatch": "^3.0.1", + "pkg-dir": "^7.0.0", + "require-from-string": "^2.0.1", + "semver": "^7.5.2", + "source-map-support": "^0.5.6", + "tmp": "^0.2.1", + "upath": "^1.1.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "ts-node": ">= 7.0.1 < 12", + "typescript": ">= 3.8.3 < 6" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@pulumi/query": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@pulumi/query/-/query-0.3.0.tgz", + "integrity": "sha512-xfo+yLRM2zVjVEA4p23IjQWzyWl1ZhWOGobsBqRpIarzLvwNH/RAGaoehdxlhx4X92302DrpdIFgTICMN4P38w==", + "license": "Apache-2.0" + }, + "node_modules/@sigstore/bundle": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.3.2.tgz", + "integrity": "sha512-wueKWDk70QixNLB363yHc2D2ItTgYiMTdPwK8D9dKQMR3ZQ0c35IxP5xnwQ8cNLoCgCRcHf14kE+CLIvNX1zmA==", + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/core": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-1.1.0.tgz", + "integrity": "sha512-JzBqdVIyqm2FRQCulY6nbQzMpJJpSiJ8XXWMhtOX9eKgaXXpfNOF53lzQEjIydlStnd/eFtuC1dW4VYdD93oRg==", + "license": "Apache-2.0", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/protobuf-specs": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.3.2.tgz", + "integrity": "sha512-c6B0ehIWxMI8wiS/bj6rHMPqeFvngFV7cDU/MY+B16P9Z3Mp9k8L93eYZ7BYzSickzuqAQqAq0V956b3Ju6mLw==", + "license": "Apache-2.0", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/sign": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-2.3.2.tgz", + "integrity": "sha512-5Vz5dPVuunIIvC5vBb0APwo7qKA4G9yM48kPWJT+OEERs40md5GoUR1yedwpekWZ4m0Hhw44m6zU+ObsON+iDA==", + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "make-fetch-happen": "^13.0.1", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/tuf": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-2.3.4.tgz", + "integrity": "sha512-44vtsveTPUpqhm9NCrbU8CWLe3Vck2HO1PNLw7RIajbB7xhtn5RBPm1VNSCMwqGYHhDsBJG8gDF0q4lgydsJvw==", + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2", + "tuf-js": "^2.2.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/verify": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-1.2.1.tgz", + "integrity": "sha512-8iKx79/F73DKbGfRf7+t4dqrc0bRr0thdPrxAtCKWRm/F0tG71i6O1rvlnScncJLLBZHn3h8M3c1BSUAb9yu8g==", + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.1.0", + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", + "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==", + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-2.0.1.tgz", + "integrity": "sha512-92F7/SFyufn4DXsha9+QfKnN03JGqtMFMXgSHbZOo8JG59WkTni7UzAouNQDf7AuP9OAMxVOPQcqG3sB7w+kkg==", + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/google-protobuf": { + "version": "3.15.12", + "resolved": "https://registry.npmjs.org/@types/google-protobuf/-/google-protobuf-3.15.12.tgz", + "integrity": "sha512-40um9QqwHjRS92qnOaDpL7RmDK15NuZYo9HihiJRbYkMQZlWnuH8AdvbMy8/o6lgLmKbDUKa+OALCltHdbOTpQ==", + "license": "MIT" + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", + "license": "MIT" + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "license": "MIT" + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "16.18.112", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.112.tgz", + "integrity": "sha512-EKrbKUGJROm17+dY/gMi31aJlGLJ75e1IkTojt9n6u+hnaTBDs+M1bIdOawpk2m6YUAXq/R2W0SxCng1tndHCg==", + "license": "MIT" + }, + "node_modules/@types/qs": { + "version": "6.9.16", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", + "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==", + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "license": "MIT" + }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/shimmer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz", + "integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==", + "license": "MIT" + }, + "node_modules/@types/tmp": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.6.tgz", + "integrity": "sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA==", + "license": "MIT" + }, + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/bin-links": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-4.0.4.tgz", + "integrity": "sha512-cMtq4W5ZsEwcutJrVId+a/tjt8GSbS+h0oNkdl6+6rBuEv8Ot33Bevj5KPm40t309zuhVic8NjpuL42QCiJWWA==", + "license": "ISC", + "dependencies": { + "cmd-shim": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "read-cmd-shim": "^4.0.0", + "write-file-atomic": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/cacache": { + "version": "18.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.4.tgz", + "integrity": "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==", + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", + "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==", + "license": "MIT" + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cmd-shim": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-6.0.3.tgz", + "integrity": "sha512-FMabTRlc5t5zjdenF6mS0MBeFZm0XqHqeOkcskKFb/LYCcRQ5fVgLOHVc4Lq9CqABd9zhjwPjMBCJvMCziSVtA==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/common-ancestor-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz", + "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==", + "license": "ISC" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "license": "MIT" + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exponential-backoff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", + "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", + "license": "Apache-2.0" + }, + "node_modules/fdir": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.0.tgz", + "integrity": "sha512-3oB133prH1o4j/L5lLW7uOCF1PlD+/It2L0eL/iAqWMB91RBbqTewABqxhj0ibBd90EEmWZq7ntIWzVaWcXTGQ==", + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "license": "MIT", + "dependencies": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs-minipass": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", + "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/google-protobuf": { + "version": "3.21.4", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.4.tgz", + "integrity": "sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ==", + "license": "(BSD-3-Clause AND Apache-2.0)" + }, + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "license": "BSD-2-Clause" + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore-walk": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.5.tgz", + "integrity": "sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==", + "license": "ISC", + "dependencies": { + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/import-in-the-middle": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.11.2.tgz", + "integrity": "sha512-gK6Rr6EykBcc6cVWRSBR5TWf8nn6hZMYSRYqCcHa0l0d1fPK7JSYo6+Mlmck76jIX9aL/IZ71c06U2VpFwl1zA==", + "license": "Apache-2.0", + "dependencies": { + "acorn": "^8.8.2", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ip-address/node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "license": "BSD-3-Clause" + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "license": "MIT" + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "license": "MIT" + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", + "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", + "license": "MIT", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/json-stringify-nice": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz", + "integrity": "sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw==", + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/just-diff": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/just-diff/-/just-diff-6.0.2.tgz", + "integrity": "sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA==", + "license": "MIT" + }, + "node_modules/just-diff-apply": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/just-diff-apply/-/just-diff-apply-5.5.0.tgz", + "integrity": "sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw==", + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "license": "MIT", + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT" + }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", + "license": "Apache-2.0" + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/make-fetch-happen": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz", + "integrity": "sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==", + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", + "http-cache-semantics": "^4.1.1", + "is-lambda": "^1.0.1", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT" + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-collect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", + "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-fetch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", + "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/module-details-from-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", + "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-gyp": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.2.0.tgz", + "integrity": "sha512-sp3FonBAaFe4aYTcFdZUn2NYkbP7xroPGYvQmP4Nl5PxamznItBnNCgjrVTKrEfQynInMsJvZrdmqUnysCJ8rw==", + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^13.0.0", + "nopt": "^7.0.0", + "proc-log": "^4.1.0", + "semver": "^7.3.5", + "tar": "^6.2.1", + "which": "^4.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-bundled": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.1.tgz", + "integrity": "sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==", + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-install-checks": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.3.0.tgz", + "integrity": "sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==", + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", + "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-package-arg": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.3.tgz", + "integrity": "sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==", + "license": "ISC", + "dependencies": { + "hosted-git-info": "^7.0.0", + "proc-log": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm-packlist": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-8.0.2.tgz", + "integrity": "sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==", + "license": "ISC", + "dependencies": { + "ignore-walk": "^6.0.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-pick-manifest": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-9.1.0.tgz", + "integrity": "sha512-nkc+3pIIhqHVQr085X9d2JzPzLyjzQS96zbruppqC9aZRm/x8xx6xhI98gHtsfELP2bE+loHq8ZaHFHhe+NauA==", + "license": "ISC", + "dependencies": { + "npm-install-checks": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "npm-package-arg": "^11.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm-registry-fetch": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-17.1.0.tgz", + "integrity": "sha512-5+bKQRH0J1xG1uZ1zMNvxW0VEyoNWgJpY9UDuluPFLKDfJ9u2JmmjmTJV1srBGQOROfdBMiVvnH2Zvpbm+xkVA==", + "license": "ISC", + "dependencies": { + "@npmcli/redact": "^2.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^13.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minizlib": "^2.1.2", + "npm-package-arg": "^11.0.0", + "proc-log": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "license": "MIT", + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/pacote": { + "version": "18.0.6", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-18.0.6.tgz", + "integrity": "sha512-+eK3G27SMwsB8kLIuj4h1FUhHtwiEUo21Tw8wNjmvdlpOEr613edv+8FUsTj/4F/VN5ywGE19X18N7CC2EJk6A==", + "license": "ISC", + "dependencies": { + "@npmcli/git": "^5.0.0", + "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/package-json": "^5.1.0", + "@npmcli/promise-spawn": "^7.0.0", + "@npmcli/run-script": "^8.0.0", + "cacache": "^18.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^11.0.0", + "npm-packlist": "^8.0.0", + "npm-pick-manifest": "^9.0.0", + "npm-registry-fetch": "^17.0.0", + "proc-log": "^4.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^2.2.0", + "ssri": "^10.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/parse-conflict-json": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/parse-conflict-json/-/parse-conflict-json-3.0.1.tgz", + "integrity": "sha512-01TvEktc68vwbJOtWZluyWeVGWjP+bZwXtPDMQVbBKzbJ/vZBif0L69KH1+cHv1SZ6e0FKLvjyHe8mqsIqYOmw==", + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^3.0.0", + "just-diff": "^6.0.0", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/picomatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", + "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-dir": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", + "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", + "license": "MIT", + "dependencies": { + "find-up": "^6.3.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/proc-log": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", + "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/proggy": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/proggy/-/proggy-2.0.0.tgz", + "integrity": "sha512-69agxLtnI8xBs9gUGqEnK26UfiexpHy+KUpBQWabiytQjnn5wFY8rklAi7GRfABIuPNnQ/ik48+LGLkYYJcy4A==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/promise-all-reject-late": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz", + "integrity": "sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==", + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/promise-call-limit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/promise-call-limit/-/promise-call-limit-3.0.2.tgz", + "integrity": "sha512-mRPQO2T1QQVw11E7+UdCJu7S61eJVWknzml9sC1heAdj1jxl0fWMBypIt9ZOcLFf8FkG995ZD7RnVk7HH72fZw==", + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "license": "ISC" + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/protobufjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-cmd-shim": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-4.0.0.tgz", + "integrity": "sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-package-json": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz", + "integrity": "sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==", + "deprecated": "This package is no longer supported. Please use @npmcli/package-json instead.", + "license": "ISC", + "dependencies": { + "glob": "^7.1.1", + "json-parse-even-better-errors": "^2.3.0", + "normalize-package-data": "^2.0.0", + "npm-normalize-package-bin": "^1.0.0" + } + }, + "node_modules/read-package-json-fast": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz", + "integrity": "sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==", + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-package-json/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/read-package-json/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/read-package-json/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "license": "ISC" + }, + "node_modules/read-package-json/node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "node_modules/read-package-json/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/read-package-json/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-package-json/node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "license": "ISC" + }, + "node_modules/read-package-json/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-in-the-middle": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.4.0.tgz", + "integrity": "sha512-X34iHADNbNDfr6OTStIAHWSAvvKQRYgLO6duASaVf7J2VA3lvmNYboAHOuLC2huav1IwgZJtyEcJCKVzFxOSMQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "license": "MIT" + }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT", + "optional": true + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==", + "license": "BSD-2-Clause" + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/sigstore": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-2.3.1.tgz", + "integrity": "sha512-8G+/XDU8wNsJOQS5ysDVO0Etg9/2uA5gR9l4ZwijjlwxBcrU6RPfwi2+jJmbP+Ap1Hlp/nVAaEO4Fj22/SL2gQ==", + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "@sigstore/sign": "^2.3.2", + "@sigstore/tuf": "^2.3.4", + "@sigstore/verify": "^1.2.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", + "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", + "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", + "license": "CC0-1.0" + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, + "node_modules/ssri": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", + "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/treeverse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/treeverse/-/treeverse-3.0.0.tgz", + "integrity": "sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/tuf-js": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-2.2.1.tgz", + "integrity": "sha512-GwIJau9XaA8nLVbUXsN3IlFi7WmQ48gBUrl3FTkkL/XLu/POhBzfmX9hd33FNMX1qAsfl6ozO1iMmW9NC8YniA==", + "license": "MIT", + "dependencies": { + "@tufjs/models": "2.0.1", + "debug": "^4.3.4", + "make-fetch-happen": "^13.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/unique-filename": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", + "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", + "license": "ISC", + "dependencies": { + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/unique-slug": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", + "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "license": "MIT", + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", + "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/walk-up-path": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-3.0.1.tgz", + "integrity": "sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==", + "license": "ISC" + }, + "node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", + "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/ai-context/workbench-ui/pulumi/package.json b/ai-context/workbench-ui/pulumi/package.json new file mode 100644 index 00000000..d7eec6a1 --- /dev/null +++ b/ai-context/workbench-ui/pulumi/package.json @@ -0,0 +1,12 @@ +{ + "name": "safety-ai", + "main": "index.ts", + "devDependencies": { + "@types/node": "^16" + }, + "dependencies": { + "@pulumi/command": "^0.7.2", + "@pulumi/gcp": "^6.58.0", + "@pulumi/pulumi": "^3.0.0" + } +} diff --git a/ai-context/workbench-ui/pulumi/tsconfig.json b/ai-context/workbench-ui/pulumi/tsconfig.json new file mode 100644 index 00000000..ab65afa6 --- /dev/null +++ b/ai-context/workbench-ui/pulumi/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "strict": true, + "outDir": "bin", + "target": "es2016", + "module": "commonjs", + "moduleResolution": "node", + "sourceMap": true, + "experimentalDecorators": true, + "pretty": true, + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.ts" + ] +} diff --git a/ai-context/workbench-ui/requirements.txt b/ai-context/workbench-ui/requirements.txt new file mode 100644 index 00000000..c1846968 --- /dev/null +++ b/ai-context/workbench-ui/requirements.txt @@ -0,0 +1,3 @@ +aiohttp +pyyaml +wheel diff --git a/ai-context/workbench-ui/src/App.scss b/ai-context/workbench-ui/src/App.scss new file mode 100644 index 00000000..366ad033 --- /dev/null +++ b/ai-context/workbench-ui/src/App.scss @@ -0,0 +1,16 @@ +#root { + margin: 0; + padding: 0; + height: 100vh; + width: 100vw; +} + +/* + +body { + width: 100vw; + height: 100vh; + margin: 0; +} + +*/ diff --git a/ai-context/workbench-ui/src/App.tsx b/ai-context/workbench-ui/src/App.tsx new file mode 100644 index 00000000..794c6cbb --- /dev/null +++ b/ai-context/workbench-ui/src/App.tsx @@ -0,0 +1,202 @@ +import { useEffect, lazy, Suspense } from "react"; + +import { Box } from "@chakra-ui/react"; +import { BrowserRouter as Router, Routes, Route } from "react-router"; + +import Layout from "./components/Layout"; + +import ChatPage from "./pages/ChatPage"; +import SearchPage from "./pages/SearchPage"; +import EntityPage from "./pages/EntityPage"; + +// Lazy load GraphPage since it includes heavy 3D visualization library (react-force-graph/Three.js) +const GraphPage = lazy(() => import("./pages/GraphPage")); +// Lazy load FlowClassesPage since it includes reactflow library +const FlowClassesPage = lazy(() => import("./pages/FlowClassesPage")); +// Lazy load less frequently used pages +const OntologiesPage = lazy(() => import("./pages/OntologiesPage")); +const StructuredQueryPage = lazy(() => import("./pages/StructuredQueryPage")); +const SettingsPage = lazy(() => import("./pages/SettingsPage")); +const SchemasPage = lazy(() => import("./pages/SchemasPage")); +const LLMModelsPage = lazy(() => import("./pages/LLMModelsPage")); +const McpToolsPage = lazy(() => import("./pages/McpToolsPage")); + +import FlowsPage from "./pages/FlowsPage"; +import LibraryPage from "./pages/LibraryPage"; +import KnowledgeCoresPage from "./pages/KnowledgeCoresPage"; +import ProcessingPage from "./pages/ProcessingPage"; +import TokenCostPage from "./pages/TokenCostPage"; +import PromptsPage from "./pages/PromptsPage"; +import ToolsPage from "./pages/ToolsPage"; + +import CenterSpinner from "./components/common/CenterSpinner"; +import Progress from "./components/common/Progress"; +import { Toaster } from "./components/ui/ToasterComponent"; + +import { useSocket, useConnectionState } from "@trustgraph/react-provider"; +import { + useProgressStateStore, + useSessionStore, +} from "@trustgraph/react-state"; + +const App = () => { + const socket = useSocket(); + const connectionState = useConnectionState(); + + const addActivity = useProgressStateStore((state) => state.addActivity); + const removeActivity = useProgressStateStore( + (state) => state.removeActivity, + ); + + const setFlowId = useSessionStore((state) => state.setFlowId); + const setFlow = useSessionStore((state) => state.setFlow); + + useEffect(() => { + // Wait for socket connection to be established before loading flows + if ( + !connectionState || + (connectionState.status !== "connected" && + connectionState.status !== "authenticated" && + connectionState.status !== "unauthenticated") + ) { + console.log( + "App: Waiting for socket connection...", + connectionState?.status, + ); + return; + } + + console.log("App: Socket connected, loading flows..."); + const act = "Load flows"; + addActivity(act); + socket + .flows() + .getFlows() + .then((ids) => { + return Promise.all( + ids.map((id) => + socket + .flows() + .getFlow(id) + .then((x) => [id, x]), + ), + ); + }) + .then((flows) => { + removeActivity(act); + + const flowIds = flows.map((fl) => fl[0]); + if (flowIds.includes("default")) { + setFlowId("default"); + const flow = flows.filter((fl) => fl[0] == "default")[0][1]; + setFlow(flow); + } else { + // No default flow, just pick first in the list. + setFlowId(flows[0][0]); + setFlow(flows[0][1]); + } + }) + .catch((err) => { + removeActivity(act); + console.log("Error:", err); + }); + }, [ + socket, + connectionState, + addActivity, + removeActivity, + setFlow, + setFlowId, + ]); + + return ( + + + + + } /> + } /> + } /> + } /> + }> + + + } + /> + } /> + }> + + + } + /> + } /> + } /> + } /> + } /> + } /> + }> + + + } + /> + }> + + + } + /> + }> + + + } + /> + } /> + }> + + + } + /> + }> + + + } + /> + }> + + + } + /> + + + + + + + + ); +}; + +export default App; diff --git a/ai-context/workbench-ui/src/api/authenticated-fetch.ts b/ai-context/workbench-ui/src/api/authenticated-fetch.ts new file mode 100644 index 00000000..06b63762 --- /dev/null +++ b/ai-context/workbench-ui/src/api/authenticated-fetch.ts @@ -0,0 +1,41 @@ +/** + * Authenticated fetch utility + * + * Provides fetch functions that automatically include Bearer token authentication + * when an API key is configured in settings. + */ + +/** + * Creates an authenticated fetch function that includes Bearer token when available + * @param apiKey - Optional API key for authentication + * @returns Fetch function with automatic auth headers + */ +export const createAuthenticatedFetch = (apiKey?: string) => { + return (url: string, options: RequestInit = {}) => { + const headers: HeadersInit = { + ...options.headers, + }; + + // Add Bearer token if API key is present + if (apiKey) { + headers["Authorization"] = `Bearer ${apiKey}`; + } + + return fetch(url, { + ...options, + headers, + }); + }; +}; + +/** + * Hook-based authenticated fetch that uses current settings + * This is a React hook that must be called from within a component + */ +export const useAuthenticatedFetch = () => { + // Note: This will be implemented when we need it in components + // For now, we'll use the createAuthenticatedFetch directly with settings + throw new Error( + "useAuthenticatedFetch not yet implemented - use createAuthenticatedFetch with settings", + ); +}; diff --git a/ai-context/workbench-ui/src/components/Layout.tsx b/ai-context/workbench-ui/src/components/Layout.tsx new file mode 100644 index 00000000..5248a925 --- /dev/null +++ b/ai-context/workbench-ui/src/components/Layout.tsx @@ -0,0 +1,20 @@ +import React from "react"; +import { Box, Flex } from "@chakra-ui/react"; +import Sidebar from "./Sidebar"; + +interface LayoutProps { + children: React.ReactNode; +} + +const Layout: React.FC = ({ children }) => { + return ( + + + + {children} + + + ); +}; + +export default Layout; diff --git a/ai-context/workbench-ui/src/components/Sidebar.tsx b/ai-context/workbench-ui/src/components/Sidebar.tsx new file mode 100644 index 00000000..f07f424e --- /dev/null +++ b/ai-context/workbench-ui/src/components/Sidebar.tsx @@ -0,0 +1,162 @@ +import React from "react"; + +import { + Box, + Flex, + VStack, + Text, + Icon, + Heading, + Separator, + chakra, +} from "@chakra-ui/react"; + +import { NavLink as ReactRouterNavLink } from "react-router"; +import { useSettings } from "@trustgraph/react-state"; + +const ChakraNavLink = chakra(ReactRouterNavLink); + +import { + TestTube2, + Hammer, + Plug, + Bot, + MessageSquareText, + Search, + Waypoints, + Rotate3d, + // FileUp, + Workflow, + ScrollText, + LibraryBig, + BrainCircuit, + CircleArrowRight, + HandCoins, + MessageCircleCode, + Database, + Network, + FileSearch, + Settings, +} from "lucide-react"; + +interface NavItemProps { + to: string; + icon: React.ElementType; + label: string; +} + +const NavItem: React.FC = ({ to, icon, label }) => { + return ( + + {({ isActive }: { isActive: boolean }) => ( + + + {label} + + )} + + ); +}; + +const Sidebar = () => { + const { settings } = useSettings(); + + return ( + + + + + + + TrustGraph + + + TG + + + + + + + + + + + + {settings.featureSwitches.flowClasses && ( + + )} + + + {settings.featureSwitches.submissions && ( + + )} + {settings.featureSwitches.tokenCost && ( + + )} + + {settings.featureSwitches.schemas && ( + + )} + {settings.featureSwitches.structuredQuery && ( + + )} + {settings.featureSwitches.ontologyEditor && ( + + )} + {settings.featureSwitches.agentTools && ( + + )} + {settings.featureSwitches.mcpTools && ( + + )} + {settings.featureSwitches.llmModels && ( + + )} + + + + ); +}; + +export default Sidebar; diff --git a/ai-context/workbench-ui/src/components/agents/Controls.tsx b/ai-context/workbench-ui/src/components/agents/Controls.tsx new file mode 100644 index 00000000..b39a9f7f --- /dev/null +++ b/ai-context/workbench-ui/src/components/agents/Controls.tsx @@ -0,0 +1,38 @@ +import React, { useState } from "react"; + +import { Plus } from "lucide-react"; + +import { Button, Box } from "@chakra-ui/react"; + +import EditDialog from "./EditDialog"; + +const Controls = () => { + const [createOpen, setCreateOpen] = useState(false); + + const onComplete = () => { + setCreateOpen(false); + }; + + return ( + + + onComplete()} + /> + + ); +}; + +export default Controls; diff --git a/ai-context/workbench-ui/src/components/agents/EditDialog.tsx b/ai-context/workbench-ui/src/components/agents/EditDialog.tsx new file mode 100644 index 00000000..4c2f01ad --- /dev/null +++ b/ai-context/workbench-ui/src/components/agents/EditDialog.tsx @@ -0,0 +1,340 @@ +import React, { useEffect, useState, useRef } from "react"; + +import { Trash, SendHorizontal, Plus } from "lucide-react"; + +import { Portal, Button, Dialog, Box, CloseButton } from "@chakra-ui/react"; + +import { useSocket } from "@trustgraph/react-provider"; +import { useAgentTools } from "@trustgraph/react-state"; +import { useMcpTools } from "@trustgraph/react-state"; +import { usePrompts } from "@trustgraph/react-state"; +import SelectField from "../common/SelectField"; +import TextAreaField from "../common/TextAreaField"; +import TextField from "../common/TextField"; +import ChipInputField from "../common/ChipInputField"; +import { toaster } from "../ui/toaster"; +import EditableArgumentsTable from "./EditableArgumentsTable"; + +const EditDialog = ({ open, onOpenChange, onComplete, id, create }) => { + const socket = useSocket(); + const { updateTool, createTool, deleteTool } = useAgentTools(); + const { tools: mcpTools } = useMcpTools(); + const { prompts } = usePrompts(); + + const [newId, setNewId] = useState(""); + const [name, setName] = useState(""); + const [description, setDescription] = useState(""); + const [type, setType] = useState("knowledge-query"); + const [args, setArgs] = useState([]); + const [templateId, setTemplateId] = useState(""); + const [mcpToolId, setMcpToolId] = useState(""); + const [collection, setCollection] = useState(""); + const [group, setGroup] = useState([]); + const [state, setState] = useState(""); + const [applicableStates, setApplicableStates] = useState([]); + + const [editArgIx, setEditArgIx] = useState(-1); + + useEffect(() => { + if (!id) return; + + socket + .config() + .getConfig([{ type: "tool", key: id }]) + .then((x) => { + return JSON.parse(x.values[0].value); + }) + .then((x) => { + // Store flow information + setName(x.name || ""); + setDescription(x.description); + setType(x.type); + setArgs(x.arguments || []); + // Handle both old 'template' and new 'template_id' attributes + setTemplateId(x.template_id || x.template || ""); + // Handle both old 'mcp-tool' and new 'mcp_tool_id' attributes + setMcpToolId(x.mcp_tool_id || x["mcp-tool"] || ""); + // Handle collection attribute for knowledge-query tools + setCollection(x.collection || ""); + // Handle new optional fields + setGroup(x.group || []); + setState(x.state || ""); + setApplicableStates(x["applicable-states"] || []); + }) + .catch((e) => { + console.log("Error:", e); + toaster.create({ + title: "Error: " + e.toString(), + type: "error", + }); + }); + }, [id, create, socket]); + + const typeOptions = [ + { + value: "text-completion", + label: "Text completion", + description: "Consults an LLM for a response with no further knowledge", + }, + { + value: "knowledge-query", + label: "Knowledge query", + description: "Uses the GraphRAG service for knowledge", + }, + { + value: "structured-query", + label: "Structured Query", + description: + "Execute natural language questions against records in a structured data / object store", + }, + { + value: "mcp-tool", + label: "MCP Tool", + description: "Uses the mcp-tool service to access a remote MCP tool", + }, + { + value: "prompt", + label: "Prompt Template", + description: "Executes a prompt template with variables", + }, + ]; + + const contentRef = useRef(null); + + // Create options for MCP tools select menu + const mcpToolOptions = mcpTools.map(([id]) => ({ + value: id, + label: id, + description: id, + })); + + // Create options for prompt templates select menu + const promptTemplateOptions = prompts.map(([id]) => ({ + value: id, + label: id, + description: id, + })); + + const onEdit = () => { + // Build the tool structure + const toolStruct = { + id: create ? newId : id, + name: name, + description: description, + type: type, + arguments: args, + ...(type === "prompt" && templateId && { template: templateId }), + ...(type === "mcp-tool" && mcpToolId && { "mcp-tool": mcpToolId }), + ...((type === "knowledge-query" || type === "structured-query") && + collection && { collection: collection }), + ...(group && group.length > 0 && { group: group }), + ...(state && { state: state }), + ...(applicableStates && + applicableStates.length > 0 && { + "applicable-states": applicableStates, + }), + }; + + if (create) { + createTool({ id: newId, tool: toolStruct, onSuccess: onComplete }); + } else { + updateTool({ id, tool: toolStruct, onSuccess: onComplete }); + } + }; + + const addArgument = () => { + setArgs((x) => [ + ...x, + { + name: "argname", + description: "???", + type: "string", + }, + ]); + }; + + const deleteArgument = (index) => { + setArgs((x) => x.filter((_, i) => i !== index)); + }; + + const setArgAttr = (id, key, value) => { + const newArgs = args.map((arg, ix) => { + if (id == ix) { + return { + ...arg, + [key]: value, + }; + } else { + return arg; + } + }); + setArgs(newArgs); + }; + + const onDelete = () => { + if (create) return; + deleteTool({ id, onSuccess: onComplete }); + }; + + return ( + { + onOpenChange(x.open); + }} + > + + + + + + {create && Create tool} + + {!create && ( + + Edit tool: {id} + + )} + + + {create && ( + setNewId(v)} + required={true} + /> + )} + + setName(v)} + required={true} + /> + + setDescription(v)} + required={true} + /> + + setType(v[0])} + contentRef={contentRef} + /> + + {type === "prompt" && ( + setTemplateId(v[0] || "")} + contentRef={contentRef} + /> + )} + + {type === "mcp-tool" && ( + setMcpToolId(v[0] || "")} + contentRef={contentRef} + /> + )} + + {(type === "knowledge-query" || type === "structured-query") && ( + setCollection(v)} + required={false} + /> + )} + + + + setState(v)} + required={false} + /> + + + + {(type === "prompt" || type === "mcp-tool") && ( + <> + + + + + + + )} + + + + { + // If a 'create' operation, there's nothing to delete, only + // present if an existing tool exists + } + {!create && ( + + )} + + + + + + + + + + ); +}; + +export default EditDialog; diff --git a/ai-context/workbench-ui/src/components/agents/EditableArgumentsTable.tsx b/ai-context/workbench-ui/src/components/agents/EditableArgumentsTable.tsx new file mode 100644 index 00000000..0a2ebb02 --- /dev/null +++ b/ai-context/workbench-ui/src/components/agents/EditableArgumentsTable.tsx @@ -0,0 +1,243 @@ +import React, { useMemo, useCallback, useRef, useEffect } from "react"; +import { + Table, + Editable, + Popover, + Text, + RadioGroup, + Stack, + Box, + IconButton, +} from "@chakra-ui/react"; +import { Trash } from "lucide-react"; +import { + createColumnHelper, + useReactTable, + getCoreRowModel, +} from "@tanstack/react-table"; +import { flexRender } from "@tanstack/react-table"; + +interface Argument { + name: string; + description: string; + type: "string" | "number"; +} + +interface EditableArgumentsTableProps { + args: Argument[]; + editArgIx: number; + setEditArgIx: (ix: number) => void; + setArgAttr: (ix: number, attr: keyof Argument, value: string) => void; + deleteArg: (ix: number) => void; +} + +const columnHelper = createColumnHelper(); + +export const EditableArgumentsTable: React.FC = ({ + args, + editArgIx, + setEditArgIx, + setArgAttr, + deleteArg, +}) => { + // Store latest function references to avoid stale closures + const setArgAttrRef = useRef(setArgAttr); + const setEditArgIxRef = useRef(setEditArgIx); + const deleteArgRef = useRef(deleteArg); + + useEffect(() => { + setArgAttrRef.current = setArgAttr; + setEditArgIxRef.current = setEditArgIx; + deleteArgRef.current = deleteArg; + }); + + // Create truly stable callback functions that never change reference + const handleNameChange = useCallback((index: number, value: string) => { + setArgAttrRef.current(index, "name", value); + }, []); + + const handleDescriptionChange = useCallback( + (index: number, value: string) => { + setArgAttrRef.current(index, "description", value); + }, + [], + ); + + const handleTypeChange = useCallback((index: number, value: string) => { + setArgAttrRef.current(index, "type", value); + setEditArgIxRef.current(-1); // Close popover after selection + }, []); + + const handleDelete = useCallback((index: number) => { + deleteArgRef.current(index); + }, []); + + const columns = useMemo( + () => [ + columnHelper.display({ + id: "name", + header: "Name", + size: 20, + cell: ({ row }) => ( + handleNameChange(row.index, v.value)} + > + + + + ), + }), + columnHelper.display({ + id: "description", + header: "Description", + size: 50, + cell: ({ row }) => ( + handleDescriptionChange(row.index, v.value)} + > + + + + ), + }), + columnHelper.display({ + id: "type", + header: "Type", + size: 30, + cell: ({ row }) => ( +
setEditArgIx(row.index)}> + {editArgIx === row.index && ( + { + // Close popover when selection changes + if (!e.open) setEditArgIx(-1); + }} + > + + {row.original.type} + + + + + + { + handleTypeChange(row.index, v.value); + }} + > + + + + + string + + + + + number + + + + + + + + )} + {editArgIx !== row.index && ( + {row.original.type} + )} +
+ ), + }), + columnHelper.display({ + id: "delete", + header: "", + size: 10, + cell: ({ row }) => ( + handleDelete(row.index)} + > + + + ), + }), + ], + // eslint-disable-next-line react-hooks/exhaustive-deps + [editArgIx], // Only editArgIx changes, callbacks are stable + ); + + const table = useReactTable({ + data: args, + columns, + getCoreRowModel: getCoreRowModel(), + }); + + // Show helpful message if no arguments yet + if (args.length === 0) { + return ( + + No arguments defined yet. Click "add argument" below to create template + variables. + + ); + } + + return ( + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} + + ))} + + ))} + + + {table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} + + ))} + + + ); +}; + +export default EditableArgumentsTable; diff --git a/ai-context/workbench-ui/src/components/agents/Tools.tsx b/ai-context/workbench-ui/src/components/agents/Tools.tsx new file mode 100644 index 00000000..92f23c5c --- /dev/null +++ b/ai-context/workbench-ui/src/components/agents/Tools.tsx @@ -0,0 +1,35 @@ +import React, { useState } from "react"; + +import { useAgentTools } from "@trustgraph/react-state"; +import EditDialog from "./EditDialog"; +import Controls from "./Controls"; +import ToolsTable from "./ToolsTable"; + +const Tools = () => { + const toolsState = useAgentTools(); + const [selected, setSelected] = useState(""); + + const onComplete = () => { + setSelected(""); + }; + + return ( + <> + setSelected("")} + onComplete={() => onComplete()} + create={false} + id={selected} + /> + + + + ); +}; + +export default Tools; diff --git a/ai-context/workbench-ui/src/components/agents/ToolsTable.tsx b/ai-context/workbench-ui/src/components/agents/ToolsTable.tsx new file mode 100644 index 00000000..685fe1c7 --- /dev/null +++ b/ai-context/workbench-ui/src/components/agents/ToolsTable.tsx @@ -0,0 +1,32 @@ +import { useMemo } from "react"; +import { getCoreRowModel, useReactTable } from "@tanstack/react-table"; + +import { columns, type AgentTool } from "../../model/agent-tools-table"; +import ClickableTable from "../common/ClickableTable"; + +const ToolsTable = ({ setSelected, tools }) => { + // Transform the raw tools data to match our table structure + const tableData: AgentTool[] = useMemo(() => { + return tools.map(([id, config]) => ({ + id, + name: config?.name || "", + description: config?.description || "", + type: config?.type || "", + })); + }, [tools]); + + // Initialize React Table with tool data and column configuration + const table = useReactTable({ + data: tableData, + columns: columns, + getCoreRowModel: getCoreRowModel(), + }); + + const onSelect = (row) => { + setSelected(row.original.id); + }; + + return ; +}; + +export default ToolsTable; diff --git a/ai-context/workbench-ui/src/components/chat/ChatConversation.tsx b/ai-context/workbench-ui/src/components/chat/ChatConversation.tsx new file mode 100644 index 00000000..3f4324a5 --- /dev/null +++ b/ai-context/workbench-ui/src/components/chat/ChatConversation.tsx @@ -0,0 +1,28 @@ +import React from "react"; + +import ChatHistory from "./ChatHistory"; +import InputArea from "./InputArea"; +import EntityList from "../common/EntityList"; + +import { useConversation, useChat } from "@trustgraph/react-state"; + +const ChatConversation = () => { + const input = useConversation((state) => state.input); + const { submitMessage } = useChat(); + + const submit = () => { + if (input.trim()) { + submitMessage({ input }); + } + }; + + return ( + <> + + submit()} /> + + + ); +}; + +export default ChatConversation; diff --git a/ai-context/workbench-ui/src/components/chat/ChatHelp.tsx b/ai-context/workbench-ui/src/components/chat/ChatHelp.tsx new file mode 100644 index 00000000..668d88be --- /dev/null +++ b/ai-context/workbench-ui/src/components/chat/ChatHelp.tsx @@ -0,0 +1,34 @@ +import React from "react"; + +import { Popover, Text, IconButton, Portal } from "@chakra-ui/react"; +import { CircleHelp } from "lucide-react"; + +const ChatHelp = () => { + return ( + + + + + + + + + + + + Chat assistant + + The Chat assistant lets you converse with the assistant in + natural language. The assistant has access to all of the + information in the knowledge graph and will use the knowledge + graph to provide information to you. + + + + + + + ); +}; + +export default ChatHelp; diff --git a/ai-context/workbench-ui/src/components/chat/ChatHistory.tsx b/ai-context/workbench-ui/src/components/chat/ChatHistory.tsx new file mode 100644 index 00000000..7a45bb22 --- /dev/null +++ b/ai-context/workbench-ui/src/components/chat/ChatHistory.tsx @@ -0,0 +1,63 @@ +import React, { useEffect, useRef } from "react"; + +import { Box, Text, VStack, HStack, Avatar, Spacer } from "@chakra-ui/react"; + +import { useConversation } from "@trustgraph/react-state"; +import ChatMessage from "./ChatMessage"; +import ChatModeSelector from "./ChatModeSelector"; + +const ChatHistory = () => { + const messages = useConversation((state) => state.messages); + + const scrollRef = useRef(null); + + const scrollToElement = () => { + const { current } = scrollRef; + if (current !== null) { + current.scrollIntoView({ behavior: "smooth" }); + } + }; + + useEffect(scrollToElement, [messages]); + + return ( + + + + + + + Assistant + + + + Online + + + + + + {messages.map((message, ix) => ( + + ))} +
+
+
+ ); +}; + +export default ChatHistory; diff --git a/ai-context/workbench-ui/src/components/chat/ChatMessage.tsx b/ai-context/workbench-ui/src/components/chat/ChatMessage.tsx new file mode 100644 index 00000000..ba7bc7fd --- /dev/null +++ b/ai-context/workbench-ui/src/components/chat/ChatMessage.tsx @@ -0,0 +1,95 @@ +import { Box, Flex, Avatar, Badge } from "@chakra-ui/react"; +import { Brain, Eye, CheckCircle } from "lucide-react"; +import Markdown from "react-markdown-it"; + +const ChatMessage = ({ message }) => { + const isUser = message.role === "human"; + const messageType = message.type || "normal"; + + // Define styles and icons for different message types + const getTypeStyles = () => { + switch (messageType) { + case "thinking": + return { + bg: "thinking.contrast", + borderColor: "thinking.muted", + borderWidth: "1px", + icon: , + badge: "Thinking", + badgeColor: "thinking", + color: "collout1.fg", + }; + case "observation": + return { + bg: "observing.contrast", + borderColor: "observing.muted", + borderWidth: "1px", + icon: , + badge: "Observation", + badgeColor: "observing", + color: "observing.fg", + }; + case "answer": + return { + bg: "insightful.contrast", + borderColor: "insightful.muted", + borderWidth: "1px", + icon: , + badge: "Answer", + badgeColor: "insightful", + color: "insightful.fg", + }; + default: + return { + bg: isUser ? "primary.solid" : "bg", + color: isUser ? "fg.inverted" : "fg", + }; + } + }; + + const typeStyles = getTypeStyles(); + + return ( + + {!isUser && ( + + + + )} + + + {typeStyles.badge && ( + + {typeStyles.icon} + + {typeStyles.badge} + + + )} + {message.text} + + + {isUser && ( + + + + )} + + ); +}; + +export default ChatMessage; diff --git a/ai-context/workbench-ui/src/components/chat/ChatModeSelector.tsx b/ai-context/workbench-ui/src/components/chat/ChatModeSelector.tsx new file mode 100644 index 00000000..9471228c --- /dev/null +++ b/ai-context/workbench-ui/src/components/chat/ChatModeSelector.tsx @@ -0,0 +1,50 @@ +import React from "react"; +import { Select, Portal, createListCollection } from "@chakra-ui/react"; +import { useConversation, ChatMode } from "@trustgraph/react-state"; + +const ChatModeSelector = () => { + const chatMode = useConversation((state) => state.chatMode); + const setChatMode = useConversation((state) => state.setChatMode); + + const chatModes = [ + { value: "graph-rag", label: "Graph RAG" }, + { value: "agent", label: "Agent" }, + { value: "basic-llm", label: "Basic LLM" }, + ]; + + const collection = createListCollection({ items: chatModes }); + + return ( + setChatMode(e.value[0] as ChatMode)} + size="sm" + width="150px" + > + + + + + + + + + + + + + {chatModes.map((mode) => ( + + {mode.label} + + + ))} + + + + + ); +}; + +export default ChatModeSelector; diff --git a/ai-context/workbench-ui/src/components/chat/InputArea.tsx b/ai-context/workbench-ui/src/components/chat/InputArea.tsx new file mode 100644 index 00000000..8193409a --- /dev/null +++ b/ai-context/workbench-ui/src/components/chat/InputArea.tsx @@ -0,0 +1,61 @@ +import React, { useRef } from "react"; + +import { Input, HStack } from "@chakra-ui/react"; + +import { + useProgressStateStore, + useConversation, +} from "@trustgraph/react-state"; +import ChatHelp from "./ChatHelp"; +import ProgressSubmitButton from "../common/ProgressSubmitButton"; + +interface InputAreaProps { + onSubmit: () => void; +} + +const InputArea: React.FC = ({ onSubmit }) => { + const input = useConversation((state) => state.input); + const setInput = useConversation((state) => state.setInput); + const activity = useProgressStateStore((state) => state.activity); + + const inputRef = useRef(null); + + const submit = () => { + onSubmit(); + if (inputRef.current) { + inputRef.current.focus(); + } + }; + + const onKeyDown = (event) => { + if (event.key == "Enter") { + onSubmit(); + } + }; + + return ( + <> + + setInput(e.target.value)} + onKeyDown={onKeyDown} + /> + + 0} + working={activity.size > 0} + onClick={() => submit()} + /> + + + + + ); +}; + +export default InputArea; diff --git a/ai-context/workbench-ui/src/components/chat/__tests__/ChatMessage.test.tsx b/ai-context/workbench-ui/src/components/chat/__tests__/ChatMessage.test.tsx new file mode 100644 index 00000000..7a585201 --- /dev/null +++ b/ai-context/workbench-ui/src/components/chat/__tests__/ChatMessage.test.tsx @@ -0,0 +1,353 @@ +import { describe, it, expect, vi } from "vitest"; +import { render, screen } from "@testing-library/react"; +import ChatMessage from "../ChatMessage"; + +// Helper function to filter out Chakra UI props +const filterChakraProps = (props: Record) => { + const chakraProps = [ + "alignItems", + "justifyContent", + "direction", + "gap", + "p", + "px", + "py", + "pt", + "pb", + "pl", + "pr", + "m", + "mx", + "my", + "mt", + "mb", + "ml", + "mr", + "w", + "h", + "maxW", + "maxH", + "minW", + "minH", + "bg", + "color", + "borderRadius", + "borderWidth", + "borderColor", + "borderStyle", + "boxShadow", + "display", + "position", + "top", + "right", + "bottom", + "left", + "zIndex", + "overflow", + "textAlign", + "fontSize", + "fontWeight", + "lineHeight", + "letterSpacing", + "textTransform", + "textDecoration", + "opacity", + "visibility", + "cursor", + "pointerEvents", + "userSelect", + "resize", + "outline", + "transform", + "transformOrigin", + "transition", + "animation", + "colorPalette", + "variant", + "size", + "loading", + "disabled", + "checked", + "selected", + "active", + "focus", + "hover", + "flexDirection", + "flexWrap", + "flex", + "flexGrow", + "flexShrink", + "flexBasis", + "alignSelf", + "justifySelf", + "order", + "gridColumn", + "gridRow", + "gridArea", + "gridTemplateColumns", + "gridTemplateRows", + "gridGap", + "rowGap", + "columnGap", + "placeItems", + "placeContent", + "placeSelf", + "area", + "colSpan", + "rowSpan", + "start", + "end", + ]; + const filtered = { ...props }; + chakraProps.forEach((prop) => delete filtered[prop]); + return filtered; +}; + +// Mock Chakra UI components +vi.mock("@chakra-ui/react", () => ({ + Box: ({ + children, + ...props + }: React.PropsWithChildren>) => ( +
+ {children} +
+ ), + Flex: ({ + children, + ...props + }: React.PropsWithChildren>) => ( +
+ {children} +
+ ), + Text: ({ + children, + ...props + }: React.PropsWithChildren>) => ( +

+ {children} +

+ ), + Avatar: { + Root: ({ + children, + ...props + }: React.PropsWithChildren>) => ( +
+ {children} +
+ ), + Fallback: ({ + name, + ...props + }: { name?: string } & Record) => ( +
+ {name} +
+ ), + }, + Badge: ({ + children, + ...props + }: React.PropsWithChildren>) => ( + + {children} + + ), +})); + +// Mock react-markdown-it +vi.mock("react-markdown-it", () => ({ + default: ({ children }: React.PropsWithChildren) => ( +
{children}
+ ), +})); + +// Mock lucide-react icons +vi.mock("lucide-react", () => ({ + Brain: () =>
Brain
, + Eye: () =>
Eye
, + CheckCircle: () =>
CheckCircle
, +})); + +describe("ChatMessage", () => { + it("should render user message with correct styling", () => { + const message = { + role: "human", + text: "Hello, how are you?", + type: "normal", + }; + + render(); + + expect(screen.getByTestId("markdown")).toHaveTextContent( + "Hello, how are you?", + ); + expect(screen.getByTestId("avatar-fallback")).toHaveTextContent("User"); + }); + + it("should render AI message with correct styling", () => { + const message = { + role: "ai", + text: "I am doing well, thank you!", + type: "normal", + }; + + render(); + + expect(screen.getByTestId("markdown")).toHaveTextContent( + "I am doing well, thank you!", + ); + expect(screen.getByTestId("avatar-fallback")).toHaveTextContent("Bot"); + }); + + it("should render thinking message with correct badge and icon", () => { + const message = { + role: "ai", + text: "Let me think about this...", + type: "thinking", + }; + + render(); + + expect(screen.getByTestId("brain-icon")).toBeInTheDocument(); + expect(screen.getByTestId("badge")).toHaveTextContent("Thinking"); + expect(screen.getByTestId("markdown")).toHaveTextContent( + "Let me think about this...", + ); + }); + + it("should render observation message with correct badge and icon", () => { + const message = { + role: "ai", + text: "I observe that...", + type: "observation", + }; + + render(); + + expect(screen.getByTestId("eye-icon")).toBeInTheDocument(); + expect(screen.getByTestId("badge")).toHaveTextContent("Observation"); + expect(screen.getByTestId("markdown")).toHaveTextContent( + "I observe that...", + ); + }); + + it("should render answer message with correct badge and icon", () => { + const message = { + role: "ai", + text: "The answer is 42.", + type: "answer", + }; + + render(); + + expect(screen.getByTestId("check-circle-icon")).toBeInTheDocument(); + expect(screen.getByTestId("badge")).toHaveTextContent("Answer"); + expect(screen.getByTestId("markdown")).toHaveTextContent( + "The answer is 42.", + ); + }); + + it("should handle message without type (defaults to normal)", () => { + const message = { + role: "ai", + text: "Regular message", + }; + + render(); + + expect(screen.getByTestId("markdown")).toHaveTextContent( + "Regular message", + ); + expect(screen.queryByTestId("badge")).not.toBeInTheDocument(); + }); + + it("should handle empty message text", () => { + const message = { + role: "human", + text: "", + type: "normal", + }; + + render(); + + expect(screen.getByTestId("markdown")).toHaveTextContent(""); + }); + + it("should handle markdown content", () => { + const message = { + role: "ai", + text: "**Bold text** and *italic text*", + type: "normal", + }; + + render(); + + expect(screen.getByTestId("markdown")).toHaveTextContent( + "**Bold text** and *italic text*", + ); + }); + + it("should differentiate between user and AI avatar placement", () => { + const userMessage = { + role: "human", + text: "User message", + type: "normal", + }; + + const { rerender } = render(); + + expect(screen.getByTestId("avatar-fallback")).toHaveTextContent("User"); + + const aiMessage = { + role: "ai", + text: "AI message", + type: "normal", + }; + + rerender(); + + expect(screen.getByTestId("avatar-fallback")).toHaveTextContent("Bot"); + }); + + it("should handle all message types with correct styling", () => { + const messageTypes = ["normal", "thinking", "observation", "answer"]; + + messageTypes.forEach((type) => { + const message = { + role: "ai", + text: `Message of type ${type}`, + type: type, + }; + + const { unmount } = render(); + + expect(screen.getByTestId("markdown")).toHaveTextContent( + `Message of type ${type}`, + ); + + if (type !== "normal") { + expect(screen.getByTestId("badge")).toBeInTheDocument(); + } + + unmount(); + }); + }); + + it("should handle unknown message type (falls back to normal)", () => { + const message = { + role: "ai", + text: "Unknown type message", + type: "unknown", + }; + + render(); + + expect(screen.getByTestId("markdown")).toHaveTextContent( + "Unknown type message", + ); + expect(screen.queryByTestId("badge")).not.toBeInTheDocument(); + }); +}); diff --git a/ai-context/workbench-ui/src/components/color-mode-toggle.tsx b/ai-context/workbench-ui/src/components/color-mode-toggle.tsx new file mode 100644 index 00000000..7dde93f3 --- /dev/null +++ b/ai-context/workbench-ui/src/components/color-mode-toggle.tsx @@ -0,0 +1,17 @@ +import { IconButton } from "@chakra-ui/react"; +import { useTheme } from "next-themes"; +import { LuMoon, LuSun } from "react-icons/lu"; + +const ColorModeToggle = () => { + const { theme, setTheme } = useTheme(); + const toggleColorMode = () => { + setTheme(theme === "light" ? "dark" : "light"); + }; + return ( + + {theme === "light" ? : } + + ); +}; + +export default ColorModeToggle; diff --git a/ai-context/workbench-ui/src/components/common/AltCard.tsx b/ai-context/workbench-ui/src/components/common/AltCard.tsx new file mode 100644 index 00000000..54448ee5 --- /dev/null +++ b/ai-context/workbench-ui/src/components/common/AltCard.tsx @@ -0,0 +1,39 @@ +import React from "react"; +import { Box, Flex, Heading, Text } from "@chakra-ui/react"; + +interface AltCardProps { + title: string; + description?: string; + icon?: React.ReactNode; + children?: React.ReactNode; +} + +const AltCard: React.FC = ({ + title, + description, + icon, + children, +}) => { + return ( + + + {icon && ( + + {icon} + + )} + + {title} + + + {description && ( + + {description} + + )} + {children} + + ); +}; + +export default AltCard; diff --git a/ai-context/workbench-ui/src/components/common/BasicTable.tsx b/ai-context/workbench-ui/src/components/common/BasicTable.tsx new file mode 100644 index 00000000..64fb802c --- /dev/null +++ b/ai-context/workbench-ui/src/components/common/BasicTable.tsx @@ -0,0 +1,40 @@ +import { Table } from "@chakra-ui/react"; +import { flexRender } from "@tanstack/react-table"; + +const BasicTable = ({ table }) => { + return ( + <> + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} + + ))} + + ))} + + + {table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} + + ))} + + + + ); +}; + +export default BasicTable; diff --git a/ai-context/workbench-ui/src/components/common/Card.tsx b/ai-context/workbench-ui/src/components/common/Card.tsx new file mode 100644 index 00000000..97c55a96 --- /dev/null +++ b/ai-context/workbench-ui/src/components/common/Card.tsx @@ -0,0 +1,34 @@ +import React from "react"; +import { Box, Flex, Heading, Text } from "@chakra-ui/react"; + +interface CardProps { + title: string; + description?: string; + icon?: React.ReactNode; + children?: React.ReactNode; +} + +const Card: React.FC = ({ title, description, icon, children }) => { + return ( + + + {icon && ( + + {icon} + + )} + + {title} + + + {description && ( + + {description} + + )} + {children} + + ); +}; + +export default Card; diff --git a/ai-context/workbench-ui/src/components/common/CenterSpinner.tsx b/ai-context/workbench-ui/src/components/common/CenterSpinner.tsx new file mode 100644 index 00000000..2873e28f --- /dev/null +++ b/ai-context/workbench-ui/src/components/common/CenterSpinner.tsx @@ -0,0 +1,28 @@ +import React from "react"; + +import { Box, Spinner } from "@chakra-ui/react"; + +import { useProgressStateStore } from "@trustgraph/react-state"; + +const CenterSpinner: React.FC = () => { + const activity = useProgressStateStore((state) => state.activity); + + if (activity.size < 1) { + return null; + } + + return ( + + + + ); +}; + +export default CenterSpinner; diff --git a/ai-context/workbench-ui/src/components/common/ChipInputField.tsx b/ai-context/workbench-ui/src/components/common/ChipInputField.tsx new file mode 100644 index 00000000..44cecd79 --- /dev/null +++ b/ai-context/workbench-ui/src/components/common/ChipInputField.tsx @@ -0,0 +1,118 @@ +import React, { useState } from "react"; + +import { Input, Tag, Wrap, Field } from "@chakra-ui/react"; + +// Represents a label added to the list. Highlighted with a close button for +// removal. +const Chip = ({ label, onCloseClick }) => ( + + {label} + + { + onCloseClick(label); + }} + /> + + +); + +// A horizontal stack of chips. Like a Pringles can on its side. +const ChipList = ({ items = [], onCloseClick }) => ( + + {items.map((item) => ( + + ))} + +); + +// Form field wrapper. +const ChipInput = ({ ...rest }) => ; + +// Field wrapping chip list and input +const ChipInputField: React.FC<{ + values: string[]; + onValuesChange: (v: string[]) => void; + label: string; +}> = ({ values, onValuesChange, label }) => { + const [inputValue, setInputValue] = useState(""); + + // Checks whether we've added this item already. + const itemChipExists = (item) => values.includes(item); + + // Add an item to the list, if it's valid and isn't already there. + const addItems = (itemsToAdd) => { + const validatedItems = itemsToAdd + .map((e) => e.trim()) + .filter((item) => !itemChipExists(item)); + + const newItems = [...values, ...validatedItems]; + + onValuesChange(newItems); + setInputValue(""); + }; + + // Remove an item from the list. + const removeItem = (item) => { + const index = values.findIndex((e) => e === item); + if (index !== -1) { + const newItems = [...values]; + newItems.splice(index, 1); + onValuesChange(newItems); + } + }; + + // Save input field contents in state when changed. + const handleChange = (e) => { + setInputValue(e.target.value); + }; + + // Validate and add the item if we press tab, enter or comma. + const handleKeyDown = (e) => { + if (["Enter", "Tab", ","].includes(e.key)) { + e.preventDefault(); + addItems([inputValue]); + } + }; + + // Split and add items when pasting. + const handlePaste = (e) => { + e.preventDefault(); + + const pastedData = e.clipboardData.getData("text"); + const pastedItems = pastedData.split(","); + addItems(pastedItems); + }; + + const handleCloseClick = (item) => { + removeItem(item); + }; + + const required = false; + + return ( + + + {label} {required && } + + + + + + + ); +}; + +export default ChipInputField; diff --git a/ai-context/workbench-ui/src/components/common/ClickableTable.tsx b/ai-context/workbench-ui/src/components/common/ClickableTable.tsx new file mode 100644 index 00000000..86bb2141 --- /dev/null +++ b/ai-context/workbench-ui/src/components/common/ClickableTable.tsx @@ -0,0 +1,40 @@ +import { Table } from "@chakra-ui/react"; +import { flexRender } from "@tanstack/react-table"; + +const ClickableTable = ({ table, onClick, ...tableProps }) => { + return ( + <> + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} + + ))} + + ))} + + + {table.getRowModel().rows.map((row) => ( + onClick(row)}> + {row.getVisibleCells().map((cell) => ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} + + ))} + + + + ); +}; + +export default ClickableTable; diff --git a/ai-context/workbench-ui/src/components/common/ConfirmDialog.tsx b/ai-context/workbench-ui/src/components/common/ConfirmDialog.tsx new file mode 100644 index 00000000..8f292a3b --- /dev/null +++ b/ai-context/workbench-ui/src/components/common/ConfirmDialog.tsx @@ -0,0 +1,139 @@ +import React from "react"; +import { Box, VStack, HStack, Text, Button } from "@chakra-ui/react"; +import { AlertTriangle, X } from "lucide-react"; + +interface ConfirmDialogProps { + isOpen: boolean; + onClose: () => void; + onConfirm: () => void; + title: string; + message: string; + confirmText?: string; + cancelText?: string; + variant?: "danger" | "warning" | "info"; +} + +export const ConfirmDialog: React.FC = ({ + isOpen, + onClose, + onConfirm, + title, + message, + confirmText = "Confirm", + cancelText = "Cancel", + variant = "warning", +}) => { + if (!isOpen) return null; + + const handleConfirm = () => { + onConfirm(); + onClose(); + }; + + const getVariantColors = () => { + switch (variant) { + case "danger": + return { + icon: "red.500", + confirmButton: "red", + bg: "red.50", + border: "red.200", + }; + case "warning": + return { + icon: "orange.500", + confirmButton: "orange", + bg: "orange.50", + border: "orange.200", + }; + case "info": + return { + icon: "blue.500", + confirmButton: "blue", + bg: "blue.50", + border: "blue.200", + }; + default: + return { + icon: "orange.500", + confirmButton: "orange", + bg: "orange.50", + border: "orange.200", + }; + } + }; + + const colors = getVariantColors(); + + return ( + + + {/* Header */} + + + + + + {title} + + + + + + + {/* Content */} + + + + + {message} + + + + + + {/* Footer */} + + + + + + + + + ); +}; diff --git a/ai-context/workbench-ui/src/components/common/ConnectionStatus.tsx b/ai-context/workbench-ui/src/components/common/ConnectionStatus.tsx new file mode 100644 index 00000000..7b6b918e --- /dev/null +++ b/ai-context/workbench-ui/src/components/common/ConnectionStatus.tsx @@ -0,0 +1,117 @@ +import React from "react"; +import { Box, HStack, Text, Tooltip } from "@chakra-ui/react"; +import { Info, Clock, Wifi, WifiOff, Shield, ShieldOff } from "lucide-react"; +import { useConnectionState } from "@trustgraph/react-provider"; +import type { ConnectionState } from "@trustgraph/client"; + +interface ConnectionStatusProps { + showDetails?: boolean; + size?: "sm" | "md" | "lg"; +} + +const getStatusDisplay = (state: ConnectionState) => { + switch (state.status) { + case "connecting": + return { + icon: Clock, + color: "yellow.500", + text: "Connecting...", + tooltip: "Establishing connection to server", + }; + + case "connected": + return { + icon: Wifi, + color: "green.500", + text: "Connected", + tooltip: "Connected to server", + }; + + case "authenticated": + return { + icon: Shield, + color: "green.500", + text: "Authenticated", + tooltip: "Connected with API key authentication", + }; + + case "unauthenticated": + return { + icon: ShieldOff, + color: "blue.500", + text: "Unauthenticated", + tooltip: "Connected but no API key provided (limited functionality)", + }; + + case "reconnecting": + return { + icon: Clock, + color: "orange.500", + text: `Reconnecting... (${state.reconnectAttempt}/${state.maxAttempts})`, + tooltip: `Attempting to reconnect. Try ${state.reconnectAttempt} of ${state.maxAttempts}`, + }; + + case "failed": + return { + icon: WifiOff, + color: "red.500", + text: "Connection Failed", + tooltip: + state.lastError || "Connection failed after maximum retry attempts", + }; + + default: + return { + icon: Info, + color: "gray.500", + text: "Unknown", + tooltip: "Unknown connection state", + }; + } +}; + +export const ConnectionStatus: React.FC = ({ + showDetails = false, + size = "md", +}) => { + const connectionState = useConnectionState(); + + if (!connectionState) { + return null; + } + + const { + icon: StatusIcon, + color, + text, + tooltip, + } = getStatusDisplay(connectionState); + + const iconSize = size === "sm" ? 16 : size === "lg" ? 24 : 20; + const fontSize = size === "sm" ? "xs" : size === "lg" ? "md" : "sm"; + + return ( + + + + + + + + {showDetails ? text : connectionState.status} + + {showDetails && connectionState.hasApiKey && ( + + (API Key) + + )} + + + + {tooltip} + + + ); +}; + +export default ConnectionStatus; diff --git a/ai-context/workbench-ui/src/components/common/EntityList.tsx b/ai-context/workbench-ui/src/components/common/EntityList.tsx new file mode 100644 index 00000000..51518a85 --- /dev/null +++ b/ai-context/workbench-ui/src/components/common/EntityList.tsx @@ -0,0 +1,39 @@ +import { useNavigate } from "react-router"; + +import { HStack, Tag } from "@chakra-ui/react"; + +import { Entity } from "@trustgraph/react-state"; +import { useWorkbenchStateStore } from "@trustgraph/react-state"; + +const EntityList = () => { + const entities = useWorkbenchStateStore((state) => state.entities); + const setSelected = useWorkbenchStateStore((state) => state.setSelected); + + const navigate = useNavigate(); + + const onSelect = (x: Entity) => { + setSelected(x); + navigate("/entity"); + }; + + return ( + + {entities.slice(0, 8).map((entity, ix) => ( + + + + ))} + + ); +}; + +export default EntityList; diff --git a/ai-context/workbench-ui/src/components/common/ExternalDocs.tsx b/ai-context/workbench-ui/src/components/common/ExternalDocs.tsx new file mode 100644 index 00000000..8aa03b59 --- /dev/null +++ b/ai-context/workbench-ui/src/components/common/ExternalDocs.tsx @@ -0,0 +1,17 @@ +import React, { PropsWithChildren } from "react"; + +import { Link } from "@chakra-ui/react"; + +const ExternalDocs: React.FC< + PropsWithChildren<{ + href: string; + }> +> = ({ href, children }) => { + return ( + + {children} + + ); +}; + +export default ExternalDocs; diff --git a/ai-context/workbench-ui/src/components/common/FlowSelector.tsx b/ai-context/workbench-ui/src/components/common/FlowSelector.tsx new file mode 100644 index 00000000..f45fc3d3 --- /dev/null +++ b/ai-context/workbench-ui/src/components/common/FlowSelector.tsx @@ -0,0 +1,255 @@ +import { useState } from "react"; + +import { Text, Box, Stack, HStack, Popover, Portal } from "@chakra-ui/react"; + +import { Database, Workflow } from "lucide-react"; + +import { useSessionStore } from "@trustgraph/react-state"; +import { useFlows } from "@trustgraph/react-state"; +import { useSettings } from "@trustgraph/react-state"; +import { useCollections } from "@trustgraph/react-state"; + +const FlowSelector = () => { + const flowState = useFlows(); + const flows = flowState.flows ? flowState.flows : []; + + const collectionsState = useCollections(); + const collections = collectionsState.collections || []; + + const flowId = useSessionStore((state) => state.flowId); + + const setFlowId = useSessionStore((state) => state.setFlowId); + const setFlow = useSessionStore((state) => state.setFlow); + + const { settings, updateSetting } = useSettings(); + + const [open, setOpen] = useState(false); + + return ( + setOpen(e.open)} + size="xl" + positioning={{ placement: "bottom-end" }} + > + + setOpen(true)} + cursor="pointer" + > + + + + {settings.collection} + + + + + + + {flowId || ""} + + + + + + + + + + + {/* Collection Selection */} + + + Select Collection + + + {collections.map((collection) => { + const isSelected = + settings.collection === collection.collection; + return ( + { + updateSetting("collection", collection.collection); + }} + > + + + {isSelected && ( + + )} + + + + {collection.name} + + + {collection.description} + + + + + ); + })} + + + + {/* Flow Selection */} + + + Select Flow + + + {flows.map((flow) => { + const isSelected = flowId === flow.id; + return ( + { + setFlowId(flow.id); + setFlow(flow); + }} + > + + + {isSelected && ( + + )} + + + + {flow.id} + + + {flow.description} + + + + + ); + })} + + + + + + + + + ); +}; + +export default FlowSelector; diff --git a/ai-context/workbench-ui/src/components/common/NumberField.tsx b/ai-context/workbench-ui/src/components/common/NumberField.tsx new file mode 100644 index 00000000..b08f8148 --- /dev/null +++ b/ai-context/workbench-ui/src/components/common/NumberField.tsx @@ -0,0 +1,45 @@ +import React from "react"; +import { Field, NumberInput } from "@chakra-ui/react"; + +interface NumberFieldProps { + label: string; + + minValue: number; + maxValue: number; + value: number; + onValueChange: (x: number) => void; +} + +const NumberField: React.FC = ({ + label, + minValue, + maxValue, + value, + onValueChange, +}) => { + return ( + + {label} + { + const numValue = + e.value === "" || e.value == null ? 0 : Number(e.value); + if (!isNaN(numValue)) { + onValueChange(numValue); + } + }} + > + + + + + + + + ); +}; + +export default NumberField; diff --git a/ai-context/workbench-ui/src/components/common/OptionWithImage.tsx b/ai-context/workbench-ui/src/components/common/OptionWithImage.tsx new file mode 100644 index 00000000..c47ae847 --- /dev/null +++ b/ai-context/workbench-ui/src/components/common/OptionWithImage.tsx @@ -0,0 +1,42 @@ +import React from "react"; +import { + Box, + Stack, + Image, + Flex, + Heading, + Text, + Center, +} from "@chakra-ui/react"; + +const OptionWithImage: React.FC<{ + image: string; + title: string; + description?: string | React.ReactNode; + badge?: React.ReactNode; +}> = ({ description, title, image, badge }) => { + return ( + + + +
+ {title} +
+
+ + + + {title} + + {badge && badge} + + + {description} + + +
+
+ ); +}; + +export default OptionWithImage; diff --git a/ai-context/workbench-ui/src/components/common/PageHeader.tsx b/ai-context/workbench-ui/src/components/common/PageHeader.tsx new file mode 100644 index 00000000..46f37f6c --- /dev/null +++ b/ai-context/workbench-ui/src/components/common/PageHeader.tsx @@ -0,0 +1,64 @@ +import React from "react"; + +import { Flex, Text, Box, HStack, VStack, Heading } from "@chakra-ui/react"; + +import ColorModeToggle from "../color-mode-toggle"; +import FlowSelector from "./FlowSelector"; +import ConnectionStatus from "./ConnectionStatus"; +import UserDisplay from "./UserDisplay"; + +interface PageHeaderProps { + title: string; + description: string; + icon?: React.ReactNode; +} + +const PageHeader: React.FC = ({ + title, + description, + icon, +}) => { + return ( + + + {icon && ( + + {icon} + + )} + + + {title} + + + {description} + + + + + + + + + + + + + + + ); +}; + +export default PageHeader; diff --git a/ai-context/workbench-ui/src/components/common/Progress.tsx b/ai-context/workbench-ui/src/components/common/Progress.tsx new file mode 100644 index 00000000..2aa1df39 --- /dev/null +++ b/ai-context/workbench-ui/src/components/common/Progress.tsx @@ -0,0 +1,41 @@ +import React from "react"; + +import { Box, Text } from "@chakra-ui/react"; + +import { useProgressStateStore } from "@trustgraph/react-state"; + +const Progress: React.FC = () => { + const activity = useProgressStateStore((state) => state.activity); + + return ( + <> + {activity.size > 0 && ( + + {Array.from(activity) + .slice(0, 4) + .map((a, ix) => ( + + {a}... + + ))} + + )} + + ); +}; + +export default Progress; diff --git a/ai-context/workbench-ui/src/components/common/ProgressSubmitButton.tsx b/ai-context/workbench-ui/src/components/common/ProgressSubmitButton.tsx new file mode 100644 index 00000000..4dface22 --- /dev/null +++ b/ai-context/workbench-ui/src/components/common/ProgressSubmitButton.tsx @@ -0,0 +1,33 @@ +import React from "react"; + +import { SendHorizontal } from "lucide-react"; + +import { Box, Button } from "@chakra-ui/react"; + +interface ProgressSubmitButtonProps { + disabled: boolean; + working: boolean; + onClick: () => void; +} + +const ProgressSubmitButton: React.FC = ({ + disabled, + working, + onClick, +}) => { + return ( + + + + ); +}; + +export default ProgressSubmitButton; diff --git a/ai-context/workbench-ui/src/components/common/RecommendedBadge.tsx b/ai-context/workbench-ui/src/components/common/RecommendedBadge.tsx new file mode 100644 index 00000000..8e45393b --- /dev/null +++ b/ai-context/workbench-ui/src/components/common/RecommendedBadge.tsx @@ -0,0 +1,11 @@ +import { Badge } from "@chakra-ui/react"; + +const RecommendedBadge = () => { + return ( + + recommended + + ); +}; + +export default RecommendedBadge; diff --git a/ai-context/workbench-ui/src/components/common/SelectField.tsx b/ai-context/workbench-ui/src/components/common/SelectField.tsx new file mode 100644 index 00000000..d9e2c08b --- /dev/null +++ b/ai-context/workbench-ui/src/components/common/SelectField.tsx @@ -0,0 +1,92 @@ +/* + * CRITICAL: DO NOT MODIFY THIS COMPONENT WITHOUT DESIGN AUTHORITY APPROVAL + * + * This SelectField component is used throughout the application by 15+ components + * across multiple domains (ontologies, flows, documents, etc.). Any changes to + * this component's interface or behavior will have extensive downstream impact. + * + * Changes to this component in September 2025 broke multiple features and required + * systematic updates across the entire application. Always prefer adapter patterns + * or feature-specific solutions over modifying this shared infrastructure. + * + * Required API contract: + * - value: string[] (arrays only) + * - onValueChange: (values: string[]) => void + * - items must include description fields with SelectOptionText/SelectOption + */ + +import React, { useMemo } from "react"; +import { + Field, + Select, + Portal, + Stack, + createListCollection, +} from "@chakra-ui/react"; + +export interface SelectFieldValue { + value: string; + label: string; + description?: string | React.ReactElement; +} + +interface SelectFieldProps { + label: string; + + items: SelectFieldValue[]; + + value: string[]; + onValueChange: (x: string[]) => void; + + contentRef?; +} + +const SelectField: React.FC = ({ + label, + items, + value, + onValueChange, + contentRef, +}) => { + // Only create new collection when items actually change + const collection = useMemo( + () => createListCollection({ items: items }), + [items], + ); + + return ( + + {label} + + onValueChange(e.value)} + > + + + + + + + + + + + + + {items.map((v) => ( + + {v.description && v.description} + + + ))} + + + + + + ); +}; + +export default SelectField; diff --git a/ai-context/workbench-ui/src/components/common/SelectOption.tsx b/ai-context/workbench-ui/src/components/common/SelectOption.tsx new file mode 100644 index 00000000..40422d13 --- /dev/null +++ b/ai-context/workbench-ui/src/components/common/SelectOption.tsx @@ -0,0 +1,34 @@ +/* + * CRITICAL: DO NOT MODIFY THIS COMPONENT WITHOUT DESIGN AUTHORITY APPROVAL + * + * This SelectOption component is used by SelectField throughout the application. + * Changes to this component's interface or styling will affect all dropdown + * options across multiple domains. Any modifications require extensive testing + * and approval from the application design authority. + */ + +import React, { PropsWithChildren } from "react"; +import { Box, Flex, Heading, Text } from "@chakra-ui/react"; + +const SelectOption: React.FC< + PropsWithChildren<{ + title: string; + badge?: React.ReactNode; + }> +> = ({ title, badge, children }) => { + return ( + + + + {title} + + {badge && badge} + + + {children} + + + ); +}; + +export default SelectOption; diff --git a/ai-context/workbench-ui/src/components/common/SelectOptionText.tsx b/ai-context/workbench-ui/src/components/common/SelectOptionText.tsx new file mode 100644 index 00000000..34d79069 --- /dev/null +++ b/ai-context/workbench-ui/src/components/common/SelectOptionText.tsx @@ -0,0 +1,23 @@ +/* + * CRITICAL: DO NOT MODIFY THIS COMPONENT WITHOUT DESIGN AUTHORITY APPROVAL + * + * This SelectOptionText component is used by SelectField throughout the application. + * Changes to this component's interface or styling will affect all dropdown + * options across multiple domains. Any modifications require extensive testing + * and approval from the application design authority. + */ + +import React from "react"; +import { Text } from "@chakra-ui/react"; + +const SelectOptionText: React.FC<{ + children: React.ReactNode; +}> = ({ children }) => { + return ( + + {children} + + ); +}; + +export default SelectOptionText; diff --git a/ai-context/workbench-ui/src/components/common/SelectableTable.tsx b/ai-context/workbench-ui/src/components/common/SelectableTable.tsx new file mode 100644 index 00000000..f080caa4 --- /dev/null +++ b/ai-context/workbench-ui/src/components/common/SelectableTable.tsx @@ -0,0 +1,40 @@ +import { Table } from "@chakra-ui/react"; +import { flexRender } from "@tanstack/react-table"; + +const SelectableTable = ({ table }) => { + return ( + <> + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} + + ))} + + ))} + + + {table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} + + ))} + + + + ); +}; + +export default SelectableTable; diff --git a/ai-context/workbench-ui/src/components/common/SimplePage.tsx b/ai-context/workbench-ui/src/components/common/SimplePage.tsx new file mode 100644 index 00000000..adb35715 --- /dev/null +++ b/ai-context/workbench-ui/src/components/common/SimplePage.tsx @@ -0,0 +1,45 @@ +import React, { PropsWithChildren } from "react"; + +import { Box, Container, Flex, Heading, Stack } from "@chakra-ui/react"; + +import UnauthedHeader from "./UnauthedHeader"; + +const SimplePage: React.FC< + PropsWithChildren<{ + title: string; + }> +> = ({ title, children }) => { + return ( + <> + + + + + + + {title} + + + {children} + + + + + + ); +}; + +export default SimplePage; diff --git a/ai-context/workbench-ui/src/components/common/Slider.tsx b/ai-context/workbench-ui/src/components/common/Slider.tsx new file mode 100644 index 00000000..506b605a --- /dev/null +++ b/ai-context/workbench-ui/src/components/common/Slider.tsx @@ -0,0 +1,45 @@ +import React from "react"; + +import { Field, Slider as ChakraSlider } from "@chakra-ui/react"; + +interface SliderProps { + label: string; + minValue: number; + maxValue: number; + value: number; + step: number; + onValueChange: (x: number) => void; +} + +const Slider: React.FC = ({ + label, + minValue, + maxValue, + value, + onValueChange, + step, +}) => { + return ( + + {label} + onValueChange(e.value[0])} + width="100%" + > + + + + + + + + + + ); +}; + +export default Slider; diff --git a/ai-context/workbench-ui/src/components/common/StatusBadge.tsx b/ai-context/workbench-ui/src/components/common/StatusBadge.tsx new file mode 100644 index 00000000..29f5a936 --- /dev/null +++ b/ai-context/workbench-ui/src/components/common/StatusBadge.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import { Badge, BadgeProps } from "@chakra-ui/react"; +type StatusType = "success" | "warning" | "error" | "info" | "default"; +interface StatusBadgeProps extends Omit { + status: StatusType; + label: string; +} +const StatusBadge: React.FC = ({ + status, + label, + ...rest +}) => { + const colorSchemes: Record = { + success: "#65c97a", + warning: "orange", + error: "red", + info: "#5285ed", + default: "gray", + }; + return ( + + {label} + + ); +}; +export default StatusBadge; diff --git a/ai-context/workbench-ui/src/components/common/TableStates.tsx b/ai-context/workbench-ui/src/components/common/TableStates.tsx new file mode 100644 index 00000000..d09a4bbc --- /dev/null +++ b/ai-context/workbench-ui/src/components/common/TableStates.tsx @@ -0,0 +1,36 @@ +import React from "react"; +import { Box, Text, Center } from "@chakra-ui/react"; + +interface ErrorStateProps { + error: Error | unknown; + title?: string; +} + +export const ErrorState: React.FC = ({ + error, + title = "Error loading data", +}) => ( + + + {title}: {error?.toString()} + + +); + +interface EmptyStateProps { + message?: string; +} + +export const EmptyState: React.FC = ({ + message = "No data found.", +}) => ( +
+ {message} +
+); diff --git a/ai-context/workbench-ui/src/components/common/TableWithStates.tsx b/ai-context/workbench-ui/src/components/common/TableWithStates.tsx new file mode 100644 index 00000000..02e0efff --- /dev/null +++ b/ai-context/workbench-ui/src/components/common/TableWithStates.tsx @@ -0,0 +1,55 @@ +import React from "react"; +import { Box } from "@chakra-ui/react"; +import { Table } from "@tanstack/react-table"; +import { ErrorState, EmptyState } from "./TableStates"; +import BasicTable from "./BasicTable"; +import ClickableTable from "./ClickableTable"; + +interface TableWithStatesProps { + table: Table; + data: T[]; + error?: Error | unknown; + onClick?: (row: T) => void; + emptyMessage?: string; + errorTitle?: string; + bordered?: boolean; +} + +const TableWithStates = ({ + table, + data, + error, + onClick, + emptyMessage = "No data found.", + errorTitle = "Error loading data", + bordered = true, +}: TableWithStatesProps) => { + // Handle error state + if (error) { + return ; + } + + // Handle empty state + if (data.length === 0) { + return ; + } + + // Render table with optional border wrapper + const TableComponent = onClick ? ( + onClick(row.original)} /> + ) : ( + + ); + + if (bordered) { + return ( + + {TableComponent} + + ); + } + + return TableComponent; +}; + +export default TableWithStates; diff --git a/ai-context/workbench-ui/src/components/common/TextAreaField.tsx b/ai-context/workbench-ui/src/components/common/TextAreaField.tsx new file mode 100644 index 00000000..44b79493 --- /dev/null +++ b/ai-context/workbench-ui/src/components/common/TextAreaField.tsx @@ -0,0 +1,40 @@ +import React from "react"; + +import { Field, Textarea } from "@chakra-ui/react"; + +interface TextFieldProps { + label: string; + placeholder?: string; + value: string; + onValueChange: (x: string) => void; + required?: boolean; + disabled?: boolean; +} + +const TextAreaField: React.FC = ({ + label, + placeholder, + value, + onValueChange, + required, + disabled, +}) => { + return ( + + + {label} {required && } + +