# Building AI Features

## Overview

Developers can build AI features into their applications using Servoy's [**AI Plugin**](https://docs.servoy.com/reference/servoyextensions/server-plugins/ai), a developer-focused toolkit, which enables a broad range of functionality using today's latest Large Language Models (LLMs).&#x20;

### How It Works

Developers can use the plugin to programmatically interact with models of their choosing for chat, vector embedding/search and agentic features. This enables them to infuse business applications with AI capabilities for a broad range of use cases.

### Use Cases

The potential use cases are virtually limitless, but we'll break it into a few categories and patterns.

#### Natural Language Interfaces (NLI)

**Business Apps are often NOT User-Friendly**\
In the real-world, we deal with systems of record, structured data, and rigid business rules. Despite our best efforts in modern UI design, the end-user has always been forced to reckon with the inherent structure of the underlying system.

**Better UX through NLIs**\
LLMs provide the potential to break this age-old incompatibility by allowing users to interact with systems using Natural Language (text, but also images and sound), both by **understanding user intent** as input, and by generative results as output.&#x20;

For example, a user today may be forced to review a complicated BI report, when she likely needs an answer to a question or to sharpen her insight or to help with a decision.

Perhaps she could just...**ask in her own words!** and be answered in plain language or even a picture? Here are some more examples:

* Let users interact with the application using everyday language
* Turn plain-language requests into searches, filters, and reports
* Reduce the need for complex or highly customized UI
* Support flexible input instead of rigid forms
* Make advanced features easier to access without training
* Capture user intent without requiring knowledge of the data model

#### Knowledge Retrieval

Most organizations rely on an ecosystem of data and information. AI-infused business applications can provide context on-demand.

* Let users find information by meaning, not just keywords
* Surface relevant documents and records automatically
* Find similar cases, issues, or documents
* Answer questions using your own content and data
* Ground AI responses in known, trusted sources

#### Unstructured Data

Most organizations sit on large amounts of *dark data* — emails, documents, notes, attachments, images, PDFs, and free-text fields that are stored but rarely used. AI makes it possible to extract meaning from this unstructured content and turn it into data that applications can search, reason over and act on.

* Turn documents archives into structured, usable data
* Automatically classify and tag content
* Extract key information from messy or inconsistent inputs
* Group and match similar content
* Make large volumes of text searchable and actionable

#### Assistants

Assistants can be embedded directly into applications to provide contextual guidance, explanations, and support at the moment it’s needed. They help users understand data, decisions, and processes without leaving the context of their work.

Assistants commonly:

* Provide in-app guidance and explanations
* Help users understand records, screens, and decisions
* Answer questions using application context
* Support better human decision-making
* Reduce friction in complex workflows

#### Agents

Agents go a step further than assistants by acting on behalf of the user. They can plan and execute multi-step tasks, call application services or external tools, and operate with varying levels of autonomy while remaining under application control. An agent would work in collaboration with a user or, using Servoy's [Automation Tools](https://docs.servoy.com/guides/develop/programming-guide/automation-and-scheduling), could run completely autonomously to fulfill tasks and pass control to users only as needed.

In General, Agents can:

* Execute multi-step tasks toward a defined goal
* Invoke application services and external tools
* Coordinate actions across systems or workflows
* Operate with user oversight or approval
* Automate repetitive or complex processes

### Model Choice

Servoy’s AI plugin is intentionally model-agnostic. It does not lock you into a specific LLM, embedding model, or vector store. Instead, it provides a consistent integration layer that lets you choose, configure, and evolve the AI components that best fit your application, architecture, and compliance requirements.

#### What This Means

* Choose your own LLMs, embedding models, and vector stores
* Switch or combine providers without changing application logic
* Manage your own API keys and credentials
* Control usage limits, quotas, and rate limiting
* Monitor and manage token consumption and costs
* Decide where models run (cloud, private, or local)
* Apply your own security, compliance, and data-handling policies

#### Why This Matters

* Avoid vendor lock-in
* Adapt quickly as models and providers evolve
* Align AI usage with your organization’s governance and cost controls

## Get Started

