Skip to content

Commit e3232b4

Browse files
committed
Merge branch 'next' of github.com:devforth/adminforth into next
2 parents 8c466f4 + fbf03c6 commit e3232b4

8 files changed

Lines changed: 127 additions & 24 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/27-dashboard.md

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -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/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,

adminforth/types/Back.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import type { ZodType } from 'zod';
55

66
import { ActionCheckSource, AdminForthFilterOperators, AdminForthSortDirections, AllowedActionsEnum, AdminForthResourcePages,
77
type AdminForthComponentDeclaration,
8-
type AdminForthResourceCommon,
98
type AdminUser, type AllowedActionsResolved,
109
type AdminForthBulkActionCommon,
1110
type AdminForthForeignResourceCommon,
@@ -59,13 +58,18 @@ export interface IAdminForthAuthenticatedEndpointHandlerInput extends IAdminFort
5958
adminUser: AdminUser;
6059
}
6160

61+
export type AgentToolMeta = {
62+
isDangerous?: boolean;
63+
};
64+
6265
export interface IAdminForthEndpointOptionsBase {
6366
method: string,
6467
path: string,
6568
description?: string,
6669
request_schema?: AnySchemaObject,
6770
response_schema?: AnySchemaObject,
6871
responce_schema?: AnySchemaObject,
72+
agent?: AgentToolMeta,
6973
meta?: Record<string, unknown>,
7074
target?: 'json' | 'upload',
7175
}
@@ -102,6 +106,11 @@ export interface IAdminForthExpressRouteSchema {
102106
*/
103107
response?: AdminForthExpressSchemaInput;
104108

109+
/**
110+
* AdminForth agent metadata.
111+
*/
112+
agent?: AgentToolMeta;
113+
105114
/**
106115
* Internal metadata for AdminForth integrations. This is not rendered in the OpenAPI document.
107116
*/
@@ -112,6 +121,7 @@ export interface IRegisteredApiSchema {
112121
method: string;
113122
path: string;
114123
description?: string;
124+
agent?: AgentToolMeta;
115125
meta?: Record<string, unknown>;
116126
request_schema?: AnySchemaObject;
117127
response_schema?: AnySchemaObject;

dev-demo/Taskfile.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ vars:
4343
- "adminforth-oauth-adapter-keycloak"
4444
- "adminforth-oauth-adapter-microsoft"
4545
- "adminforth-oauth-adapter-twitch"
46+
- "adminforth-oauth-adapter-telegram"
47+
- "adminforth-chat-surface-adapter-telegram"
4648
- "adminforth-image-generation-adapter-openai"
4749
- "adminforth-storage-adapter-amazon-s3"
4850
- "adminforth-storage-adapter-local"

0 commit comments

Comments
 (0)