adminizer 4.4.0-build.175 → 4.4.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/assets/add-DFzzaDhZ.js +1 -0
- package/assets/{add-form-BfIXZ_9g.js → add-form-Taz9dTw_.js} +1 -1
- package/assets/add-group-BVYkGCd9.js +1 -0
- package/assets/{add-group-form-BkXmTsXF.js → add-group-form-DMz0_Oo9.js} +1 -1
- package/assets/add-user-Dq_U0RCT.js +1 -0
- package/assets/{add-user-form-CeLtuCJN.js → add-user-form-izlXhv2-.js} +1 -1
- package/assets/{app-CUnGKHb5.css → app-ByR7F4XX.css} +1 -1
- package/assets/app-layout-C7q3_eFS.js +1 -0
- package/assets/{app-shell-CggZrBz6.js → app-shell-C8Cnr5Hy.js} +1 -1
- package/assets/{app-sidebar-Do2eoo3w.js → app-sidebar-BCtD_nHo.js} +1 -1
- package/assets/app-sidebar-header-Bcpas2Np.js +1 -0
- package/assets/app.js +2 -2
- package/assets/breadcrumbs-DSMzNYiS.js +1 -0
- package/assets/{catalog-BbgPgewa.js → catalog-DYJ6dK_P.js} +1 -1
- package/assets/{dashboard-Chht3NJZ.js → dashboard-DEVNMK2g.js} +1 -1
- package/assets/{date-picker-BjXNej9c.js → date-picker-kW3wZQr_.js} +1 -1
- package/assets/del-modal-DyIWFflP.js +1 -0
- package/assets/{field-renderer-CUYWPzfi.js → field-renderer-CVUxftOw.js} +3 -3
- package/assets/{form-DMLPoAW8.js → form-1GnTJlbE.js} +1 -1
- package/assets/form-B4ymMuwj.js +1 -0
- package/assets/{geo-json-CM4o8sUY.js → geo-json-YeWQOYnT.js} +1 -1
- package/assets/{home-OvDENcVX.js → home-ipCg51mN.js} +2 -2
- package/assets/list-C_KPYQc4.js +1 -0
- package/assets/{list-table-CmYyTdS5.js → list-table-Do8_doRe.js} +3 -3
- package/assets/manifest.json +75 -75
- package/assets/{media-manager-CGPeuOR2.js → media-manager-IknQnWel.js} +1 -1
- package/assets/{module-DxNCvjiw.js → module-Bf9rzSBs.js} +1 -1
- package/assets/{nav-footer-2ZwcY1Pj.js → nav-footer-B85HLj-j.js} +1 -1
- package/assets/{nav-main-CZa4xkP1.js → nav-main-UuH4U_0H.js} +1 -1
- package/assets/{nav-user-e1Cj0abB.js → nav-user-DuCZNsZX.js} +1 -1
- package/assets/{notification-DWxL0Sxm.js → notification-C6X6f-xS.js} +1 -1
- package/assets/{pagination-render-C1OX0c6q.js → pagination-render-io0M2m2O.js} +1 -1
- package/assets/{tabs-7Dl6--VG.js → tabs-BhkFoerH.js} +1 -1
- package/assets/theme-switcher-cgyA7_cO.js +1 -0
- package/assets/{toast-editor-Bso8riqX.js → toast-editor-DwRk6WqT.js} +1 -1
- package/assets/{user-info-DrdQBvFb.js → user-info-BGQvqBmO.js} +1 -1
- package/assets/{user-menu-content-ePAt_cwK.js → user-menu-content-BDm88xaM.js} +1 -1
- package/assets/{welcome-Buo3uXmE.js → welcome-Cu8G7BE9.js} +1 -1
- package/interfaces/adminpanelConfig.d.ts +0 -5
- package/interfaces/types.d.ts +0 -13
- package/lib/Adminizer.d.ts +0 -2
- package/lib/Adminizer.js +0 -4
- package/package.json +3 -6
- package/system/Router.js +0 -7
- package/system/bindInertia.js +0 -4
- package/system/defaults.js +0 -5
- package/assets/add-DlVWR7Qx.js +0 -1
- package/assets/add-group-CLSUO35F.js +0 -1
- package/assets/add-user-Bw5AuxqF.js +0 -1
- package/assets/app-layout-mpp8ni0G.js +0 -1
- package/assets/app-sidebar-header-pfquDpHT.js +0 -1
- package/assets/breadcrumbs-BS5uXMaM.js +0 -1
- package/assets/del-modal-P8KiMFNx.js +0 -1
- package/assets/form-wcpZWHjp.js +0 -1
- package/assets/list-DfqnQZpI.js +0 -1
- package/assets/theme-switcher-xFldiRZz.js +0 -1
- package/controllers/ai/AiAssistantController.d.ts +0 -7
- package/controllers/ai/AiAssistantController.js +0 -92
- package/lib/ai-assistant/AbstractAiModelService.d.ts +0 -17
- package/lib/ai-assistant/AbstractAiModelService.js +0 -32
- package/lib/ai-assistant/AiAssistantHandler.d.ts +0 -19
- package/lib/ai-assistant/AiAssistantHandler.js +0 -58
- package/lib/ai-assistant/DummyAiModelService.d.ts +0 -8
- package/lib/ai-assistant/DummyAiModelService.js +0 -14
- package/lib/ai-assistant/OpenAiModelService.d.ts +0 -24
- package/lib/ai-assistant/OpenAiModelService.js +0 -185
- package/system/bindAiAssistant.d.ts +0 -2
- package/system/bindAiAssistant.js +0 -30
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { Adminizer } from '../../lib/Adminizer.js';
|
|
2
|
-
export class AiAssistantController {
|
|
3
|
-
static async getModels(req, res) {
|
|
4
|
-
if (!req.adminizer.config.aiAssistant?.enabled) {
|
|
5
|
-
return res.json([]);
|
|
6
|
-
}
|
|
7
|
-
const handler = req.adminizer.aiAssistantHandler;
|
|
8
|
-
if (!handler) {
|
|
9
|
-
Adminizer.log.warn('AI assistant handler is not initialized');
|
|
10
|
-
return res.json([]);
|
|
11
|
-
}
|
|
12
|
-
const models = handler
|
|
13
|
-
.getModels()
|
|
14
|
-
.filter((model) => req.adminizer.accessRightsHelper.hasPermission(`ai-assistant-${model.id}`, req.user));
|
|
15
|
-
return res.json(models);
|
|
16
|
-
}
|
|
17
|
-
static async getHistory(req, res) {
|
|
18
|
-
if (!req.adminizer.config.aiAssistant?.enabled) {
|
|
19
|
-
return res.json({ history: [] });
|
|
20
|
-
}
|
|
21
|
-
const { modelId } = req.params;
|
|
22
|
-
if (!modelId) {
|
|
23
|
-
return res.status(400).json({ error: 'Model id is required' });
|
|
24
|
-
}
|
|
25
|
-
if (!req.adminizer.accessRightsHelper.hasPermission(`ai-assistant-${modelId}`, req.user)) {
|
|
26
|
-
return res.status(403).json({ error: 'Forbidden' });
|
|
27
|
-
}
|
|
28
|
-
const handler = req.adminizer.aiAssistantHandler;
|
|
29
|
-
if (!handler) {
|
|
30
|
-
return res.status(500).json({ error: 'AI assistant is not available' });
|
|
31
|
-
}
|
|
32
|
-
const history = handler.getHistory(req.user.id, modelId).map(AiAssistantController.serializeMessage);
|
|
33
|
-
return res.json({ history });
|
|
34
|
-
}
|
|
35
|
-
static async sendMessage(req, res) {
|
|
36
|
-
if (!req.adminizer.config.aiAssistant?.enabled) {
|
|
37
|
-
return res.status(503).json({ error: 'AI assistant is disabled' });
|
|
38
|
-
}
|
|
39
|
-
const { modelId, message } = req.body;
|
|
40
|
-
if (!modelId || typeof modelId !== 'string') {
|
|
41
|
-
return res.status(400).json({ error: 'Model id is required' });
|
|
42
|
-
}
|
|
43
|
-
if (!message || typeof message !== 'string' || message.trim().length === 0) {
|
|
44
|
-
return res.status(400).json({ error: 'Message is required' });
|
|
45
|
-
}
|
|
46
|
-
if (!req.adminizer.accessRightsHelper.hasPermission(`ai-assistant-${modelId}`, req.user)) {
|
|
47
|
-
return res.status(403).json({ error: 'Forbidden' });
|
|
48
|
-
}
|
|
49
|
-
const handler = req.adminizer.aiAssistantHandler;
|
|
50
|
-
if (!handler) {
|
|
51
|
-
return res.status(500).json({ error: 'AI assistant is not available' });
|
|
52
|
-
}
|
|
53
|
-
try {
|
|
54
|
-
const response = await handler.sendMessage(req.user, modelId, message.trim());
|
|
55
|
-
return res.json({
|
|
56
|
-
modelId,
|
|
57
|
-
history: response.history.map(AiAssistantController.serializeMessage),
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
catch (error) {
|
|
61
|
-
Adminizer.log.error('AI assistant error', error);
|
|
62
|
-
return res.status(500).json({ error: 'Failed to process request' });
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
static async resetHistory(req, res) {
|
|
66
|
-
if (!req.adminizer.config.aiAssistant?.enabled) {
|
|
67
|
-
return res.status(503).json({ error: 'AI assistant is disabled' });
|
|
68
|
-
}
|
|
69
|
-
const { modelId } = req.params;
|
|
70
|
-
if (!modelId) {
|
|
71
|
-
return res.status(400).json({ error: 'Model id is required' });
|
|
72
|
-
}
|
|
73
|
-
if (!req.adminizer.accessRightsHelper.hasPermission(`ai-assistant-${modelId}`, req.user)) {
|
|
74
|
-
return res.status(403).json({ error: 'Forbidden' });
|
|
75
|
-
}
|
|
76
|
-
const handler = req.adminizer.aiAssistantHandler;
|
|
77
|
-
if (!handler) {
|
|
78
|
-
return res.status(500).json({ error: 'AI assistant is not available' });
|
|
79
|
-
}
|
|
80
|
-
handler.resetHistory(req.user.id, modelId);
|
|
81
|
-
return res.json({ history: [] });
|
|
82
|
-
}
|
|
83
|
-
static serializeMessage(message) {
|
|
84
|
-
return {
|
|
85
|
-
id: message.id,
|
|
86
|
-
role: message.role,
|
|
87
|
-
content: message.content,
|
|
88
|
-
timestamp: message.timestamp instanceof Date ? message.timestamp.toISOString() : message.timestamp,
|
|
89
|
-
modelId: message.modelId,
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { Adminizer } from '../Adminizer';
|
|
2
|
-
import { AiAssistantMessage, AiAssistantModelInfo } from '../../interfaces/types';
|
|
3
|
-
import { UserAP } from '../../models/UserAP';
|
|
4
|
-
/**
|
|
5
|
-
* Base class for AI assistant models. It is responsible for registering
|
|
6
|
-
* access rights for the model and providing metadata for the front-end.
|
|
7
|
-
*/
|
|
8
|
-
export declare abstract class AbstractAiModelService {
|
|
9
|
-
protected readonly adminizer: Adminizer;
|
|
10
|
-
readonly id: string;
|
|
11
|
-
readonly name: string;
|
|
12
|
-
readonly description?: string;
|
|
13
|
-
protected constructor(adminizer: Adminizer, metadata: AiAssistantModelInfo);
|
|
14
|
-
protected registerAccessRight(): void;
|
|
15
|
-
getMetadata(): AiAssistantModelInfo;
|
|
16
|
-
abstract generateReply(prompt: string, history: AiAssistantMessage[], user: UserAP): Promise<string>;
|
|
17
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Base class for AI assistant models. It is responsible for registering
|
|
3
|
-
* access rights for the model and providing metadata for the front-end.
|
|
4
|
-
*/
|
|
5
|
-
export class AbstractAiModelService {
|
|
6
|
-
adminizer;
|
|
7
|
-
id;
|
|
8
|
-
name;
|
|
9
|
-
description;
|
|
10
|
-
constructor(adminizer, metadata) {
|
|
11
|
-
this.adminizer = adminizer;
|
|
12
|
-
this.id = metadata.id;
|
|
13
|
-
this.name = metadata.name;
|
|
14
|
-
this.description = metadata.description;
|
|
15
|
-
this.registerAccessRight();
|
|
16
|
-
}
|
|
17
|
-
registerAccessRight() {
|
|
18
|
-
this.adminizer.accessRightsHelper.registerToken({
|
|
19
|
-
id: `ai-assistant-${this.id}`,
|
|
20
|
-
name: this.name,
|
|
21
|
-
description: `Access to AI assistant model ${this.name}`,
|
|
22
|
-
department: 'AI Assistant',
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
getMetadata() {
|
|
26
|
-
return {
|
|
27
|
-
id: this.id,
|
|
28
|
-
name: this.name,
|
|
29
|
-
description: this.description,
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { Adminizer } from '../Adminizer';
|
|
2
|
-
import { AbstractAiModelService } from './AbstractAiModelService';
|
|
3
|
-
import { AiAssistantMessage, AiAssistantModelInfo } from '../../interfaces/types';
|
|
4
|
-
import { UserAP } from '../../models/UserAP';
|
|
5
|
-
export declare class AiAssistantHandler {
|
|
6
|
-
private readonly adminizer;
|
|
7
|
-
private readonly models;
|
|
8
|
-
private readonly history;
|
|
9
|
-
constructor(adminizer: Adminizer);
|
|
10
|
-
registerModel(service: AbstractAiModelService): void;
|
|
11
|
-
getModel(id: string): AbstractAiModelService | undefined;
|
|
12
|
-
getModels(): AiAssistantModelInfo[];
|
|
13
|
-
getHistory(userId: number, modelId: string): AiAssistantMessage[];
|
|
14
|
-
resetHistory(userId: number, modelId: string): void;
|
|
15
|
-
sendMessage(user: UserAP, modelId: string, prompt: string): Promise<{
|
|
16
|
-
history: AiAssistantMessage[];
|
|
17
|
-
}>;
|
|
18
|
-
private getSessionKey;
|
|
19
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { v4 as uuid } from 'uuid';
|
|
2
|
-
import { Adminizer } from '../Adminizer.js';
|
|
3
|
-
export class AiAssistantHandler {
|
|
4
|
-
adminizer;
|
|
5
|
-
models = new Map();
|
|
6
|
-
history = new Map();
|
|
7
|
-
constructor(adminizer) {
|
|
8
|
-
this.adminizer = adminizer;
|
|
9
|
-
}
|
|
10
|
-
registerModel(service) {
|
|
11
|
-
if (this.models.has(service.id)) {
|
|
12
|
-
Adminizer.log.warn(`AI assistant model with id "${service.id}" is already registered. Overwriting.`);
|
|
13
|
-
}
|
|
14
|
-
this.models.set(service.id, service);
|
|
15
|
-
}
|
|
16
|
-
getModel(id) {
|
|
17
|
-
return this.models.get(id);
|
|
18
|
-
}
|
|
19
|
-
getModels() {
|
|
20
|
-
return Array.from(this.models.values()).map((service) => service.getMetadata());
|
|
21
|
-
}
|
|
22
|
-
getHistory(userId, modelId) {
|
|
23
|
-
return this.history.get(this.getSessionKey(userId, modelId)) ?? [];
|
|
24
|
-
}
|
|
25
|
-
resetHistory(userId, modelId) {
|
|
26
|
-
this.history.delete(this.getSessionKey(userId, modelId));
|
|
27
|
-
}
|
|
28
|
-
async sendMessage(user, modelId, prompt) {
|
|
29
|
-
const service = this.getModel(modelId);
|
|
30
|
-
if (!service) {
|
|
31
|
-
throw new Error(`AI assistant model not found: ${modelId}`);
|
|
32
|
-
}
|
|
33
|
-
const sessionKey = this.getSessionKey(user.id, modelId);
|
|
34
|
-
const currentHistory = [...this.getHistory(user.id, modelId)];
|
|
35
|
-
const userMessage = {
|
|
36
|
-
id: uuid(),
|
|
37
|
-
role: 'user',
|
|
38
|
-
content: prompt,
|
|
39
|
-
timestamp: new Date(),
|
|
40
|
-
modelId,
|
|
41
|
-
};
|
|
42
|
-
currentHistory.push(userMessage);
|
|
43
|
-
const reply = await service.generateReply(prompt, currentHistory, user);
|
|
44
|
-
const assistantMessage = {
|
|
45
|
-
id: uuid(),
|
|
46
|
-
role: 'assistant',
|
|
47
|
-
content: reply,
|
|
48
|
-
timestamp: new Date(),
|
|
49
|
-
modelId,
|
|
50
|
-
};
|
|
51
|
-
currentHistory.push(assistantMessage);
|
|
52
|
-
this.history.set(sessionKey, currentHistory);
|
|
53
|
-
return { history: currentHistory };
|
|
54
|
-
}
|
|
55
|
-
getSessionKey(userId, modelId) {
|
|
56
|
-
return `${userId}:${modelId}`;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { AbstractAiModelService } from './AbstractAiModelService';
|
|
2
|
-
import { AiAssistantMessage } from '../../interfaces/types';
|
|
3
|
-
import { UserAP } from '../../models/UserAP';
|
|
4
|
-
import { Adminizer } from '../Adminizer';
|
|
5
|
-
export declare class DummyAiModelService extends AbstractAiModelService {
|
|
6
|
-
constructor(adminizer: Adminizer);
|
|
7
|
-
generateReply(_prompt: string, _history: AiAssistantMessage[], _user: UserAP): Promise<string>;
|
|
8
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { AbstractAiModelService } from './AbstractAiModelService.js';
|
|
2
|
-
const DUMMY_RESPONSE = 'Ai-assystant dummy in deveploment';
|
|
3
|
-
export class DummyAiModelService extends AbstractAiModelService {
|
|
4
|
-
constructor(adminizer) {
|
|
5
|
-
super(adminizer, {
|
|
6
|
-
id: 'dummy',
|
|
7
|
-
name: 'Dummy assistant',
|
|
8
|
-
description: 'Development placeholder model that returns a static response.',
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
async generateReply(_prompt, _history, _user) {
|
|
12
|
-
return DUMMY_RESPONSE;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { AbstractAiModelService } from './AbstractAiModelService';
|
|
2
|
-
import { AiAssistantMessage } from '../../interfaces/types';
|
|
3
|
-
import { UserAP } from '../../models/UserAP';
|
|
4
|
-
import { Adminizer } from '../Adminizer';
|
|
5
|
-
import { ActionType } from '../../interfaces/adminpanelConfig';
|
|
6
|
-
import { Entity } from '../../interfaces/types';
|
|
7
|
-
import { DataAccessor } from '../DataAccessor';
|
|
8
|
-
type DataAccessorFactory = (entity: Entity, user: UserAP, action: ActionType) => DataAccessor;
|
|
9
|
-
export declare class OpenAiModelService extends AbstractAiModelService {
|
|
10
|
-
private readonly createAccessor;
|
|
11
|
-
constructor(adminizer: Adminizer, accessorFactory?: DataAccessorFactory);
|
|
12
|
-
generateReply(prompt: string, _history: AiAssistantMessage[], user: UserAP): Promise<string>;
|
|
13
|
-
private handleCreate;
|
|
14
|
-
private parseInstruction;
|
|
15
|
-
private usageMessage;
|
|
16
|
-
private resolveEntity;
|
|
17
|
-
private normalizeModelConfig;
|
|
18
|
-
private userHasPermission;
|
|
19
|
-
private getPermissionToken;
|
|
20
|
-
private extractPrimaryKey;
|
|
21
|
-
private extractString;
|
|
22
|
-
private extractObject;
|
|
23
|
-
}
|
|
24
|
-
export {};
|
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
import { AbstractAiModelService } from './AbstractAiModelService.js';
|
|
2
|
-
import { Adminizer } from '../Adminizer.js';
|
|
3
|
-
import { DataAccessor } from '../DataAccessor.js';
|
|
4
|
-
const ACTION_TOKENS = {
|
|
5
|
-
add: 'create',
|
|
6
|
-
edit: 'update',
|
|
7
|
-
list: 'read',
|
|
8
|
-
view: 'read',
|
|
9
|
-
remove: 'delete',
|
|
10
|
-
};
|
|
11
|
-
export class OpenAiModelService extends AbstractAiModelService {
|
|
12
|
-
createAccessor;
|
|
13
|
-
constructor(adminizer, accessorFactory) {
|
|
14
|
-
super(adminizer, {
|
|
15
|
-
id: 'openai',
|
|
16
|
-
name: 'OpenAI fixture agent',
|
|
17
|
-
description: 'Executes structured commands with DataAccessor using the current user permissions.',
|
|
18
|
-
});
|
|
19
|
-
this.createAccessor = accessorFactory ?? ((entity, user, action) => new DataAccessor(adminizer, user, entity, action));
|
|
20
|
-
}
|
|
21
|
-
async generateReply(prompt, _history, user) {
|
|
22
|
-
const instruction = this.parseInstruction(prompt);
|
|
23
|
-
if (!instruction) {
|
|
24
|
-
return this.usageMessage();
|
|
25
|
-
}
|
|
26
|
-
switch (instruction.action) {
|
|
27
|
-
case 'create':
|
|
28
|
-
return this.handleCreate(instruction, user);
|
|
29
|
-
default:
|
|
30
|
-
return `Unsupported action "${instruction.action}". The OpenAI agent currently supports only the "create" action.`;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
async handleCreate(instruction, user) {
|
|
34
|
-
const entity = this.resolveEntity(instruction.entity);
|
|
35
|
-
if (!entity || !entity.model) {
|
|
36
|
-
return `Model "${instruction.entity}" is not available in this project.`;
|
|
37
|
-
}
|
|
38
|
-
if (!this.userHasPermission(entity, user, 'add')) {
|
|
39
|
-
return `User "${user.login}" does not have permission to create ${entity.name} records.`;
|
|
40
|
-
}
|
|
41
|
-
if (!instruction.payload || typeof instruction.payload !== 'object') {
|
|
42
|
-
return 'The "create" action requires a "data" object with field values.';
|
|
43
|
-
}
|
|
44
|
-
try {
|
|
45
|
-
const accessor = this.createAccessor(entity, user, 'add');
|
|
46
|
-
const created = await entity.model.create(instruction.payload, accessor);
|
|
47
|
-
const preview = JSON.stringify(created, null, 2);
|
|
48
|
-
const recordId = this.extractPrimaryKey(created, entity);
|
|
49
|
-
const idMessage = recordId !== undefined ? ` (id: ${recordId})` : '';
|
|
50
|
-
return `Record created in ${entity.name}${idMessage}:\n${preview}`;
|
|
51
|
-
}
|
|
52
|
-
catch (error) {
|
|
53
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
54
|
-
Adminizer.log.error('OpenAI agent failed to create record', error);
|
|
55
|
-
return `Failed to create ${entity.name} record: ${message}`;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
parseInstruction(prompt) {
|
|
59
|
-
try {
|
|
60
|
-
const parsed = JSON.parse(prompt);
|
|
61
|
-
if (!parsed || typeof parsed !== 'object') {
|
|
62
|
-
return null;
|
|
63
|
-
}
|
|
64
|
-
const rawAction = this.extractString(parsed, ['action', 'type']);
|
|
65
|
-
if (!rawAction) {
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
const action = rawAction.toLowerCase();
|
|
69
|
-
if (!['create', 'list', 'update', 'delete'].includes(action)) {
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
const entity = this.extractString(parsed, ['entity', 'model']);
|
|
73
|
-
if (!entity) {
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
76
|
-
const payload = this.extractObject(parsed, ['data', 'payload', 'record']);
|
|
77
|
-
const criteria = this.extractObject(parsed, ['criteria', 'where', 'filter']);
|
|
78
|
-
return {
|
|
79
|
-
action: action,
|
|
80
|
-
entity,
|
|
81
|
-
payload,
|
|
82
|
-
criteria,
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
catch {
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
usageMessage() {
|
|
90
|
-
return [
|
|
91
|
-
'Provide JSON instructions so the OpenAI agent can execute them with your permissions.',
|
|
92
|
-
'Example:',
|
|
93
|
-
'```json',
|
|
94
|
-
'{',
|
|
95
|
-
' "action": "create",',
|
|
96
|
-
' "entity": "Example",',
|
|
97
|
-
' "data": { "title": "Hello from the agent" }',
|
|
98
|
-
'}',
|
|
99
|
-
'```',
|
|
100
|
-
'The agent uses DataAccessor, so any field-level restrictions from your account are respected.',
|
|
101
|
-
].join('\n');
|
|
102
|
-
}
|
|
103
|
-
resolveEntity(modelName) {
|
|
104
|
-
const models = this.adminizer.config.models;
|
|
105
|
-
if (!models) {
|
|
106
|
-
return null;
|
|
107
|
-
}
|
|
108
|
-
const loweredName = modelName.toLowerCase();
|
|
109
|
-
const match = Object.entries(models).find(([key, config]) => {
|
|
110
|
-
if (key.toLowerCase() === loweredName) {
|
|
111
|
-
return true;
|
|
112
|
-
}
|
|
113
|
-
if (typeof config === 'object' && config && 'model' in config && typeof config.model === 'string') {
|
|
114
|
-
return config.model.toLowerCase() === loweredName;
|
|
115
|
-
}
|
|
116
|
-
return false;
|
|
117
|
-
});
|
|
118
|
-
if (!match) {
|
|
119
|
-
return null;
|
|
120
|
-
}
|
|
121
|
-
const [configName, configValue] = match;
|
|
122
|
-
const normalizedConfig = this.normalizeModelConfig(configName, configValue);
|
|
123
|
-
const model = this.adminizer.modelHandler.model.get(normalizedConfig.model);
|
|
124
|
-
if (!model) {
|
|
125
|
-
return null;
|
|
126
|
-
}
|
|
127
|
-
return {
|
|
128
|
-
name: configName,
|
|
129
|
-
uri: `${this.adminizer.config.routePrefix}/model/${configName}`,
|
|
130
|
-
type: 'model',
|
|
131
|
-
config: normalizedConfig,
|
|
132
|
-
model,
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
normalizeModelConfig(name, config) {
|
|
136
|
-
const baseConfig = {
|
|
137
|
-
model: name,
|
|
138
|
-
icon: 'description',
|
|
139
|
-
title: name,
|
|
140
|
-
list: true,
|
|
141
|
-
add: true,
|
|
142
|
-
edit: true,
|
|
143
|
-
remove: true,
|
|
144
|
-
view: true,
|
|
145
|
-
};
|
|
146
|
-
if (typeof config === 'boolean') {
|
|
147
|
-
return config ? baseConfig : { ...baseConfig, list: false, add: false, edit: false, remove: false, view: false };
|
|
148
|
-
}
|
|
149
|
-
return { ...baseConfig, ...config };
|
|
150
|
-
}
|
|
151
|
-
userHasPermission(entity, user, action) {
|
|
152
|
-
const token = this.getPermissionToken(entity, action);
|
|
153
|
-
return this.adminizer.accessRightsHelper.hasPermission(token, user);
|
|
154
|
-
}
|
|
155
|
-
getPermissionToken(entity, action) {
|
|
156
|
-
const verb = ACTION_TOKENS[action];
|
|
157
|
-
const modelName = entity.model?.modelname ?? entity.config?.model ?? entity.name;
|
|
158
|
-
return `${verb}-${modelName}-${entity.type}`.toLowerCase();
|
|
159
|
-
}
|
|
160
|
-
extractPrimaryKey(record, entity) {
|
|
161
|
-
if (!record) {
|
|
162
|
-
return undefined;
|
|
163
|
-
}
|
|
164
|
-
const primaryKey = entity.model?.primaryKey ?? 'id';
|
|
165
|
-
return record[primaryKey];
|
|
166
|
-
}
|
|
167
|
-
extractString(source, keys) {
|
|
168
|
-
for (const key of keys) {
|
|
169
|
-
const value = source?.[key];
|
|
170
|
-
if (typeof value === 'string' && value.trim().length > 0) {
|
|
171
|
-
return value.trim();
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
return undefined;
|
|
175
|
-
}
|
|
176
|
-
extractObject(source, keys) {
|
|
177
|
-
for (const key of keys) {
|
|
178
|
-
const value = source?.[key];
|
|
179
|
-
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
180
|
-
return value;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
return undefined;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { Adminizer } from '../lib/Adminizer.js';
|
|
2
|
-
import { AiAssistantHandler } from '../lib/ai-assistant/AiAssistantHandler.js';
|
|
3
|
-
import { DummyAiModelService } from '../lib/ai-assistant/DummyAiModelService.js';
|
|
4
|
-
import { OpenAiModelService } from '../lib/ai-assistant/OpenAiModelService.js';
|
|
5
|
-
const modelFactories = {
|
|
6
|
-
dummy: (adminizer) => new DummyAiModelService(adminizer),
|
|
7
|
-
openai: (adminizer) => new OpenAiModelService(adminizer),
|
|
8
|
-
};
|
|
9
|
-
export async function bindAiAssistant(adminizer) {
|
|
10
|
-
// Early return if AI assistant is disabled
|
|
11
|
-
if (!adminizer.config.aiAssistant?.enabled) {
|
|
12
|
-
Adminizer.log.info('AI assistant is disabled in configuration. Skipping initialization.');
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
adminizer.aiAssistantHandler = new AiAssistantHandler(adminizer);
|
|
16
|
-
const configModels = adminizer.config.aiAssistant?.models;
|
|
17
|
-
const requestedModels = Array.isArray(configModels) && configModels.length > 0
|
|
18
|
-
? configModels
|
|
19
|
-
: ['dummy'];
|
|
20
|
-
requestedModels.forEach((modelId) => {
|
|
21
|
-
const factory = modelFactories[modelId];
|
|
22
|
-
if (!factory) {
|
|
23
|
-
Adminizer.log.warn(`AI assistant model "${modelId}" is not registered. Skipping.`);
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
const service = factory(adminizer);
|
|
27
|
-
adminizer.aiAssistantHandler.registerModel(service);
|
|
28
|
-
});
|
|
29
|
-
Adminizer.log.info(`AI assistant initialized with models: ${adminizer.aiAssistantHandler.getModels().map((m) => m.id).join(', ')}`);
|
|
30
|
-
}
|