create-lykomi-app 0.0.0-stage → 0.9.1
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/LICENSE +21 -0
- package/README.md +95 -2
- package/dist/index.mjs +25 -0
- package/package.json +50 -3
- package/presets/foundry-iq/files/AGENTS.md +72 -0
- package/presets/foundry-iq/files/README.md +145 -0
- package/presets/foundry-iq/files/agent/_env.example +22 -0
- package/presets/foundry-iq/files/agent/app/agent.py +26 -0
- package/presets/foundry-iq/files/agent/app/foundry_iq.py +380 -0
- package/presets/foundry-iq/files/agent/app/main.py +79 -0
- package/presets/foundry-iq/files/agent/pyproject.toml +20 -0
- package/presets/foundry-iq/files/src/main.tsx +27 -0
- package/presets/foundry-iq/files/src/pages/chat-page.tsx +11 -0
- package/presets/foundry-iq/files/src/tools/foundry-iq-module.tsx +7 -0
- package/presets/foundry-iq/files/src/tools/search-results-tool-ui.tsx +854 -0
- package/presets/foundry-iq/manifest.json +27 -0
- package/presets/neo4j/files/AGENTS.md +90 -0
- package/presets/neo4j/files/CLAUDE.md +1 -0
- package/presets/neo4j/files/README.md +187 -0
- package/presets/neo4j/files/agent/_env.example +44 -0
- package/presets/neo4j/files/agent/app/__init__.py +12 -0
- package/presets/neo4j/files/agent/app/agent.py +62 -0
- package/presets/neo4j/files/agent/app/graph_db.py +276 -0
- package/presets/neo4j/files/agent/app/graph_payload.py +323 -0
- package/presets/neo4j/files/agent/app/graph_tools.py +626 -0
- package/presets/neo4j/files/agent/app/main.py +75 -0
- package/presets/neo4j/files/agent/pyproject.toml +40 -0
- package/presets/neo4j/files/agent/tests/conftest.py +35 -0
- package/presets/neo4j/files/agent/tests/test_graph_live.py +395 -0
- package/presets/neo4j/files/agent/tests/test_graph_payload.py +131 -0
- package/presets/neo4j/files/agent/tests/test_graph_tools.py +145 -0
- package/presets/neo4j/files/docker-compose.yml +38 -0
- package/presets/neo4j/files/scripts/seed_graph.py +222 -0
- package/presets/neo4j/files/src/index.css +30 -0
- package/presets/neo4j/files/src/main.tsx +43 -0
- package/presets/neo4j/files/src/pages/chat-page.tsx +11 -0
- package/presets/neo4j/files/src/tools/graph-module.tsx +103 -0
- package/presets/neo4j/files/vite.config.ts +118 -0
- package/presets/neo4j/manifest.json +67 -0
- package/template/AGENTS.md +73 -0
- package/template/CLAUDE.md +1 -0
- package/template/README.md +199 -0
- package/template/_gitignore +17 -0
- package/template/agent/_env.example +9 -0
- package/template/agent/app/__init__.py +1 -0
- package/template/agent/app/agent.py +43 -0
- package/template/agent/app/main.py +62 -0
- package/template/agent/app/tools.py +26 -0
- package/template/agent/pyproject.toml +21 -0
- package/template/eslint.config.js +24 -0
- package/template/index.html +13 -0
- package/template/package.json +61 -0
- package/template/pnpm-workspace.yaml +20 -0
- package/template/public/favicon.svg +4 -0
- package/template/src/index.css +30 -0
- package/template/src/main.tsx +45 -0
- package/template/src/pages/chat-page.tsx +11 -0
- package/template/src/pages/index.tsx +21 -0
- package/template/src/vite-env.d.ts +16 -0
- package/template/tsconfig.app.json +26 -0
- package/template/tsconfig.json +4 -0
- package/template/tsconfig.node.json +15 -0
- package/template/vite.config.ts +100 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 iLoveAgents, a brand of Leitwolf GmbH
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,96 @@
|
|
|
1
|
-
#
|
|
1
|
+
# create-lykomi-app
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Create a Foundry UI agent app: React chat UI, AG-UI protocol, Microsoft Agent Framework, and [Microsoft Foundry](https://learn.microsoft.com/azure/foundry/).
|
|
4
|
+
|
|
5
|
+
Foundry UI is built by [iLoveAgents](https://iloveagents.ai).
|
|
6
|
+
|
|
7
|
+
## Create An App
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm create lykomi-app my-agent-app
|
|
11
|
+
cd my-agent-app
|
|
12
|
+
cp agent/.env.example agent/.env
|
|
13
|
+
az login
|
|
14
|
+
pnpm dev
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Open `http://localhost:8010`.
|
|
18
|
+
|
|
19
|
+
Set these in `agent/.env`:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
FOUNDRY_PROJECT_ENDPOINT=https://<account>.services.ai.azure.com/api/projects/<project>
|
|
23
|
+
FOUNDRY_MODEL=gpt-5.4
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The generated app starts:
|
|
27
|
+
|
|
28
|
+
- UI: `http://localhost:8010`
|
|
29
|
+
- Agent service: `http://localhost:8020`
|
|
30
|
+
- AG-UI endpoint: `/api/agent`
|
|
31
|
+
|
|
32
|
+
The first screen is the chat. The bundled agent can call `hello_world` and `get_weather`.
|
|
33
|
+
|
|
34
|
+
## Theme The App
|
|
35
|
+
|
|
36
|
+
Generated apps default to `Foundry UI` for both the shell title and browser title. Change branding, logos, colors, radius, and fonts through the `theme` layers in `src/main.tsx`.
|
|
37
|
+
|
|
38
|
+
## Two Ways To Use It
|
|
39
|
+
|
|
40
|
+
| Mode | Use when | Update story |
|
|
41
|
+
| ---------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
|
42
|
+
| Package-backed app | You want central Foundry UI fixes and a small local app surface. | Keep the generated app and update `@iloveagents/foundry-*` packages with your package manager. |
|
|
43
|
+
| 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
|
+
|
|
45
|
+
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
|
+
|
|
47
|
+
## Requirements
|
|
48
|
+
|
|
49
|
+
- Node.js 22+
|
|
50
|
+
- pnpm
|
|
51
|
+
- uv
|
|
52
|
+
- Azure CLI for local `az login`
|
|
53
|
+
|
|
54
|
+
## Add Tools
|
|
55
|
+
|
|
56
|
+
Add Agent Framework tools in `agent/app/tools.py` and include them in `AGENT_TOOLS`.
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
AGENT_TOOLS = [hello_world, get_weather, my_new_tool]
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The generated `AGENTS.md` explains the local project contract for coding agents. `CLAUDE.md` imports it for Claude Code.
|
|
63
|
+
|
|
64
|
+
## Options
|
|
65
|
+
|
|
66
|
+
| Flag | Description |
|
|
67
|
+
| ----------------- | -------------------------------------------- |
|
|
68
|
+
| `--preset <name>` | Preset to scaffold from. Default: `default`. |
|
|
69
|
+
| `--no-install` | Skip dependency install. |
|
|
70
|
+
| `--force` | Scaffold into a non-empty directory. |
|
|
71
|
+
|
|
72
|
+
## Presets
|
|
73
|
+
|
|
74
|
+
| Preset | What you get |
|
|
75
|
+
| ------------ | --------------------------------------------------------------------------------------- |
|
|
76
|
+
| `default` | Chat-first Foundry UI app with FastAPI Agent Framework service and hello/weather tools. |
|
|
77
|
+
| `foundry-iq` | Default plus an Azure AI Search Foundry IQ knowledge base retrieval tool. |
|
|
78
|
+
|
|
79
|
+
## Packages
|
|
80
|
+
|
|
81
|
+
The starter consumes the Foundry UI packages from npm:
|
|
82
|
+
|
|
83
|
+
- `@iloveagents/foundry-web-shell` - browser shell and `bootstrapShell`
|
|
84
|
+
- `@iloveagents/foundry-web-ui` - chat UI, AG-UI adapter, tool panels, theme resource
|
|
85
|
+
- `@iloveagents/foundry-web-primitives` - shared UI primitives
|
|
86
|
+
- `@iloveagents/foundry-agent` - AG-UI client transport
|
|
87
|
+
|
|
88
|
+
The packages publish built ESM JavaScript and `.d.ts` declarations. The
|
|
89
|
+
generated Vite app is already configured to scan their Tailwind classes from the
|
|
90
|
+
package `dist` folders.
|
|
91
|
+
|
|
92
|
+
Source: [`lykomi/ui`](https://github.com/lykomi/ui).
|
|
93
|
+
|
|
94
|
+
## License
|
|
95
|
+
|
|
96
|
+
MIT
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
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,foundryPackageVersion: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,foundryPackageVersion:e.foundryPackageVersion,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=[`@iloveagents/foundry-agent`,`@iloveagents/foundry-web-graph`,`@iloveagents/foundry-web-primitives`,`@iloveagents/foundry-web-shell`,`@iloveagents/foundry-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-foundry-ui-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-foundry-ui-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
|
+
|
|
6
|
+
Scaffold a new LYKOMI app — AG-UI protocol + Microsoft Agent Framework + Azure Foundry.
|
|
7
|
+
|
|
8
|
+
Options:
|
|
9
|
+
--preset <name> Preset to scaffold from (default: "default" — OSS shell + AG-UI agent service).
|
|
10
|
+
Use "foundry-iq" for an Azure AI Search Foundry IQ knowledge base demo,
|
|
11
|
+
or "neo4j" for an agent over a Neo4j graph with an interactive
|
|
12
|
+
visualization (local database or Neo4j Aura).
|
|
13
|
+
--no-install Skip running pnpm install in the new directory.
|
|
14
|
+
--force Allow scaffolding into a non-empty directory.
|
|
15
|
+
-h, --help Show this message.
|
|
16
|
+
|
|
17
|
+
Example:
|
|
18
|
+
npx create-lykomi-app my-app
|
|
19
|
+
npx create-lykomi-app my-app --preset foundry-iq
|
|
20
|
+
npx create-lykomi-app my-app --preset neo4j
|
|
21
|
+
`)}function j(e){if(!e||e.length===0)return;let t=e.filter(e=>e.required),n=e.filter(e=>!e.required);t.length>0&&f.log.info(`Required env vars (set before running the app):
|
|
22
|
+
`+t.map(e=>` - ${e.name}: ${e.description}`).join(`
|
|
23
|
+
`)),n.length>0&&f.log.info(`Optional env vars:
|
|
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?.[`@iloveagents/foundry-agent`];if(!n)throw Error(`Template package.json is missing @iloveagents/foundry-agent.`);if(!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(n))throw Error(`Template @iloveagents/foundry-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-foundry-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,foundryPackageVersion: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,6 +1,53 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-lykomi-app",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
3
|
+
"version": "0.9.1",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "Scaffold a new LYKOMI app — AG-UI protocol + Microsoft Agent Framework + Azure Foundry, ready to run.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"create-app",
|
|
8
|
+
"foundry-ui",
|
|
9
|
+
"ag-ui",
|
|
10
|
+
"agent-framework",
|
|
11
|
+
"azure-foundry",
|
|
12
|
+
"scaffold"
|
|
13
|
+
],
|
|
14
|
+
"type": "module",
|
|
15
|
+
"main": "./dist/index.mjs",
|
|
16
|
+
"bin": {
|
|
17
|
+
"create-lykomi-app": "./dist/index.mjs"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"presets",
|
|
22
|
+
"template",
|
|
23
|
+
"README.md",
|
|
24
|
+
"LICENSE"
|
|
25
|
+
],
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/lykomi/ui.git",
|
|
29
|
+
"directory": "packages/create-foundry-ui-app"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://github.com/lykomi/ui/tree/main/packages/create-foundry-ui-app#readme",
|
|
32
|
+
"bugs": "https://github.com/lykomi/ui/issues",
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=22"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@clack/prompts": "^1.2.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/node": "^24.10.1",
|
|
44
|
+
"tsdown": "^0.21.10",
|
|
45
|
+
"typescript": "~5.9.3",
|
|
46
|
+
"vitest": "^4.1.4"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsdown",
|
|
50
|
+
"test:unit": "vitest run",
|
|
51
|
+
"typecheck": "tsc --noEmit"
|
|
52
|
+
}
|
|
6
53
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
This is a Foundry 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
|
+
|
|
3
|
+
# Architecture
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
src/main.tsx Foundry UI shell bootstrap
|
|
7
|
+
src/pages/chat-page.tsx Chat screen and Foundry IQ starter prompts
|
|
8
|
+
src/index.css Tailwind v4 plus @iloveagents/foundry-web-ui/styles.css
|
|
9
|
+
src/tools/ Foundry IQ search result tool card and ChatModule registration
|
|
10
|
+
agent/app/main.py FastAPI app, AG-UI endpoint, and request header capture
|
|
11
|
+
agent/app/agent.py FoundryChatClient + Agent configuration
|
|
12
|
+
agent/app/foundry_iq.py Foundry IQ MCP retrieval tool
|
|
13
|
+
agent/app/tools.py Local Agent Framework tools; update AGENT_TOOLS here
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
# Run
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm install
|
|
20
|
+
cp agent/.env.example agent/.env
|
|
21
|
+
az login
|
|
22
|
+
pnpm dev
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The UI runs on `http://localhost:8010`. The agent service runs on `http://localhost:8020`; Vite proxies `/api/agent` to it.
|
|
26
|
+
|
|
27
|
+
# Environment
|
|
28
|
+
|
|
29
|
+
Set these in `agent/.env`:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
FOUNDRY_PROJECT_ENDPOINT=https://<account>.services.ai.azure.com/api/projects/<project>
|
|
33
|
+
FOUNDRY_MODEL=gpt-5.4
|
|
34
|
+
AZURE_AI_SEARCH_ENDPOINT=https://<search-service>.search.windows.net
|
|
35
|
+
FOUNDRY_IQ_KNOWLEDGE_BASE=<knowledge-base-name>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`FoundryChatClient` reads its Microsoft Agent Framework env vars directly. Do not add duplicate manual env probing unless Agent Framework removes that behavior.
|
|
39
|
+
|
|
40
|
+
# Foundry IQ
|
|
41
|
+
|
|
42
|
+
- The Foundry IQ MCP endpoint is derived as `${AZURE_AI_SEARCH_ENDPOINT}/knowledgebases/${FOUNDRY_IQ_KNOWLEDGE_BASE}/mcp?api-version=2026-05-01-preview`.
|
|
43
|
+
- The MCP tool name defaults to `knowledge_base_retrieve`.
|
|
44
|
+
- The activity viewer uses the documented retrieve action with `includeActivity: true`; never synthesize activity rows.
|
|
45
|
+
- `agent/app/foundry_iq.py` uses Azure AI Search AAD auth with `DefaultAzureCredential`.
|
|
46
|
+
- For remote SharePoint knowledge sources, forward the signed-in user's token per request through `x-ms-query-source-authorization` or `authorization` on `/api/agent` to both MCP and retrieve calls.
|
|
47
|
+
- Hosted Foundry Agent Service MCP headers are static in the current preview. Do not claim that a hosted agent can vary MCP headers per request.
|
|
48
|
+
|
|
49
|
+
# Agent Tools
|
|
50
|
+
|
|
51
|
+
- Add local tools in `agent/app/tools.py`.
|
|
52
|
+
- Decorate local tools with `@tool(approval_mode="never_require")` unless you intentionally add an approval flow.
|
|
53
|
+
- Add new local tools to `AGENT_TOOLS`; otherwise the agent cannot call them.
|
|
54
|
+
- Keep the Foundry IQ tool in `agent/app/foundry_iq.py` unless you are changing retrieval behavior.
|
|
55
|
+
|
|
56
|
+
# UI
|
|
57
|
+
|
|
58
|
+
- Keep the starter chat-first. Do not add landing pages, marketing heroes, or domain-specific clutter.
|
|
59
|
+
- Configure quick starts in `src/pages/chat-page.tsx`.
|
|
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
|
+
- Configure app name, browser title, logos, colors, radius, and fonts through `theme` layers in `src/main.tsx`.
|
|
62
|
+
- Keep `@source "../node_modules/@iloveagents/.../dist"` entries in `src/index.css`; Tailwind v4 needs them to scan package component classes.
|
|
63
|
+
- Keep `@import "@iloveagents/foundry-web-ui/styles.css";`; it provides the Foundry UI theme tokens.
|
|
64
|
+
|
|
65
|
+
# Checks
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pnpm typecheck
|
|
69
|
+
pnpm build
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Use browser verification for layout or chat-flow changes.
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# __PROJECT_NAME__
|
|
2
|
+
|
|
3
|
+
Foundry UI starter with a Microsoft Agent Framework service, AG-UI chat, and a Foundry IQ knowledge base tool.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm install
|
|
9
|
+
cp agent/.env.example agent/.env
|
|
10
|
+
az login
|
|
11
|
+
pnpm dev
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Open `http://localhost:8010`.
|
|
15
|
+
|
|
16
|
+
`pnpm dev` starts:
|
|
17
|
+
|
|
18
|
+
- UI: `http://localhost:8010`
|
|
19
|
+
- Agent service: `http://localhost:8020`
|
|
20
|
+
- AG-UI endpoint: `/api/agent` proxied to the agent service
|
|
21
|
+
|
|
22
|
+
## Configure Foundry
|
|
23
|
+
|
|
24
|
+
Edit `agent/.env`:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
FOUNDRY_PROJECT_ENDPOINT=https://<account>.services.ai.azure.com/api/projects/<project>
|
|
28
|
+
FOUNDRY_MODEL=gpt-5.4
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`FoundryChatClient` reads the Microsoft Agent Framework environment variables directly. The starter passes `DefaultAzureCredential`, so local development works with `az login`.
|
|
32
|
+
|
|
33
|
+
## Configure Foundry IQ
|
|
34
|
+
|
|
35
|
+
Set the Azure AI Search endpoint and knowledge base name:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
AZURE_AI_SEARCH_ENDPOINT=https://<search-service>.search.windows.net
|
|
39
|
+
FOUNDRY_IQ_KNOWLEDGE_BASE=<knowledge-base-name>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The tool derives the MCP endpoint documented for Foundry IQ:
|
|
43
|
+
|
|
44
|
+
```text
|
|
45
|
+
${AZURE_AI_SEARCH_ENDPOINT}/knowledgebases/${FOUNDRY_IQ_KNOWLEDGE_BASE}/mcp?api-version=2026-05-01-preview
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The MCP tool name defaults to `knowledge_base_retrieve`. Override only if your service exposes a different tool:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
FOUNDRY_IQ_MCP_TOOL_NAME=knowledge_base_retrieve
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
For the activity viewer, the starter also calls the documented Azure AI Search retrieve action with `includeActivity: true`:
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
${AZURE_AI_SEARCH_ENDPOINT}/knowledgebases/${FOUNDRY_IQ_KNOWLEDGE_BASE}/retrieve?api-version=2026-05-01-preview
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
This is real service activity data from the `activity` array. Disable the extra diagnostic call only when you want MCP-only retrieval:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
FOUNDRY_IQ_INCLUDE_ACTIVITY=false
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Security Trimming
|
|
67
|
+
|
|
68
|
+
For remote SharePoint knowledge sources, Foundry IQ requires the `x-ms-query-source-authorization` header. In this starter, `agent/app/main.py` captures the incoming request's `x-ms-query-source-authorization` or `authorization` header and `agent/app/foundry_iq.py` forwards it per request to the MCP endpoint and the retrieve action.
|
|
69
|
+
|
|
70
|
+
For local development without a signed-in UI user, you can set:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
FOUNDRY_IQ_QUERY_SOURCE_AUTHORIZATION=Bearer <user-token>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Do not use that fallback for production multi-user apps. Hosted Foundry Agent Service MCP headers are static in the current preview; for per-user security trimming, keep the per-request forwarding in your app/API layer.
|
|
77
|
+
|
|
78
|
+
## Try It
|
|
79
|
+
|
|
80
|
+
The first screen is the chat. Try:
|
|
81
|
+
|
|
82
|
+
- `Search Foundry IQ for a knowledge base summary`
|
|
83
|
+
- `What sources are available in the knowledge base?`
|
|
84
|
+
- `Say hello world using the hello_world tool`
|
|
85
|
+
|
|
86
|
+
The Foundry IQ tool lives in `agent/app/foundry_iq.py`; normal demo tools live in `agent/app/tools.py`.
|
|
87
|
+
|
|
88
|
+
The chat renders `foundry_iq_search` with a search-results tool card and an expandable activity viewer for tuning query planning, search-source calls, token usage, timings, and result counts. The UI lives in:
|
|
89
|
+
|
|
90
|
+
```text
|
|
91
|
+
src/tools/search-results-tool-ui.tsx
|
|
92
|
+
src/tools/foundry-iq-module.tsx
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Use that pattern for other search/retrieval tools: create a `makeAssistantToolUI` for the tool name, normalize the tool payload in the card, then register it through a `ChatModule` in `src/main.tsx`.
|
|
96
|
+
|
|
97
|
+
## Add A Tool
|
|
98
|
+
|
|
99
|
+
Add a function in `agent/app/tools.py` and include it in `AGENT_TOOLS`:
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from typing import Annotated
|
|
103
|
+
|
|
104
|
+
from agent_framework import tool
|
|
105
|
+
from pydantic import Field
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@tool(approval_mode="never_require")
|
|
109
|
+
def get_time(
|
|
110
|
+
city: Annotated[str, Field(description="The city to report time for.")],
|
|
111
|
+
) -> str:
|
|
112
|
+
"""Return a demo time response."""
|
|
113
|
+
return f"It is demo time in {city}."
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
AGENT_TOOLS = [hello_world, get_weather, get_time]
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Restart `pnpm dev` after changing Python dependencies or environment variables. Tool code itself reloads through Uvicorn.
|
|
120
|
+
|
|
121
|
+
## Project Map
|
|
122
|
+
|
|
123
|
+
```text
|
|
124
|
+
agent/ FastAPI + Microsoft Agent Framework AG-UI endpoint
|
|
125
|
+
agent/app/foundry_iq.py Foundry IQ MCP retrieval tool
|
|
126
|
+
agent/app/tools.py Add local agent tools here
|
|
127
|
+
src/main.tsx Foundry UI shell bootstrap
|
|
128
|
+
src/pages/chat-page.tsx Chat screen and quick starts
|
|
129
|
+
src/tools/ Custom tool cards registered through ChatModules
|
|
130
|
+
src/index.css Tailwind v4 + Foundry UI theme resource and package dist scanning
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Auth
|
|
134
|
+
|
|
135
|
+
This starter uses local pass-through auth so the demo runs without an Entra app registration.
|
|
136
|
+
|
|
137
|
+
For production MSAL auth, remove `authProvider` and `agentFetch` from `src/main.tsx`, then set:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
VITE_MSAL_CLIENT_ID=<client-id>
|
|
141
|
+
VITE_MSAL_AUTHORITY=https://login.microsoftonline.com/<tenant-id>
|
|
142
|
+
VITE_MSAL_API_SCOPE=api://<api-app-id>/access_as_user
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Ensure your authenticated fetch path forwards the user's bearer token to `/api/agent`.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Copy to agent/.env and fill these values for Microsoft Agent Framework.
|
|
2
|
+
FOUNDRY_PROJECT_ENDPOINT=https://your-project.services.ai.azure.com/api/projects/your-project
|
|
3
|
+
FOUNDRY_MODEL=gpt-5.4
|
|
4
|
+
|
|
5
|
+
# Foundry IQ MCP endpoint inputs.
|
|
6
|
+
# The app derives:
|
|
7
|
+
# ${AZURE_AI_SEARCH_ENDPOINT}/knowledgebases/${FOUNDRY_IQ_KNOWLEDGE_BASE}/mcp?api-version=2026-05-01-preview
|
|
8
|
+
# and uses the documented retrieve action with includeActivity=true for real activity diagnostics.
|
|
9
|
+
AZURE_AI_SEARCH_ENDPOINT=https://your-search.search.windows.net
|
|
10
|
+
FOUNDRY_IQ_KNOWLEDGE_BASE=your-knowledge-base
|
|
11
|
+
# FOUNDRY_IQ_INCLUDE_ACTIVITY=true
|
|
12
|
+
# FOUNDRY_IQ_MAX_OUTPUT_DOCUMENTS=6
|
|
13
|
+
|
|
14
|
+
# Optional local-dev fallback for SharePoint-backed knowledge sources.
|
|
15
|
+
# Production should pass the signed-in user's bearer token per request instead.
|
|
16
|
+
# FOUNDRY_IQ_QUERY_SOURCE_AUTHORIZATION=Bearer <user-token>
|
|
17
|
+
|
|
18
|
+
# DefaultAzureCredential uses `az login` locally, service principal env vars in CI,
|
|
19
|
+
# and managed identity in Azure. Uncomment tenant/client values only when needed.
|
|
20
|
+
# AZURE_TENANT_ID=
|
|
21
|
+
# AZURE_CLIENT_ID=
|
|
22
|
+
# AZURE_CLIENT_SECRET=
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from functools import lru_cache
|
|
2
|
+
|
|
3
|
+
from agent_framework import Agent
|
|
4
|
+
from agent_framework.foundry import FoundryChatClient
|
|
5
|
+
from azure.identity import DefaultAzureCredential
|
|
6
|
+
|
|
7
|
+
from .foundry_iq import FOUNDRY_IQ_TOOLS
|
|
8
|
+
from .tools import AGENT_TOOLS
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@lru_cache(maxsize=1)
|
|
12
|
+
def get_agent() -> Agent:
|
|
13
|
+
client = FoundryChatClient(credential=DefaultAzureCredential())
|
|
14
|
+
|
|
15
|
+
return Agent(
|
|
16
|
+
client=client,
|
|
17
|
+
name="FoundryIQAgent",
|
|
18
|
+
instructions=(
|
|
19
|
+
"You are a concise assistant for a Foundry UI app. "
|
|
20
|
+
"Use foundry_iq_search when the user asks about knowledge-base content, "
|
|
21
|
+
"documents, SharePoint-backed content, or retrieval-backed answers. "
|
|
22
|
+
"Use hello_world when the user asks for hello world or a hello demo. "
|
|
23
|
+
"Use get_weather when weather is requested."
|
|
24
|
+
),
|
|
25
|
+
tools=[*AGENT_TOOLS, *FOUNDRY_IQ_TOOLS],
|
|
26
|
+
)
|