Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: improve chat prompt #3191

Merged
merged 1 commit into from
Nov 19, 2023
Merged
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
22 changes: 0 additions & 22 deletions packages/ai-native/src/browser/ai-chat.module.less
Original file line number Diff line number Diff line change
Expand Up @@ -289,26 +289,4 @@
}
}
}

// 文档搜索样式
.ai_chat_search_container {
display: flex;
flex-direction: column;
white-space: pre-wrap;

p {
margin-bottom: 0;
}

sup {
top: 0;

.link_block {
color: #669ced;
border-radius: 4px;
background-color: #243753;
margin: 0 2px;
}
}
}
}
35 changes: 30 additions & 5 deletions packages/ai-native/src/browser/ai-chat.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,28 @@ export class AiChatService extends Disposable {
return { type, message };
}

if (input.startsWith(InstructionEnum.aiTestKey)) {
type = AISerivceType.Test;
message = input.split(InstructionEnum.aiTestKey)[1];

if (!prompt) {
prompt = this.generateTestCodePrompt(message);
}

return { type, message: prompt };
}

if (input.startsWith(InstructionEnum.aiOptimzeKey)) {
type = AISerivceType.Optimize;
message = input.split(InstructionEnum.aiOptimzeKey)[1];

if (!prompt) {
prompt = this.optimzeCodePrompt(message);
}

return { type, message: prompt };
}

