Skip to content

Commit

Permalink
refactor: chat service & support sendReplyMessage api (#3539)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ricbet authored Apr 15, 2024
1 parent 83d9d25 commit 8938d69
Show file tree
Hide file tree
Showing 16 changed files with 210 additions and 142 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { localize } from '@opensumi/ide-core-browser';
import { Disposable, IAIReporter } from '@opensumi/ide-core-common';
import { ITerminalConnection, ITerminalController } from '@opensumi/ide-terminal-next';

import { ChatService } from '../chat/chat.service';
import { ChatInternalService } from '../chat/chat.internal.service';
import { MsgStreamManager } from '../model/msg-stream-manager';

import { AITerminalPrompt, SmartCommandDesc } from './component/terminal-command-suggest-controller';
Expand All @@ -27,8 +27,8 @@ export class PS1TerminalService extends Disposable {
@Autowired(ITerminalController)
private terminalController: ITerminalController;

@Autowired(ChatService)
private readonly aiChatService: ChatService;
@Autowired(ChatInternalService)
private readonly aiChatService: ChatInternalService;

@Autowired(MsgStreamManager)
private readonly msgStreamManager: MsgStreamManager;
Expand Down
12 changes: 7 additions & 5 deletions packages/ai-native/src/browser/chat/chat-agent.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Autowired, Injectable } from '@opensumi/di';
import {
CancellationToken,
ChatFeatureRegistryToken,
ChatServiceToken,
Disposable,
Emitter,
IDisposable,
Expand All @@ -12,7 +13,6 @@ import {
} from '@opensumi/ide-core-common';

import {
IAIChatService,
IChatAgent,
IChatAgentCommand,
IChatAgentMetadata,
Expand All @@ -21,13 +21,15 @@ import {
IChatAgentService,
IChatContent,
IChatFollowup,
IChatInternalService,
IChatMessage,
IChatMessageStructure,
IChatProgress,
} from '../../common';
import { IChatFeatureRegistry } from '../types';

import { ChatService } from './chat.service';
import { ChatService } from './chat.api.service';
import { ChatInternalService } from './chat.internal.service';

@Injectable()
export class ChatAgentService extends Disposable implements IChatAgentService {
Expand All @@ -44,8 +46,8 @@ export class ChatAgentService extends Disposable implements IChatAgentService {
@Autowired(ILogger)
logger: ILogger;

@Autowired(IAIChatService)
aiChatService: ChatService;
@Autowired(ChatServiceToken)
private aiChatService: ChatService;

@Autowired(ChatFeatureRegistryToken)
private readonly chatFeatureRegistry: IChatFeatureRegistry;
Expand Down Expand Up @@ -127,7 +129,7 @@ export class ChatAgentService extends Disposable implements IChatAgentService {
}

populateChatInput(id: string, message: IChatMessageStructure) {
this.aiChatService.launchChatMessage({
this.aiChatService.sendMessage({
...message,
agentId: id,
immediate: false,
Expand Down
29 changes: 29 additions & 0 deletions packages/ai-native/src/browser/chat/chat.api.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Autowired, Injectable } from '@opensumi/di';
import { Disposable, Emitter, Event } from '@opensumi/ide-core-common';

import { IChatInternalService, IChatMessageStructure } from '../../common';

import { ChatInternalService } from './chat.internal.service';

@Injectable()
export class ChatService extends Disposable {
@Autowired(IChatInternalService)
private chatInternalService: ChatInternalService;

private readonly _onChatMessageLaunch = new Emitter<IChatMessageStructure>();
public readonly onChatMessageLaunch: Event<IChatMessageStructure> = this._onChatMessageLaunch.event;

private readonly _onChatReplyMessageLaunch = new Emitter<string>();
public readonly onChatReplyMessageLaunch: Event<string> = this._onChatReplyMessageLaunch.event;

public sendMessage(data: IChatMessageStructure) {
this._onChatMessageLaunch.fire(data);
}

/**
* 主动以 ai role 的身份回复消息
*/
public sendReplyMessage(data: string) {
this._onChatReplyMessageLaunch.fire(data);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ import { MsgStreamManager } from '../model/msg-stream-manager';
import { ChatManagerService } from './chat-manager.service';
import { ChatModel, ChatRequestModel } from './chat-model';

/**
* @internal
*/
@Injectable()
export class ChatService extends Disposable {
export class ChatInternalService extends Disposable {
@Autowired(AIBackSerivcePath)
public aiBackService: IAIBackService;

Expand All @@ -36,9 +39,6 @@ export class ChatService extends Disposable {
@Autowired(IChatManagerService)
private chatManagerService: ChatManagerService;

private readonly _onChatMessageLaunch = new Emitter<IChatMessageStructure>();
public readonly onChatMessageLaunch: Event<IChatMessageStructure> = this._onChatMessageLaunch.event;

private readonly _onChangeSessionId = new Emitter<string>();
public readonly onChangeSessionId: Event<string> = this._onChangeSessionId.event;

Expand All @@ -57,10 +57,6 @@ export class ChatService extends Disposable {
this.#sessionModel = this.chatManagerService.startSession();
}

public launchChatMessage(data: IChatMessageStructure) {
this._onChatMessageLaunch.fire(data);
}

public cancelIndicatorChatView = new CancellationTokenSource();

public cancelChatViewToken() {
Expand Down
8 changes: 8 additions & 0 deletions packages/ai-native/src/browser/chat/chat.module.less
Original file line number Diff line number Diff line change
Expand Up @@ -336,3 +336,11 @@
}
}
}

.chat_with_more_actions {
&:hover {
div[class*='chat_msg_more_actions___'] {
display: flex;
}
}
}
161 changes: 98 additions & 63 deletions packages/ai-native/src/browser/chat/chat.view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
AISerivceType,
ChatFeatureRegistryToken,
ChatRenderRegistryToken,
ChatServiceToken,
Disposable,
IAIReporter,
localize,
uuid,
Expand All @@ -20,8 +22,8 @@ import { isMarkdownString } from '@opensumi/monaco-editor-core/esm/vs/base/commo
import 'react-chat-elements/dist/main.css';
import {
AI_CHAT_VIEW_ID,
IAIChatService,
IChatAgentService,
IChatInternalService,
IChatMessageStructure,
IChatReplyFollowup,
ISampleQuestions,
Expand All @@ -37,15 +39,16 @@ import { MessageData, createMessageByAI, createMessageByUser, extractIcon } from
import { EMsgStreamStatus, MsgStreamManager } from '../model/msg-stream-manager';
import { IChatSlashCommandHandler, TSlashCommandCustomRender } from '../types';

import { ChatService } from './chat.api.service';
import { ChatFeatureRegistry } from './chat.feature.registry';
import { ChatInternalService } from './chat.internal.service';
import styles from './chat.module.less';
import { ChatRenderRegistry } from './chat.render.registry';
import { ChatService } from './chat.service';

const SCROLL_CLASSNAME = 'chat_scroll';

const InitMsgComponent = () => {
const aiChatService = useInjectable<ChatService>(IAIChatService);
const aiChatService = useInjectable<ChatService>(ChatServiceToken);
const chatAgentService = useInjectable<IChatAgentService>(IChatAgentService);
const chatFeatureRegistry = useInjectable<ChatFeatureRegistry>(ChatFeatureRegistryToken);
const chatRenderRegistry = useInjectable<ChatRenderRegistry>(ChatRenderRegistryToken);
Expand Down Expand Up @@ -107,7 +110,7 @@ const InitMsgComponent = () => {
href='javascript:void(0)'
className={styles.link_item}
onClick={() => {
aiChatService.launchChatMessage(chatAgentService.parseMessage(data.message));
aiChatService.sendMessage(chatAgentService.parseMessage(data.message));
}}
>
{data.icon ? <Icon className={data.icon} style={{ color: 'inherit', marginRight: '4px' }} /> : ''}
Expand All @@ -131,7 +134,8 @@ const InitMsgComponent = () => {
};

export const AIChatView = observer(() => {
const aiChatService = useInjectable<ChatService>(IAIChatService);
const aiChatService = useInjectable<ChatInternalService>(IChatInternalService);
const chatApiService = useInjectable<ChatService>(ChatServiceToken);
const aiReporter = useInjectable<IAIReporter>(IAIReporter);
const msgStreamManager = useInjectable<MsgStreamManager>(MsgStreamManager);
const chatAgentService = useInjectable<IChatAgentService>(IChatAgentService);
Expand Down Expand Up @@ -177,6 +181,10 @@ export const AIChatView = observer(() => {
} else if (event === EMsgStreamStatus.THINKING) {
setLoading2(true);
}

requestAnimationFrame(() => {
scrollToBottom();
});
});
return () => {
msgStreamManager.dispose();
Expand Down Expand Up @@ -212,65 +220,94 @@ export const AIChatView = observer(() => {
}, [loading, loading2]);

React.useEffect(() => {
const dispose = msgStreamManager.onMsgStatus(() => {
requestAnimationFrame(() => {
scrollToBottom();
});
});
return () => dispose.dispose();
}, [msgStreamManager.onMsgStatus]);

React.useEffect(() => {
const dispose = aiChatService.onChatMessageLaunch(async (message) => {
if (message.immediate !== false) {
if (loading || loading2) {
return;
}
await handleSend(message);
} else {
if (message.agentId) {
setAgentId(message.agentId);
}
if (message.command) {
setCommand(message.command);
const disposer = new Disposable();

disposer.addDispose(
chatApiService.onChatMessageLaunch(async (message) => {
if (message.immediate !== false) {
if (loading || loading2) {
return;
}
await handleSend(message);
} else {
if (message.agentId) {
setAgentId(message.agentId);
}
if (message.command) {
setCommand(message.command);
}
chatInputRef?.current?.setInputValue(message.message);
}
chatInputRef?.current?.setInputValue(message.message);
}
});
return () => dispose.dispose();
}, [messageListData, loading, loading2]);
}),
);

React.useEffect(() => {
const disposer = chatAgentService.onDidSendMessage((chunk) => {
const relationId = aiReporter.start(AISerivceType.Agent, {
msgType: AISerivceType.Agent,
message: '',
});
disposer.addDispose(
chatApiService.onChatReplyMessageLaunch((chunk) => {
const userInput = {
type: AISerivceType.CustomReplay,
message: chunk,
};

const notifyMessage = createMessageByAI(
{
const relationId = aiReporter.start(AISerivceType.CustomReplay, {
msgType: userInput.type,
message: userInput.message,
});

let renderContent = <ChatMarkdown markdown={userInput.message} fillInIncompleteTokens />;

if (chatRenderRegistry.chatAIRoleRender) {
const ChatAIRoleRender = chatRenderRegistry.chatAIRoleRender;
renderContent = <ChatAIRoleRender content={userInput.message} status={EMsgStreamStatus.DONE} />;
}

const aiMessage = createMessageByAI({
id: uuid(6),
relationId,
text: <ChatNotify relationId={relationId} chunk={chunk} />,
},
styles.chat_notify,
);
dispatchMessage({ type: 'add', payload: [notifyMessage] });
requestAnimationFrame(() => {
scrollToBottom();
});
});
text: renderContent,
className: styles.chat_with_more_actions,
});

dispatchMessage({ type: 'add', payload: [aiMessage] });
}),
);

return () => disposer.dispose();
}, []);
}, [chatApiService, chatRenderRegistry.chatAIRoleRender]);

React.useEffect(() => {
const disposer = chatAgentService.onDidChangeAgents(async () => {
const newDefaultAgentId = chatAgentService.getDefaultAgentId();
const disposer = new Disposable();

disposer.addDispose(
chatAgentService.onDidSendMessage((chunk) => {
const relationId = aiReporter.start(AISerivceType.Agent, {
msgType: AISerivceType.Agent,
message: '',
});

const notifyMessage = createMessageByAI(
{
id: uuid(6),
relationId,
text: <ChatNotify relationId={relationId} chunk={chunk} />,
},
styles.chat_notify,
);
dispatchMessage({ type: 'add', payload: [notifyMessage] });
requestAnimationFrame(() => {
scrollToBottom();
});
}),
);

disposer.addDispose(
chatAgentService.onDidChangeAgents(async () => {
const newDefaultAgentId = chatAgentService.getDefaultAgentId();
setDefaultAgentId(newDefaultAgentId ?? '');
}),
);

setDefaultAgentId(newDefaultAgentId ?? '');
});
return () => disposer.dispose();
}, []);
}, [chatAgentService]);

const handleSlashCustomRender = React.useCallback(
async (value: { message: string; render: TSlashCommandCustomRender; relationId: string; startTime: number }) => {
Expand Down Expand Up @@ -413,14 +450,7 @@ export const AIChatView = observer(() => {

setLoading(true);

handleReply(userInput, {
aiChatService,
aiReporter,
chatAgentService,
relationId,
startTime,
rawMessage: message,
});
handleReply(userInput, { relationId });
},
[
messageListData,
Expand All @@ -439,7 +469,12 @@ export const AIChatView = observer(() => {
chatRenderRegistry.chatAIRoleRender!({ content, status });
}

const aiMessage = StreamReplyRender(userInput.message, replayCommandProps);
const aiMessage = createMessageByAI({
id: uuid(6),
relationId: replayCommandProps.relationId,
text: <StreamReplyRender prompt={userInput.message} params={replayCommandProps} />,
className: styles.chat_with_more_actions,
});

if (aiMessage) {
dispatchMessage({ type: 'add', payload: [aiMessage] });
Expand Down
Loading

0 comments on commit 8938d69

Please sign in to comment.