11/* eslint-disable @typescript-eslint/no-explicit-any */
22import type { ForestAdminHttpDriverServices } from './services' ;
3- import type { AgentOptions , AgentOptionsWithDefaults , HttpCallback } from './types' ;
3+ import type {
4+ AgentOptions ,
5+ AgentOptionsWithDefaults ,
6+ AiConfiguration ,
7+ HttpCallback ,
8+ } from './types' ;
49import type {
510 CollectionCustomizer ,
611 DataSourceChartDefinition ,
@@ -12,6 +17,7 @@ import type {
1217import type { DataSource , DataSourceFactory } from '@forestadmin/datasource-toolkit' ;
1318import type { ForestSchema } from '@forestadmin/forestadmin-client' ;
1419
20+ import { isModelSupportingTools } from '@forestadmin/ai-proxy' ;
1521import { DataSourceCustomizer } from '@forestadmin/datasource-customizer' ;
1622import bodyParser from '@koa/bodyparser' ;
1723import cors from '@koa/cors' ;
@@ -42,6 +48,7 @@ export default class Agent<S extends TSchema = TSchema> extends FrameworkMounter
4248 protected nocodeCustomizer : DataSourceCustomizer < S > ;
4349 protected customizationService : CustomizationService ;
4450 protected schemaGenerator : SchemaGenerator ;
51+ protected aiConfigurations : AiConfiguration [ ] = [ ] ;
4552
4653 /** Whether MCP server should be mounted */
4754 private mcpEnabled = false ;
@@ -210,8 +217,55 @@ export default class Agent<S extends TSchema = TSchema> extends FrameworkMounter
210217 return this ;
211218 }
212219
220+ /**
221+ * Enable AI features for your Forest Admin panel.
222+ *
223+ * All AI requests from Forest Admin are forwarded to your agent and processed locally.
224+ * Your data and API keys never transit through Forest Admin servers, ensuring full privacy.
225+ *
226+ * @param configuration - The AI provider configuration
227+ * @param configuration.name - A unique name to identify this AI configuration
228+ * @param configuration.provider - The AI provider to use ('openai')
229+ * @param configuration.apiKey - Your API key for the chosen provider
230+ * @param configuration.model - The model to use (e.g., 'gpt-4o')
231+ * @returns The agent instance for chaining
232+ * @throws Error if addAi is called more than once
233+ *
234+ * @example
235+ * agent.addAi({
236+ * name: 'assistant',
237+ * provider: 'openai',
238+ * apiKey: process.env.OPENAI_API_KEY,
239+ * model: 'gpt-4o',
240+ * });
241+ */
242+ addAi ( configuration : AiConfiguration ) : this {
243+ if ( this . aiConfigurations . length > 0 ) {
244+ throw new Error (
245+ 'addAi can only be called once. Multiple AI configurations are not supported yet.' ,
246+ ) ;
247+ }
248+
249+ if ( ! isModelSupportingTools ( configuration . model ) ) {
250+ throw new Error (
251+ `Model '${ configuration . model } ' does not support function calling (tools). ` +
252+ 'Please use a compatible model like gpt-4o, gpt-4o-mini, or gpt-4-turbo.' ,
253+ ) ;
254+ }
255+
256+ this . options . logger (
257+ 'Warn' ,
258+ `AI configuration added with model '${ configuration . model } '. ` +
259+ 'Make sure to test Forest Admin AI features thoroughly to ensure compatibility.' ,
260+ ) ;
261+
262+ this . aiConfigurations . push ( configuration ) ;
263+
264+ return this ;
265+ }
266+
213267 protected getRoutes ( dataSource : DataSource , services : ForestAdminHttpDriverServices ) {
214- return makeRoutes ( dataSource , this . options , services ) ;
268+ return makeRoutes ( dataSource , this . options , services , this . aiConfigurations ) ;
215269 }
216270
217271 /**
@@ -333,7 +387,11 @@ export default class Agent<S extends TSchema = TSchema> extends FrameworkMounter
333387 // Either load the schema from the file system or build it
334388 let schema : Pick < ForestSchema , 'collections' > ;
335389
336- const { meta } = SchemaGenerator . buildMetadata ( this . customizationService . buildFeatures ( ) ) ;
390+ // Get the AI configurations for schema metadata
391+ const { meta } = SchemaGenerator . buildMetadata (
392+ this . customizationService . buildFeatures ( ) ,
393+ this . aiConfigurations ,
394+ ) ;
337395
338396 // When using experimental no-code features even in production we need to build a new schema
339397 if ( ! experimental ?. webhookCustomActions && isProduction ) {
0 commit comments