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 @@ -272,6 +272,9 @@ extend type Mutation @since(version: "23.2.2") {
"Clears the chat messages in the AI chat conversation. The messages will be removed from the conversation."
aiClearLastChatMessages(conversationId: ID!, messageId: ID!): Boolean! @since(version: "25.1.1")

"Cancels the in-progress AI response generation in the specified conversation."
aiCancelChatMessage(conversationId: ID!): Boolean! @since(version: "26.1.5")

"Saves AI settings (e.g. supporting confirming metadata transfer) for the specified connection."
aiSaveDataSourceSettings(dataSourceId: DataSourceIdInput!, settings: AIDataSourceSettingsInput!): AIDataSourceSettingsInfo! @since(version: "25.3.3")
}
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,12 @@ boolean setLastChatMessage(
@NotNull String messageId
) throws DBWebException;

@WebAction
boolean cancelChatMessage(
@NotNull WebSession webSession,
@NotNull String conversationId
) throws DBWebException;

@NotNull
@WebAction
WebAIDataSourceSettings getDataSourceAiSettings(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,18 @@ public boolean setLastChatMessage(
return true;
}

@Override
public boolean cancelChatMessage(
@NotNull WebSession webSession,
@NotNull String conversationId
) throws DBWebException {
WebAIUtils.validateAiPluginEnabled();
AIChatConversation conversation = WebAIUtils.getAiChatConversation(webSession, conversationId);
conversation.cancelConversation();
webSession.removeAttribute(WebAIUtils.getWaitingAttr(conversation));
return true;
}

@NotNull
@Override
public WebAIDataSourceSettings getDataSourceAiSettings(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,12 @@ public void bindWiring(DBWBindingContext model) {
getArgumentVal(env, "conversationId"),
getArgumentVal(env, "messageId")
)
).dataFetcher(
"aiCancelChatMessage",
env -> getService(env).cancelChatMessage(
getWebSession(env),
getArgumentVal(env, "conversationId")
)
).dataFetcher(
"aiCreateProfile", env -> getService(env).createProfile(
getWebSession(env),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* NOTICE: All information contained herein is, and remains
* the property of DBeaver Corp and its suppliers, if any.
* The intellectual and technical concepts contained
* herein are proprietary to DBeaver Corp and its suppliers
* and may be covered by U.S. and Foreign Patents,
* patents in process, and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from DBeaver Corp.
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.service.ai.model;

Expand All @@ -25,6 +25,7 @@
import org.jkiss.utils.CommonUtils;

import java.util.List;
import java.util.concurrent.CancellationException;

public class WebAiChatResponseConsumer implements AIChatResponseConsumer {
private final StringBuilder responseBuilder;
Expand Down Expand Up @@ -78,7 +79,10 @@

@Override
public void error(@NotNull Throwable throwable) {
var errorMessage = conversation.addMessage(AIMessage.errorMessage(throwable));

AIMessage aiMessage = throwable instanceof CancellationException cancellationException? AIMessage.warningMessage(cancellationException.getMessage()) : AIMessage.errorMessage(throwable);

Check warning on line 83 in server/bundles/io.cloudbeaver.service.ai/src/io/cloudbeaver/service/ai/model/WebAiChatResponseConsumer.java

View workflow job for this annotation

GitHub Actions / Server / Lint

[checkstyle] reported by reviewdog 🐶 WhitespaceAround: '?' is not preceded with whitespace. Raw Output: /github/workspace/./server/bundles/io.cloudbeaver.service.ai/src/io/cloudbeaver/service/ai/model/WebAiChatResponseConsumer.java:83:95: warning: WhitespaceAround: '?' is not preceded with whitespace. (com.puppycrawl.tools.checkstyle.checks.whitespace.WhitespaceAroundCheck)

Check warning on line 83 in server/bundles/io.cloudbeaver.service.ai/src/io/cloudbeaver/service/ai/model/WebAiChatResponseConsumer.java

View workflow job for this annotation

GitHub Actions / Server / Lint

[checkstyle] reported by reviewdog 🐶 Line is longer than 140 characters (found 193). Raw Output: /github/workspace/./server/bundles/io.cloudbeaver.service.ai/src/io/cloudbeaver/service/ai/model/WebAiChatResponseConsumer.java:83:0: warning: Line is longer than 140 characters (found 193). (com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck)

AIChatMessage errorMessage = conversation.addMessage(aiMessage);
if (responseBuilder.isEmpty()) {
webSession.addSessionEvent(
new WSAiChatMessageEvent(new WebAIMessage(errorMessage, conversation)));
Expand All @@ -94,12 +98,16 @@
}

@Override
public void complete(@NotNull List<AIMessageMeta> meta, boolean finishConversation) {
public void complete(@NotNull List<AIMessageMeta> meta, boolean finishConversation, boolean isCanceled) {
if (responseBuilder.isEmpty()) {
return;
}
AIChatMessage responseMessage = conversation.addMessage(AIMessage.assistantMessage(responseBuilder.toString(), meta));
chatSession.notifyMessageAdd(conversation, responseMessage);
webSession.addSessionEvent(new WSAiChatMessageChunkEvent(conversation.getId(), responseMessage.id(), null, true));

if (isCanceled) {
warning("Response generation cancelled by user.");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mutation cancelConversation($conversationId: ID!) {
result: aiCancelChatMessage(conversationId: $conversationId)
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ export class AIChatConversationsResource extends CachedMapResource<string, AICha
return this.get(conversation.id)!;
}

async cancelConversation(conversationId: string): Promise<boolean> {
const { result } = await this.graphQLService.sdk.cancelConversation({ conversationId });
return result;
}

protected async loader(originalKey: ResourceKey<string>): Promise<Map<string, AIChatConversationInfo>> {
const conversationList: AIChatConversationInfo[] = [];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ import { ActionIconButton, AutoResizeTextarea, Form, s, useS, useTranslate } fro
import { useService } from '@cloudbeaver/core-di';
import { getOS, OperatingSystem } from '@cloudbeaver/core-utils';
import { NotificationService } from '@cloudbeaver/core-events';
import { Command } from '@dbeaver/ui-kit';

import { AIChatMessageService } from './AIChatMessageService.js';
import { AIChatConversationsService } from '../AIChatConversation/AIChatConversationsService.js';
import { AIChatContext } from '../AIChatContext.js';
import { AIChatConversationsResource } from '../AIChatConversation/AIChatConversationsResource.js';
import classes from './AIChatMessageForm.module.css';

interface Props {
Expand All @@ -30,6 +32,7 @@ export const AIChatMessageForm = observer<PropsWithChildren<Props>>(function AIC
const notificationService = useService(NotificationService);
const aiChatMessageService = useService(AIChatMessageService);
const aiChatConversationsService = useService(AIChatConversationsService);
const aiChatConversationsResource = useService(AIChatConversationsResource);

const [value, setValue] = useState('');

Expand All @@ -54,6 +57,16 @@ export const AIChatMessageForm = observer<PropsWithChildren<Props>>(function AIC
}
}

async function cancel() {
if (currentConversationId) {
try {
await aiChatConversationsResource.cancelConversation(currentConversationId);
} catch (exception: any) {
notificationService.logException(exception, 'plugin_ai_chat_conversation_cancel_failed');
}
}
}

function getPlaceholder() {
const OS = getOS();
const symbol = OS === OperatingSystem.macOS ? '⌘' : 'Ctrl';
Expand All @@ -65,7 +78,12 @@ export const AIChatMessageForm = observer<PropsWithChildren<Props>>(function AIC

return (
<div className={s(styles, { container: true })}>
<Form className="tw:flex tw:items-end tw:gap-2" disableEnterSubmit={disabled} contents onSubmit={sendMessage}>
<Form
className="tw:flex tw:items-end tw:gap-2"
disableEnterSubmit={disabled}
contents
onSubmit={aiChatConversationsService.processing ? cancel : sendMessage}
>
<div className={s(styles, { textareaContainer: true })}>
<AutoResizeTextarea
className={s(styles, { textarea: true })}
Expand All @@ -74,7 +92,16 @@ export const AIChatMessageForm = observer<PropsWithChildren<Props>>(function AIC
autoFocus
onChange={v => setValue(v)}
/>
<ActionIconButton name="/icons/send.svg" disabled={disabled} img onClick={sendMessage} />
{!aiChatConversationsService.processing ? (
<ActionIconButton name="/icons/send.svg" disabled={disabled} img onClick={sendMessage} />
) : (
<Command
className="tw:cursor-pointer tw:w-10 tw:h-10 tw:bg-[var(--theme-primary)] tw:rounded-md tw:flex tw:items-center tw:justify-center tw:focus:opacity-80 tw:hover:opacity-80 tw:transition-opacity"
onClick={cancel}
>
<div className="tw:w-3 tw:h-3 tw:bg-[var(--theme-surface)] tw:rounded-xs" />
</Command>
Comment on lines +98 to +103

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.

Button?

)}
</div>
</Form>
{children}
Expand Down
2 changes: 2 additions & 0 deletions webapp/packages/plugin-ai-chat/src/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ export default [
['plugin_ai_chat_conversation_history_date_group_last_7_days', 'Last 7 days'],
['plugin_ai_chat_conversation_history_date_group_over_week_ago', 'Over a week ago'],

['plugin_ai_chat_conversation_cancel_failed', 'Failed to cancel the conversation'],

['plugin_ai_chat_scope_change', 'Configure AI context'],
['plugin_ai_chat_scope_change_fail', 'Failed to change context'],
['plugin_ai_chat_profile_group', 'Active configuration'],
Expand Down
2 changes: 2 additions & 0 deletions webapp/packages/plugin-ai-chat/src/locales/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ export default [
['plugin_ai_chat_conversation_history_date_group_last_7_days', 'Last 7 days'],
['plugin_ai_chat_conversation_history_date_group_over_week_ago', 'Over a week ago'],

['plugin_ai_chat_conversation_cancel_failed', 'Impossibile annullare la conversazione'],

['plugin_ai_chat_scope_change', 'Configure AI context'],
['plugin_ai_chat_scope_change_fail', 'Failed to change context'],
['plugin_ai_chat_profile_group', 'Active configuration'],
Expand Down
2 changes: 2 additions & 0 deletions webapp/packages/plugin-ai-chat/src/locales/ru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ export default [
['plugin_ai_chat_conversation_history_date_group_last_7_days', 'Последние 7 дней'],
['plugin_ai_chat_conversation_history_date_group_over_week_ago', 'Более недели назад'],

['plugin_ai_chat_conversation_cancel_failed', 'Не удалось отменить разговор'],

['plugin_ai_chat_scope_change', 'Настроить AI контекст'],
['plugin_ai_chat_scope_change_fail', 'Не удалось изменить контекст'],
['plugin_ai_chat_profile_group', 'Активная конфигурация'],
Expand Down
2 changes: 2 additions & 0 deletions webapp/packages/plugin-ai-chat/src/locales/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ export default [
['plugin_ai_chat_conversation_history_date_group_last_7_days', 'Last 7 days'],
['plugin_ai_chat_conversation_history_date_group_over_week_ago', 'Over a week ago'],

['plugin_ai_chat_conversation_cancel_failed', '无法取消对话'],

['plugin_ai_chat_scope_change', 'Configure AI context'],
['plugin_ai_chat_scope_change_fail', 'Failed to change context'],
['plugin_ai_chat_profile_group', 'Active configuration'],
Expand Down
Loading