{"id":26534807,"url":"https://github.com/ortus-boxlang/bx-ai","last_synced_at":"2025-07-06T17:05:44.348Z","repository":{"id":280895738,"uuid":"943377122","full_name":"ortus-boxlang/bx-ai","owner":"ortus-boxlang","description":"One Fluent/Functional API to access Large Language Models in BoxLang","archived":false,"fork":false,"pushed_at":"2025-03-21T16:33:11.000Z","size":165,"stargazers_count":2,"open_issues_count":0,"forks_count":4,"subscribers_count":9,"default_branch":"development","last_synced_at":"2025-03-21T16:57:02.370Z","etag":null,"topics":["ai","boxlang","deepseek","dynamic-programming","gemini","grok","java","jvm","llm","openai"],"latest_commit_sha":null,"homepage":"https://www.boxlang.io","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ortus-boxlang.png","metadata":{"files":{"readme":"readme.md","changelog":"changelog.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.YML","license":null,"code_of_conduct":".github/CODE_OF_CONDUCT.MD","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":".github/SECURITY.md","support":".github/SUPPORT.md","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"patreon":"ortussolutions"}},"created_at":"2025-03-05T15:58:13.000Z","updated_at":"2025-03-21T16:33:14.000Z","dependencies_parsed_at":"2025-03-05T22:32:32.117Z","dependency_job_id":"016c998e-c4d9-46fe-857f-cb37eaad2402","html_url":"https://github.com/ortus-boxlang/bx-ai","commit_stats":null,"previous_names":["ortus-boxlang/bx-ai"],"tags_count":1,"template":false,"template_full_name":"ortus-boxlang/boxlang-module-template","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ortus-boxlang%2Fbx-ai","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ortus-boxlang%2Fbx-ai/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ortus-boxlang%2Fbx-ai/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ortus-boxlang%2Fbx-ai/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ortus-boxlang","download_url":"https://codeload.github.com/ortus-boxlang/bx-ai/tar.gz/refs/heads/development","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244861073,"owners_count":20522547,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","boxlang","deepseek","dynamic-programming","gemini","grok","java","jvm","llm","openai"],"created_at":"2025-03-21T20:20:19.246Z","updated_at":"2025-07-06T17:05:44.339Z","avatar_url":"https://github.com/ortus-boxlang.png","language":"Java","funding_links":["https://patreon.com/ortussolutions"],"categories":["人工智能"],"sub_categories":["LLM框架"],"readme":"# ⚡︎ BoxLang AI Module\n\n```\n|:------------------------------------------------------:|\n| ⚡︎ B o x L a n g ⚡︎\n| Dynamic : Modular : Productive\n|:------------------------------------------------------:|\n```\n\n\u003cblockquote\u003e\n\tCopyright Since 2023 by Ortus Solutions, Corp\n\t\u003cbr\u003e\n\t\u003ca href=\"https://www.boxlang.io\"\u003ewww.boxlang.io\u003c/a\u003e |\n\t\u003ca href=\"https://www.ortussolutions.com\"\u003ewww.ortussolutions.com\u003c/a\u003e\n\u003c/blockquote\u003e\n\n\u003cp\u003e\u0026nbsp;\u003c/p\u003e\n\n## Welcome\n\n![BoxLang AI Module](BoxLangAI.png)\n\nWelcome to the BoxLang AI Module. This module provides AI generation capabilities to your [BoxLang](www.boxlang.io) applications in an easy to use and abstracted API, so you can interact with ANY AI provider in a consistent manner.\n\n\u003e We also have an `bx-aiplus` module that enhances this module with more AI providers, capabilities and features.  The `bx-aiplus` module is part of our [BoxLang +/++ subscriptions](https://boxlang.io/plans).\n\n## License\n\nBoxLang is open source and licensed under the [Apache 2](https://www.apache.org/licenses/LICENSE-2.0.html) license.\n\n## Getting Started\n\nYou can easily get started with BoxLang AI by using the module installer:\n\n```bash\ninstall-bx-module bx-ai\n```\n\nIf you would like to leverage it in your CommandBox Based Web applications, make sure you add it to your `server.json` or use `box install bx-ai`.\n\nOnce installed you can leverage the global functions (BIFs) in your BoxLang code.  Here is a simple example:\n\n```java\n// chat.bxs\nanswer = aiChat( \"How amazing is BoxLang?\" )\nprintln( answer )\n```\n\n## Providers\n\nThe following are the AI providers supported by this module.  **Please note that in order to interact with these providers you will need to have an account with them and an API key.**\n\n- [Claude Anthropic](https://www.anthropic.com/claude)\n- [DeepSeek](https://www.deepseek.com/)\n- [Gemini](https://gemini.google.com/)\n- [Grok](https://grok.com/)\n- [OpenAI](https://www.openai.com/)\n- [Perplexity](https://docs.perplexity.ai/)\n\n\u003e More providers and features are available in our `bx-aiplus` module.\n\n## Features\n\nHere are some of the features of this module:\n\n- Integration with multiple AI providers\n- Compose raw chat requests\n- Build message objects\n- Create AI service objects\n- Create AI tool objects\n- Fluent API\n- Asynchronous chat requests\n- Global defaults\n- And much more\n\n## Tooling Matrix\n\nHere is a matrix of the providers and if they support real-time tools.  Please keep checking as we will be adding more providers and features to this module.\n\n| Provider   | Real-time Tools |\n|------------|-----------------|\n| Claude    \t| ✅ |\n| DeepSeek  | ✅ |\n| Gemini    \t| [Coming Soon]   |\n| Grok      \t | ✅ |\n| OpenAI       | ✅ |\n| Perplexity   | ✅ |\n\n## Settings\n\nHere are the settings you can place in your `boxlang.json` file:\n\n```json\n{\n\t\"modules\" : {\n\t\t\"bxai\" : {\n\t\t\t\"settings\": {\n\t\t\t\t// The default provider to use: openai, deepseek, gemini, grok, perplexity, etc\n\t\t\t\t\"provider\" : \"openai\",\n\t\t\t\t// The default API Key for the provider\n\t\t\t\t\"apiKey\" : \"\",\n\t\t\t\t// The default request params to use when calling a provider\n\t\t\t\t// Ex: { temperature: 0.5, max_tokens: 100, model: \"gpt-3.5-turbo\" }\n\t\t\t\t\"defaultParams\" : {\n\t\t\t\t\t// model: \"gpt-3.5-turbo\"\n\t\t\t\t},\n\t\t\t\t// The default timeout of the ai requests\n\t\t\t\t\"timeout\" : 30,\n\t\t\t\t// If true, log request to the ai.log\n\t\t\t\t\"logRequest\" : false,\n\t\t\t\t// If true, log request to the console\n\t\t\t\t\"logRequestToConsole\" : false,\n\t\t\t\t// If true, log the response to the ai.log\n\t\t\t\t\"logResponse\" : false,\n\t\t\t\t// If true, log the response to the console\n\t\t\t\t\"logResponseToConsole\" : false,\n\t\t\t\t// The default return format of the AI response: single, all, raw\n\t\t\t\t\"returnFormat\" : \"single\"\n\t\t\t}\n\t\t}\n\t}\n}\n```\n\n## Return Formats\n\nThe AI module supports different return formats for the responses. You can specify the return format in the `options` struct when calling the `aiChat()` or `aiChatAsync()` functions, globally in the settings (as we saw above), or in the `ChatRequest` object.\n\n| Format | Description |\n|--------|-------------|\n| `single` | Returns a single message as a string. This is the default format. |\n| `all` | Returns an array of messages. Each message is a struct with `role` and `content` keys, or whatever the LLM returns |\n| `raw` | Returns the raw response from the AI provider. This is useful for debugging or when you need the full response structure so you can mold it as you see fit |\n\n## Global Functions (BIFs)\n\n| Function | Purpose | Parameters | Return Type | Async Support |\n|----------|---------|------------|-------------|---------------|\n| `aiChat()` | Chat with AI provider | `messages`, `params={}`, `options={}` | String/Array/Struct | ❌ |\n| `aiChatAsync()` | Async chat with AI provider | `messages`, `params={}`, `options={}` | BoxLang Future | ✅ |\n| `aiChatRequest()` | Compose raw chat request | `messages`, `params`, `options`, `headers` | ChatRequest Object | N/A |\n| `aiMessage()` | Build message object | `message` | ChatMessage Object | N/A |\n| `aiService()` | Create AI service provider | `provider`, `apiKey` | IService Object | N/A |\n| `aiTool()` | Create tool for real-time processing | `name`, `description`, `callable` | Tool Object | N/A |\n\n### Quick Usage Examples\n\n```java\n// Simple chat\nresult = aiChat( \"Hello, world!\" )\n\n// Async chat with callback\nfuture = aiChatAsync( \"Hello!\" ).then( r -\u003e println(r) )\n\n// Build complex request\nrequest = aiChatRequest( messages, { model: \"gpt-4\" }, { provider: \"openai\" } )\n\n// Fluent message building\nmsg = aiMessage().system( \"Be helpful\" ).user( \"Hello\" )\n\n// Service with custom settings\nservice = aiService( \"openai\", \"my-key\" ).defaults( { temperature: 0.7 } )\n\n// Tool for function calling\ntool = aiTool( \"weather\", \"Get weather data\", location =\u003e getWeather(location) )\n```\n\nThis module exposes the following BoxLang global functions (BIFs) for you to interact with the AI providers:\n\n- `aiChat( messages, struct params={}, struct options={} )` : This function will allow you to chat with the AI provider and get responses back.  This is the easiest way to interact with the AI providers.\n- `aiChatAsync( messages, struct params={}, struct options={} )` : This function will allow you to chat with the AI provider and get a BoxLang future back so you can build fluent asynchronous code pipelines.\n- `aiChatRequest( messages, struct params, struct options, struct headers)` - This allows you to compose a raw chat request that you can then later send to an AI service.  The return is a `ChatRequest` object that you can then send to the AI service.\n- `aiMessage( message )` - Allows you to build a message object that you can then use to send to the `aiChat()` or `aiChatRequest()` functions.  It allows you to fluently build up messages as well.\n- `aiService( provider, apiKey )` - Creates a reference to an AI Service provider that you can then use to interact with the AI service.  This is useful if you want to create a service object and then use it multiple times.  You can pass in optional `provider` and `apiKey` to override the global settings.\n- `aiTool( name, description, callable)` - Creates a tool object that you can use to add to a chat request for real-time system processing.  This is useful if you want to create a tool that can be used in multiple chat requests against localized resources.  You can then pass in the tool to the `aiChat()` or `aiChatRequest()` functions.\n\n## aiChat()/aiChatAsync() - Chat with the AI\n\nThe `aiChat(), aiChatAsync()` functions are the easiest way to interact with the AI providers in a consistent and abstracted way.  Here are the signatures of the function:\n\n```js\naiChat( messages, struct params={}, struct options={} )\naiChatAsync( messages, struct params={}, struct options={} )\n```\n\nHere are the parameters:\n\n- `messages` : This can be any of the following\n  - A `string` : A message with a default `role` of `user` will be used\n  - A `struct` : A struct with a `role` and `content` key message\n  - An `array of structs` : An array of messages that must have a `role` and a `content` keys\n  - A `ChatMessage` object\n- `params` : This is a struct of request parameters that will be passed to the AI provider.  This can be anything the provider supports.  Usually this is the `model`, `temperature`, `max_tokens`, etc.\n- `options` : This is a struct of options that can be used to control the behavior of the AI provider.  The available options are:\n  - `provider:string` : The provider to use, if not passed it will use the global setting\n  - `apiKey:string` : The API Key to use, if not passed it will use the global setting\n  - `timeout:numeric` : The timeout in milliseconds for the request. Default is 30 seconds.\n  - `logRequest:boolean` : Log the request to the `ai.log`. Default is `false`\n  - `logRequestToConsole:boolean` : Log the request to the console for debugging. Default is `false`\n  - `logResponse:boolean` : Log the response to the `ai.log`. Default is `false`\n  - `logResponseToConsole:boolean` : Log the response to the console for debugging. Default is `false`\n  - `returnFormat:string` : The format of the response.  The default is a `single` message.  The available formats are:\n    - `single` : A single message\n    - `all` : An array of messages\n    - `raw` : The raw response from the AI provider\n\nThe `aiChat()` function will return a message according to the `options.returnFormat` type. If you use `aiChatAsync()` it will return a BoxLang future so you can build fluent asynchronous code pipelines.\n\n\u003e Don't worry that you must do a `role` and `content` in your messages if you use a struct or an array of structs.  The ai providers will understand the structure and process it accordingly.\n\n### System Messages\n\nThe `messages` argument as explained allows you to send 3 different types of messages.  Another caveat is that there can only be one `system` message per request.\n\n### Examples\n\nHere are some examples of chatting with the AI:\n\n```js\n// Simple chat\naiChat( \"Write a haiku about recursion in programming.\" );\n\n// Structural chat\naiChat( {\n\t\"role\": \"user\",\n\t\"content\": \"Write a haiku about recursion in programming.\"\n} );\n\n// Using an array of messages\naiChat( [\n\t{\n\t\t\"role\": \"system\",\n\t\t\"content\": \"You are a helpful assistant.\"\n\t},\n\t{\n\t\t\"role\": \"user\",\n\t\t\"content\": \"Write a haiku about recursion in programming.\"\n\t}\n] );\n\n// Analyze an image\naiChat( {\n\t\"role\": \"user\",\n\t\"content\": [\n\t\t{\n\t\t\t\"type\": \"text\",\n\t\t\t\"text\": \"What is in this image?\"\n\t\t},\n\t\t{\n\t\t\t\"type\": \"image_url\",\n\t\t\t\"image_url\": {\n\t\t\t\t\"url\": \"https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg\"\n\t\t\t}\n\t\t}\n\t]\n} );\n```\n\nNow let's do some async chatting.  The benefit of async chatting is that you can build fluent asynchronous code pipelines and not block the main thread.  Once you are ready for retrieval of the results, then you can use the blocking `get()` method on the future.\n\n```java\nvar future = aiChatAsync( \"Write a haiku about recursion in programming.\" )\n    .then( result -\u003e {\n        println( \"AI Response: \" + result );\n        return result;\n    } )\n    .onError( error -\u003e {\n        writeLog( text: \"AI Chat failed: \" + error.getMessage(), type: \"error\" );\n        return \"An error occurred. Please try again.\";\n    } );\n\n// Later in the code, you can retrieve the result\n\u003ch2\u003eChat Response\u003c/h2\u003e\n\u003cp\u003e#future.get()#\u003c/p\u003e\n\n// Transforming and formatting the response\nvar future = aiChatAsync( \"Write a haiku about recursion in programming.\" )\n    .then( result -\u003e \"### AI-Generated Haiku\n\t\u003cbr\u003e\n\t#result.trim()#\n\t\" )\n    .onError( error -\u003e {\n        writeLog( text: \"AI Chat failed: \" + error.getMessage(), type: \"error\" );\n        return \"### AI Error\n\t\t\u003cp\u003e#error.getMessage()#\u003c/p\u003e\n\t\t\u003cp\u003eAn unexpected error occurred\u003c/p\u003e\n\t\t\u003cp\u003ePlease try again\u003c/p\u003e\n\t\t\";\n    } );\n\n// Print or return the formatted result\nprintln( future.get() );\n```\n\n## aiChatRequest() - Compose a Chat Request\n\nThe `aiChatRequest()` function allows you to compose a raw chat request that you can then later send to an AI service.  The return is a `ChatRequest` object that you can then send to the AI service.\n\n```js\naiChatRequest( messages, struct params, struct options, struct headers )\n```\n\nHere are the parameters:\n\n- `messages` : This can be any of the following\n  - A `string` : A message with a default `role` of `user` will be used\n  - A `struct` : A struct with a `role` and `content` key message\n  - An `array of structs` : An array of messages that must have a `role` and a `content` keys\n  - A `ChatMessage` object\n- `params` : This is a struct of request parameters that will be passed to the AI provider.  This can be anything the provider supports.  Usually this is the `model`, `temperature`, `max_tokens`, etc.\n- `options` : This is a struct of options that can be used to control the behavior of the AI provider.  The available options are:\n  - `provider:string` : The provider to use, if not passed it will use the global setting\n  - `apiKey:string` : The API Key to use, if not passed it will use the global setting\n  - `timeout:numeric` : The timeout in milliseconds for the request. Default is 30 seconds.\n  - `logRequest:boolean` : Log the request to the `ai.log`. Default is `false`\n  - `logRequestToConsole:boolean` : Log the request to the console for debugging. Default is `false`\n  - `logResponse:boolean` : Log the response to the `ai.log`. Default is `false`\n  - `logResponseToConsole:boolean` : Log the response to the console for debugging. Default is `false`\n  - `returnFormat:string` : The format of the response.  The default is a `single` message.  The available formats are:\n    - `single` : A single message\n    - `all` : An array of messages\n    - `raw` : The raw response from the AI provider\n- `headers` : This is a struct of headers that can be used to send to the AI provider.\n\n### ChatRequest Properties\n\nThe `ChatRequest` object has several properties that you can use to interact with the request.  All of them have a getter and a setter.\n\n- `messages:array` : The messages to send to the AI provider\n- `chatMessage:ChatMessage` : The original `ChatMessage` object that was used to create the request\n- `params:struct` : The request parameters to send to the AI provider\n- `provider:string` : The provider to use\n- `apiKey:string` : The API Key to use\n- `logRequest:boolean` : Log the request to the `ai.log`, default is `false`\n- `logRequestToConsole:boolean` : Log the request to the console, default is `false`\n- `logResponse:boolean` : Log the response to the `ai.log` default is `false`\n- `logResponseToConsole:boolean` : Log the response to the console, default is `false`\n- `returnFormat:string` : The format of the response\n- `model:string` : The model to use for the request\n- `timeout:numeric` : The timeout in milliseconds for the request. Default is 30 seconds.\n- `sendAuthHeader:boolean` : Send the API Key as an Authorization header. Default is `true`\n- `headers:struct` : The headers to send to the AI provider\n\n### ChatRequest Methods\n\nThe `ChatRequest` object has several methods that you can use to interact with the request apart from the aforementioned properties setters and getters.\n\n- `addHeader( name, value ):ChatRequest` : Add a header to the request\n- `getTool( name ):Attempt` : Get a tool from the defined params\n- `hasMessages():boolean` : Check if the request has messages\n- `hasModel():boolean` : Check if the request has a model\n- `setModelIfEmpty( model ):ChatRequest` : Set the model if it is empty\n- `hasApiKey():boolean` : Check if the request has an API Key\n- `setApiKeyIfEmpty( apiKey ):ChatRequest` : Set the API Key if it is empty\n- `mergeServiceParams( params ):ChatRequest` : Merge service default parameters into the request (only if not already set)\n- `mergeServiceHeaders( headers ):ChatRequest` : Merge service default headers into the request (only if not already set)\n\n### Examples\n\nHere are some examples of composing a chat request:\n\n```js\n// Simple chat request\nchatRequest = aiChatRequest( \"Write a haiku about recursion in programming.\" )\nresponse = aiService().invoke( chatRequest )\n\n// Advanced request\nchatRequest = aiChatRequest( \"Write a haiku about recursion in programming.\", {\n\t\t\"model\": \"gpt-3.5-turbo\",\n\t\t\"temperature\": 0.5,\n\t\t\"max_tokens\": 100\n\t},\n\t{\n\t\t\"provider\": \"grok\",\n\t\t\"timeout\": 10,\n\t\t\"logRequest\": true,\n\t\t\"logResponse\": true,\n\t\t\"returnFormat\": \"raw\"\n\t} );\nresponse = aiService().invoke( chatRequest )\n```\n\n## aiMessage() - Build a Message Object\n\nThis function allows you to build up messages that you can then use to send to the `aiChat()` or `aiChatRequest()` functions.  It allows you to fluently build up messages as well as it implements `onMissingMethod()`. Meaning that any method call that is not found in the `ChatMessage` object will be treated as `roled` message: `system( \"message\" ), user( \"message\" ), assistant( \"message\" )`.  This method returns a `ChatMessage` object.\n\nThis is also useful so you can keep track of your messages.\n\n\u003e Please note that the ai-plus module supports chat memory and more.\n\nThe `aiMessage()` function has the following signature:\n\n```js\naiMessage( message )\n```\n\nHere are the parameters:\n\n- `message` : This can be any of the following\n  - A `string` : A message with a default `role` of `user` will be used\n  - A `struct` : A struct with a `role` and `content` key message\n  - An `array of structs` : An array of messages that must have a `role` and a `content` keys\n  - A `ChatMessage` object itself.\n\n### ChatMessage Methods\n\nThe `ChatMessage` object has several methods that you can use to interact with the message.\n\n- `add( content ):ChatMessage` : Add a message to the messages array\n- `count():numeric` : Get the count of messages\n- `clear():ChatMessage` : Clear the messages\n- `getMessages():array` : Get the messages\n- `getNonSystemMessages():array` : Get all messages except the system message\n- `getSystemMessage():string` : Get the system message, if any.\n- `hasSystemMessage():boolean` : Check if the message has a system message\n- `replaceSystemMessage( content )` : Replace the system message with a new one\n- `setMessages( messagaes ):ChatMessage` : Set the messages\n\n### ChatMessage Dynamic Methods\n\nThe `ChatMessage` object is dynamic and will treat any method call that is not found as a **roled** message according to the name of the method you call.  This allows you to build up messages fluently.\n\n```java\naiMessage()\n\t.system( \"You are a helpful assistant.\" )\n\t.user( \"Write a haiku about recursion in programming.\" )\n\t.user( \"What is the capital of France?\" )\n```\n\n### Examples\n\nHere are a few examples of building up messages and sending them to the `aiChat()` or `aiChatRequest()` functions:\n\n```js\naiChat(\n\taiMessage()\n\t\t.system( \"You are a helpful assistant.\" )\n\t\t.user( \"Write a haiku about recursion in programming.\" )\n\t\t.user( \"What is the capital of France?\" )\n)\n```\n\n## aiService() - Create an AI Service Object\n\nThis function allows you to create a reference to an AI Service provider that you can then use to interact with an AI service.  This is useful when you need to interact with a specific implementation of our `IAService` interface.\n\nThe `aiService()` function has the following signature:\n\n```js\naiService( provider, apiKey )\n```\n\nHere are the parameters:\n\n- `provider` : The provider to use, if not passed it will use the global setting\n- `apiKey` : The API Key to use, if not passed it will use the global setting\n\n\n### Service Methods\n\nHere are some useful methods each provider implements and gets via the `BaseService` abstract class.\n\n- `getName():string` : Get the name of the AI Service\n- `configure( apiKey ):IService` : Configure the service with an override API key\n- `invoke( chatRequest ):any` : Invoke the provider service with a ChatRequest object\n- `getChatURL():string` : Get the chat URL of the provider\n- `setChatURL( url ):IService` : Set the chat URL of the provider\n- `defaults( struct params ):IService` : Set the default parameters for the provider\n\n### IAiService Interface\n\nHere is the interface that all AI Service providers must implement:\n\n```java\n/**\n * Interface for all AI Service classes\n */\ninterface{\n\n\t/**\n\t * Get the name of the LLM\n\t */\n\tfunction getName();\n\n\t/**\n\t * Configure the service with an override API key\n\t *\n\t * @apiKey - The API key to use with the provider\n\t *\n\t * @return The service instance\n\t */\n\tIService function configure( required any apiKey );\n\n\t/**\n\t * Invoke the provider service with a ChatRequest object\n\t *\n\t * @chatRequest The ChatRequest object to send to the provider\n\t *\n\t * @return The response from the service, which can be anything according to their specs: string, or struct, or whatever\n\t */\n\tfunction invoke( required ChatRequest chatRequest );\n\n}\n```\n\n### BaseService\n\nWe have also provided a `BaseService` that implements the interface using the `OpenAI` standard.  This is a great starting point for you to create your own AI Service provider if needed.\n\n### Examples\n\nHere are a few examples of creating an AI Service object and interacting with it:\n\n```js\n// Create a service object\nservice = aiProvider( \"grok\" )\n\t.configure( \"myApiKey\" )\n\t.defaults( { model: \"gpt-3.5-turbo\", temperature: 0.5, max_tokens: 100 } )\n\n// Invoke the service\nresponse = service.invoke( aiChatRequest( \"Write a haiku about recursion in programming.\" ) )\n// Or\nresponse = service.invoke(\n\taiChatRequest( \"Write a haiku about recursion in programming.\", { model: \"gpt-3.5-turbo\", temperature: 0.5, max_tokens: 100 } )\n)\n```\n\n## aiTool() - Create a Tool Object\n\nThis function allows you to create a tool object that you can use to add to a chat request for real-time system processing.  This is useful if you want to create a tool that can be used in multiple chat requests against localized resources.  You can then pass in the tool to the `aiChat()` or `aiChatRequest()` functions.\n\nThe `aiTool()` function has the following signature:\n\n```js\naiTool( name, description, callable )\n```\n\nHere are the parameters:\n\n- `name` : The name of the tool sent to the AI provider\n- `description` : Describe the function. This is used by the AI to communicate the purpose of the function.\n- `callable` : A closure/lambda to call when the tool is invoked.\n\nOnce a tool object is made, you can pass them into a chat's or chat request's `params` via the `tools` array.\n\n```java\nresult = aiChat( messages = \"How hot is it in Kansas City? What about San Salvador? Answer with only the name of the warmer city, nothing else.\", params = {\n\ttools: [ tool1, tool2, tool3 ],\n\tseed: 27\n} )\n```\n\n### Tool Properties\n\nThe `Tool` object has several properties that you can use to interact with the tool.\n\n- `name:string` : The name of the tool\n- `description:string` : The description of the tool\n- `callable:function` : The closure/lambda to call when the tool is invoked\n- `schema:struct` : The schema of the tool\n- `argDescriptions:struc` : The argument descriptions of the tool\n\nEach of them have a getter and a setter.\n\n### Tool Methods\n\nThe `Tool` object has several methods that you can use to interact with the tool.\n\n- `describeFunction( description ):Tool` : Describe the function of the tool\n- `describeArg( name, description ):Tool` : Describe an argument of the tool\n- `call( callable ):Tool` : Set the callable closure/lambda of the tool\n- `getArgumentsSchema():struct` : Get the arguments schema of the tool. This is useful for providers that require a schema for the tool arguments.\n- `setSchema( schema ):Tool` : Set a custom schema for the tool (OpenAI function schema format)\n- `getSchema():struct` : Get the tool's schema (auto-generated if not manually set)\n- `invoke( args ):string` : Invoke the tool with the provided arguments\n\n### Dynamic Tool Methods\n\nThe `Tool` object also listens to dynamic methods so you can build fluent descriptions of the function or arguments using the `describe{argument}()` methods.\n\n```java\naiTool(\n\t\"myTool\",\n\t( args ) -\u003e {\n\t\treturn \"Hello World\";\n\t} )\n\t.describe( \"My Tool Function\" )\n\t.describeName( \"The name of the person\" )\n\t.describeAge( \"The age of the person\" )\n```\n\n### Examples\n\nLet's build a sample AI tool that can be used in a chat request and talk to our local runtime to get realtime weather information.\n\n```java\nweatherTool = aiTool(\n\t\"get_weather\",\n\t\"Get current temperature for a given location.\",\n\tlocation =\u003e {\n\t\tif( location contains \"Kansas City\" ) {\n\t\t\treturn \"85\"\n\t\t}\n\n\t\tif( location contains \"San Salvador\" ){\n\t\t\treturn \"90\"\n\t\t}\n\n\t\treturn \"unknown\";\n\t})\n\t.describeLocation( \"City and country e.g. Bogotá, Colombia\" )\n\nresult = aiChat( \"How hot is it in Kansas City? What about San Salvador? Answer with only the name of the warmer city, nothing else.\", {\n\ttools: [ weatherTool ],\n\tseed: 27\n} )\n\nprintln( result )\n```\n\n## Events\n\nThe BoxLang AI module emits several events throughout the AI processing lifecycle that allow you to intercept, modify, or extend functionality. These events are useful for logging, debugging, custom providers, and response processing.\n\n### Event Reference Table\n\n| Event | When Fired | Data Emitted | Use Cases |\n|-------|------------|--------------|-----------|\n| `onAIRequest` | Before sending request to AI provider | `dataPacket`, `chatRequest`, `provider` | Request logging, modification, authentication |\n| `onAIResponse` | After receiving response from AI provider | `chatRequest`, `response`, `rawResponse`, `provider` | Response processing, logging, caching |\n| `onAIProviderRequest` | When unsupported provider is requested | `provider`, `apiKey`, `service` | Custom provider registration |\n| `onAIProviderCreate` | After AI service provider is created | `provider` | Provider initialization, configuration |\n| `onAIChatRequestCreate` | After ChatRequest object is created | `chatRequest` | Request validation, modification |\n| `onAIChatMessageCreate` | After ChatMessage object is created | `chatMessage` | Message validation, formatting |\n\n### Event Details\n\n#### onAIRequest\n\nFired before sending a request to the AI provider. This allows you to inspect or modify the request before it's sent.\n\n**Data Structure:**\n\n```java\n{\n    \"dataPacket\": {}, // The request payload being sent to the provider\n    \"chatRequest\": ChatRequest, // The ChatRequest object\n    \"provider\": BaseService // The provider service instance\n}\n```\n\n**Example Usage:**\n\n```java\n// In your module/app event handler\nfunction onAIRequest( required struct data ) {\n    // Log all requests\n    writeLog( text: \"AI Request: #serializeJSON(data.dataPacket)#\", type: \"information\" );\n\n    // Add custom headers or modify request\n    data.dataPacket.custom_header = \"my-value\";\n}\n```\n\n#### onAIResponse\n\nFired after receiving a response from the AI provider. This allows you to process, modify, or cache responses.\n\n**Data Structure:**\n```java\n{\n    \"chatRequest\": ChatRequest, // The original ChatRequest object\n    \"response\": {}, // The deserialized JSON response from the provider\n    \"rawResponse\": {}, // The raw HTTP response object\n    \"provider\": BaseService // The provider service instance\n}\n```\n\n**Example Usage:**\n\n```java\nfunction onAIResponse( required struct data ) {\n    // Cache responses\n    cacheService.put( \"ai_response_#hash(data.chatRequest.toString())#\", data.response );\n\n    // Modify response before returning\n    if( structKeyExists(data.response, \"choices\") ) {\n        data.response.custom_processed = true;\n    }\n}\n```\n\n#### onAIProviderRequest\n\nFired when an unsupported provider is requested, allowing custom provider registration.\n\n**Data Structure:**\n\n```java\n{\n    \"provider\": \"string\", // The provider name requested\n    \"apiKey\": \"string\", // The API key provided\n    \"service\": null // Initially null, set this to your custom service\n}\n```\n\n**Example Usage:**\n\n```java\nfunction onAIProviderRequest( required struct data ) {\n    if( data.provider == \"mycustomprovider\" ) {\n        data.service = new MyCustomAIService().configure( data.apiKey );\n    }\n}\n```\n\n#### onAIProviderCreate\n\nFired after an AI service provider is successfully created.\n\n**Data Structure:**\n\n```java\n{\n    \"provider\": BaseService // The created provider service instance\n}\n```\n\n**Example Usage:**\n\n```java\nfunction onAIProviderCreate( required struct data ) {\n    // Log provider creation\n    writeLog( text: \"AI Provider Created: #data.provider.getName()#\", type: \"information\" );\n\n    // Apply global configuration\n    data.provider.defaults({ timeout: 60 });\n}\n```\n\n#### onAIChatRequestCreate\n\nFired after a ChatRequest object is created.\n\n**Data Structure:**\n\n```java\n{\n    \"chatRequest\": ChatRequest // The created ChatRequest object\n}\n```\n\n**Example Usage:**\n\n```java\nfunction onAIChatRequestCreate( required struct data ) {\n    // Add default model if not specified\n    if( !data.chatRequest.hasModel() ) {\n        data.chatRequest.setModelIfEmpty( \"gpt-4\" );\n    }\n\n    // Add audit trail\n    data.chatRequest.addHeader( \"X-Request-ID\", createUUID() );\n}\n```\n\n#### onAIChatMessageCreate\n\nFired after a ChatMessage object is created.\n\n**Data Structure:**\n\n```java\n{\n    \"chatMessage\": ChatMessage // The created ChatMessage object\n}\n```\n\n**Example Usage:**\n\n```java\nfunction onAIChatMessageCreate( required struct data ) {\n    // Validate messages\n    if( data.chatMessage.count() == 0 ) {\n        throw( message: \"Empty chat message not allowed\" );\n    }\n\n    // Add timestamp to system messages\n    if( data.chatMessage.hasSystemMessage() ) {\n        var systemMsg = data.chatMessage.getSystemMessage();\n        data.chatMessage.replaceSystemMessage( systemMsg \u0026 \" [Generated at #now()#]\" );\n    }\n}\n```\n\n### Event Registration\n\nLeverage the `BoxRegisterListener()` BIF, or if you are developing a module, you can use the `interceptors` structure.\n\n```java\nboxRegisterInterceptor( \"onAIRequest\", myRequestHandler );\nboxRegisterInterceptor( \"onAIResponse\", myResponseHandler );\n```\n\n## GitHub Repository and Reporting Issues\n\nVisit the [GitHub repository](https://github.com/ortus-boxlang/bx-ai) for release notes. You can also file a bug report or improvement suggestion  via [Jira](https://ortussolutions.atlassian.net/secure/CreateIssueDetails!init.jspa?pid=13359\u0026components=27149\u0026issuetype=1).\n\n----\n\n## Ortus Sponsors\n\nBoxLang is a professional open-source project and it is completely funded by the [community](https://patreon.com/ortussolutions) and [Ortus Solutions, Corp](https://www.ortussolutions.com). Ortus Patreons get many benefits like a cfcasts account, a FORGEBOX Pro account and so much more. If you are interested in becoming a sponsor, please visit our patronage page: [https://patreon.com/ortussolutions](https://patreon.com/ortussolutions)\n\n### THE DAILY BREAD\n\n\u003e \"I am the way, and the truth, and the life; no one comes to the Father, but by me (JESUS)\" Jn 14:1-12\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fortus-boxlang%2Fbx-ai","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fortus-boxlang%2Fbx-ai","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fortus-boxlang%2Fbx-ai/lists"}