BILLmanager 6
en En
es Es

Creating an adapter for the AI assistant

The following terms are used in this article:

The article describes the operating principles, step-by-step instructions, and the API of the module for creating your own AI assistant adapter. For information on how to configure working with the AI assistant in the platform interface, see the article Working with the AI assistant.

What an adapter is

An adapter is a module that connects the chat with an external LLM provider (OpenAI-compatible API, Yandex GPT, your own service, and so on). The chat server does not contact the provider directly: it sends the request to a registered adapter, and the adapter returns the response in a single internal format.

The capabilities of the adapter mechanism are:

  • connecting any LLM without changing the server code;
  • using several adapters in one chat instance. For example, you can use different adapters for different providers;
  • supporting streaming (SSE) and standard response generation;
  • model management: the adapter can optionally pass a list of supported models for selection in BILLmanager settings;
  • implementing custom LLM logic. For example, function calling, request filtering, and so on;
  • passing the API key, URL, and system prompt from the project settings.

Custom adapters are loaded automatically when the chat server starts from the /app/server/adapters/ directory inside the Docker container. Rebuilding the image is not required — just place the adapter file in the container and restart the container.

Chat architecture
Chat architecture

Built-in adapters

After installing the "AI assistant" module, the following adapters are available in the platform:

NameDescription
openaiAn adapter for communicating with OpenAI-like LLMs.
deep-seekAn adapter for communicating with DeepSeek.

Creating an adapter

Below is an instruction for creating an adapter for communicating with an LLM through the OpenAI package. The example uses the adapter name my-llm. Instead of my-llm, you can specify another name.
To create an adapter for communicating with an LLM through the OpenAI package:

  1. Create the adapter file [adapter-name].adapter.js. For example, my-llm.adapter.js.
  2. Create the adapter class and implement the required methods:
    • getName — get the adapter name;
    • getCompletion — get a response from the AI without SSE;
    • getCompletionStream — get a response from the AI with SSE.
    A detailed description of the API for input and output parameters is available below;
    Adapter creation example
  3. Copy the adapter file to the /app/server/adapters directory of the running chat container. To keep the added adapters after rebuilding the image, create your own image based on the chat Docker image.

    Example of creating an image with Dockerfile
    FROM docker-registry.ispsystem.com/ispsystem5/chat:1.0.0
    
    WORKDIR /app
    COPY ./my-llm.adapter.js ./server/adapters/my-llm.adapter.js
    Where:
    • docker-registry.ispsystem.com/ispsystem5/chat:1.0.0 — address to the public image of the chat container.
  4. Create an image from the Dockerfile:
     docker build . -t my-llm 
  5. Specify the tag of the created image in the script for managing chat images /usr/local/mgr5/etc/docker/services/aichat.sh. Replace the SERVICE_IMAGE variable with the tag of the new image SERVICE_IMAGE="my-llm"
  6. Restart the chat container:
    sh etc/docker/services/aichat.sh restart 
  7. In the BILLmanager web interface, go to AI asistant → Settings → select the AI assistant → click Edit.
  8. In the Main section, in the Adaptor field, select the new adapter.
  9. Set the required settings: API URL, API key. For more details, see the article Working with the AI assistant.
  10. Save changes.

After configuration, the chat is ready to use. Requests will be sent through the created adapter.

Writing an adapter with external dependencies

The chat service imports files ending with .adapter.js . If this file needs dependencies (for example, the node_modules directory) or packages, they must also be moved into the container into the /app/server/adapters/ directory.
When the node_modules directory is moved into the container, the dependencies become available through the require import.

Import example
// The OpenAI library is available for import by default.
const OpenAI = require('openai');
// An example of importing a dependency when moving node_modules with the necessary dependencies to the /app/server/adapters/ directory
const customPackage = require('custom-package');

class MyLLMAdapter {
  ...
}

module.exports = { MyLLMAdapter };

Adapter API

Function descriptions

getName()

Returns the adapter’s unique string name.

AcceptsNone
Returnsstring — unique adapter identifier. The chat service uses the deep-seek and openai adapters by default.
RequiredYes

getCompletion(params)

Synchronous response generation without a stream.

AcceptsAdapterCompletionParams
Returns

Promise<ModelResponse>

RequiredYes

getCompletionStream(params)

Streaming response generation for SSE mode.

AcceptsAdapterCompletionParams
Returns

AsyncIterable<ModelStreamEvent> (the function must be an asynchronous generator )

RequiredYes

getModels()

Returns a list of models available for selection in the settings.

AcceptsNone
Returns

string[]

RequiredNo

Interface descriptions

ModelStreamEvent

Stream event (for SSE) is one of the following types:

text_delta — intermediate text fragment.

FieldTypeRequiredDescription
typetext_deltaYes

Event type.

deltastringYesAdded text fragment.

completed — generation finished (required final event).

FieldTypeRequiredDescription
typecompletedYes

Event type.

responseModelResponseYesFinal answer.

tool_call — tool call notification.

FieldTypeRequiredDescription
typetool_callYes

Event type.

toolstringYesTool identifier to display in the interface.

AdapterCompletionParams

FieldTypeRequiredDescription
apiKeystringYes

Provider API key from the settings.

inputModelMessageYesMessage history for the LLM.
abortSignalAbortSignalYesSignal for canceling requests when the connection to the client is lost.
apiUrlstringNoBase API URL from the settings.
modelstringNoModel name from the chat settings.
systemPromptstringNoSystem prompt from the settings.

ModelMessage

FieldTypeRequiredDescription
roleuser | assistant | systemYes

Message author role.

contentstringYesMessage text.

ModelResponse

FieldTypeRequiredDescription
outputTextstringYesFull assistant response text.
output

ModelResponseOutputMessage[]

Yes

Structured output.

relatedContent

ModelResponseRelatedContent

No

Additional content related to the response.

ModelResponseOutputMessage
FieldTypeRequiredDescription
roleassistant | userYesMessage author role.
content

{type: 'text', text: string}[]

Yes

Text fragments.

status

string

No

Generation status (for example, completed ).

ModelResponseRelatedContent
FieldTypeRequiredDescription
links{ title: string, url: string }[]NoSources used by the LLM when generating the response. Display them as links below the message text message.

Example of an adapter with function calling


Adapter example