🔧(backend) make frontend ai bot configurable
We make the AI bot configurable with settings. We will be able to have different AI bot name per instance.
This commit is contained in:
@@ -11,6 +11,7 @@ These are the environment variables you can set for the `impress-backend` contai
|
||||
| AI_ALLOW_REACH_FROM | Users that can use AI must be this level. options are "public", "authenticated", "restricted" | authenticated |
|
||||
| AI_API_KEY | AI key to be used for AI Base url | |
|
||||
| AI_BASE_URL | OpenAI compatible AI base url | |
|
||||
| AI_BOT | Information to give to the frontend about the AI bot | { "name": "Docs AI", "color": "#8bc6ff" }
|
||||
| AI_FEATURE_ENABLED | Enable AI options | false |
|
||||
| AI_MODEL | AI Model to use | |
|
||||
| ALLOW_LOGOUT_GET_METHOD | Allow get logout method | true |
|
||||
|
||||
@@ -2349,7 +2349,9 @@ class ConfigView(drf.views.APIView):
|
||||
Return a dictionary of public settings.
|
||||
"""
|
||||
array_settings = [
|
||||
"AI_BOT",
|
||||
"AI_FEATURE_ENABLED",
|
||||
"AI_MODEL",
|
||||
"COLLABORATION_WS_URL",
|
||||
"COLLABORATION_WS_NOT_CONNECTED_READY_ONLY",
|
||||
"CONVERSION_FILE_EXTENSIONS_ALLOWED",
|
||||
|
||||
@@ -19,7 +19,9 @@ pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
@override_settings(
|
||||
AI_BOT={"name": "Test Bot", "color": "#000000"},
|
||||
AI_FEATURE_ENABLED=False,
|
||||
AI_MODEL="test-model",
|
||||
COLLABORATION_WS_URL="http://testcollab/",
|
||||
COLLABORATION_WS_NOT_CONNECTED_READY_ONLY=True,
|
||||
CRISP_WEBSITE_ID="123",
|
||||
@@ -43,6 +45,9 @@ def test_api_config(is_authenticated):
|
||||
response = client.get("/api/v1.0/config/")
|
||||
assert response.status_code == HTTP_200_OK
|
||||
assert response.json() == {
|
||||
"AI_BOT": {"name": "Test Bot", "color": "#000000"},
|
||||
"AI_FEATURE_ENABLED": False,
|
||||
"AI_MODEL": "test-model",
|
||||
"AI_FEATURE_ENABLED": False,
|
||||
"COLLABORATION_WS_URL": "http://testcollab/",
|
||||
"COLLABORATION_WS_NOT_CONNECTED_READY_ONLY": True,
|
||||
|
||||
@@ -670,24 +670,32 @@ class Base(Configuration):
|
||||
default=True, environ_name="ALLOW_LOGOUT_GET_METHOD", environ_prefix=None
|
||||
)
|
||||
|
||||
# AI service
|
||||
AI_FEATURE_ENABLED = values.BooleanValue(
|
||||
default=False, environ_name="AI_FEATURE_ENABLED", environ_prefix=None
|
||||
)
|
||||
AI_API_KEY = SecretFileValue(None, environ_name="AI_API_KEY", environ_prefix=None)
|
||||
AI_BASE_URL = values.Value(None, environ_name="AI_BASE_URL", environ_prefix=None)
|
||||
AI_MODEL = values.Value(None, environ_name="AI_MODEL", environ_prefix=None)
|
||||
# AI settings
|
||||
AI_ALLOW_REACH_FROM = values.Value(
|
||||
choices=("public", "authenticated", "restricted"),
|
||||
default="authenticated",
|
||||
environ_name="AI_ALLOW_REACH_FROM",
|
||||
environ_prefix=None,
|
||||
)
|
||||
AI_API_KEY = SecretFileValue(None, environ_name="AI_API_KEY", environ_prefix=None)
|
||||
AI_BASE_URL = values.Value(None, environ_name="AI_BASE_URL", environ_prefix=None)
|
||||
AI_BOT = values.DictValue(
|
||||
default={
|
||||
"name": _("Docs AI"),
|
||||
"color": "#8bc6ff",
|
||||
},
|
||||
environ_name="AI_BOT",
|
||||
environ_prefix=None,
|
||||
)
|
||||
AI_DOCUMENT_RATE_THROTTLE_RATES = {
|
||||
"minute": 5,
|
||||
"hour": 100,
|
||||
"day": 500,
|
||||
}
|
||||
AI_FEATURE_ENABLED = values.BooleanValue(
|
||||
default=False, environ_name="AI_FEATURE_ENABLED", environ_prefix=None
|
||||
)
|
||||
AI_MODEL = values.Value(None, environ_name="AI_MODEL", environ_prefix=None)
|
||||
AI_USER_RATE_THROTTLE_RATES = {
|
||||
"minute": 3,
|
||||
"hour": 50,
|
||||
|
||||
@@ -390,6 +390,15 @@ test.describe('Doc Editor', () => {
|
||||
});
|
||||
|
||||
test('it checks the AI feature', async ({ page, browserName }) => {
|
||||
await overrideConfig(page, {
|
||||
AI_BOT: {
|
||||
name: 'Albert AI',
|
||||
color: '#8bc6ff',
|
||||
},
|
||||
});
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
await page.route(/.*\/ai-proxy\//, async (route) => {
|
||||
const request = route.request();
|
||||
if (request.method().includes('POST')) {
|
||||
@@ -474,7 +483,7 @@ test.describe('Doc Editor', () => {
|
||||
await page.getByRole('option', { name: 'Translate' }).click();
|
||||
await page.getByPlaceholder('Ask AI anything…').fill('French');
|
||||
await page.getByPlaceholder('Ask AI anything…').press('Enter');
|
||||
await expect(editor.getByText('Docs AI')).toBeVisible();
|
||||
await expect(editor.getByText('Albert AI')).toBeVisible();
|
||||
await page
|
||||
.locator('p.bn-mt-suggestion-menu-item-title')
|
||||
.getByText('Accept')
|
||||
|
||||
@@ -4,7 +4,12 @@ export type BrowserName = 'chromium' | 'firefox' | 'webkit';
|
||||
export const BROWSERS: BrowserName[] = ['chromium', 'webkit', 'firefox'];
|
||||
|
||||
export const CONFIG = {
|
||||
AI_BOT: {
|
||||
name: 'Docs AI',
|
||||
color: '#8bc6ff',
|
||||
},
|
||||
AI_FEATURE_ENABLED: true,
|
||||
AI_MODEL: 'llama',
|
||||
CRISP_WEBSITE_ID: null,
|
||||
COLLABORATION_WS_URL: 'ws://localhost:4444/collaboration/ws/',
|
||||
COLLABORATION_WS_NOT_CONNECTED_READY_ONLY: true,
|
||||
|
||||
@@ -15,7 +15,9 @@ interface ThemeCustomization {
|
||||
}
|
||||
|
||||
export interface ConfigResponse {
|
||||
AI_BOT: { name: string; color: string };
|
||||
AI_FEATURE_ENABLED?: boolean;
|
||||
AI_MODEL?: string;
|
||||
COLLABORATION_WS_URL?: string;
|
||||
COLLABORATION_WS_NOT_CONNECTED_READY_ONLY?: boolean;
|
||||
CONVERSION_FILE_EXTENSIONS_ALLOWED: string[];
|
||||
|
||||
@@ -3,6 +3,7 @@ import { createAIExtension, createBlockNoteAIClient } from '@blocknote/xl-ai';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { fetchAPI } from '@/api';
|
||||
import { useConfig } from '@/core';
|
||||
import { Doc } from '@/docs/doc-management';
|
||||
|
||||
const client = createBlockNoteAIClient({
|
||||
@@ -17,7 +18,13 @@ const client = createBlockNoteAIClient({
|
||||
* Custom prompts can be invoked using the pattern !promptName in the AI input field.
|
||||
*/
|
||||
export const useAI = (docId: Doc['id']) => {
|
||||
const conf = useConfig().data;
|
||||
|
||||
return useMemo(() => {
|
||||
if (!conf?.AI_MODEL) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const openai = createOpenAI({
|
||||
...client.getProviderSettings('openai'),
|
||||
fetch: (input, init) => {
|
||||
@@ -31,17 +38,14 @@ export const useAI = (docId: Doc['id']) => {
|
||||
});
|
||||
},
|
||||
});
|
||||
const model = openai.chat('neuralmagic/Meta-Llama-3.1-70B-Instruct-FP8');
|
||||
const model = openai.chat(conf.AI_MODEL);
|
||||
|
||||
const extension = createAIExtension({
|
||||
stream: false,
|
||||
model,
|
||||
agentCursor: {
|
||||
name: 'Albert',
|
||||
color: '#8bc6ff',
|
||||
},
|
||||
agentCursor: conf?.AI_BOT,
|
||||
});
|
||||
|
||||
return extension;
|
||||
}, [docId]);
|
||||
}, [docId, conf?.AI_BOT, conf?.AI_MODEL]);
|
||||
};
|
||||
|
||||
+1
-1
@@ -252,7 +252,7 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
|
||||
comments={showComments}
|
||||
aria-label={t('Document editor')}
|
||||
>
|
||||
<AIMenuController aiMenu={AIMenu} />
|
||||
{aiExtension && <AIMenuController aiMenu={AIMenu} />}
|
||||
<BlockNoteSuggestionMenu />
|
||||
<BlockNoteToolbar />
|
||||
</BlockNoteView>
|
||||
|
||||
Reference in New Issue
Block a user