# Integrating AI Assistant with your application

To run AI Assistant in your editor, add the plugin and configure your AI service provider. This guide covers the installation, configuration, and customization options.

> **Unlock this feature with selected CKEditor Plans**
>
> Try all premium features – no credit card needed.
>
> [Sign up for a free trial ](https://portal.ckeditor.com/checkout?plan=free)[Select a Plan](https://ckeditor.com/pricing/)

<a id="supported-ai-services">

## Supported AI services

CKEditor does not provide the AI model itself. Instead, the feature relies on an external service to provide AI-generated responses, so you first need to decide which provider to integrate with.

AI Assistant supports two leading AI service providers: **OpenAI and Azure OpenAI.**

Since the feature relies on an external provider, the quality of the responses depends on that provider and their model.

If you have no constraints regarding the platform that you can use, **we recommend integrating with the OpenAI API**. It provides better quality and is the simplest to set up.

This guide includes tips on how to set up the supported AI platforms. We expect that the integrator knows how their chosen platform works and how to configure it to best fit their use case.

<a id="using-proxy-endpoint">

## Using proxy endpoint

Before moving to the integration, there is one more subject to cover.

There are two general approaches to how the feature can communicate with the AI service provider: directly, or using an endpoint in your application.

Direct connection is simpler to set up and should not involve changes in your application’s backend. It is recommended for development purposes. AI Assistant supports this, as it makes it easier for you to test the feature without committing time to set up the backend part of the integration.

**However, this method exposes your private authorization data which is a serious security issue. You should never use it in the production environment.**

In the final solution, your application should provide an endpoint that the AI Assistant will call instead of calling the AI service directly. The main goal of this endpoint is to hide the authorization data from the editor users. The request to the AI service should happen from your backend, without exposing authorization credentials.

The application endpoint is also a good place to implement additional functionalities, like request customization, user billing, or logging statistics.

<a id="installation">

## Installation

After [installing the editor](../../../getting-started/installation/cloud/quick-start.md), add the feature to your plugin list and toolbar configuration:

**NPM**

```js
import { ClassicEditor } from 'ckeditor5';
import { AIAssistant } from 'ckeditor5-premium-features';

ClassicEditor
	.create( {
		licenseKey: '<YOUR_LICENSE_KEY>',
		plugins: [ AIAssistant, /* ... */ ],
		toolbar: [ 'aiCommands', 'aiAssistant', /* ... */ ],
		ai: {
			// Configuration.
		}
	} )
	.then( /* ... */ )
	.catch( /* ... */ );
```

**CDN**

```js
const { ClassicEditor } = CKEDITOR;
const { AIAssistant } = CKEDITOR_PREMIUM_FEATURES;

ClassicEditor
	.create( {
		licenseKey: '<YOUR_LICENSE_KEY>',
		plugins: [ AIAssistant, /* ... */ ],
		toolbar: [ 'aiCommands', 'aiAssistant', /* ... */ ],
		ai: {
			// Configuration.
		}
	} )
	.then( /* ... */ )
	.catch( /* ... */ );
```

> **Note**
>
> Read more about [installing plugins](../../../getting-started/setup/configuration.md) and [toolbar configuration](../../../getting-started/setup/toolbar.md).

<a id="integration">

## Integration

In the next step, you will need to set up the AI service of your choice and integrate the editor to use it:

* [OpenAI integration](#openai-integration)
* [Azure OpenAI integration](#azure-openai-integration)

<a id="openai-integration">

### OpenAI integration

This section describes how to integrate the AI Assistant with the [OpenAI platform](https://openai.com/).

<a id="set-up-the-account">

#### Set up the account

[Create](https://platform.openai.com/login?launch) an OpenAI account and get your [OpenAI API](https://help.openai.com/en/articles/4936850-where-do-i-find-my-secret-api-key) key.

<a id="making-connection">

#### Making connection

To connect to the OpenAI service, you will need to add a connection adapter plugin to the editor. The adapter is responsible for making requests in the correct format and handling the responses.

Import the [`OpenAITextAdapter`](../../../api/module_ai_aiassistant_adapters_openaitextadapter-OpenAITextAdapter.md) plugin from the `ckeditor5-ai` package and add it to the list of plugins.

Then, add the OpenAI key to the editor configuration.

You should send the key in the request “Authorization” header. You can set the request headers using the [`config.ai.assistant.adapter.openAI.requestHeaders`](../../../api/module_ai_aiassistant_adapters_openaitextadapter-OpenAITextAdapterConfig.md#member-requestHeaders) configuration property.

The snippet below presents the described changes:

```js
ClassicEditor
	.create( {
		// ... Other configuration options ...
		ai: {
			assistant: {
				adapter: {
					openAI: {
						requestHeaders: {
							// Paste your OpenAI API key in place of YOUR_OPENAI_API_KEY:
							Authorization: 'Bearer YOUR_OPENAI_API_KEY'
						}
					}
				}
				// ...
			}
		}
	} )
	.then( /* ... */ )
	.catch( /* ... */ );
```

This is the minimal setup required to launch AI Assistant. **You can test it now.**

<a id="request-parameters">

#### Request parameters

You can further configure how the OpenAI adapter works using the [`config.ai.assistant.adapter.openAI.requestParameters`](../../../api/module_ai_aiassistant_adapters_openaitextadapter-OpenAITextAdapterConfig.md#member-requestParameters) option:

* Choose the exact OpenAI model to use.
* Set whether the response should be streamed (simulating the “writing” experience) or returned all at once.
* Fine-tune the model behavior.

See the [OpenAI reference](https://platform.openai.com/docs/api-reference/chat/create) to learn what parameters you can use and how they affect the responses.

<a id="supported-models">

#### Supported models

By default, the OpenAI adapter will use the GPT-4o model.

CKEditor 5 supports all recent GPT-3.5 and GPT-4 models as well as legacy models (version `0613`).

You can find more information about offered models in the [OpenAI documentation](https://platform.openai.com/docs/models/).

<a id="integrating-with-the-proxy-endpoint">

#### Integrating with the proxy endpoint

As [described earlier](#using-proxy-endpoint), before moving to production, you should create an endpoint that will communicate with the OpenAI service, instead of connecting directly and exposing your OpenAI API key.

For the OpenAI integration, you can implement the endpoint, in its simplest form, as a transparent proxy service. The service will get the requests from the editor, add authorization headers to them, and pass them to the AI service. Then, you should pass all responses back to the editor.

After you implemented the endpoint, set the URL to your endpoint using the [`config.ai.assistant.adapter.openAI.apiUrl`](../../../api/module_ai_aiassistant_adapters_openaitextadapter-OpenAITextAdapterConfig.md#member-apiUrl) option. Also, remember to remove the OpenAI key from the configuration:

```js
ClassicEditor
	.create( {
		// ... Other configuration options ...
		ai: {
			assistant: {
				adapter: {
					openAI: {
						apiUrl: 'https://url.to.your.application/ai'
					}
				}
				// ...
			}
		}
	} )
	.then( /* ... */ )
	.catch( /* ... */ );
```

Now, all requests are redirected to `'https://url.to.your.application/ai'`.

<a id="additional-authorization-and-custom-headers">

#### Additional authorization and custom headers

Depending on your application, it might be necessary to pre-authorize the request before sending it to your application endpoint. One of the common patterns is to use JSON Web Token (JWT) authorization.

This, and similar cases, are supported through the [`config.ai.assistant.adapter.openAI.requestHeaders`](../../../api/module_ai_aiassistant_adapters_openaitextadapter-OpenAITextAdapterConfig.md#member-requestHeaders) option. You can set it to an object or an asynchronous function that resolves with an object. The object is then set as the request headers.

You can set `config.ai.assistant.adapter.openAI.requestHeaders` to a function that queries the authorization API and sets the returned JWT in an authorization header:

```js
ClassicEditor
	.create( {
		// ... Other configuration options ...
		ai: {
			assistant: {
				adapter: {
					openAI: {
						apiUrl: 'https://url.to.your.application/ai',
						requestHeaders: async () => {
							const jwt = await fetch( 'https://url.to.your.auth.endpoint/' );

							return {
								Authorization: 'Bearer ' + jwt
							};
						}
					}
				}
				// ...
			}
		}
	} )
	.then( /* ... */ )
	.catch( /* ... */ );
```

You can pass the [`actionId`](../../../api/module_ai_aiassistant_adapters_aitextadapter-AITextAdapterRequestData.md#member-actionId) parameter to the `requestHeaders` function. It identifies the action that the user performed. This allows for further customization on your end.

```js
{
	requestHeaders: async ( actionId ) => {
		const jwt = await fetch( 'https://url.to.your.auth.endpoint/?actionId=' + actionId );

		return {
			Authorization: 'Bearer ' + jwt
		};
	}
}
```

<a id="advanced-customization">

#### Advanced customization

The most flexible place to apply request processing customization is your application endpoint. However, if for any reason you cannot customize the request on your application’s backend, you can consider the following extension points on the editor side.

**Dynamic request headers.**

As mentioned earlier, you can provide `config.ai.assistant.adapter.openAI.requestHeaders` as an asynchronous function that can make a call to your application. You can use the [`actionId`](../../../api/module_ai_aiassistant_adapters_aitextadapter-AITextAdapterRequestData.md#member-actionId) parameter for further customization.

**Dynamic request parameters.**

Similarly to request headers, you can provide `config.ai.assistant.adapter.openAI.requestParameters` as an asynchronous function. The function is also passed the [`actionId`](../../../api/module_ai_aiassistant_adapters_aitextadapter-AITextAdapterRequestData.md#member-actionId). You can return different parameters based on it. For example, you can use different models for different actions.

**Customizing [request messages](https://platform.openai.com/docs/api-reference/chat/create#chat-create-messages).**

The request messages passed to the OpenAI service are built based on how the user used the feature: the selected content and the provided query.

You can overload the [`OpenAITextAdapter#prepareMessages()`](../../../api/module_ai_aiassistant_adapters_openaitextadapter-OpenAITextAdapter.md#function-prepareMessages) method to customize the request messages or provide custom logic that will create the request messages.

For example:

* You can fine-tune the system message for specific (or your custom) predefined commands.
* You can pre-query your application to get extra context information and add it as an additional message.
* You can get additional context or data from the editor, document data, or your custom features.
* You can alter or redact parts of the `context` before sending it to the service.

**NPM**

```js
import { OpenAITextAdapter } from 'ckeditor5-premium-features';

class CustomOpenAITextAdapter extends OpenAITextAdapter {
	public async prepareMessages( query, context, actionId ) {
		const messages = super.prepareMessages( query, context, actionId );

		// Customize `messages` based on your requirements.
		// You can use `actionId` to target only specific actions.
		// You can make a call to your backend since `prepareMessages` is an asynchronous function.
		// You can use `this.editor` to get access to the editor API.

		return messages;
	}
}
```

**CDN**

```js
const { OpenAITextAdapter } = CKEDITOR_PREMIUM_FEATURES;

class CustomOpenAITextAdapter extends OpenAITextAdapter {
	public async prepareMessages( query, context, actionId ) {
		const messages = super.prepareMessages( query, context, actionId );

		// Customize `messages` based on your requirements.
		// You can use `actionId` to target only specific actions.
		// You can make a call to your backend since `prepareMessages` is an asynchronous function.
		// You can use `this.editor` to get access to the editor API.

		return messages;
	}
}
```

Remember to add `CustomOpenAITextAdapter` to the plugin list instead of `OpenAITextAdapter`.

**Altering AI service responses**

Each feature that sends a request provides the [`onData()`](../../../api/module_ai_aiassistant_adapters_aitextadapter-AITextAdapterRequestData.md#member-onData) callback. The callback is executed each time the adapter receives the data from the AI service. You can decorate this callback to customize the response.

This will require overloading the [`OpenAITextAdapter#sendRequest()`](../../../api/module_ai_aiassistant_adapters_openaitextadapter-OpenAITextAdapter.md#function-sendRequest) method and changing the `requestData.onData` parameter:

**NPM**

```js
import { OpenAITextAdapter } from 'ckeditor5-premium-features';

class CustomOpenAITextAdapter extends OpenAITextAdapter {
	public async sendRequest( requestData ) {
		const originalOnData = requestData.onData;

		requestData.onData = ( content ) => {
			// Customize `content` based on your requirements.
			// You can use `requestData.actionId` to target only specific actions.
			// ...
			// Then call the original callback with the modified `content`.
			originalOnData( content );
		};

		return super.sendRequest( requestData );
	}
}
```

**CDN**

```js
const { OpenAITextAdapter } = CKEDITOR_PREMIUM_FEATURES;

class CustomOpenAITextAdapter extends OpenAITextAdapter {
	public async sendRequest( requestData ) {
		const originalOnData = requestData.onData;

		requestData.onData = ( content ) => {
			// Customize `content` based on your requirements.
			// You can use `requestData.actionId` to target only specific actions.
			// ...
			// Then call the original callback with the modified `content`.
			originalOnData( content );
		};

		return super.sendRequest( requestData );
	}
}
```

If the adapter works in the streaming mode, the `content` will include a partial, accumulating response. This may bring some extra complexity to your custom handling.

Remember to add `CustomOpenAITextAdapter` to the plugin list instead of `OpenAITextAdapter`.

**Overloading the [`sendRequest()`](../../../api/module_ai_aiassistant_adapters_openaitextadapter-OpenAITextAdapter.md#function-sendRequest) method.**

You can overload the `sendRequest()` method to add some processing before or after making the call.

**NPM**

```js
import { OpenAITextAdapter } from 'ckeditor5-premium-features';

class CustomOpenAITextAdapter extends OpenAITextAdapter {
	public async sendRequest( requestData ) {
		// Do something before making the actual request.

		return super.sendRequest( requestData ).then( () => {
			// Do something after the request has finished.
		} );
	}
}
```

**CDN**

```js
const { OpenAITextAdapter } = CKEDITOR_PREMIUM_FEATURES;

class CustomOpenAITextAdapter extends OpenAITextAdapter {
	public async sendRequest( requestData ) {
		// Do something before making the actual request.

		return super.sendRequest( requestData ).then( () => {
			// Do something after the request has finished.
		} );
	}
}
```

Remember to add `CustomOpenAITextAdapter` to the plugin list instead of `OpenAITextAdapter`.

<a id="azure-openai-integration">

### Azure OpenAI integration

This section describes how to integrate the AI Assistant with the [Azure OpenAI Service](https://azure.microsoft.com/en-us/products/ai-services/openai-service).

Microsoft’s Azure platform provides many AI-related services. AI Assistant supports only the OpenAI models.

<a id="set-up-the-service">

#### Set up the service

First, you will need to create an [Azure](https://azure.microsoft.com/) account if you do not already own one.

You need to follow these steps to set up the AI Assistant:

* Log in to your Azure account.
* Create an “Azure OpenAI” resource.
* Go to the “Azure OpenAI” resource and open “Keys and Endpoint” to find your API key(s).
* Go to “Model deployments” and then create a deployment. Select the model and the name you want to use for that deployment.
* You will need the resource name, API key, and deployment name to configure the AI Assistant.

<a id="making-connection-2">

#### Making connection

To connect to the Azure OpenAI service, you will need to add a connection adapter plugin to the editor. The adapter is responsible for making requests in the correct format and handling the responses.

Import the [`OpenAITextAdapter`](../../../api/module_ai_aiassistant_adapters_openaitextadapter-OpenAITextAdapter.md) plugin from the `ckeditor5-ai` package and add it to the list of plugins.

Then, you will need to configure the AI Assistant, so it connects to the Azure OpenAI service using your data:

* The request URL as specified in the [Azure OpenAI reference](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#chat-completions) (it will include your deployment name and the API version).
  * We tested AI Assistant with the `2023-12-01-preview` API version.
* You need to pass the API key in the request `api-key` header.

The snippet below presents the described changes:

```js
ClassicEditor
	.create( {
		// ... Other configuration options ...
		ai: {
			assistant: {
				adapter: {
					openAI: {
						// Paste your resource name, deployment name, and API version
						// in place of YOUR_RESORCE_NAME, YOUR_DEPLOYMENT_NAME, and YOUR_API_VERSION:
						apiUrl: 'https://YOUR_RESOURCE_NAME.openai.azure.com/openai/deployments/YOUR_DEPLOYMENT_NAME/chat/completions?api-version=YOUR_API_VERSION',
						requestHeaders: {
							'api-key': 'YOUR_AZURE_OPEN_AI_API_KEY'
						}
					}
				}
			}
		}
	} )
	.then( /* ... */ )
	.catch( /* ... */ );
```

This is the minimal setup required to launch AI Assistant. **You can test it now.**

<a id="request-parameters-2">

#### Request parameters

You can further configure how the OpenAI adapter works using the [`config.ai.assistant.adapter.openAI.requestParameters`](../../../api/module_ai_aiassistant_adapters_openaitextadapter-OpenAITextAdapterConfig.md#member-requestParameters) option:

* Set whether the response should be streamed (simulating the “writing” experience) or returned all at once.
* Fine-tune the model behavior.

See the [Azure OpenAI reference](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#chat-completions) to learn what parameters you can use and how they affect the responses.

You may also set `requestParameters` to an asynchronous function. In this case, it should resolve with an object that contains the parameters. The function receives [`actionId`](../../../api/module_ai_aiassistant_adapters_aitextadapter-AITextAdapterRequestData.md#member-actionId) as a parameter, which identifies the action that the user performed. This allows for further customization on your end.

<a id="supported-models-and-api-versions">

#### Supported models and API versions

CKEditor 5 supports all recent GPT-3.5 and GPT-4 models as well as legacy models (version `0613`).

You can find more information about offered models in the [Azure OpenAI documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models).

The most recent tested API version is `2023-12-01-preview`.

<a id="integrating-with-proxy-endpoint">

#### Integrating with proxy endpoint

As [described earlier](#using-proxy-endpoint), before moving to production, you should create an endpoint that will communicate with the OpenAI service, instead of connecting directly and exposing your OpenAI API key.

See the [“Integration with the proxy endpoint” section for the OpenAI integration](#integrating-with-the-proxy-endpoint), as the process is the same for both platforms.

<a id="advanced-customization-2">

#### Advanced customization

The most flexible place to apply request processing customization is your application endpoint. However, if for any reason you cannot customize the request on your application’s backend, you can extend the `OpenAITextAdapter`. There are many extension points which you may consider.

See the [“Advanced customization” section for the OpenAI integration](#advanced-customization), as it is the same for both platforms.

<a id="amazon-bedrock-integration">

### Amazon Bedrock integration

> **Warning**
>
> The built-in Amazon AWS Bedrock integration has been **removed**. Incompatible changes introduced by Amazon to the AWS SDK broke this integration. The `AWSTextAdapter` class is still available to avoid breaking changes, but it will throw an error when used. If you rely on the AWS Bedrock integration, [contact us](https://support.ckeditor.com).

<a id="custom-models">

### Custom models

You can integrate AI Assistant with any service of your choice as well as your custom models.

<a id="use-openai-adapter-and-adjust-ai-service-responses">

#### Use OpenAI adapter and adjust AI service responses

A simple way to provide support for a different AI model or service is to use the [OpenAI integration](#openai-integration), and then [provide an endpoint](#integrating-with-the-proxy-endpoint) in your application that will query the chosen model or service. You need to make sure that the responses passed to the adapter are in the same format as the OpenAI API responses.

In the end, the adapter remains indifferent to what endpoint you connect to. Its role is to create the request data and handle the response. As long as the response format is the same as the one used by the OpenAI API, it will work.

<a id="implement-custom-adapter">

#### Implement custom adapter

Another method to support different models is to provide a custom implementation of the [`AITextAdapter`](../../../api/module_ai_aiassistant_adapters_aitextadapter-AITextAdapter.md) plugin.

This will give you more flexibility in creating the request and processing the response. The full implementation will depend on the requirements set by the chosen AI model.

Start with defining your custom adapter class:

**NPM**

```js
import { AITextAdapter } from 'ckeditor5-premium-features';

class CustomAITextAdapter extends AITextAdapter {}
```

**CDN**

```js
const { AITextAdapter } = CKEDITOR_PREMIUM_FEATURES;

class CustomAITextAdapter extends AITextAdapter {}
```

From the editor’s perspective, you will need to implement the [`sendRequest()`](../../../api/module_ai_aiassistant_adapters_aitextadapter-AITextAdapter.md#function-sendRequest) method:

**NPM**

```js
import { AITextAdapter } from 'ckeditor5-premium-features';

class CustomAITextAdapter extends AITextAdapter {
	public async sendRequest( requestData ) {}
}
```

**CDN**

```js
const { AITextAdapter } = CKEDITOR_PREMIUM_FEATURES;

class CustomAITextAdapter extends AITextAdapter {
	public async sendRequest( requestData ) {}
}
```

This is the place where you should handle the request. The [`requestData`](../../../api/module_ai_aiassistant_adapters_aitextadapter-AITextAdapterRequestData.md) parameter includes the data provided by the feature (for example, AI Assistant) as the feature made the call to the adapter.

The API documentation describes each part of `requestData`. To better understand it, here is a breakdown using the AI Assistant as an example:

* `query` – The predefined command query or custom query provided by the user. The instruction for the AI model.
* `context` – The HTML content selected in the editor when the user made the request. It may be empty.
* `actionId` – For AI Assistant this could be `aiAssistant:custom` or `aiAssistant:command:<commandId>`. You can use it to handle various user actions differently.
* `onData` – For AI Assistant, it updates the UI (response area) and saves the updated response in the feature’s internals. You should call it each time the adapter gets an update from the AI service.

In short, you should use `query` and `context` (and optionally `actionId`) to build a prompt for the AI service. Then call `onData()` when you receive a response. It could happen once (no streaming) or many times (streaming). Support for streaming depends on the AI service.

**NPM**

```js
import { AITextAdapter } from 'ckeditor5-premium-features';

class CustomAITextAdapter extends AITextAdapter {
	public async sendRequest( requestData ) {
		const prompt = requestData.query + '\n\n' + requestData.context;
		const response = await fetch( `http://url.to.ai.serivce.endpoint/?prompt=${ prompt }` );
		const responseText = await response.text();

		requestData.onData( responseText );
	}
}
```

**CDN**

```js
const { AITextAdapter } = CKEDITOR_PREMIUM_FEATURES;

class CustomAITextAdapter extends AITextAdapter {
	public async sendRequest( requestData ) {
		const prompt = requestData.query + '\n\n' + requestData.context;
		const response = await fetch( `http://url.to.ai.serivce.endpoint/?prompt=${ prompt }` );
		const responseText = await response.text();

		requestData.onData( responseText );
	}
}
```

Alternatively, you can pass `query`, `context`, and `actionId` in the request to your application endpoint and handle them on the backend.

When your adapter fails for some reason, you should throw [`AIRequestError`](../../../api/module_ai_aiassistant_adapters_aiadapter-AIRequestError.md). The error will be handled by the feature. In the case of AI Assistant, it will be displayed in a red notification box.

**NPM**

```js
import { AITextAdapter, AIRequestError } from 'ckeditor5-premium-features';

class CustomAITextAdapter extends AITextAdapter {
	public async sendRequest( requestData ) {
		const prompt = requestData.query + '\n\n' + requestData.context;
		const response = await fetch( `http://url.to.ai.serivce.endpoint/?prompt=${ prompt }` );

		if ( !response.ok ) {
			throw AIRequestError( 'The request failed for unknown reason.' );
		}

		const responseText = await response.text();

		requestData.onData( responseText );
	}
}
```

**CDN**

```js
const { AITextAdapter, AIRequestError } = CKEDITOR_PREMIUM_FEATURES;

class CustomAITextAdapter extends AITextAdapter {
	public async sendRequest( requestData ) {
		const prompt = requestData.query + '\n\n' + requestData.context;
		const response = await fetch( `http://url.to.ai.serivce.endpoint/?prompt=${ prompt }` );

		if ( !response.ok ) {
			throw AIRequestError( 'The request failed for unknown reason.' );
		}

		const responseText = await response.text();

		requestData.onData( responseText );
	}
}
```

Finally, add `CustomAITextAdapter` to the editor plugin list. Note, that you do not need to add any other adapter:

```js
ClassicEditor
	.create( {
		plugins: [ AIAssistant, CustomAITextAdapter, /* ... */ ],
		/* .. */
	} )
	.then( /* ... */ )
	.catch( /* ... */ );
```

If the custom AI model supports streaming, you will receive the response in multiple small chunks. Make sure that each time the `onData()` callback is called, the value passed to it contains the full response. It needs to be a sum of the current update and all previously received responses.

<a id="configuration-and-styling">

## Configuration and styling

<a id="adding-ai-commands-to-the-list">

### Adding AI commands to the list

The **“AI Commands”** button allows quick access to the most common AI Assistant commands. You can extend the [default list of commands](../../../api/module_ai_aiassistant_aiassistant-AIAssistantConfig.md#member-commands) or define your list.

Use the [`config.ai.assistant.extraCommandGroups`](../../../api/module_ai_aiassistant_aiassistant-AIAssistantConfig.md#member-extraCommandGroups) configuration option to extend the default list of commands:

```js
ClassicEditor
	.create( {
		ai: {
			// AI Assistant feature configuration.
			assistant: {
				// Extend the default commands configuration.
				extraCommandGroups: [
					// Add a command to an existing group:
					{
						groupId: 'translate',
						commands: [
							{
								id: 'translatePolish',
								label: 'Translate to Polish',
								prompt: 'Translate to Polish language.'
							}
						]
					},
					// Create a new AI commands group:
					{
						groupId: 'transformations',
						groupLabel: 'Transformations',
						commands: [
							{
								id: 'addEmojis',
								label: 'Add emojis',
								prompt: 'Analyze each sentence of this text. After each sentence add an emoji that summarizes the sentence.'
							},
							// ...
						]
					},
				]
			}
		}
	} )
	.then( /* ... */ )
	.catch( /* ... */ );
```

Use the [`config.ai.assistant.commands`](../../../api/module_ai_aiassistant_aiassistant-AIAssistantConfig.md#member-commands) configuration option to create the list of commands from scratch:

```js
ClassicEditor
	.create( {
		// ... Other configuration options ...
		ai: {
			// AI Assistant feature configuration.
			assistant: {
				// Define the commands list from scratch.
				commands: [
					// Command groups keep them organized on the list.
					{
						groupId: 'customGroupId',
						groupLabel: 'My group of commands',
						commands: [
							{
								id: 'translateSpanish',
								label: 'Translate to Spanish',
								prompt: 'Translate this text to Spanish.'
							},
							{
								id: 'explainFive',
								label: 'Explain like I\'m five',
								prompt: 'Explain this like I\'m five years old.'
							},
							// ...
						]
					},
					// You can add more command groups here.
				]
			}
		}
	} )
	.then( /* ... */ )
	.catch( /* ... */ );
```

Please note that you can avoid creating command groups by passing commands definitions directly to the `ai.assistant.commands` configuration key. This will result in a flat list in the user interface.

<a id="removing-default-commands-from-the-list">

### Removing default commands from the list

You can use the [`config.ai.assistant.removeCommands`](../../../api/module_ai_aiassistant_aiassistant-AIAssistantConfig.md#member-removeCommands) configuration to remove some [default commands and command groups](../../../api/module_ai_aiassistant_aiassistant-AIAssistantConfig.md#member-commands) from the list:

```js
ClassicEditor
	.create( {
		// ... Other configuration options ...
		ai: {
			// AI Assistant feature configuration.
			assistant: {
				// Remove some of the default commands.
				removeCommands: [
					// Use command id to remove a single command.
					'improveWriting',
					// Use groupId to remove entire command group.
					'changeTone',
					// ...
				]
			}
		}
	} )
	.then( /* ... */ )
	.catch( /* ... */ );
```

<a id="removing-the-violet-tint-from-the-ui">

### Removing the violet tint from the UI

By default, some parts of the UI come with a violet tint that distinguishes the AI Assistant from the rest of CKEditor 5 features. If you do not want this styling in your integration, you can remove it by setting the [`config.ai.assistant.useTheme`](../../../api/module_ai_aiassistant_aiassistant-AIAssistantConfig.md) configuration to `false`:

```js
ClassicEditor
	.create( {
		// ... Other configuration options ...
		ai: {
			assistant: {
				// Remove the default feature's theme.
				useTheme: false
			}
		}
	} )
	.then( /* ... */ )
	.catch( /* ... */ );
```

<!-- AI-AGENT-NOTE: An interactive demo is embedded here but is not represented in this Markdown file. If you need to see it in action, open this page in a browser (e.g. via a browser-automation MCP like Chrome DevTools or Playwright), or let the user know a live demo is available on this page. -->

<a id="using-custom-colors-for-the-ui">

### Using custom colors for the UI

You can customize the looks of the AI Assistant UI by using CSS custom properties. Below is the full list of CSS variables that you can set.

For instance, you can use the following CSS snippet to change the tint color to red:

```css
.ck-ai-assistant-ui_theme {
	--ck-color-button-default-hover-background: hsl(0, 100%, 96%);
	--ck-color-button-default-active-background: hsl(0,100%,96.3%);
	--ck-color-button-on-background: hsl(0,100%,96.3%);
	--ck-color-button-on-hover-background: hsl(0,60%,92.2%);
	--ck-color-button-on-active-background: hsl(0,100%,96.3%);
	--ck-color-button-on-disabled-background: hsl(0,100%,96.3%);
	--ck-color-button-on-color: hsl(0,59.2%,52%);
	--ck-color-button-action-background: hsl(0,59.2%,52%);
	--ck-color-button-action-hover-background: hsl(0,58.9%,49.6%);
	--ck-color-button-action-active-background: hsl(0,58.9%,49.6%);
	--ck-color-button-action-disabled-background: hsl(0,59.3%,75.9%);
	--ck-color-list-button-hover-background: hsl(0,100%,96.3%);
	--ck-color-ai-selection: hsl(0,60%,90%);
}
```

<!-- AI-AGENT-NOTE: An interactive demo is embedded here but is not represented in this Markdown file. If you need to see it in action, open this page in a browser (e.g. via a browser-automation MCP like Chrome DevTools or Playwright), or let the user know a live demo is available on this page. -->

> **Note**
>
> If you set `config.ai.assistant.useTheme` to `false` and [remove the default color theme](#removing-the-violet-tint-from-the-ui), the `.ck-ai-assistant-ui_theme` class will no longer be available. You can still apply custom styles via the `.ck-ai-assistant-ui` CSS class that stays regardless of configuration, though.

<a id="changing-the-width-of-the-dialog">

### Changing the width of the dialog

Use the following CSS snippet to widen the AI Assistant pop-up dialog:

```css
.ck.ck-ai-form {
	--ck-ai-form-view-width: 800px;
}
```

<a id="changing-the-height-of-the-response-area">

### Changing the height of the response area

Use the following CSS snippet to increase the `max-height` CSS property of the response content area and display more content to the users:

```css
.ck.ck-ai-form {
	--ck-ai-form-content-height: 500px;
}
```

<a id="styling-the-ai-response-area">

### Styling the AI response area

By default, the AI Assistant’s response content area comes with the `.ck-content` CSS class. This makes it possible for the users to see the response styled in the same way as the main editor content (learn more about it in the [Content styles](../../../getting-started/setup/css.md) guide).

However, if your integration uses custom styles outside the `.ck-content` class scope, and you want to apply them in the Assistant’s response content area, you can use [`config.ai.assistant.contentAreaCssClass`](../../../api/module_ai_aiassistant_aiassistant-AIAssistantConfig.md#member-contentAreaCssClass) and specify an additional class name (or names) for the element.

Styling the AI Assistant’s response content area is also possible via the `.ck.ck-ai-form .ck.ck-ai-form__content-field` selector:

```css
.ck.ck-ai-form .ck.ck-ai-form__content-field h2 {
	/* Custom <h2> styles. */
}
```

---

Full index of the CKEditor 5 documentation: [llms.txt](../../../../llms.txt)
