Skip to content

Commit 5ca0af3

Browse files
committed
feat: add Agent Plugin documentation and image assets
1 parent 1a055f0 commit 5ca0af3

2 files changed

Lines changed: 274 additions & 0 deletions

File tree

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
> TODO: this plugin tutorial is in progress, some information might be missing, we are actively working on it now. If you have any questions regarding this plugin, please reach out to us in GitHub issues
2+
3+
4+
# Agent Plugin
5+
6+
This plugin adds an AI agent with a chat surface to AdminForth which is capable of default skills like searching/editing data and extending with custom skills.
7+
8+
It stores session history in your own resources and uses any AdminForth completion adapter to generate responses.
9+
10+
## Installation
11+
12+
```bash
13+
pnpm i adminforth-agent --save
14+
pnpm i @adminforth/completion-adapter-open-ai-chat-gpt --save
15+
```
16+
17+
Add your LLM credentials to `.env`:
18+
19+
```env title=.env
20+
...
21+
OPENAI_API_KEY=your_key
22+
```
23+
24+
You can replace the OpenAI adapter with any completion adapter from [List of adapters](/docs/tutorial/ListOfAdapters/).
25+
26+
## Setup
27+
28+
First create two resources for sessions and turns:
29+
30+
```ts title="./resources/agent_resources/sessions.ts"
31+
import AdminForth, { AdminForthDataTypes } from 'adminforth';
32+
import type { AdminForthResourceInput } from 'adminforth';
33+
import { randomUUID } from 'crypto';
34+
35+
export default {
36+
dataSource: 'sqlite',
37+
table: 'sessions',
38+
resourceId: 'sessions',
39+
label: 'Sessions',
40+
columns: [
41+
{
42+
name: 'id',
43+
primaryKey: true,
44+
type: AdminForthDataTypes.STRING,
45+
fillOnCreate: () => randomUUID(),
46+
showIn: {
47+
edit: false,
48+
create: false,
49+
},
50+
},
51+
{
52+
name: 'title',
53+
type: AdminForthDataTypes.STRING,
54+
},
55+
{
56+
name: 'turns',
57+
type: AdminForthDataTypes.INTEGER,
58+
},
59+
{
60+
name: 'asker_id',
61+
type: AdminForthDataTypes.STRING,
62+
},
63+
{
64+
name: 'created_at',
65+
type: AdminForthDataTypes.DATETIME,
66+
fillOnCreate: () => (new Date()).toISOString(),
67+
showIn: {
68+
edit: false,
69+
create: false,
70+
},
71+
},
72+
],
73+
} as AdminForthResourceInput;
74+
```
75+
76+
```ts title="./resources/agent_resources/turns.ts"
77+
import AdminForth, { AdminForthDataTypes } from 'adminforth';
78+
import type { AdminForthResourceInput } from 'adminforth';
79+
import { randomUUID } from 'crypto';
80+
81+
export default {
82+
dataSource: 'sqlite',
83+
table: 'turns',
84+
resourceId: 'turns',
85+
label: 'Turns',
86+
columns: [
87+
{
88+
name: 'id',
89+
primaryKey: true,
90+
type: AdminForthDataTypes.STRING,
91+
fillOnCreate: () => randomUUID(),
92+
showIn: {
93+
edit: false,
94+
create: false,
95+
},
96+
},
97+
{
98+
name: 'session_id',
99+
type: AdminForthDataTypes.STRING,
100+
},
101+
{
102+
name: 'created_at',
103+
type: AdminForthDataTypes.DATE,
104+
fillOnCreate: () => (new Date()).toISOString(),
105+
showIn: {
106+
edit: false,
107+
create: false,
108+
},
109+
},
110+
{
111+
name: 'prompt',
112+
type: AdminForthDataTypes.TEXT,
113+
},
114+
{
115+
name: 'response',
116+
type: AdminForthDataTypes.TEXT,
117+
},
118+
],
119+
} as AdminForthResourceInput;
120+
```
121+
122+
`asker_id` must store the current admin user's primary key, and `created_at` should be filled automatically because the plugin sorts sessions and turns by it. The `turns` field can stay nullable, but the plugin configuration still expects it.
123+
124+
Add matching tables to your schema:
125+
126+
```prisma title='./schema.prisma'
127+
model sessions {
128+
id String @id
129+
title String
130+
turns Int?
131+
asker_id String
132+
created_at DateTime
133+
}
134+
135+
model turns {
136+
id String @id
137+
session_id String
138+
created_at DateTime
139+
prompt String?
140+
response String?
141+
}
142+
```
143+
144+
Run migration:
145+
146+
```bash
147+
pnpm makemigration --name add-adminforth-agent-tables ; pnpm migrate:local
148+
```
149+
150+
Register both resources in your app:
151+
152+
```ts title="./index.ts"
153+
import sessions_resource from './resources/agent_resources/sessions.js';
154+
import turns_resource from './resources/agent_resources/turns.js';
155+
156+
export const admin = new AdminForth({
157+
...
158+
resources: [
159+
...
160+
sessions_resource,
161+
turns_resource,
162+
],
163+
...
164+
});
165+
```
166+
167+
Then attach the plugin once, usually to your `adminuser` resource:
168+
169+
```ts title="./resources/adminuser.ts"
170+
import AdminForthAgent from 'adminforth-agent';
171+
import CompletionAdapterOpenAIChatGPT from '@adminforth/completion-adapter-open-ai-chat-gpt';
172+
173+
...
174+
175+
plugins: [
176+
...
177+
new AdminForthAgent({
178+
completionAdapter: new CompletionAdapterOpenAIChatGPT({
179+
openAiApiKey: process.env.OPENAI_API_KEY as string,
180+
model: 'gpt-5.4-mini',
181+
}),
182+
maxTokens: 10000,
183+
reasoning: 'none',
184+
sessionResource: {
185+
resourceId: 'sessions',
186+
idField: 'id',
187+
titleField: 'title',
188+
turnsField: 'turns',
189+
askerIdField: 'asker_id',
190+
createdAtField: 'created_at',
191+
},
192+
turnResource: {
193+
resourceId: 'turns',
194+
idField: 'id',
195+
sessionIdField: 'session_id',
196+
createdAtField: 'created_at',
197+
promptField: 'prompt',
198+
responseField: 'response',
199+
// optional
200+
// debugField: 'debug',
201+
},
202+
}),
203+
]
204+
```
205+
206+
The plugin adds a chat surface to the admin UI and keeps session history per admin user.
207+
208+
## Reverse proxy and CDN configuration for streaming
209+
210+
The agent streams responses from `<baseURL>/adminapi/v1/agent/response` using server-sent events, where `<baseURL>` is your AdminForth base path or an empty string when deployed at the domain root. If your proxy buffers responses, the UI will receive the answer only after generation is finished.
211+
212+
For Nginx, disable response buffering on this endpoint. The critical line is `proxy_buffering off;`.
213+
214+
```nginx
215+
location <baseURL>/adminapi/v1/agent/response {
216+
proxy_http_version 1.1;
217+
proxy_read_timeout 600s;
218+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
219+
proxy_set_header Host $http_host;
220+
proxy_set_header Connection "";
221+
proxy_buffering off; # required for streaming
222+
proxy_pass http://127.0.0.1:3500;
223+
}
224+
```
225+
226+
Traefik forwards streaming responses immediately by default. The line that must stay off this route is any buffering middleware attachment such as `traefik.http.routers.adminforth-agent.middlewares=buffering@docker`. If your main router uses extra middlewares, create a dedicated router for the agent stream endpoint and do not attach buffering to it:
227+
228+
```yaml title='./compose.yml'
229+
services:
230+
adminforth:
231+
labels:
232+
- "traefik.enable=true"
233+
- "traefik.http.services.adminforth.loadbalancer.server.port=3500"
234+
235+
- "traefik.http.routers.adminforth.rule=PathPrefix(`/`)"
236+
- "traefik.http.routers.adminforth.tls=true"
237+
- "traefik.http.routers.adminforth.tls.certresolver=myresolver"
238+
- "traefik.http.routers.adminforth.middlewares=secure-headers,buffering@docker"
239+
240+
- "traefik.http.routers.adminforth-agent.rule=Path(`<baseURL>/adminapi/v1/agent/response`)"
241+
- "traefik.http.routers.adminforth-agent.priority=100"
242+
- "traefik.http.routers.adminforth-agent.service=adminforth"
243+
- "traefik.http.routers.adminforth-agent.tls=true"
244+
- "traefik.http.routers.adminforth-agent.tls.certresolver=myresolver"
245+
# keep buffering OFF for SSE
246+
# do not add:
247+
# - "traefik.http.routers.adminforth-agent.middlewares=buffering@docker"
248+
```
249+
250+
Replace `<baseURL>` with the same base path you use for AdminForth. For example, when `ADMIN_BASE_URL = '/admin/'`, the endpoint becomes `/admin/adminapi/v1/agent/response`.
251+
252+
### CDN
253+
254+
Cloudflare by default buffers responses, which breaks streaming. To fix it, create a page rule for your domain with a "Response Body Buffering" setting turned off for the agent stream endpoint (`<baseURL>/adminapi/v1/agent/response`).
255+
256+
![alt text](image-6.png)
257+
258+
259+
## Custom skills and tools
260+
261+
262+
Place you skills in `custom/skills/<skill_name>/SKILL.md` file. The plugin will pick them up automatically and make available in agent's toolbox.
263+
264+
265+
To define custom tools, create api endpoints, prefer `admin.express.withSchema(...)` from [Custom Pages / API docs](/docs/tutorial/Customization/customPages/). That exposes machine-readable request and response schemas the agent can use.
266+
267+
In skills markdown file, merge which tool exactlu agent should load.
268+
269+
270+
Skill example:
271+
272+
// TODO
273+
274+
103 KB
Loading

0 commit comments

Comments
 (0)