if (input.startsWith(InstructionEnum.aiExplainKey)) {
type = AISerivceType.Explain;
message = input.split(InstructionEnum.aiExplainKey)[1];
Expand Down Expand Up @@ -131,6 +153,14 @@ export class AiChatService extends Disposable {
return { type, message };
}

public generateTestCodePrompt(message = ''): string {
return `为以下代码写单测:\n\`\`\`\n ${message}\n\`\`\``;
}

public optimzeCodePrompt(message = ''): string {
return `优化以下代码:\n\`\`\`\n ${message}\`\`\``;
}

// 解释当前文件的代码或者选中的某个代码片段的 prompt,也可以用于对选中的代码加上用户的描述进行解释
public explainCodePrompt(message = ''): string {
if (!this.currentEditor) {
Expand Down Expand Up @@ -179,11 +209,6 @@ export class AiChatService extends Disposable {
return messageWithPrompt;
}

public opensumiRolePrompt(message: string): string {
const withPrompt = message;
return withPrompt;
}

/**
* by backend service
* @param message
Expand Down
105 changes: 59 additions & 46 deletions packages/ai-native/src/browser/ai-chat.view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { AiSumiService } from './ai-sumi/sumi.service';
import { NOTFOUND_COMMAND, ERROR_RESPONSE, NOTFOUND_COMMAND_TIP } from './common-reponse';
import { CodeBlockWrapper, CodeBlockWrapperInput } from './components/ChatEditor';
import { ChatInput } from './components/ChatInput';
import { ChatMarkdown } from './components/ChatMarkdown';
import { ChatMoreActions } from './components/ChatMoreActions';
import { AILogoAvatar, EnhanceIcon } from './components/Icon';
import { LineVertical } from './components/lineVertical';
Expand Down Expand Up @@ -213,21 +214,25 @@ export const AiChatView = observer(() => {
async (userInput: { type: AISerivceType; message: string }, replayCommandProps: ReplayComponentParam) => {
let aiMessage;

if (userInput!.type === AISerivceType.SearchDoc) {
if (userInput.type === AISerivceType.SearchDoc) {
aiMessage = await AISearch(userInput.message, userInput.type, replayCommandProps);
} else if (userInput!.type === AISerivceType.SearchCode) {
} else if (userInput.type === AISerivceType.SearchCode) {
aiMessage = await AISearch(userInput.message, userInput.type, replayCommandProps);
} else if (userInput!.type === AISerivceType.Sumi) {
aiMessage = await aiSumiService.searchCommand(userInput!.message!);
aiMessage = await AIWithCommandReply(userInput.message, aiMessage, opener, replayCommandProps, async () => handleCommonRetry(userInput, replayCommandProps));
} else if (userInput!.type === AISerivceType.GPT) {
const withPrompt = aiChatService.opensumiRolePrompt(userInput!.message!);
aiMessage = await AIStreamReply(withPrompt, replayCommandProps);
} else if (userInput!.type === AISerivceType.Explain) {
aiMessage = await AIStreamReply(userInput!.message!, replayCommandProps);
} else if (userInput!.type === AISerivceType.Run) {
} else if (userInput.type === AISerivceType.Sumi) {
aiMessage = await aiSumiService.searchCommand(userInput.message);
aiMessage = await AIWithCommandReply(userInput.message, aiMessage, opener, replayCommandProps, async () =>
handleCommonRetry(userInput, replayCommandProps),
);
} else if (
userInput.type === AISerivceType.GPT ||
userInput.type === AISerivceType.Explain ||
userInput.type === AISerivceType.Optimize ||
userInput.type === AISerivceType.Test
) {
aiMessage = await AIStreamReply(userInput.message, replayCommandProps);
} else if (userInput.type === AISerivceType.Run) {
aiMessage = await aiRunService.requestBackService(
userInput!.message!,
userInput.message,
aiChatService.cancelIndicatorChatView.token,
);
aiMessage = await AIChatRunReply(aiMessage, replayCommandProps);
Expand All @@ -243,16 +248,21 @@ export const AiChatView = observer(() => {
}

setLoading(false);
}, [messageListData]);
},
[messageListData],
);

const handleCommonRetry = React.useCallback(async (userInput: { type: AISerivceType; message: string }, replayCommandProps: ReplayComponentParam) => {
setLoading(true);
messageListData.pop();
setMessageListData([...messageListData]);
const handleCommonRetry = React.useCallback(
async (userInput: { type: AISerivceType; message: string }, replayCommandProps: ReplayComponentParam) => {
setLoading(true);
messageListData.pop();
setMessageListData([...messageListData]);

const startTime = +new Date();
await handleReply(userInput, { ...replayCommandProps, startTime, isRetry: true });
}, [messageListData]);
const startTime = +new Date();
await handleReply(userInput, { ...replayCommandProps, startTime, isRetry: true });
},
[messageListData],
);

const handleClear = React.useCallback(() => {
aiChatService.cancelChatViewToken();
Expand Down Expand Up @@ -405,16 +415,6 @@ const AiReply = ({ text, endNode = <></>, immediately = false }) => {
);
};

const renderSearchLinkBlock = new (class extends DefaultMarkedRenderer {
link(href: string | null, title: string | null, text: string): string {
return `<a class="${styles.link_block}" rel="noopener" target="_blank" href="${href}" target="${href}" title="${
title ?? href
}">${text}</a>`;
}
})();

const renderMarkdown = (content: string) => <Markdown value={content} renderer={renderSearchLinkBlock}></Markdown>;

const AISearch = async (
input: string,
type: AISerivceType.SearchDoc | AISerivceType.SearchCode,
Expand Down Expand Up @@ -446,11 +446,7 @@ const AISearch = async (
renderContent={(content) => (
<div className={styles.ai_chat_search_container}>
<div className={styles.ai_response_text}>
{type === AISerivceType.SearchDoc ? (
renderMarkdown(content)
) : (
<CodeBlockWrapper text={content} renderText={(text) => renderMarkdown(text)} />
)}
<CodeBlockWrapper text={content} renderText={(text) => <ChatMarkdown content={text} />} />
</div>
</div>
)}
Expand Down Expand Up @@ -551,31 +547,48 @@ const AIWithCommandReply = async (
commandRes: IAiBackServiceResponse<Command>,
opener: CommandOpener,
params: ReplayComponentParam,
onRetry: () => Promise<void>
onRetry: () => Promise<void>,
) => {
const { aiChatService, aiReporter, relationId, startTime, isRetry } = params;

aiChatService.setLatestSessionId(relationId);

const failedText = commandRes.errorCode ? ERROR_RESPONSE : !commandRes.data ? NOTFOUND_COMMAND : '';

aiReporter.end(relationId, { replytime: +new Date() - startTime, success: !!failedText, msgType: AISerivceType.Sumi, isRetry });
aiReporter.end(relationId, {
replytime: +new Date() - startTime,
success: !!failedText,
msgType: AISerivceType.Sumi,
isRetry,
});

if (failedText) {
return createMessageByAI({
id: uuid(6),
relationId,
text: (
<ChatMoreActions sessionId={relationId} onRetry={onRetry}>
{
failedText === NOTFOUND_COMMAND ? (
<div>
<p>{failedText}</p>
<p>{NOTFOUND_COMMAND_TIP}</p>
<Button onClick={() => opener.open(URI.from({ scheme: 'command', path: QUICK_OPEN_COMMANDS.OPEN.id, query: JSON.stringify([userInput]) }))}>打开命令面板</Button>
</div>
) : failedText
}
{failedText === NOTFOUND_COMMAND ? (
<div>
<p>{failedText}</p>
<p>{NOTFOUND_COMMAND_TIP}</p>
<Button
onClick={() =>
opener.open(
URI.from({
scheme: 'command',
path: QUICK_OPEN_COMMANDS.OPEN.id,
query: JSON.stringify([userInput]),
}),
)
}
>
打开命令面板
</Button>
</div>
) : (
failedText
)}
</ChatMoreActions>
),
});
Expand Down
7 changes: 4 additions & 3 deletions packages/ai-native/src/browser/ai-editor.contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ export class AiEditorContribution extends Disposable implements IEditorFeatureCo
if (value === EInlineOperation.Comments) {
prompt = `为以下代码添加注释: \`\`\`\n ${crossCode}\`\`\`。要求只返回代码结果,不需要解释`;
} else if (value === EInlineOperation.Optimize) {
prompt = `优化以下代码: \`\`\`\n ${crossCode}\`\`\`。要求只返回代码结果,不需要解释`;
prompt = this.aiChatService.optimzeCodePrompt(crossCode);
}

const relationId = this.aiReporter.start(value, { message: prompt });
Expand Down Expand Up @@ -362,7 +362,7 @@ export class AiEditorContribution extends Disposable implements IEditorFeatureCo
if (value === EInlineOperation.Test) {
const selectionValue = model.getValueInRange(crossSelection);

const prompt = `为以下代码写单测:\n\`\`\`${model.getLanguageId()}\n${selectionValue}\n\`\`\``;
const prompt = this.aiChatService.generateTestCodePrompt(selectionValue);

this.aiChatService.launchChatMessage({
message: prompt,
Expand Down Expand Up @@ -419,7 +419,8 @@ export class AiEditorContribution extends Disposable implements IEditorFeatureCo
}

dispose = monaco.languages.registerInlineCompletionsProvider(model.getLanguageId(), {
provideInlineCompletions: async (model, position, context, token) => inlineCompleteProvider.provideInlineCompletionItems(model, position, context, token),
provideInlineCompletions: async (model, position, context, token) =>
inlineCompleteProvider.provideInlineCompletionItems(model, position, context, token),
freeInlineCompletions(completions: InlineCompletions<InlineCompletion>) {},
});
this.disposables.push(dispose);
Expand Down
19 changes: 19 additions & 0 deletions packages/ai-native/src/browser/components/ChatMarkdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';

import { DefaultMarkedRenderer, Markdown } from '@opensumi/ide-components/lib/markdown';

import * as styles from './components.module.less';

const renderSearchLinkBlock = new (class extends DefaultMarkedRenderer {
link(href: string | null, title: string | null, text: string): string {
return `<a class="${styles.link_block}" rel="noopener" target="_blank" href="${href}" target="${href}" title="${
title ?? href
}">${text}</a>`;
}
})();

export const ChatMarkdown = ({ content }: { content: string }) => (
<div className={styles.ai_chat_markdown_container}>
<Markdown value={content} renderer={renderSearchLinkBlock}></Markdown>
</div>
);
Loading
Loading