create-lykomi-app 0.9.21 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -18
- package/dist/index.mjs +4 -4
- package/package.json +4 -4
- package/presets/foundry-iq/files/AGENTS.md +4 -4
- package/presets/foundry-iq/files/README.md +3 -3
- package/presets/foundry-iq/files/agent/app/agent.py +1 -1
- package/presets/foundry-iq/files/agent/app/foundry_iq.py +1 -1
- package/presets/foundry-iq/files/agent/app/main.py +1 -1
- package/presets/foundry-iq/files/src/main.tsx +4 -4
- package/presets/foundry-iq/files/src/pages/chat-page.tsx +1 -1
- package/presets/foundry-iq/files/src/tools/foundry-iq-module.tsx +1 -1
- package/presets/foundry-iq/files/src/tools/search-results-tool-ui.tsx +2 -2
- package/presets/neo4j/files/AGENTS.md +6 -6
- package/presets/neo4j/files/README.md +2 -2
- package/presets/neo4j/files/agent/app/graph_payload.py +2 -2
- package/presets/neo4j/files/agent/app/graph_tools.py +1 -1
- package/presets/neo4j/files/agent/app/main.py +1 -1
- package/presets/neo4j/files/agent/tests/test_graph_payload.py +1 -1
- package/presets/neo4j/files/src/index.css +7 -7
- package/presets/neo4j/files/src/main.tsx +3 -3
- package/presets/neo4j/files/src/pages/chat-page.tsx +1 -1
- package/presets/neo4j/files/src/tools/graph-module.tsx +2 -2
- package/presets/neo4j/files/vite.config.ts +10 -10
- package/presets/neo4j/manifest.json +1 -1
- package/template/AGENTS.md +17 -12
- package/template/README.md +32 -41
- package/template/agent/_env.example +1 -1
- package/template/agent/app/agent.py +1 -1
- package/template/agent/app/main.py +11 -1
- package/template/agent/app/tools.py +1 -1
- package/template/index.html +1 -1
- package/template/package.json +7 -6
- package/template/public/favicon-dark.svg +1 -0
- package/template/public/favicon.svg +1 -4
- package/template/public/fonts/Manrope-Variable.ttf +0 -0
- package/template/public/fonts/OFL.txt +93 -0
- package/template/src/index.css +15 -6
- package/template/src/main.tsx +11 -15
- package/template/src/pages/chat-page.tsx +2 -8
- package/template/src/pages/guide-page.tsx +134 -0
- package/template/src/pages/guide-windows.ts +32 -0
- package/template/src/pages/index.tsx +5 -4
- package/template/src/pages/starter-prompts.tsx +30 -0
- package/template/src/starter-module.ts +51 -0
- package/template/src/theme.ts +82 -0
- package/template/vite.config.ts +9 -9
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# create-lykomi-app
|
|
2
2
|
|
|
3
|
-
Create a
|
|
3
|
+
Create a LYKOMI UI agent app: React chat UI, AG-UI protocol, Microsoft Agent Framework, and [Microsoft Foundry](https://learn.microsoft.com/azure/foundry/).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
LYKOMI UI is maintained at [lykomi/ui](https://github.com/lykomi/ui).
|
|
6
6
|
|
|
7
7
|
## Create An App
|
|
8
8
|
|
|
@@ -10,6 +10,7 @@ Foundry UI is built by [iLoveAgents](https://iloveagents.ai).
|
|
|
10
10
|
npm create lykomi-app my-agent-app
|
|
11
11
|
cd my-agent-app
|
|
12
12
|
cp agent/.env.example agent/.env
|
|
13
|
+
# Edit agent/.env with your project endpoint and model deployment.
|
|
13
14
|
az login
|
|
14
15
|
pnpm dev
|
|
15
16
|
```
|
|
@@ -20,7 +21,7 @@ Set these in `agent/.env`:
|
|
|
20
21
|
|
|
21
22
|
```bash
|
|
22
23
|
FOUNDRY_PROJECT_ENDPOINT=https://<account>.services.ai.azure.com/api/projects/<project>
|
|
23
|
-
FOUNDRY_MODEL
|
|
24
|
+
FOUNDRY_MODEL=<model-deployment-name>
|
|
24
25
|
```
|
|
25
26
|
|
|
26
27
|
The generated app starts:
|
|
@@ -29,18 +30,20 @@ The generated app starts:
|
|
|
29
30
|
- Agent service: `http://localhost:8020`
|
|
30
31
|
- AG-UI endpoint: `/api/agent`
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
Home opens with a chat composer and clickable prompts. Guide demonstrates a page beside the persistent chat. The bundled agent can call `hello_world` and `get_weather`.
|
|
34
|
+
|
|
35
|
+
The lower interface menu links to the Guide in local demo mode. Connecting a real auth provider makes it the signed-in user menu.
|
|
33
36
|
|
|
34
37
|
## Theme The App
|
|
35
38
|
|
|
36
|
-
Generated apps
|
|
39
|
+
Generated apps use LYKOMI's neutral and deep-petrol palette, light and dark V2 favicons, a locally bundled Manrope font, and `LYKOMI UI` for the shell and browser title. Edit `src/theme.ts` to change branding, colors, radius, and typography.
|
|
37
40
|
|
|
38
41
|
## Two Ways To Use It
|
|
39
42
|
|
|
40
|
-
| Mode | Use when
|
|
41
|
-
| ---------------------- |
|
|
42
|
-
| Package-backed app | You want central
|
|
43
|
-
| GitHub template / copy | You want to fully own and reshape the app.
|
|
43
|
+
| Mode | Use when | Update story |
|
|
44
|
+
| ---------------------- | --------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
|
45
|
+
| Package-backed app | You want central LYKOMI UI fixes and a small local app surface. | Keep the generated app and update `@lykomi/*` packages with your package manager. |
|
|
46
|
+
| GitHub template / copy | You want to fully own and reshape the app. | Copy the generated app into your own template repo. You can customize everything, but you manually pull upstream ideas. |
|
|
44
47
|
|
|
45
48
|
The package-backed path updates shared UI, shell, theme resources, and AG-UI transport through npm packages. It does not overwrite local files like `agent/app/tools.py`, `src/main.tsx`, or your README.
|
|
46
49
|
|
|
@@ -71,19 +74,19 @@ The generated `AGENTS.md` explains the local project contract for coding agents.
|
|
|
71
74
|
|
|
72
75
|
## Presets
|
|
73
76
|
|
|
74
|
-
| Preset | What you get
|
|
75
|
-
| ------------ |
|
|
76
|
-
| `default` | Chat-first
|
|
77
|
-
| `foundry-iq` | Default plus an Azure AI Search Foundry IQ knowledge base retrieval tool.
|
|
77
|
+
| Preset | What you get |
|
|
78
|
+
| ------------ | -------------------------------------------------------------------------------------- |
|
|
79
|
+
| `default` | Chat-first LYKOMI UI app with FastAPI Agent Framework service and hello/weather tools. |
|
|
80
|
+
| `foundry-iq` | Default plus an Azure AI Search Foundry IQ knowledge base retrieval tool. |
|
|
78
81
|
|
|
79
82
|
## Packages
|
|
80
83
|
|
|
81
|
-
The starter consumes the
|
|
84
|
+
The starter consumes the LYKOMI UI packages from npm:
|
|
82
85
|
|
|
83
|
-
- `@
|
|
84
|
-
- `@
|
|
85
|
-
- `@
|
|
86
|
-
- `@
|
|
86
|
+
- `@lykomi/web-shell` - browser shell and `bootstrapShell`
|
|
87
|
+
- `@lykomi/web-ui` - chat UI, AG-UI adapter, tool panels, theme resource
|
|
88
|
+
- `@lykomi/web-primitives` - shared UI primitives
|
|
89
|
+
- `@lykomi/agent` - AG-UI client transport
|
|
87
90
|
|
|
88
91
|
The packages publish built ESM JavaScript and `.d.ts` declarations. The
|
|
89
92
|
generated Vite app is already configured to scan their Tailwind classes from the
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{copyFileSync as e,existsSync as t,mkdirSync as n,readFileSync as r,readdirSync as i,statSync as a,writeFileSync as o}from"node:fs";import{basename as s,dirname as c,join as l,resolve as u}from"node:path";import{spawnSync as d}from"node:child_process";import*as f from"@clack/prompts";import{readFile as p}from"node:fs/promises";import{fileURLToPath as m}from"node:url";const h=new Set([`.ts`,`.tsx`,`.js`,`.jsx`,`.mjs`,`.cjs`,`.md`,`.css`,`.html`,`.yml`,`.yaml`,`.toml`,`.env`,`.sh`,`.py`]);function g(e){let t=e.lastIndexOf(`.`);return t===-1?!0:h.has(e.slice(t))}function _(t){let{srcDir:s,dstDir:c,projectName:u,
|
|
3
|
-
`);continue}if(g(m)){o(_,r(i,`utf-8`).split(`__PROJECT_NAME__`).join(u));continue}e(i,_)}}p(s,c,0)}async function v(e){_({srcDir:e.sourceDir,dstDir:e.targetDir,projectName:e.projectName,
|
|
4
|
-
`)}function x(e){let t={};for(let n of Object.keys(e).sort())t[n]=e[n];return t}const S=[`@
|
|
2
|
+
import{copyFileSync as e,existsSync as t,mkdirSync as n,readFileSync as r,readdirSync as i,statSync as a,writeFileSync as o}from"node:fs";import{basename as s,dirname as c,join as l,resolve as u}from"node:path";import{spawnSync as d}from"node:child_process";import*as f from"@clack/prompts";import{readFile as p}from"node:fs/promises";import{fileURLToPath as m}from"node:url";const h=new Set([`.ts`,`.tsx`,`.js`,`.jsx`,`.mjs`,`.cjs`,`.md`,`.css`,`.html`,`.yml`,`.yaml`,`.toml`,`.env`,`.sh`,`.py`]);function g(e){let t=e.lastIndexOf(`.`);return t===-1?!0:h.has(e.slice(t))}function _(t){let{srcDir:s,dstDir:c,projectName:u,lykomiPackageVersion:d,rewriteRootPackageJson:f}=t;function p(t,s,c){for(let m of i(t)){let i=l(t,m),h=a(i),_=l(s,m.startsWith(`_`)&&!m.startsWith(`__`)&&m!==`_`?`.`+m.slice(1):m);if(h.isDirectory()){n(_,{recursive:!0}),p(i,_,c+1);continue}if(f&&c===0&&m===`package.json`){let e=JSON.parse(r(i,`utf-8`));e.name=u,d&&C(e,d),o(_,JSON.stringify(e,null,2)+`
|
|
3
|
+
`);continue}if(g(m)){o(_,r(i,`utf-8`).split(`__PROJECT_NAME__`).join(u));continue}e(i,_)}}p(s,c,0)}async function v(e){_({srcDir:e.sourceDir,dstDir:e.targetDir,projectName:e.projectName,lykomiPackageVersion:e.lykomiPackageVersion,rewriteRootPackageJson:!0})}async function y(e){_({srcDir:e.overlayDir,dstDir:e.targetDir,projectName:e.projectName,rewriteRootPackageJson:!1})}async function b(e){if(!e.extraDependencies&&!e.extraDevDependencies&&!e.extraScripts)return;let t=l(e.targetDir,`package.json`),n=JSON.parse(r(t,`utf-8`));e.extraDependencies&&(n.dependencies=x({...n.dependencies??{},...e.extraDependencies})),e.extraDevDependencies&&(n.devDependencies=x({...n.devDependencies??{},...e.extraDevDependencies})),e.extraScripts&&(n.scripts={...n.scripts??{},...e.extraScripts}),o(t,JSON.stringify(n,null,2)+`
|
|
4
|
+
`)}function x(e){let t={};for(let n of Object.keys(e).sort())t[n]=e[n];return t}const S=[`@lykomi/agent`,`@lykomi/web-graph`,`@lykomi/web-primitives`,`@lykomi/web-shell`,`@lykomi/web-ui`];function C(e,t){for(let n of[`dependencies`,`devDependencies`]){let r=e[n];if(!r||typeof r!=`object`||Array.isArray(r))continue;let i=r;for(let e of S)e in i&&(i[e]=t)}}async function w(e){if(e===`default`)return{name:e,templateDir:T()};if(e===`foundry-iq`||e===`neo4j`)return await D({name:e,presetRoot:E(e)});throw Error(`Unknown preset: ${e}`)}function T(){let e=c(m(import.meta.url)),n=e;for(let e=0;e<5;e++){let e=l(n,`template`);if(t(e))return e;let r=c(n);if(r===n)break;n=r}throw Error(`Could not locate bundled template/ relative to ${e}. Did you build the CLI? Run \`pnpm build\` in packages/create-lykomi-app.`)}function E(e){let n=c(m(import.meta.url)),r=n;for(let n=0;n<5;n++){let n=l(r,`presets`,e);if(t(n))return n;let i=c(r);if(i===r)break;r=i}throw Error(`Could not locate bundled presets/${e}/ relative to ${n}. Did you build the CLI? Run \`pnpm build\` in packages/create-lykomi-app.`)}async function D(e){let n=l(e.presetRoot,`manifest.json`);if(!t(n))throw Error(`Bundled preset '${e.name}' has no manifest.json.`);let r=JSON.parse(await p(n,`utf-8`));return O(e.name,e.presetRoot,r)}function O(e,n,r){if(r.extends&&r.extends!==`default`)throw Error(`Preset '${e}' extends '${r.extends}', but only 'default' is supported.`);let i=r.filesDir??`files`,a=l(n,i);if(!t(a))throw Error(r.filesDir===void 0?`Preset '${e}' has no '${i}/' directory at the package root (default filesDir).`:`Preset '${e}' declares filesDir='${i}' but the directory is missing in the package.`);return{name:e,templateDir:T(),overlayDir:a,extraDependencies:r.extraDependencies,extraDevDependencies:r.extraDevDependencies,extraScripts:r.extraScripts,envVars:r.envVars,postInstall:r.postInstall}}function k(e){let t={targetDir:null,preset:`default`,install:!0,force:!1,help:!1};for(let n=0;n<e.length;n++){let r=e[n];switch(r){case`-h`:case`--help`:t.help=!0;break;case`--no-install`:t.install=!1;break;case`--force`:t.force=!0;break;case`--preset`:t.preset=e[++n]??`default`;break;default:if(r.startsWith(`--`))throw Error(`Unknown flag: ${r}`);t.targetDir===null&&(t.targetDir=r)}}return t}function A(){console.log(`Usage: create-lykomi-app <target-dir> [options]
|
|
5
5
|
|
|
6
6
|
Scaffold a new LYKOMI app — AG-UI protocol + Microsoft Agent Framework + Azure Foundry.
|
|
7
7
|
|
|
@@ -22,4 +22,4 @@ Example:
|
|
|
22
22
|
`+t.map(e=>` - ${e.name}: ${e.description}`).join(`
|
|
23
23
|
`)),n.length>0&&f.log.info(`Optional env vars:
|
|
24
24
|
`+n.map(e=>` - ${e.name}: ${e.description}`).join(`
|
|
25
|
-
`))}function M(e){let t=l(e,`package.json`),n=JSON.parse(r(t,`utf-8`)).dependencies?.[`@
|
|
25
|
+
`))}function M(e){let t=l(e,`package.json`),n=JSON.parse(r(t,`utf-8`)).dependencies?.[`@lykomi/agent`];if(!n)throw Error(`Template package.json is missing @lykomi/agent.`);if(!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(n))throw Error(`Template @lykomi/agent must use an exact version, got '${n}'.`);return n}async function N(){let e=k(process.argv.slice(2));if(e.help)return A(),0;let r=e.targetDir;if(!r){let e=await f.text({message:`Project directory:`,placeholder:`my-lykomi-app`,validate:e=>!e||e.length===0?`Required`:void 0});if(f.isCancel(e))return f.cancel(`Cancelled.`),1;r=String(e)}let a=u(process.cwd(),r),o=t(a);if(o&&!e.force&&i(a).length>0)return f.log.error(`Target ${a} is not empty. Use --force to scaffold over it.`),1;o||n(a,{recursive:!0}),f.log.step(`Scaffolding into ${a}`),f.log.step(`Preset: ${e.preset}`);let c=await w(e.preset),l=s(a).replace(/^[^a-zA-Z]+/,``).replace(/[^a-zA-Z0-9-_]/g,`-`)||`my-app`;if(await v({sourceDir:c.templateDir,targetDir:a,projectName:l,lykomiPackageVersion:M(c.templateDir)}),c.overlayDir&&await y({overlayDir:c.overlayDir,targetDir:a,projectName:l}),await b({targetDir:a,extraDependencies:c.extraDependencies,extraDevDependencies:c.extraDevDependencies,extraScripts:c.extraScripts}),f.log.success(`Files copied (preset: ${c.name}).`),e.install){let e=f.spinner();e.start(`pnpm install`);let t=d(`pnpm`,[`install`],{cwd:a,stdio:`inherit`});if(t.status!==0)return e.error(`pnpm install failed`),f.log.warn("Scaffold completed but install failed. cd into the dir and re-run `pnpm install`."),t.status??1;e.stop(`pnpm install done`)}else f.log.info(`Skipped pnpm install (--no-install).`);return j(c.envVars),f.outro(`✓ Done. Next:\n cd ${r}\n pnpm dev`),0}N().then(e=>process.exit(e),e=>{console.error(e instanceof Error?e.message:e),process.exit(1)});export{};
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-lykomi-app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Scaffold a new LYKOMI app — AG-UI protocol + Microsoft Agent Framework + Azure Foundry, ready to run.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"create-app",
|
|
8
|
-
"
|
|
8
|
+
"lykomi-ui",
|
|
9
9
|
"ag-ui",
|
|
10
10
|
"agent-framework",
|
|
11
11
|
"azure-foundry",
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
"repository": {
|
|
27
27
|
"type": "git",
|
|
28
28
|
"url": "git+https://github.com/lykomi/ui.git",
|
|
29
|
-
"directory": "packages/create-
|
|
29
|
+
"directory": "packages/create-lykomi-app"
|
|
30
30
|
},
|
|
31
|
-
"homepage": "https://github.com/lykomi/ui/tree/main/packages/create-
|
|
31
|
+
"homepage": "https://github.com/lykomi/ui/tree/main/packages/create-lykomi-app#readme",
|
|
32
32
|
"bugs": "https://github.com/lykomi/ui/issues",
|
|
33
33
|
"publishConfig": {
|
|
34
34
|
"access": "public"
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
This is a
|
|
1
|
+
This is a LYKOMI UI starter app with Foundry IQ retrieval: React chat UI, AG-UI transport, Microsoft Agent Framework, and an Azure AI Search knowledge base MCP tool.
|
|
2
2
|
|
|
3
3
|
# Architecture
|
|
4
4
|
|
|
5
5
|
```text
|
|
6
|
-
src/main.tsx
|
|
6
|
+
src/main.tsx LYKOMI UI shell bootstrap
|
|
7
7
|
src/pages/chat-page.tsx Chat screen and Foundry IQ starter prompts
|
|
8
|
-
src/index.css Tailwind v4 plus @
|
|
8
|
+
src/index.css Tailwind v4 plus @lykomi/web-ui/styles.css
|
|
9
9
|
src/tools/ Foundry IQ search result tool card and ChatModule registration
|
|
10
10
|
agent/app/main.py FastAPI app, AG-UI endpoint, and request header capture
|
|
11
11
|
agent/app/agent.py FoundryChatClient + Agent configuration
|
|
@@ -60,7 +60,7 @@ FOUNDRY_IQ_KNOWLEDGE_BASE=<knowledge-base-name>
|
|
|
60
60
|
- Keep the `foundry_iq_search` tool card and activity viewer in `src/tools/search-results-tool-ui.tsx`. If the tool output shape changes, normalize it there instead of leaking Foundry IQ-specific parsing into the shared package.
|
|
61
61
|
- Configure app name, browser title, logos, colors, radius, and fonts through `theme` layers in `src/main.tsx`.
|
|
62
62
|
- Keep `@source "../node_modules/@iloveagents/.../dist"` entries in `src/index.css`; Tailwind v4 needs them to scan package component classes.
|
|
63
|
-
- Keep `@import "@
|
|
63
|
+
- Keep `@import "@lykomi/web-ui/styles.css";`; it provides the LYKOMI UI theme tokens.
|
|
64
64
|
|
|
65
65
|
# Checks
|
|
66
66
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# __PROJECT_NAME__
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
LYKOMI UI starter with a Microsoft Agent Framework service, AG-UI chat, and a Foundry IQ knowledge base tool.
|
|
4
4
|
|
|
5
5
|
## Quick Start
|
|
6
6
|
|
|
@@ -124,10 +124,10 @@ Restart `pnpm dev` after changing Python dependencies or environment variables.
|
|
|
124
124
|
agent/ FastAPI + Microsoft Agent Framework AG-UI endpoint
|
|
125
125
|
agent/app/foundry_iq.py Foundry IQ MCP retrieval tool
|
|
126
126
|
agent/app/tools.py Add local agent tools here
|
|
127
|
-
src/main.tsx
|
|
127
|
+
src/main.tsx LYKOMI UI shell bootstrap
|
|
128
128
|
src/pages/chat-page.tsx Chat screen and quick starts
|
|
129
129
|
src/tools/ Custom tool cards registered through ChatModules
|
|
130
|
-
src/index.css Tailwind v4 +
|
|
130
|
+
src/index.css Tailwind v4 + LYKOMI UI theme resource and package dist scanning
|
|
131
131
|
```
|
|
132
132
|
|
|
133
133
|
## Auth
|
|
@@ -16,7 +16,7 @@ def get_agent() -> Agent:
|
|
|
16
16
|
client=client,
|
|
17
17
|
name="FoundryIQAgent",
|
|
18
18
|
instructions=(
|
|
19
|
-
"You are a concise assistant for a
|
|
19
|
+
"You are a concise assistant for a LYKOMI UI app. "
|
|
20
20
|
"Use foundry_iq_search when the user asks about knowledge-base content, "
|
|
21
21
|
"documents, SharePoint-backed content, or retrieval-backed answers. "
|
|
22
22
|
"Use hello_world when the user asks for hello world or a hello demo. "
|
|
@@ -316,7 +316,7 @@ async def _call_knowledge_base(query: str) -> str:
|
|
|
316
316
|
"params": {
|
|
317
317
|
"protocolVersion": MCP_PROTOCOL_VERSION,
|
|
318
318
|
"capabilities": {},
|
|
319
|
-
"clientInfo": {"name": "
|
|
319
|
+
"clientInfo": {"name": "lykomi-ui-starter", "version": "0.0.0"},
|
|
320
320
|
},
|
|
321
321
|
},
|
|
322
322
|
)
|
|
@@ -36,7 +36,7 @@ async def foundry_iq_security_trimming_context(request: Request, call_next):
|
|
|
36
36
|
STATE_SCHEMA = {
|
|
37
37
|
"navigation": {
|
|
38
38
|
"type": "object",
|
|
39
|
-
"description": "Current app route and navigation metadata from
|
|
39
|
+
"description": "Current app route and navigation metadata from LYKOMI UI.",
|
|
40
40
|
"additionalProperties": True,
|
|
41
41
|
},
|
|
42
42
|
"selectedItemId": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./index.css";
|
|
2
|
-
import { bootstrapShell } from "@
|
|
3
|
-
import type { AuthAdapter } from "@
|
|
2
|
+
import { bootstrapShell } from "@lykomi/web-shell";
|
|
3
|
+
import type { AuthAdapter } from "@lykomi/web-shell";
|
|
4
4
|
import { customerPages } from "@/pages/index.tsx";
|
|
5
5
|
import { foundryIqModule } from "@/tools/foundry-iq-module.tsx";
|
|
6
6
|
|
|
@@ -18,8 +18,8 @@ bootstrapShell({
|
|
|
18
18
|
id: "app-branding",
|
|
19
19
|
definition: {
|
|
20
20
|
branding: {
|
|
21
|
-
appName: "
|
|
22
|
-
appTitle: "
|
|
21
|
+
appName: "LYKOMI UI",
|
|
22
|
+
appTitle: "LYKOMI UI",
|
|
23
23
|
},
|
|
24
24
|
},
|
|
25
25
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { makeAssistantToolUI } from "@assistant-ui/react";
|
|
2
|
-
import { cn } from "@
|
|
3
|
-
import { ToolCallCard, type ToolCallStatus } from "@
|
|
2
|
+
import { cn } from "@lykomi/web-primitives";
|
|
3
|
+
import { ToolCallCard, type ToolCallStatus } from "@lykomi/web-ui";
|
|
4
4
|
import { Brain, Clock3, Database, ExternalLink, FileText, Search, Wrench } from "lucide-react";
|
|
5
5
|
|
|
6
6
|
interface SearchToolArgs {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
This is a
|
|
1
|
+
This is a LYKOMI UI starter that gives an agent a Neo4j graph: React chat UI, AG-UI transport, Microsoft Agent Framework, and graph tools whose results render as an interactive graph.
|
|
2
2
|
|
|
3
3
|
# Architecture
|
|
4
4
|
|
|
5
5
|
```text
|
|
6
|
-
src/main.tsx
|
|
6
|
+
src/main.tsx LYKOMI UI shell bootstrap
|
|
7
7
|
src/pages/chat-page.tsx Chat screen and graph starter prompts
|
|
8
|
-
src/index.css Tailwind v4 +
|
|
8
|
+
src/index.css Tailwind v4 + web-ui and web-graph styles
|
|
9
9
|
src/tools/graph-module.tsx Binds tool names to the graph card; registers the panel renderer
|
|
10
10
|
vite.config.ts manualChunks — the vendor-graph rule MUST stay above the react rule
|
|
11
11
|
agent/app/main.py FastAPI app, AG-UI endpoint, driver lifespan
|
|
@@ -65,8 +65,8 @@ Aura instead of local: change `NEO4J_URI` to `neo4j+s://<instance-id>.databases.
|
|
|
65
65
|
interface, so on a shared network anyone routable could read and rewrite the
|
|
66
66
|
graph.
|
|
67
67
|
- **Parameters, never string interpolation.** Cypher is built as a constant with `$name` placeholders. No f-strings, no `.format()`, no `%`.
|
|
68
|
-
- **`vite.config.ts` chunk order.** `react-force-graph-2d`, `react-kapsule` and `
|
|
69
|
-
- **`src/index.css` needs the `@source` line for `
|
|
68
|
+
- **`vite.config.ts` chunk order.** `react-force-graph-2d`, `react-kapsule` and `web-graph` all contain "react"; the `vendor-graph` rule has to come before `id.includes("react")` or the whole force engine lands in `vendor-react`.
|
|
69
|
+
- **`src/index.css` needs the `@source` line for `web-graph/dist`.** Tailwind v4 cannot infer classes from package output without it, and the graph chrome renders unstyled.
|
|
70
70
|
|
|
71
71
|
# Adding a graph tool
|
|
72
72
|
|
|
@@ -87,4 +87,4 @@ The agent can also call `show_graph` with a payload to put any graph in the side
|
|
|
87
87
|
|
|
88
88
|
# Where the visualization lives
|
|
89
89
|
|
|
90
|
-
`@
|
|
90
|
+
`@lykomi/web-graph`, an npm package. It is source-neutral: it takes the payload contract and knows nothing about Neo4j or Cypher. Its `@lykomi/web-graph/adapters/neo4j` entry point converts driver-shaped records in the browser, for apps that query a graph directly rather than through an agent.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# __PROJECT_NAME__
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A LYKOMI UI starter that gives an agent a Neo4j graph: it reads the schema,
|
|
4
4
|
writes its own Cypher, and every result comes back as an interactive graph
|
|
5
5
|
rather than a wall of JSON.
|
|
6
6
|
|
|
@@ -182,6 +182,6 @@ scripts/seed_graph.py the demo graph
|
|
|
182
182
|
docker-compose.yml local Neo4j (development only)
|
|
183
183
|
```
|
|
184
184
|
|
|
185
|
-
The visualization itself is `@
|
|
185
|
+
The visualization itself is `@lykomi/web-graph`, which knows
|
|
186
186
|
nothing about Neo4j — it takes a source-neutral payload. You can point it at
|
|
187
187
|
any graph data you have.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Neo4j driver records -> the ``foundry.graph`` payload the UI renders.
|
|
2
2
|
|
|
3
|
-
The contract is defined by ``@
|
|
3
|
+
The contract is defined by ``@lykomi/web-graph`` and is
|
|
4
4
|
deliberately source-neutral: the visualizer never learns that Neo4j exists.
|
|
5
5
|
Keep the field names here in step with ``GraphPayload`` in that package.
|
|
6
6
|
"""
|
|
@@ -207,7 +207,7 @@ def select_connected_core(
|
|
|
207
207
|
Every node after the first seed is adjacent to one already chosen, so the
|
|
208
208
|
result is a handful of connected neighbourhoods rather than scattered dust.
|
|
209
209
|
|
|
210
|
-
Mirrors ``selectConnectedCore`` in ``@
|
|
210
|
+
Mirrors ``selectConnectedCore`` in ``@lykomi/web-graph``; both
|
|
211
211
|
sides cap, and they have to agree on what the important part of a graph is.
|
|
212
212
|
"""
|
|
213
213
|
if len(node_ids) <= limit:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Graph tools for the agent.
|
|
2
2
|
|
|
3
3
|
Every tool returns the ``foundry.graph`` payload that
|
|
4
|
-
``@
|
|
4
|
+
``@lykomi/web-graph`` renders, so any result the agent produces can
|
|
5
5
|
be drawn as a graph rather than read as JSON.
|
|
6
6
|
|
|
7
7
|
Read and write are separate tools, and the write tool is only registered when
|
|
@@ -32,7 +32,7 @@ app.add_middleware(
|
|
|
32
32
|
STATE_SCHEMA = {
|
|
33
33
|
"navigation": {
|
|
34
34
|
"type": "object",
|
|
35
|
-
"description": "Current app route and navigation metadata from
|
|
35
|
+
"description": "Current app route and navigation metadata from LYKOMI UI.",
|
|
36
36
|
"additionalProperties": True,
|
|
37
37
|
},
|
|
38
38
|
"selectedItemId": {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""The payload contract, without a database.
|
|
2
2
|
|
|
3
3
|
Everything here is pure: given driver-shaped values, does the payload come out
|
|
4
|
-
in the shape ``@
|
|
4
|
+
in the shape ``@lykomi/web-graph`` renders? These are the rules a
|
|
5
5
|
change to the tools is most likely to break silently, because the failure
|
|
6
6
|
shows up as a graph that looks slightly wrong rather than as an error.
|
|
7
7
|
"""
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
@import "tailwindcss";
|
|
2
|
-
@import "@
|
|
2
|
+
@import "@lykomi/web-ui/styles.css";
|
|
3
3
|
/* Graph node palette. Optional — without it the graph falls back to the
|
|
4
4
|
* host's --chart-* tokens — but these give eight well-separated hues instead
|
|
5
5
|
* of five. */
|
|
6
|
-
@import "@
|
|
6
|
+
@import "@lykomi/web-graph/styles.css";
|
|
7
7
|
|
|
8
8
|
/* Tailwind v4 needs explicit @source paths to scan package output
|
|
9
|
-
* for utility classes. Without these, the
|
|
9
|
+
* for utility classes. Without these, the LYKOMI UI components render
|
|
10
10
|
* without spacing, borders, or theme tokens. */
|
|
11
|
-
@source "../node_modules/@
|
|
12
|
-
@source "../node_modules/@
|
|
13
|
-
@source "../node_modules/@
|
|
14
|
-
@source "../node_modules/@
|
|
11
|
+
@source "../node_modules/@lykomi/web-primitives/dist";
|
|
12
|
+
@source "../node_modules/@lykomi/web-ui/dist";
|
|
13
|
+
@source "../node_modules/@lykomi/web-shell/dist";
|
|
14
|
+
@source "../node_modules/@lykomi/web-graph/dist";
|
|
15
15
|
|
|
16
16
|
@plugin "tailwindcss-animate";
|
|
17
17
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "./index.css";
|
|
2
|
-
import { bootstrapShell } from "@
|
|
3
|
-
import type { AuthAdapter } from "@
|
|
4
|
-
import { createVoiceModule } from "@
|
|
2
|
+
import { bootstrapShell } from "@lykomi/web-shell";
|
|
3
|
+
import type { AuthAdapter } from "@lykomi/web-shell";
|
|
4
|
+
import { createVoiceModule } from "@lykomi/web-voice";
|
|
5
5
|
import { customerPages } from "@/pages/index.tsx";
|
|
6
6
|
import { graphModule } from "@/tools/graph-module.tsx";
|
|
7
7
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { useEffect } from "react";
|
|
2
|
-
import { defineChatModule } from "@
|
|
2
|
+
import { defineChatModule } from "@lykomi/web-shell";
|
|
3
3
|
import {
|
|
4
4
|
GraphResultSeeder,
|
|
5
5
|
GraphToolUI,
|
|
6
6
|
makeGraphToolUI,
|
|
7
7
|
registerGraphClientTools,
|
|
8
8
|
registerGraphPanelRenderer,
|
|
9
|
-
} from "@
|
|
9
|
+
} from "@lykomi/web-graph/assistant-ui";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Bind the graph card to this app's tool names.
|
|
@@ -33,8 +33,8 @@ export default defineConfig({
|
|
|
33
33
|
manualChunks(id) {
|
|
34
34
|
if (!id.includes("node_modules")) return;
|
|
35
35
|
// Match on the PACKAGE, not the path: pnpm's store directories
|
|
36
|
-
// embed peer names ("…
|
|
37
|
-
// so a substring match on the full path filed
|
|
36
|
+
// embed peer names ("…web-ui@…_@ag-ui+client@…_@assistant-u…"),
|
|
37
|
+
// so a substring match on the full path filed web-ui under
|
|
38
38
|
// whichever peer name the rules met first, and "react" matched
|
|
39
39
|
// half the store. The last node_modules segment is the package.
|
|
40
40
|
const pkg = id.slice(id.lastIndexOf("node_modules/") + "node_modules/".length);
|
|
@@ -50,7 +50,7 @@ export default defineConfig({
|
|
|
50
50
|
// The force engine and its d3 stack, in one chunk of their own.
|
|
51
51
|
// ORDER MATTERS: "react-force-graph-2d" and "react-kapsule" would
|
|
52
52
|
// otherwise be caught by a react rule and load on first paint.
|
|
53
|
-
// And note what is NOT here: `@
|
|
53
|
+
// And note what is NOT here: `@lykomi/web-graph` is
|
|
54
54
|
// deliberately left unassigned. It lazy-imports its canvas module,
|
|
55
55
|
// and naming the package in a manualChunk puts the importer and the
|
|
56
56
|
// imported module in the SAME chunk — which collapses the boundary
|
|
@@ -74,16 +74,16 @@ export default defineConfig({
|
|
|
74
74
|
if (pkg.startsWith("@ag-ui/")) return "vendor-ag-ui";
|
|
75
75
|
if (pkg.startsWith("@azure/msal")) return "vendor-auth";
|
|
76
76
|
if (
|
|
77
|
-
pkg.startsWith("@
|
|
78
|
-
pkg.startsWith("@
|
|
77
|
+
pkg.startsWith("@lykomi/web-ui/dist/workbench.js") ||
|
|
78
|
+
pkg.startsWith("@lykomi/web-ui/dist/workbench/")
|
|
79
79
|
)
|
|
80
|
-
return "
|
|
81
|
-
if (pkg.startsWith("@
|
|
82
|
-
if (pkg.startsWith("@
|
|
83
|
-
if (pkg.startsWith("@
|
|
80
|
+
return "lykomi-workbench";
|
|
81
|
+
if (pkg.startsWith("@lykomi/web-ui/")) return "vendor-lykomi-ui";
|
|
82
|
+
if (pkg.startsWith("@lykomi/web-shell/")) return "vendor-lykomi-shell";
|
|
83
|
+
if (pkg.startsWith("@lykomi/agent/")) return "vendor-lykomi-agent";
|
|
84
84
|
// Voice is optional at runtime and heavy at build; its own chunk
|
|
85
85
|
// keeps the main path light and the size gate honest.
|
|
86
|
-
if (pkg.startsWith("@
|
|
86
|
+
if (pkg.startsWith("@lykomi/web-voice/")) return "vendor-lykomi-voice";
|
|
87
87
|
// Before the react rule: recharts drags react-smooth + the d3
|
|
88
88
|
// sub-packages along, and "react-smooth" would land in
|
|
89
89
|
// vendor-react and push it past the chunk-size gate.
|
package/template/AGENTS.md
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
This is a
|
|
1
|
+
This is a LYKOMI UI starter app: React chat UI, AG-UI transport, and a Microsoft Agent Framework service.
|
|
2
2
|
|
|
3
3
|
# Architecture
|
|
4
4
|
|
|
5
5
|
```text
|
|
6
|
-
src/main.tsx
|
|
7
|
-
src/pages/chat-page.tsx Chat
|
|
8
|
-
src/
|
|
6
|
+
src/main.tsx LYKOMI UI shell bootstrap
|
|
7
|
+
src/pages/chat-page.tsx Chat route
|
|
8
|
+
src/pages/starter-prompts.tsx Home prompts
|
|
9
|
+
src/pages/guide-page.tsx Example content page
|
|
10
|
+
src/starter-module.ts Navigation, agent context, interface menu
|
|
11
|
+
src/theme.ts App palette and branding
|
|
12
|
+
src/index.css Tailwind v4 plus @lykomi/web-ui/styles.css
|
|
9
13
|
agent/app/main.py FastAPI app and AG-UI endpoint
|
|
10
14
|
agent/app/agent.py FoundryChatClient + Agent configuration
|
|
11
15
|
agent/app/tools.py Agent Framework tools; update AGENT_TOOLS here
|
|
@@ -28,7 +32,7 @@ Set these in `agent/.env`:
|
|
|
28
32
|
|
|
29
33
|
```bash
|
|
30
34
|
FOUNDRY_PROJECT_ENDPOINT=https://<account>.services.ai.azure.com/api/projects/<project>
|
|
31
|
-
FOUNDRY_MODEL=
|
|
35
|
+
FOUNDRY_MODEL=your-model-deployment
|
|
32
36
|
```
|
|
33
37
|
|
|
34
38
|
`FoundryChatClient` reads the Microsoft Agent Framework env vars directly. The Python agent passes `DefaultAzureCredential`; prefer `az login` locally and managed identity or service principal env vars in hosted environments.
|
|
@@ -43,19 +47,20 @@ FOUNDRY_MODEL=gpt-5.4
|
|
|
43
47
|
|
|
44
48
|
# Update Modes
|
|
45
49
|
|
|
46
|
-
- Package-backed mode: keep `@
|
|
50
|
+
- Package-backed mode: keep `@lykomi/*` dependencies and update them through npm/pnpm for central fixes to UI, shell, theme resources, and AG-UI transport.
|
|
47
51
|
- Copy/template mode: once this app is copied into its own GitHub template or deeply customized repo, local `src/` and `agent/` files are owned by that repo. Pull upstream starter ideas manually.
|
|
48
52
|
- Package updates must not overwrite local app code. Template structure changes only apply when a new app is scaffolded or manually merged.
|
|
49
53
|
|
|
50
54
|
# UI
|
|
51
55
|
|
|
52
56
|
- Keep the starter chat-first. Do not add landing pages, marketing heroes, or domain-specific clutter.
|
|
53
|
-
- Configure quick starts in `src/pages/
|
|
54
|
-
- Configure app name, browser title, logos, colors, radius, and fonts
|
|
55
|
-
- Keep the
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
- Keep `@
|
|
57
|
+
- Configure quick starts in `src/pages/starter-prompts.tsx`.
|
|
58
|
+
- Configure app name, browser title, logos, colors, radius, and fonts in `src/theme.ts`.
|
|
59
|
+
- Keep the Guide page and its `ChatModule` as an example of page context and persistent chat; replace them when building your app.
|
|
60
|
+
- Keep the default generated app name and browser title as `LYKOMI UI` unless the app is intentionally rebranded.
|
|
61
|
+
- Use LYKOMI UI components from `@lykomi/web-ui` and primitives from `@lykomi/web-primitives`.
|
|
62
|
+
- Keep `@source "../node_modules/@lykomi/.../dist"` entries in `src/index.css`; Tailwind v4 needs them to scan package component classes.
|
|
63
|
+
- Keep `@import "@lykomi/web-ui/styles.css";`; it provides the LYKOMI UI theme tokens.
|
|
59
64
|
|
|
60
65
|
# Attachments
|
|
61
66
|
|