Skip to content

Commit 6284fb5

Browse files
committed
Merge branch 'next' of github.com:devforth/adminforth
2 parents 41f671e + e3232b4 commit 6284fb5

19 files changed

Lines changed: 276 additions & 34 deletions

File tree

adminforth/documentation/docs/tutorial/05-Adapters/05-ai-completion-adapters.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ new CompletionAdapterOpenAIResponses({
129129

130130
You can specify any GPT model you need. The default is `gpt-5-nano` as it is cheapest though may behave weakly.
131131

132-
By default, this adapter uses the OpenAI `responses` API (`v1/responses`) unless `useComplitionApi` is set to `true`. If `useComplitionApi` is `true`, it uses the older Chat Completions API (`v1/chat/completions`).
132+
By default, this adapter uses the OpenAI `responses` API (`v1/responses`) unless `useCompletionApi` is set to `true`. If `useCompletionApi` is `true`, it uses the older Chat Completions API (`v1/chat/completions`).
133133

134134

135135
### Using with OpenAI-compatible API providers (for example based on self-hosted vLLM docker images)
@@ -141,25 +141,25 @@ new CompletionAdapterOpenAIResponses({
141141
openAiApiKey: process.env.OVH_AI_ENDPOINTS_ACCESS_TOKEN as string,
142142
baseUrl: 'https://oai.endpoints.kepler.ai.cloud.ovh.net/v1',
143143
model: 'gpt-oss-20b',
144-
useComplitionApi: true,
144+
useCompletionApi: true,
145145
extraRequestBodyParameters: {
146146
store: false,
147147
},
148148
}),
149149
```
150150

151-
If `useComplitionApi` is omitted, the adapter keeps the current default behavior:
151+
If `useCompletionApi` is omitted, the adapter keeps the current default behavior:
152152

153153
- official OpenAI uses the `responses` API
154154
- custom `baseUrl` providers use the Chat Completions API.
155155

156-
This is because many OpenAI-compatible providers do not yet support the `responses` API or support it unstably (for example OVH AI Endpoints still - Apr 2026 does not fully support the `responses`, so `useComplitionApi: false` may work unstably there, though you can re-test it by manually enabling it by setting `useComplitionApi: true` and checking if it works).
156+
This is because many OpenAI-compatible providers do not yet support the `responses` API or support it unstably (for example OVH AI Endpoints still - Apr 2026 does not fully support the `responses`, so `useCompletionApi: false` may work unstably there, though you can re-test it by manually enabling it by setting `useCompletionApi: true` and checking if it works).
157157
Any 3rd-party API providers might have next reasones of pure `responses` API compatibility:
158158

159159
1) If they use vLLM open-source software under the hood they might have outdated version
160160
2) Custom non-vLLM implmentation might have reliable chat API implementation while give less priority to responses API as it is more complex and new.
161161

162-
We recommend you to try responses API first by setting `false` in `useComplitionApi` because it gives rich features set, including summarization and better structured outputs, and if it does not work for your provider, then set it to `true` to have less features but working implementation.
162+
We recommend you to try responses API first by setting `false` in `useCompletionApi` because it gives rich features set, including summarization and better structured outputs, and if it does not work for your provider, then set it to `true` to have less features but working implementation.
163163

164164

165165

adminforth/documentation/docs/tutorial/08-Plugins/01-agent.md

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -579,18 +579,18 @@ completionAdapter: new CompletionAdapterOpenAIResponses({
579579

580580
However some of 3rd party providers might serve outdated vLLM and still don't fully support the Responses API needed for langchain internal implmentation, for example [OVH AI Endpoints](https://www.ovhcloud.com/en/public-cloud/ai-endpoints/) in Responses mode still don't play well with langchain proxy (25 Apr 2026)
581581

582-
In that case you can try to use the OpenAI Complition API mode of the plugin, which is less efficient but more compatible with older APIs, you can force Chat Completions API mode with `useComplitionApi: true`:
582+
In that case you can try to use the OpenAI Chat Completions API mode of the plugin, which is less efficient but more compatible with older APIs, you can force Chat Completions API mode with `useCompletionApi: true`:
583583

584584
```ts
585585
completionAdapter: new CompletionAdapterOpenAIResponses({
586586
openAiApiKey: process.env.OVH_AI_ENDPOINTS_ACCESS_TOKEN as string,
587587
baseUrl: 'https://oai.endpoints.kepler.ai.cloud.ovh.net/v1',
588588
model: 'gpt-oss-120b',
589-
useComplitionApi: true,
589+
useCompletionApi: true,
590590
})
591591
```
592592

593-
OVH AI Endpoints still does not fully support the OpenAI `responses` API, so `useComplitionApi: false` may work unstably there.
593+
OVH AI Endpoints still does not fully support the OpenAI `responses` API, so `useCompletionApi: false` may work unstably there.
594594

595595

596596
## Turn on audio chat support
@@ -655,6 +655,29 @@ To define a custom tool, register an API endpoint with `admin.express.endpoint`.
655655

656656
By default, `admin.express.endpoint` applies AdminForth authorization. The endpoint handler receives `adminUser` from the user who is controlling the agent. In other words, all permissions and access rights of the agent are defined by that admin user. At the same time, actions done by the agent are automatically attributed in the audit log to the admin user who is controlling the agent.
657657

658+
If a tool is risky, you can attach AdminForth agent metadata directly to the endpoint with the `agent` field.
659+
660+
```ts
661+
type AgentRiskLevel = 'safe' | 'danger';
662+
663+
type AgentToolMeta = {
664+
riskLevel?: AgentRiskLevel;
665+
confirmation?: {
666+
title?: string;
667+
message?: string;
668+
confirmLabel?: string;
669+
};
670+
};
671+
```
672+
673+
Use it in `.endpoint(...)` like this:
674+
675+
- `riskLevel: 'safe'` marks the tool as low-risk.
676+
- `riskLevel: 'danger'` marks the tool as dangerous.
677+
- `confirmation` customizes the confirmation dialog shown before the tool is executed.
678+
679+
This metadata is rendered into the generated OpenAPI document, so the agent can understand that a tool is dangerous and requires an explicit confirmation UI before execution.
680+
658681
This example uses the same email adapter pattern shown in the Email Invite and Email Password Reset plugins. The transport below uses Mailgun only to keep the snippet short; you can replace it with SES or any other adapter from [List of adapters](/docs/tutorial/ListOfAdapters/).
659682

660683
```ts title="./api.ts"
@@ -691,6 +714,14 @@ export function initApi(app: Express, admin: IAdminForth) {
691714
method: 'POST',
692715
path: '/send_email_to_user',
693716
description: 'Send an email to one AdminForth user by id. Use this after the user row is resolved.',
717+
agent: {
718+
riskLevel: 'danger',
719+
confirmation: {
720+
title: 'Send email to user',
721+
message: 'This action will send a real email to the selected user.',
722+
confirmLabel: 'Send email',
723+
},
724+
},
694725
request_schema: {
695726
type: 'object',
696727
additionalProperties: false,

adminforth/documentation/docs/tutorial/08-Plugins/17-bulk-ai-flow.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,31 @@ new BulkAiFlowPlugin({
283283
}),
284284
```
285285
286+
## Image generation quality
287+
288+
`ImageGenerationAdapterOpenAI` accepts an `extraParams` object that is passed directly to the OpenAI API. The most useful option here is `quality`, which controls the fidelity and cost of each generated image:
289+
290+
| Value | Description |
291+
|-------|-------------|
292+
| `'low'` | Fastest generation, lowest cost. Good for drafts or high-volume batch jobs where speed matters more than visual fidelity. |
293+
| `'medium'` | Balanced quality and speed. A sensible default for most use cases. |
294+
| `'high'` | Best image quality, slowest and most expensive. Use for final promotional assets or when visual detail is critical. |
295+
296+
```ts
297+
new ImageGenerationAdapterOpenAI({
298+
openAiApiKey: process.env.OPENAI_API_KEY as string,
299+
model: 'gpt-image-1',
300+
//diff-add
301+
extraParams: {
302+
//diff-add
303+
quality: 'low', // 'low' | 'medium' | 'high'
304+
//diff-add
305+
},
306+
}),
307+
```
308+
309+
> ☝️ `'low'` quality is a great starting point when processing large datasets — you can always re-run generation with `'high'` for selected records once you're happy with the prompts.
310+
286311
## Rate Limiting and Best Practices
287312
288313
- Use `rateLimit` for individual image generation operations and for the bulk image generation
@@ -429,6 +454,21 @@ If you are processing large sets of data, you might want to limit the number of
429454
430455
And there won't be more than 5 parallel requests being handled.
431456
457+
## Controlling page size in the generation dialog
458+
459+
When users trigger bulk generation, records are displayed as cards in a paginated dialog. By default, 6 cards are shown per page. Use `pageSize` to tune this — lower values help when cards contain large images or long text, keeping the dialog fast and reviewable:
460+
461+
```ts
462+
new BulkAiFlowPlugin({
463+
actionName: 'Generate description and Price',
464+
465+
//diff-add
466+
pageSize: 10, // default is 6
467+
468+
// ...
469+
}),
470+
```
471+
432472
## Confirming long-running generations
433473
434474
For very large datasets, you can pause generation at specific checkpoints so users can review results before continuing.

adminforth/documentation/docs/tutorial/08-Plugins/27-dashboard.md

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
2-
title: Dashboard
3-
description: "Guide to the Dashboard plugin."
2+
title: Dashboard plugin
3+
description: "Documentation for the Dashboard plugin."
44
slug: /tutorial/Plugins/dashboard
55
---
66

@@ -24,7 +24,7 @@ Supported widgets:
2424
pnpm install @adminforth/dashboard --save
2525
```
2626

27-
## Create Dashboard Configs Table
27+
### Create Dashboard Configs Table
2828

2929
The plugin needs one resource to store dashboard definitions. For Prisma-based projects, add the table to your schema:
3030

@@ -64,7 +64,7 @@ CREATE INDEX "dashboard_configs_slug_idx" ON "dashboard_configs"("slug");
6464

6565
Use the JSON column type supported by your database connector. For example, PostgreSQL migrations might use `JSONB`, while SQLite migrations can use `JSON`.
6666

67-
## Create Resource
67+
### Create Resource
6868

6969
Create a resource that points to the `dashboard_configs` table:
7070

@@ -136,7 +136,7 @@ export const admin = new AdminForth({
136136
});
137137
```
138138

139-
## Configure Plugin
139+
### Configure Plugin
140140

141141
Attach the plugin to one of your resources, usually the users resource:
142142

@@ -332,7 +332,7 @@ query:
332332

333333
### Chart With Multiple Sources
334334

335-
Use `query.steps` when funnel steps come from different resources. Each step returns one row with `name` and the metric alias.
335+
Use `query.source: steps` when chart data comes from different resources. Each step returns one row with `name`, `resource`, and the selected aggregate aliases.
336336

337337
```yaml
338338
label: Sales Funnel
@@ -343,27 +343,68 @@ chart:
343343
type: funnel
344344
title: Sales funnel
345345
query:
346+
source: steps
346347
steps:
347348
- name: Leads
348349
resource: leads
349-
metric:
350-
agg: count
351-
as: value
350+
select:
351+
- agg: count
352+
as: value
352353
- name: Qualified
353354
resource: leads
354-
metric:
355-
agg: count
356-
as: value
355+
select:
356+
- agg: count
357+
as: value
357358
filters:
358359
and:
359360
- field: status
360361
eq: qualified
361362
- name: Customers
362363
resource: orders
363-
metric:
364-
agg: count_distinct
365-
field: customer_id
366-
as: value
364+
select:
365+
- agg: count_distinct
366+
field: customer_id
367+
as: value
368+
```
369+
370+
For the same numeric buckets across multiple resources, add `query.bucket` and render the result as a stacked bar chart. The dashboard runs every step once per bucket and returns rows with `label`, `name`, `resource`, and the aggregate aliases:
371+
372+
```yaml
373+
label: Cars by Price Range and Database
374+
target: chart
375+
size: wide
376+
height: 360
377+
chart:
378+
type: stacked_bar
379+
x:
380+
field: label
381+
y:
382+
field: count
383+
series:
384+
field: name
385+
query:
386+
source: steps
387+
bucket:
388+
field: price
389+
buckets:
390+
- label: Budget
391+
max: 3500
392+
- label: Mid-range
393+
min: 3500
394+
max: 7000
395+
- label: Premium
396+
min: 7000
397+
steps:
398+
- name: SQLite
399+
resource: cars_sl
400+
select:
401+
- agg: count
402+
as: count
403+
- name: MySQL
404+
resource: cars_mysql
405+
select:
406+
- agg: count
407+
as: count
367408
```
368409

369410
### KPI Card

adminforth/documentation/src/pages/index.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,18 @@ const images = [
140140
link: '/docs/tutorial/Plugins/background-jobs/',
141141
description: 'Use background jobs to handle long-running tasks efficiently. Schedule, monitor, and manage your background processes with ease even after server restarts'
142142
},
143+
{
144+
original: require('@site/static/img/previews/agent.png').default,
145+
title: 'Agent Plugin - give AI any task and let it handle it',
146+
link: '/docs/tutorial/Plugins/agent/',
147+
description: 'Provides an internal agent that can perform various tasks based on natural language instructions. Connect it to your data and let it help you with content generation, data management, or any custom use case you can think of'
148+
},
149+
{
150+
original: require('@site/static/img/previews/dashboards-plugin.png').default,
151+
title: 'Dashboard Plugin - creare custom dashboards from web interface',
152+
link: '/docs/tutorial/Plugins/dashboard/',
153+
description: 'Provides a customizable dashboard plugin that allows you to create and manage dashboards for your data. Connect it to your resources and visualize your data in a meaningful way'
154+
},
143155
];
144156

145157

562 KB
Loading
448 KB
Loading

adminforth/modules/restApi.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1927,6 +1927,9 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
19271927
method: 'POST',
19281928
path: '/create_record',
19291929
description: 'Creates a new record in the specified resource. The endpoint validates create permissions, required fields, hidden or backend-only field rules, polymorphic foreign keys, and resource hooks before persisting and returning the created primary key.',
1930+
agent: {
1931+
isDangerous: true,
1932+
},
19301933
request_schema: createRecordRequestSchema,
19311934
response_schema: createRecordResponseSchema,
19321935
handler: async ({ body, adminUser, query, headers, cookies, requestUrl, response }) => {
@@ -2075,9 +2078,12 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
20752078
}
20762079
});
20772080
server.endpoint({
2078-
method: 'POST',
2079-
path: '/update_record',
2081+
method: 'POST',
2082+
path: '/update_record',
20802083
description: 'Updates an existing record by primary key. The endpoint validates edit permissions, current record existence, hidden, backend-only, and read-only field rules, polymorphic foreign keys, and resource hooks before saving changes.',
2084+
agent: {
2085+
isDangerous: true,
2086+
},
20812087
request_schema: updateRecordRequestSchema,
20822088
response_schema: updateRecordResponseSchema,
20832089
handler: async ({ body, adminUser, query, headers, cookies, requestUrl, response }) => {
@@ -2216,9 +2222,12 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
22162222
}
22172223
});
22182224
server.endpoint({
2219-
method: 'POST',
2220-
path: '/delete_record',
2225+
method: 'POST',
2226+
path: '/delete_record',
22212227
description: 'Deletes an existing record by primary key. The endpoint validates delete permissions, loads the current record, executes configured cascade child deletion, and then removes the record.',
2228+
agent: {
2229+
isDangerous: true,
2230+
},
22222231
request_schema: deleteRecordRequestSchema,
22232232
response_schema: deleteRecordResponseSchema,
22242233
handler: async ({ body, adminUser, query, headers, cookies, requestUrl, response }) => {
@@ -2305,6 +2314,9 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
23052314
method: 'POST',
23062315
path: '/start_custom_action',
23072316
description: 'Executes a custom resource action for a single record. The endpoint validates the resource, action existence, and action permissions, then either returns a redirect URL or executes the action handler and returns its result together with action context.',
2317+
agent: {
2318+
isDangerous: true,
2319+
},
23082320
request_schema: startCustomActionRequestSchema,
23092321
response_schema: startCustomActionResponseSchema,
23102322
handler: async ({ body, adminUser, tr, cookies, response, headers }) => {
@@ -2361,6 +2373,9 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
23612373
method: 'POST',
23622374
path: '/start_custom_bulk_action',
23632375
description: 'Executes a custom resource action in bulk mode for multiple records. The endpoint validates the resource, action existence, bulk handler availability, and permissions, then runs the bulk handler and returns its result together with action context.',
2376+
agent: {
2377+
isDangerous: true,
2378+
},
23642379
request_schema: startCustomBulkActionRequestSchema,
23652380
response_schema: startCustomBulkActionResponseSchema,
23662381
handler: async ({ body, adminUser, tr, response, cookies, headers }) => {

adminforth/servers/express.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,7 @@ class ExpressServer implements IExpressHttpServer {
479479
method: method.toUpperCase(),
480480
path: routePath,
481481
description: schema.description,
482+
agent: schema.agent,
482483
request_schema: schema.request,
483484
response_schema: schema.response,
484485
meta: schema.meta,
@@ -568,6 +569,7 @@ class ExpressServer implements IExpressHttpServer {
568569
request_schema,
569570
response_schema,
570571
responce_schema,
572+
agent,
571573
target='json'
572574
} = options;
573575
if (!path.startsWith('/')) {
@@ -583,6 +585,7 @@ class ExpressServer implements IExpressHttpServer {
583585
description,
584586
request_schema,
585587
response_schema: normalizedResponseSchema,
588+
agent,
586589
handler,
587590
})
588591
: null;

adminforth/servers/openapi.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class OpenApiRegistry implements IOpenApiRegistry {
5656
method: options.method.toLowerCase(),
5757
path: options.path,
5858
description: options.description,
59+
agent: options.agent,
5960
request_schema: options.request_schema,
6061
response_schema: responseSchema,
6162
meta: options.meta,

0 commit comments

Comments
 (0)