Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ type AIEngineInfo @since(version: "23.2.2") {
id: ID!
"Name of the AI engine."
name: String!
"Path to the AI engine icon."
icon: String
}

type AIChatConversationInfo @since(version: "25.1.1") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public interface DBWServiceAI extends DBWService {
WebAISettingsConfig getAiSettings() throws DBWebException;

@NotNull
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
@WebAction
List<WebAIEngine> getEngineConfigurations() throws DBWebException;

@NotNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
*/
package io.cloudbeaver.service.ai.gql;

import io.cloudbeaver.WebServiceUtils;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.model.ai.registry.AIEngineDescriptor;
import org.jkiss.dbeaver.model.meta.Property;

Expand All @@ -41,4 +43,10 @@ public String getName() {
return descriptor.getLabel();
}

@Nullable
@Property
public String getIcon() {
return WebServiceUtils.makeIconId(descriptor.getIcon());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ query getEngines {
engines: aiListEngines {
id
name
icon
}
}
1 change: 1 addition & 0 deletions webapp/packages/plugin-ai-administration/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"@cloudbeaver/core-sdk": "workspace:*",
"@cloudbeaver/core-ui": "workspace:*",
"@cloudbeaver/core-utils": "workspace:*",
"@cloudbeaver/plugin-ai": "workspace:*",
"@cloudbeaver/plugin-data-grid": "workspace:*",
"@dbeaver/js-helpers": "workspace:*",
"@dbeaver/ui-kit": "workspace:*",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,15 @@ import { LANGUAGE_OPTIONS } from './AISettingsForm/getLanguageOptions.js';
import type { AdministrationAISettingsFormState } from './AISettingsForm/AdministrationAISettingsFormState.js';
import { getFirstException } from '@cloudbeaver/core-utils';
import { isDefined } from '@dbeaver/js-helpers';
import { AiEnginesResource } from '@cloudbeaver/plugin-ai';

export const AIAdministrationPage = observer<{
formState: AdministrationAISettingsFormState;
}>(function AIAdministrationPage({ formState }) {
const translate = useTranslate();
const notificationService = useService(NotificationService);
const profilesLoader = useResource(AIAdministrationPage, AIProfilesResource, CachedMapAllKey);
const aiEnginesResource = useResource(AIAdministrationPage, AiEnginesResource, undefined);
const profiles = profilesLoader.data.filter(isDefined);

const settingsInfoPart = getAdministrationAISettingsFormInfoPart(formState);
Expand Down Expand Up @@ -102,6 +104,7 @@ export const AIAdministrationPage = observer<{
value={settingsInfoPart.state.defaultConfiguration}
description={translate('plugin_ai_administration_default_profile_description')}
disabled={!profiles.length}
iconSelector={value => aiEnginesResource.data.find(e => e.id === value.engineId)?.icon}
small
onSelect={selectDefaultProfile}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ import { useService } from '@cloudbeaver/core-di';
import { NotificationService } from '@cloudbeaver/core-events';
import { FormMode, type TabContainerPanelComponent } from '@cloudbeaver/core-ui';
import { getObjectPropertyOptionName, getObjectPropertyOptionValue } from '@cloudbeaver/core-sdk';
import { AiEnginesResource } from '@cloudbeaver/plugin-ai';

import { EnginesResource } from '../../../Engines/EnginesResource.js';
import { AIEnginePropertiesResource, MODEL_PROPERTY_ID } from '../../AIEnginePropertiesResource.js';
import type { IAIProfileFormProps } from '../IAIProfileFormProps.js';
import { AI_PROFILE_NAME_MAX_LENGTH, AI_PROFILE_NAME_MIN_LENGTH } from './AIProfileSchema.js';
Expand All @@ -37,7 +37,7 @@ import { getAIProfileFormPart } from './getAIProfileFormPart.js';
export const AIProfileOptions: TabContainerPanelComponent<IAIProfileFormProps> = observer(function AIProfileOptions({ formState }) {
const translate = useTranslate();
const notificationService = useService(NotificationService);
const enginesLoader = useResource(AIProfileOptions, EnginesResource, undefined);
const enginesLoader = useResource(AIProfileOptions, AiEnginesResource, undefined);
const part = getAIProfileFormPart(formState);
const propertiesLoader = useResource(AIProfileOptions, AIEnginePropertiesResource, part.state.engineId || null);
const propertiesInfo = propertiesLoader.data ?? [];
Expand Down Expand Up @@ -96,6 +96,7 @@ export const AIProfileOptions: TabContainerPanelComponent<IAIProfileFormProps> =
keySelector={value => value.id}
value={part.state.engineId}
disabled={formState.isDisabled || isEditMode}
iconSelector={value => value.icon}
small
required
onSelect={value => value && part.changeEngine(value)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
import { reaction } from 'mobx';
import { observer } from 'mobx-react-lite';

import { Link, s, TextPlaceholder, useResource, useS, useTranslate } from '@cloudbeaver/core-blocks';
import { IconOrImage, Link, s, TextPlaceholder, useResource, useS, useTranslate } from '@cloudbeaver/core-blocks';
import { useService } from '@cloudbeaver/core-di';
import { ADMINISTRATION_TABLE_DEFAULT_ROW_HEIGHT, AdministrationTableStyles } from '@cloudbeaver/core-administration';
import { DataGrid, TableRowSelect, useCreateGridReactiveValue } from '@cloudbeaver/plugin-data-grid';
import { AiEnginesResource } from '@cloudbeaver/plugin-ai';
import { Command } from '@dbeaver/ui-kit';

import { EnginesResource } from '../Engines/EnginesResource.js';
import { AIProfileFormService } from './AIProfileForm/AIProfileFormService.js';
import type { AIProfile } from './AIProfilesResource.js';

Expand All @@ -34,7 +34,7 @@ export const AIProfilesTable = observer<Props>(function AIProfilesTable({ profil
const translate = useTranslate();
const styles = useS(AdministrationTableStyles);
const aiProfileFormService = useService(AIProfileFormService);
const enginesLoader = useResource(AIProfilesTable, EnginesResource, undefined);
const enginesLoader = useResource(AIProfilesTable, AiEnginesResource, undefined);

const columnsCount = useCreateGridReactiveValue(() => COLUMNS.length, null, [COLUMNS]);
const rowsCount = useCreateGridReactiveValue(
Expand Down Expand Up @@ -82,7 +82,18 @@ export const AIProfilesTable = observer<Props>(function AIProfilesTable({ profil

if (column.key === ENGINE_COLUMN.key) {
const engine = enginesLoader.data.find(engine => engine.id === profile.engineId);
return <span>{engine?.name ?? profile.engineId}</span>;
const title = engine?.name ?? profile.engineId;

if (engine?.icon) {
return (
<div title={title} className="tw:flex tw:gap-2">
<IconOrImage icon={engine.icon} />
<span className="tw:truncate">{title}</span>
</div>
);
}

return <span title={title}>{title}</span>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets show and icon and the engine name. users cannot know each engine by only icon. right now they have to hover too see the name and we can easily avoid this action since there is enough space in the table

}

return null;
Expand Down
3 changes: 0 additions & 3 deletions webapp/packages/plugin-ai-administration/src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

import { Bootstrap, Dependency, ModuleRegistry, proxy } from '@cloudbeaver/core-di';
import { LocaleService } from './LocaleService.js';
import { EnginesResource } from './Engines/EnginesResource.js';
import { AISettingsResource } from './AISettingsResource.js';
import { AISettingsService } from './AISettingsService.js';
import { AIEnginePropertiesResource } from './AIProfiles/AIEnginePropertiesResource.js';
Expand All @@ -29,11 +28,9 @@ export default ModuleRegistry.add({
.addSingleton(AIAdministrationBootstrap)
.addSingleton(Bootstrap, LocaleService)
.addSingleton(Bootstrap, proxy(AIAdministrationTabsService))
.addSingleton(Dependency, proxy(EnginesResource))
.addSingleton(Dependency, proxy(AISettingsResource))
.addSingleton(Dependency, proxy(AIProfilesResource))
.addSingleton(Dependency, proxy(AIEnginePropertiesResource))
.addSingleton(EnginesResource)
.addSingleton(AISettingsResource)
.addSingleton(AISettingsService)
.addSingleton(AIProfilesResource)
Expand Down
3 changes: 3 additions & 0 deletions webapp/packages/plugin-ai-administration/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@
{
"path": "../core-utils"
},
{
"path": "../plugin-ai"
},
{
"path": "../plugin-data-grid"
}
Expand Down
1 change: 1 addition & 0 deletions webapp/packages/plugin-ai-chat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"@cloudbeaver/core-ui": "workspace:*",
"@cloudbeaver/core-utils": "workspace:*",
"@cloudbeaver/core-view": "workspace:*",
"@cloudbeaver/plugin-ai": "workspace:*",
"@cloudbeaver/plugin-codemirror6": "workspace:*",
"@cloudbeaver/plugin-datasource-context-switch": "workspace:*",
"@cloudbeaver/plugin-navigation-tabs": "workspace:*",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
import { observer } from 'mobx-react-lite';

import { MenuGroup, MenuGroupLabel, MenuItemRadio } from '@dbeaver/ui-kit';
import { RadioIndicator, useTranslate } from '@cloudbeaver/core-blocks';
import { IconOrImage, RadioIndicator, useResource, useTranslate } from '@cloudbeaver/core-blocks';
import { useService } from '@cloudbeaver/core-di';
import { NotificationService } from '@cloudbeaver/core-events';
import { AiEnginesResource } from '@cloudbeaver/plugin-ai';

import type { AIChatProfile } from '../../../AIChatProfilesResource.js';
import type { AIChatConversationInfo } from '../AIChatConversationsResource.js';
Expand All @@ -28,6 +29,8 @@ export const AIChatConversationProfile = observer<Props>(function AIChatConversa
const notificationService = useService(NotificationService);
const aiChatConversationsService = useService(AIChatConversationsService);

const aiEnginesResource = useResource(AIChatConversationProfile, AiEnginesResource, undefined);

async function selectProfile(profileId: string) {
try {
await aiChatConversationsService.updateConversationProfile(conversation.id, profileId);
Expand All @@ -46,6 +49,7 @@ export const AIChatConversationProfile = observer<Props>(function AIChatConversa

{profiles.map(profile => {
const isCurrent = conversation.profile === profile.id;
const engine = aiEnginesResource.data.find(e => e.id === profile.engineId);

return (
<MenuItemRadio
Expand All @@ -62,6 +66,7 @@ export const AIChatConversationProfile = observer<Props>(function AIChatConversa
>
<div className="tw:flex tw:items-center tw:gap-2 tw:overflow-hidden">
<RadioIndicator size="small" checked={isCurrent} />
{engine?.icon && <IconOrImage className="tw:w-[14px]" icon={engine.icon} />}
<span className="tw:truncate">{profile.name}</span>
</div>
</MenuItemRadio>
Expand Down
3 changes: 3 additions & 0 deletions webapp/packages/plugin-ai-chat/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@
{
"path": "../core-view"
},
{
"path": "../plugin-ai"
},
{
"path": "../plugin-codemirror6"
},
Expand Down
17 changes: 17 additions & 0 deletions webapp/packages/plugin-ai/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# dependencies
/node_modules

# testing
/coverage

# production
/lib

# misc
.DS_Store
.env*

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
41 changes: 41 additions & 0 deletions webapp/packages/plugin-ai/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"name": "@cloudbeaver/plugin-ai",
"type": "module",
"sideEffects": [
"./lib/module.js",
"./lib/index.js",
"src/**/*.css",
"public/**/*"
],
"version": "0.1.0",
"description": "",
"license": "Apache-2.0",
"exports": {
".": "./lib/index.js",
"./module": "./lib/module.js"
},
"scripts": {
"build": "tsc -b",
"clean": "rimraf --glob lib",
"lint": "eslint ./src/ --ext .ts,.tsx",
"validate-dependencies": "core-cli-validate-dependencies"
},
"dependencies": {
"@cloudbeaver/core-di": "workspace:*",
"@cloudbeaver/core-resource": "workspace:*",
"@cloudbeaver/core-root": "workspace:*",
"@cloudbeaver/core-sdk": "workspace:*",
"mobx": "^6",
"mobx-react-lite": "^4",
"react": "^19",
"react-dom": "^19",
"tslib": "^2"
},
"devDependencies": {
"@cloudbeaver/core-cli": "workspace:*",
"@cloudbeaver/tsconfig": "workspace:*",
"@types/react": "^19",
"rimraf": "^6",
"typescript": "^5"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,24 @@
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/

import { injectable } from '@cloudbeaver/core-di';
import { CachedDataResource } from '@cloudbeaver/core-resource';
import { EAdminPermission, ServerConfigResource, SessionPermissionsResource } from '@cloudbeaver/core-root';
import { ServerConfigResource } from '@cloudbeaver/core-root';
import { GraphQLService, type AiEngineConfig, type AiEngineInfo } from '@cloudbeaver/core-sdk';

export type EngineInfo = AiEngineInfo;
export type EngineConfig = AiEngineConfig;

@injectable(() => [GraphQLService, SessionPermissionsResource, ServerConfigResource])
export class EnginesResource extends CachedDataResource<EngineInfo[]> {
@injectable(() => [GraphQLService, ServerConfigResource])
export class AiEnginesResource extends CachedDataResource<EngineInfo[]> {
constructor(
private readonly graphQLService: GraphQLService,
permissionsResource: SessionPermissionsResource,
serverConfigResource: ServerConfigResource,
) {
super(() => []);

this.sync(serverConfigResource);

permissionsResource.require(this, EAdminPermission.admin).outdateResource(this);
}

protected async loader(): Promise<EngineInfo[]> {
Expand Down
11 changes: 11 additions & 0 deletions webapp/packages/plugin-ai/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/

import './module.js';

export * from './AiEnginesResource.js';
19 changes: 19 additions & 0 deletions webapp/packages/plugin-ai/src/module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/

import { Dependency, ModuleRegistry, proxy } from '@cloudbeaver/core-di';

import { AiEnginesResource } from './AiEnginesResource.js';

export default ModuleRegistry.add({
name: '@cloudbeaver/plugin-ai',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

plugin-ai is too common name. in the future we may want to place everything to it instead of decomposing ai-features to the plugins, I guess

plugin-ai-engines?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have plugin-ai-administration, so it seems like plugin-ai would be a suitable public plugin name here, at least for now


configure: serviceCollection => {
serviceCollection.addSingleton(Dependency, proxy(AiEnginesResource)).addSingleton(AiEnginesResource);
},
});
36 changes: 36 additions & 0 deletions webapp/packages/plugin-ai/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"extends": "@cloudbeaver/tsconfig/tsconfig.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "lib",
"tsBuildInfoFile": "lib/tsconfig.tsbuildinfo",
"composite": true
},
"exclude": [
"lib/**/*",
"**/node_modules"
],
"references": [
{
"path": "../core-cli"
},
{
"path": "../core-di"
},
{
"path": "../core-resource"
},
{
"path": "../core-root"
},
{
"path": "../core-sdk"
}
],
"include": [
"__custom_mocks__/**/*",
"src/**/*",
"src/**/*.json",
"src/**/*.css"
]
}
Loading
Loading