### Get the Plugin

The [**AI Plugin**](https://docs.servoy.com/reference/servoyextensions/server-plugins/ai) ships with Servoy version **`2025.12`** and beyond. If for some reason you need to install it, you can download the binary from the [**releases**](https://github.com/Servoy/AIPlugin/releases) page on Github. (Be sure to stop ServoyDeveloper and extract the plugin .jar and dependencies into the `/application_server/plugins` directory.)

### Get you API keys

The plugin operates on a BYO (Bring-Your-Own) model principle. Therefore you will need to provision your own **API Key**(s) from the vendors of your choice.&#x20;

[OpenAI Platform](https://platform.openai.com/api-keys)

[Gemini Platform](https://ai.google.dev/gemini-api/docs/api-key)

### Get the Example Solution

Most of the topics in this guide are also covered in `the Example_AI_Plugin` solution, which can be downloaded via the [Servoy Package Manager](https://docs.servoy.com/reference/servoy-developer/package-manager) or from the [releases](https://github.com/Servoy/AIPlugin/releases) page on Github.

## Building Chat Flows

#### Quick Overview

All language models work in essentially the same way: they take text as input and return text as output. A "chat completion" is simply a structured way to send prompts, instructions, and conversation history to a model and receive a response.

The model itself does not understand your application, data, or workflows. It is up to the developer to provide context, design prompt templates, manage conversation state, and connect the model’s output to application logic and UI.

The examples below show how chat completions can be used as a foundational building block that you can extend with context, retrieval, and application behavior.

{% hint style="info" %}
**You don't&#x20;*****have*****&#x20;to build a chatbot**\
While chat completions can be used to build chatbot-style interfaces, they are not limited to chat-based UX. In many applications, chat completions run entirely behind the scenes — generating explanations, interpreting user intent, transforming text, or driving application logic — without the end user ever seeing a “chat” interface.
{% endhint %}

### Basic Chat Completion

This is the **Hello World** example of starting up a Chat-Cilent, sending in a prompt and receiving the response. It's just a few lines of code and every use case builds upon this.

#### **Build a Chat Client instance**

First, you will need to create an instance of a [**`ChatClient`**](https://docs.servoy.com/reference/servoyextensions/server-plugins/ai/chatclient) using a **`Builder`** object. There are different builders, depending on the for the model provider of your choice.

```js
// OpenAI Example
let client = plugins.ai.createOpenAiChatBuilder()
	  .apiKey(MY_API_KEY) // Plug-in your API key
		.modelName('gpt-3.5-turbo') // Select a model of your choice
		.addSystemMessage('You are a helpful assistant') // The guiding message for this model
		.build(); //builds the instance
```

```js
// Gemini Example
let client = plugins.ai.createGeminiChatBuilder()
	  .apiKey(MY_API_KEY) // Plug-in your API key
		.modelName('gemini-3-pro') // Select a model of your choice
		.addSystemMessage('You are a helpful assistant') // The guiding message for this model
		.build(); //builds the instance
```

Here you see a couple of example builders for different providers, both taking several parameters. Some are optional, but the following are required:&#x20;

* **API Key** - Obtain this from your model's vendor after you set up your developer account.
* **Model Name** - Obtain a list of compatible model names from your vendor of choice.
* **System Message** - This is a guiding principle for the model, including what type of responses it generates. It could be that it is a domain expert (i.e. US Copywrite Law) or it always answers in Strict JSON output, etc.

{% hint style="info" %}
**Handing API Keys**\
As a best practice, do not hardcode it, but load it from a secure location, such as your `servoy.properties` file, i.e. `application.`[`getServoyProperty`](https://docs.servoy.com/reference/servoycore/dev-api/application#getservoyproperty-name)`('openai_api_key');`
{% endhint %}

#### **Prompt the Model**

```javascript
// send the userMessage
let promise = client.chat(userMessage);
```

Once you have created a `ChatClient` instance, sending your prompt into the model is a single line of code to call the [**`chat`**](https://docs.servoy.com/reference/servoyextensions/server-plugins/ai) method, passing in the `userMessage` parameter. This method runs *asynchronously* and will return a JavaScript `Promise` object to manage the response. More on that below.

#### **Handle the Response**

```js
// send the user message and 
	client.chat(userMessage).then(
	
		/** @param {plugins.ai.ChatResponse} response */
		function(response) {
			application.output(response.getResponse());
		
		// handle errors
		}).catch(
			function(error) {
				application.output('Error: ' + error.message);
			}
		);
```

Here you can see that the `Promise` object resolves to a [**`ChatResponse`**](https://docs.servoy.com/reference/servoyextensions/server-plugins/ai/chatresponse) object, which is passed into the handling function. Then simply call the [**`getResponse`**](https://docs.servoy.com/reference/servoyextensions/server-plugins/ai/chatresponse#getresponse) method to get the `String` value of the response.

### Conversation Memory

Chat Models, contrary to many assumptions, don't actually preserve any state or memory. The illusion of a continuous conversation is created by sending the chat history with every call. Fortunately, Servoy's AI plugin handles this for you. All you have to do is enable memory.&#x20;

#### Setting the Max Memory

```javascript
let client = plugins.ai.createOpenAiChatBuilder()
	  .apiKey(MY_API_KEY) // Plug-in your API key
		.modelName('gpt-3.5-turbo') // Select a model of your choice
		.maxMemoryTokens(1000) // Enables memory
		.addSystemMessage('You are a helpful assistant') // The guiding message for this model
		.build(); //builds the instance
```

To enable chat memory, simply set the max number of tokens that is "remembered" when you provision the `ChatClient` by calling [**`maxMemoryTokens`**](https://docs.servoy.com/reference/servoyextensions/server-plugins/ai/openaichatbuilder#maxmemorytokens-tokens) on the builder. After this, you can **reuse** the client object and every time you call [`chat()`](https://docs.servoy.com/reference/servoyextensions/server-plugins/ai/chatclient#chat-usermessage), it will automatically remember the last X tokens from your session to be used for context.

{% hint style="info" %}
**Token Management**\
Memory is not enabled unless specified and it is recommended to only use memory for use cases where you need to keep a conversation thread as part of your context. Keep in mind your costs per token and your feature requirements when setting your max value.
{% endhint %}

#### Streaming Response

The response payload from a chat completion request is actually delivered in *chunks* as it is generated. However, the default approach is to call the `then()` method of the `Promise` object, which resolves to the *entire* response payload. This is really all you need if you are doing pure programmatic interactions.&#x20;

However, if you are displaying the response content to the user, you may want to display the chucks as they become available.&#x20;

```javascript
// send the user message and handle the response
	client.chat(userMessage,
		
		// on partial response (append string)
		/** @param {plugins.ai.ChatResponse} response */
		function(response){
			responseMessage += response
		},
		
		// on completion
		/** @param {plugins.ai.ChatResponse} response */
		function(response){
			responseMessage += response.getResponse();
		},
		
		// on error
		function(e){
			application.output('Error during streaming chat: ' + e.message, LOGGINGLEVEL.ERROR);
		}
	);
```

To handle a streaming response, call the `chat()` method as before, but with slightly different parameters:

1. `String` The prompt input (same as the first example)
2. `Function` This function is called on-partial-complete and a `String` is passed in. You can append this String to a local variable to show the response as it is generated.
3. `Function` This next function is called when the final chunk is generated. You can append this String one last time to complete the transaction.

Remember that all you need to do is create a [Form Variable](https://docs.servoy.com/reference/servoycore/object-model/solution/variable) and append the chunks to it in each callback. You can render the result with a data-bound component (such as a [TextArea](https://docs.servoy.com/guides/develop/application-design/ui-components/input-controls/text-area)) as it is generated.&#x20;

## Vector Embedding & Search

### Overview

Vector Embeddings are a way to represent text as numerical values that capture its meaning rather than its exact wording. By converting text into embeddings, applications can compare, search, and match content based on semantic similarity instead of keywords alone.

The Servoy AI plugin supports creating Embedding Models to generating embeddings from text, and store those embeddings in a Vector Store. Once stored, embeddings can be searched to find related or similar content, enabling features such as semantic search, similarity matching, and retrieval of relevant context for AI-driven workflows.

Developers control how embeddings are created, what content is embedded, where embeddings are stored, and how search results are used within the application. This makes vector search a flexible building block that can be applied to many use cases, including document search, case matching, recommendation, and context retrieval for language models.

### Basic Text Embedding

While there are many use cases and approaches, all of them follow a general pattern&#x20;

* Configure an **Embedding Model** to convert text into vector representations
* Generate embeddings for documents, records, or other content
* Store those embeddings in a **Vector Store**, along with relevant metadata
* Convert a **search** content into an embedding using the *same* model
* Search the vector store for the most **semantically similar** vectors
* Use the search results in application logic, UI, or prompt construction&#x20;

#### Create an Embedding Model

```javascript
var embeddingModel = plugins.ai.createOpenAiEmbeddingModelBuilder()
		.apiKey(scopes.exampleAIPlugin.getOpenAIApiKey())
		.modelName('text-embedding-3-small')
		.build();
```

Embedding Models are a type of LLM that is specifically designed to generate vector embeddings. This is your first step. To create a model instance, you'll need:

1. Your API key (i.e. for OpenAI or Gemini)
2. The name of the model that you want to use

#### Create a Vector Store

While you can directly use the model to create vector embeddings (An array of numbers), it's most common to pass those vectors into a store. You can do this in one step by creating a store from the model, then generating the embeddings and storing them in a single line of code. &#x20;

```javascript
// The text to embedd
var textToEmbed = [
    'The quick, brown fox jumped over the lazy dog',
    'The woman asked the waiter for the check',
    'Force equals mass multiplied by acceleration'
];

// create the store (use in-mem implementation)
var inMemoryVectorStore = embeddingModel.createInMemoryStore();

// embedd the text
var promise = inMemoryVectorStore.embed([myText]);

// on success/error (optional)
promise.then(
    function(){application.output('Embedding complete');}
);
```

In this example, the [`embed()`](https://docs.servoy.com/reference/servoyextensions/server-plugins/ai/embeddingstore#embed-data-metadata) method takes a single argument:

1. An Array of Strings to embed and store.

#### Vector Search

Once you have stored content as vectors in a store, you can do similarity searches.

```js
// The max number of results to retrieve
var maxResults = 3;

// The search text
var searchText = 'A physics equation';
var results = inMemoryVectorStore.search(searchText,maxResults);

// Loop over the results
results.forEach(
		/** @param {plugins.ai.SearchResult} result */
		function(result){
		  // print the result
			application.output('Result: ' + result.getText() + ' with score: ' + result.getScore());
		}
);
```

In this example, we search for similarity scores and print the resulting score

{% code title="Search Results Printed to Console" %}

```
Result: Force equals mass multiplied b..., Score: 0.7347
Result: The quick, brown fox jumped ov..., Score: 0.5615
Result: The woman asked the waiter for..., Score: 0.5313
```

{% endcode %}

You can see that each item in the Vector Store is returned with a **Similarity Score** (0-1). This is a simple example, but if you take this to scale, you can embed, classify and search documents and unstructured data.

### Document Understanding

Continuing with the vector embedding example, let's take a look at how one could digest and search an entire document.

#### Chunking and Embedding Documents

```js
// get a file reference
var file = plugins.file.convertToJSFile('product-manual.pdf');

// Create the in-memory embedding store
store = plugins.ai.createOpenAiEmbeddingModelBuilder()
	        .apiKey(scopes.exampleAIPlugin.getOpenAIApiKey())
	        .modelName('text-embedding-3-small').build()
	        .createInMemoryStore();
	
// Embed the file with a chunk size of 500 and an overlap of 50
store.embed(file,500,50);
```

This example uses a Vector Store, as before, but this time the call to [**`embed`**](https://docs.servoy.com/reference/servoyextensions/server-plugins/ai/embeddingstore#embed-pdfsource-maxsegmentsizeinchars-maxoverlapsizeinchars) is taking the following arguments:

1. **File** - a [`JSFile`](https://docs.servoy.com/reference/servoyextensions/server-plugins/file/jsfile) object. This is the file, whose contents will be embedded.
2. **Chunk Size** — `Number`\
   The text content is split into smaller chunks, and each chunk is embedded separately. This value defines the maximum size of each chunk, measured in *tokens*.
3. **Chunk Overlap** — `Number`\
   Specifies how much text (in tokens) is shared between adjacent chunks. Using an overlap helps preserve context when content is split at arbitrary boundaries, reducing the risk of losing meaning across chunks.

{% hint style="info" %}
**What is the ideal chunk strategy?**\
It depends on the document content and use case. For general documents, start with **300-500 tokens** with a **10-15% overlap**. Very long-form text may need larger chunks to capture context. Highly structured (dense) text, such as code, lists, tables, etc. may require smaller chunks. &#x20;
{% endhint %}

#### Searching Documents

Once you have chunked and embedded your document(s), you can search for matching chunks using the exact same approach from the previous example:&#x20;

```js
var results = store.search('battery type',maxResults);
```

This will return an array of results, each containing the matching chunk and its similarity score.&#x20;

#### Using Documents as a Knowledge Base

Let's build on this example to show how you can use documents as a knowledge base by leveraging vector search and chat together. Imagine that you have digested a repository of product manuals and the end-user can interact with the the knowledge base via chat.

```javascript
// question or user-input
var question = 'Are the earbuds water resistant';

// Get the top-ranking chunk
var results = store.search(question,1);

// build a chat client to ask the question with context
var chatClient = plugins.ai.createOpenAiChatBuilder()
		.apiKey(scopes.exampleAIPlugin.getOpenAIApiKey())
	    .modelName('gpt-4o')
		  .addSystemMessage('You are great at answering questions about products when given context directly from the product manual.')
		  .build();
		
// build the prompt with the context from the most relevant chunk
var prompt = 'Use the following context to answer the question.\n\n\
The question is: "' + question + '"\n\n\
The context is:\n\n"' + results[0].getText();

// ask the chat model
chatClient.chat(prompt).then(function(response){
	application.output('Answer: ' + response.getResponse());
});
```

In this example, we used the search result from the document as an input for context in a chat session. (For simplicity, we chose only the top-ranking chunk, but you can imagine a more complex scenario that fuses and ranks multiple results to provide context for the chat input.)

{% hint style="success" %}
**Combined Tooling and Approaches!**\
There is no limit to the combination of tools and approaches that you can take. This simple example shows how you can chain calls between *two* models, but the combinations are endless.
{% endhint %}

#### Using Vector Metadata

When you embed text into vectors, the embedding captures meaning — but not *where it came from*. Metadata solves that by storing structured fields alongside each vector so you can (a) trace results back to the original source and (b) filter or scope searches to the right subset of content.

Practical Uses for Metadata

* **Traceability:** show “this result came from Document X (chunk 12)”
* **Filtering:** search only within a customer, tenant, project, category, date range, etc.
* **Security scoping:** restrict retrieval to what the current user is allowed to access
* **Navigation:** open the exact record or document location directly from results

{% code title="Attach metadata at the time of embedding" %}

```javascript
// document metadata
var metadata = {fileName:'earbuds-product-manua.pdf, fileID:123}
store.embed(file,500,50,metadata);
```

{% endcode %}

{% code title="Evaluate metadata when " %}

```js
var results = store.search(question,1);
application.output('Matching file: ' + result[0].getMetadata().fileName);
```

{% endcode %}

### Embedding Relational Data

Vector embeddings are not limited to documents and files. Text derived from relational data can also be embedded to enable semantic search, similarity matching, and intent-based retrieval over application records.

For example, suppose an end-user is searching a database of products and enters a key word "beer"

In this approach, selected fields from a database record are combined into a textual representation and embedded as a vector, while metadata is used to preserve the record’s identity, type, and access scope. This allows applications to perform semantic search across structured records—such as customers, cases, tickets, or products—while still resolving results back to concrete records that users can view and act on.

#### Create Embeddings for Record Sets

{% code title="Embedding based on Servoy FoundSet" %}

```javascript
// get a foundset based on products table
var fs = datasources.db.example_data.products.getFoundSet();
fs.loadAllRecords();

// embed the product name and descritpion
store.embedAll(fs,'productname', 'product_desc')
		.then(
			function(){application.output('embedded success')},
			function(e){application.output('embedded failed: ' + e)}
		);
```

{% endcode %}

In this example, Servoy's AI plugin offers a shortcut: By calling [**`embedAll`**](https://docs.servoy.com/reference/servoyextensions/server-plugins/ai/embeddingstore#embedall-foundset-textcolumns) and passing a [`FoundSet`](https://docs.servoy.com/reference/servoycore/dev-api/database-manager/jsfoundset) object, you can embed a set of records in one simple call. You'll notice that the method is overloaded, so you can embed data from **multiple text columns** in a single call. Finally, you don't have to worry about **metadata**. The plugin will automatically store each record's primary key (PK) column(s).

#### Semantic Search of Record Embeddings

```javascript
var results = store.search(searchText,10); // get the top-ten results
	
// store ids of matching records
var ids = [];
for(var i in results){
		// include all matches with a strong similarity (above 0.7)
		if(results[i].getScore() > .7){
			ids.push(results[i].getMetadata().productid); // access the metadata to link it back
		}
}
// load the best-matching records
foundset.loadRecords(databaseManager.convertToDataSet(ids));
```

In this example, we search the vector store for products with a name similar to the input search text. Critically, we use the [**`getMetadata`**](https://docs.servoy.com/reference/servoyextensions/server-plugins/ai/searchresult#getmetadata) method of the results to link it back to the products entity. This method returns a simple JavaScript object, from which you can directly access the PK value(s) by column name.

{% hint style="success" %}
**Semantic Search**\
You've just seen how easy it is to turn your database into something which can be searched "semantically" and without SQL. (In a later example, we'll see how to combine vector *and* SQL into a powerful search!)
{% endhint %}

### Types of Vector Stores

#### In-Memory Vector Store

Until this point, all the examples have used an **In-Memory** Vector Store. This implementation is great for getting started, because it is easy to setup and has the same functionality. Depending on your use case, it may be perfectly adequate.

**When NOT to use an in-memory store:**

As the name suggests, this type of vector store caches the vectors and therefore has the following limitations:

* **Not persistent** — Vectors stored in memory will not persist beyond the current runtime in which they were embedded. Therefore it is not ideal for using across sessions.
* **Not Scalable** — If you need to embed large volumes of data, then you should consider a proper Vector Database. In-memory is ideal for quick, one-off jobs, such as a single document. However, it's not uncommon for an organization to vectorize *many* documents in a single store. In this case,  you would want to avoid the in-mem implementation.

#### Persistent Vector Store

Many relational databases include extensions to provide Vector Embedding and Search capabilities. Servoy's AI plugin gives you the option to connect to vector-enabled databases and use them as your store.

Since `v2025.12`, Servoy Developer ships **PG Vector**, a vector extension for the PostgreSQL database. You can use this out-of-the-box on your PostgreSQL servers. &#x20;

{% code title="Create a DB-backed Vector Store" %}

```javascript
var store = model.createServoyEmbeddingStoreBuilder()
		.serverName('example_data')
		.tableName('file_embeddings')
		.recreate(true)
		.addText(true)
		.metaDataColumn().name('filename').columnType(JSColumn.TEXT).add()
		.build();
```

{% endcode %}

In this example, we use the [**`createServoyEmbeddingStoreBuilder`**](https://docs.servoy.com/reference/servoyextensions/server-plugins/ai/embeddingmodel) method of the [`EmbeddingModel`](https://docs.servoy.com/reference/servoyextensions/server-plugins/ai/embeddingmodel) to create a persistent Vector Store, with the following options:

* **serverName** — The target server (must have PG Vector or vector extension)
* **tableName** — The name of the table that will be created on that server
* **recreate** — Boolean, true if you want the persistence cleared (not used across sessions)
* **addText** — Boolean, true if you want to add the plain (unembedded) text
* **metadataColumns** — sub-builder to capture metadata, for later filtering and retrieval

## Tool Calling

Tool calling allows a language model to call application functions instead of just returning text. Developers expose selected functions as tools, describing what they do and the parameters they accept. The model can then decide when to call a tool, pass the required inputs, and use the result to continue processing.

This makes it possible to build agents that can take action—such as querying data, updating records, or triggering workflows—while keeping all execution logic inside the application.

### Add a Tool to your ChatClient

Adding tools to your chat client is surprisingly easy.

{% code title="Adding Tools when Building a Chat Client" %}

```javascript
let client = plugins.ai.createOpenAiChatBuilder()
		.modelName('gpt-4.1')
		.apiKey(MY_API_KEY)
		.createTool(productLookUp,'productLookUp','Looks up a product by name and returns the product ID (Integer). If not found, returns -1.')
			.addStringParameter('productName','The name of the product',true)
			.build()
		.createTool(customerLookUp,'customerLookUp','Looks up a customer by name and returns the customer ID (String). If not found, returns an empty string.')
			.addStringParameter('customerName','The name of the customer',true)
			.build()
		.createTool(createOrder,'createOrder','Creates a new order for the given customer ID.')
			.addStringParameter('customerId','The ID of the customer',true)
			.build()
		.createTool(addItemToOrder,'addItemToOrder','Adds an item to the current order with the given product ID and quantity.')
			.addNumberParameter('productId','The ID of the product',true)
			.addNumberParameter('quantity','The quantity of the product to add',true)
			.build()
		.build();
```

{% endcode %}

Here you can see that the [`createTool`](https://docs.servoy.com/reference/servoyextensions/server-plugins/ai/openaichatbuilder#createtool-toolfunction-name-description) method of the [`ChatBuilder`](https://docs.servoy.com/reference/servoyextensions/server-plugins/ai/openaichatbuilder) was invoked. It takes three parameters:

* **Tool Function** — A reference to the callable function for the tool
* **Tool Name** — The name of the tool. (In general, this can just be the same as the function name.)
* **Tool Description** — A description of the tool. This parameter tells the LLM about the tool, so be sure to be thorough.

{% hint style="info" %}
You can chain together calls to create multiple tools. Be sure to call the `build()` method after creating each tool. &#x20;
{% endhint %}

### Prompt the Tool-Enabled Client

Now that you have added one or more tools to your client, you simply send a chat and see if the model chooses your tools. In this example, the client can use the tools to search for customer and product records and to create orders and add items.

{% code title="Reads an email and potentially creates an order from it" %}

```javascript
let emailContent = '...'; // imagine content pulled from an email server
let instruction = 'You are an automated ordering system. Your task is to process customer orders received via email.\n---\n' + emailContent;
	model.chat(instruction).then(function(response){
		application.output('Response: ' + response.getResponse());
	}).catch(function(err){
		application.output('Error: ' + err);
	});
```

{% endcode %}

{% hint style="info" %}
**Security and Governance**\
Remember that Tool-Calling gives some control of your application functionality to the LLM. Therefore, it's important to understand exactly what capabilities you have exposed. The good news is that any application security and guardrails that you have already designed into your solution will be enforced. Just be sure that you have aligned the roles/permissions for the logged-in LLM user.
{% endhint %}

## FAQ

### How am I charged for model usage?

Model API pricing is determined by the provider you choose. Most language model and embedding APIs are usage-based and typically charge based on the number of tokens processed. Tokens generally represent pieces of text and include both input (prompts, context) and output (generated responses).

Because the Servoy AI plugin is model-agnostic, Servoy does not set or manage pricing. Developers are responsible for selecting providers, managing API keys, monitoring usage, and applying any limits or cost controls offered by the chosen vendor.

### Can I use a local or open source model?

The plugin theoretically supports many vendors/models, but the current implementation provides implementations for:

* **OpenAI**
* **Gemini**

We are looking into adding support for other providers, including Llama. If you have a request for a particular model/vendor, please contact [support.servoy.com](https://support.servoy.com/). &#x20;
