Skip to content

Commit 100dd1a

Browse files
authored
Merge pull request #113 from ant-xuexiao/feat_bot_builder_edit
feat: enabel to edit bot configuration information through dialogue
2 parents ac150e6 + b3b9059 commit 100dd1a

File tree

6 files changed

+178
-127
lines changed

6 files changed

+178
-127
lines changed

lui/src/Chat/index.tsx

Lines changed: 117 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -37,135 +37,135 @@ const Chat: FC<ChatProps> = memo(({ helloMessage, host, drawerWidth }) => {
3737
? `calc(${drawerWidth}px - 90px)`
3838
: '100%';
3939
return (
40-
<div className="petercat-lui">
41-
<div
42-
className="h-full w-full"
43-
style={{ backgroundColor: globalToken.chatBoxBackgroundColor }}
44-
>
45-
<ProChat
46-
showTitle
47-
chats={chats}
48-
onChatsChange={(chats) => {
49-
setChats(chats);
50-
}}
51-
chatRef={proChatRef}
52-
helloMessage={helloMessage || BOT_INFO.work_info.prologue}
53-
userMeta={{ title: 'User' }}
54-
chatItemRenderConfig={{
55-
avatarRender: (props: ChatItemProps) => {
56-
if (props.originData?.role === Role.user) {
57-
return <></>;
58-
}
59-
if (
60-
props.originData?.role === Role.tool ||
61-
props.originData?.role === Role.knowledge
62-
) {
63-
return <div className="w-[40px] h-[40px]" />;
64-
}
65-
},
66-
contentRender: (props: ChatItemProps, defaultDom: ReactNode) => {
67-
const originData = props.originData || {};
68-
if (originData?.role === Role.user) {
69-
return defaultDom;
70-
}
71-
const message = originData.content;
72-
const defaultMessageContent = (
73-
<div style={{ minWidth: messageMinWidth }}>{defaultDom}</div>
74-
);
75-
76-
if (!message || !message.includes('<TOOL>')) {
77-
return defaultMessageContent;
78-
}
79-
80-
const [toolStr, answerStr] = message.split('<ANSWER>');
81-
const tools = toolStr.split('\n').filter(Boolean);
82-
const lastTool = tools[tools.length - 1];
40+
<div className="petercat-lui" style={{ height: '100%' }}>
41+
<div
42+
className="h-full w-full"
43+
style={{ backgroundColor: globalToken.chatBoxBackgroundColor }}
44+
>
45+
<ProChat
46+
showTitle
47+
chats={chats}
48+
onChatsChange={(chats) => {
49+
setChats(chats);
50+
}}
51+
chatRef={proChatRef}
52+
helloMessage={helloMessage || BOT_INFO.work_info.prologue}
53+
userMeta={{ title: 'User' }}
54+
chatItemRenderConfig={{
55+
avatarRender: (props: ChatItemProps) => {
56+
if (props.originData?.role === Role.user) {
57+
return <></>;
58+
}
59+
if (
60+
props.originData?.role === Role.tool ||
61+
props.originData?.role === Role.knowledge
62+
) {
63+
return <div className="w-[40px] h-[40px]" />;
64+
}
65+
},
66+
contentRender: (props: ChatItemProps, defaultDom: ReactNode) => {
67+
const originData = props.originData || {};
68+
if (originData?.role === Role.user) {
69+
return defaultDom;
70+
}
71+
const message = originData.content;
72+
const defaultMessageContent = (
73+
<div style={{ minWidth: messageMinWidth }}>{defaultDom}</div>
74+
);
8375

84-
const regex = /<TOOL>(.*)/;
85-
const match = lastTool.match(regex);
76+
if (!message || !message.includes('<TOOL>')) {
77+
return defaultMessageContent;
78+
}
8679

87-
if (!match) {
88-
console.error('No valid JSON found in input');
89-
return defaultMessageContent;
90-
}
80+
const [toolStr, answerStr] = message.split('<ANSWER>');
81+
const tools = toolStr.split('\n').filter(Boolean);
82+
const lastTool = tools[tools.length - 1];
9183

92-
try {
93-
const config = JSON.parse(match[1]);
94-
const { type, extra } = config;
84+
const regex = /<TOOL>(.*)/;
85+
const match = lastTool.match(regex);
9586

96-
if (![Role.knowledge, Role.tool].includes(type)) {
87+
if (!match) {
88+
console.error('No valid JSON found in input');
9789
return defaultMessageContent;
9890
}
9991

100-
const { status, source } = extra;
92+
try {
93+
const config = JSON.parse(match[1]);
94+
const { type, extra } = config;
10195

102-
return (
103-
<div
104-
className="p-2 bg-white rounded-md "
105-
style={{ minWidth: messageMinWidth }}
106-
>
107-
<div className="mb-1">
108-
<ThoughtChain
109-
content={extra}
110-
status={status}
111-
source={source}
112-
/>
96+
if (![Role.knowledge, Role.tool].includes(type)) {
97+
return defaultMessageContent;
98+
}
99+
100+
const { status, source } = extra;
101+
102+
return (
103+
<div
104+
className="p-2 bg-white rounded-md "
105+
style={{ minWidth: messageMinWidth }}
106+
>
107+
<div className="mb-1">
108+
<ThoughtChain
109+
content={extra}
110+
status={status}
111+
source={source}
112+
/>
113+
</div>
114+
<Markdown style={{ overflowX: 'hidden', overflowY: 'auto' }}>
115+
{answerStr}
116+
</Markdown>
113117
</div>
114-
<Markdown style={{ overflowX: 'hidden', overflowY: 'auto' }}>
115-
{answerStr}
116-
</Markdown>
117-
</div>
118+
);
119+
} catch (error) {
120+
console.error(`JSON parse error: ${error}`);
121+
return defaultMessageContent;
122+
}
123+
},
124+
}}
125+
assistantMeta={{
126+
avatar: BOT_INFO.avatar,
127+
title: BOT_INFO.resourceName,
128+
}}
129+
autocompleteRequest={async (value) => {
130+
if (value === '/') {
131+
return BOT_INFO.work_info.suggested_questions.map(
132+
(prompt: string) => ({
133+
value: prompt,
134+
label: prompt,
135+
}),
118136
);
119-
} catch (error) {
120-
console.error(`JSON parse error: ${error}`);
121-
return defaultMessageContent;
122137
}
123-
},
124-
}}
125-
assistantMeta={{
126-
avatar: BOT_INFO.avatar,
127-
title: BOT_INFO.resourceName,
128-
}}
129-
autocompleteRequest={async (value) => {
130-
if (value === '/') {
131-
return BOT_INFO.work_info.suggested_questions.map(
132-
(prompt: string) => ({
133-
value: prompt,
134-
label: prompt,
135-
}),
136-
);
137-
}
138-
return [];
139-
}}
140-
request={async (messages) => {
141-
const newMessages = messages
142-
.filter(
143-
(item) => item.role !== Role.tool && item.role !== Role.knowledge,
144-
)
145-
.map((message) => ({
146-
role: message.role,
147-
content: message.content as string,
148-
}));
138+
return [];
139+
}}
140+
request={async (messages) => {
141+
const newMessages = messages
142+
.filter(
143+
(item) => item.role !== Role.tool && item.role !== Role.knowledge,
144+
)
145+
.map((message) => ({
146+
role: message.role,
147+
content: message.content as string,
148+
}));
149149

150-
const response = await streamChat(newMessages, host);
151-
return handleStream(response);
152-
}}
153-
inputAreaProps={{ className: 'userInputBox h-24 !important' }}
154-
actions={{
155-
render: () => [
156-
<StopBtn
157-
key="StopBtn"
158-
visible={!!proChatRef?.current?.getChatLoadingId()}
159-
action={() => proChatRef?.current?.stopGenerateMessage()}
160-
/>,
161-
<Actions key="Actions"></Actions>,
162-
],
163-
flexConfig: {
164-
gap: 24,
165-
direction: 'vertical',
166-
justify: 'space-between',
167-
},
168-
}}
150+
const response = await streamChat(newMessages, host);
151+
return handleStream(response);
152+
}}
153+
inputAreaProps={{ className: 'userInputBox h-24 !important' }}
154+
actions={{
155+
render: () => [
156+
<StopBtn
157+
key="StopBtn"
158+
visible={!!proChatRef?.current?.getChatLoadingId()}
159+
action={() => proChatRef?.current?.stopGenerateMessage()}
160+
/>,
161+
<Actions key="Actions"></Actions>,
162+
],
163+
flexConfig: {
164+
gap: 24,
165+
direction: 'vertical',
166+
justify: 'space-between',
167+
},
168+
}}
169169
/>
170170
</div>
171171
</div>

server/agent/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def init_tavily_tools(self):
5151
return [tavily_tool]
5252

5353
def _create_agent_with_tools(self) -> AgentExecutor:
54-
llm = ChatOpenAI(model="gpt-4-1106-preview", temperature=self.temperature, streaming=True, max_tokens=self.max_tokens, openai_api_key=OPEN_API_KEY)
54+
llm = ChatOpenAI(model="gpt-4-turbo", temperature=self.temperature, streaming=True, max_tokens=self.max_tokens, openai_api_key=OPEN_API_KEY)
5555

5656
tools = self.init_tavily_tools() if self.enable_tavily else []
5757
for tool in self.tools.values():

server/agent/bot_builder.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
- 使用 bot_builder 工具根据用户提供的 Github 仓库名创建机器人。
1717
1818
### 技能3:修改机器人的配置
19-
- 根据用户的描述进行机器人的配置信息修改。
19+
- 根据用户的描述使用 bot_builder 工具进行机器人的配置信息修改。每次使用该工具,请默认创建机器人获得的 id 作为编辑的该机器人 id
20+
- 如果用户想要修改头像,请回答暂不支持改头像能力,敬请期待
2021
2122
## 限制
2223
- 只能基于用户提供的Github仓库信息创建答疑机器人。
@@ -27,6 +28,7 @@
2728

2829
TOOL_MAPPING = {
2930
"bot_builder": bot_builder.create_bot,
31+
"edit_bot": bot_builder.edit_bot,
3032
}
3133

3234
def agent_chat(input_data: ChatData) -> AsyncIterator[str]:

server/rag/retrieval.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import json
2-
from typing import Optional
32
from langchain_openai import OpenAIEmbeddings
43
from langchain_community.vectorstores import SupabaseVectorStore
54
from db.supabase.client import get_client

server/routers/rag.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
from typing import Optional
21
from fastapi import APIRouter, Depends
32
from rag import retrieval
4-
from data_class import GitDocConfig, GitIssueConfig, S3Config
3+
from data_class import GitDocConfig, GitIssueConfig
54
from verify.rate_limit import verify_rate_limit
65

76
router = APIRouter(
@@ -11,17 +10,17 @@
1110
)
1211

1312

14-
@router.post("/rag/add_knowledge_by_doc")
13+
@router.post("/rag/add_knowledge_by_doc", dependencies=[Depends(verify_rate_limit)])
1514
def add_knowledge_by_doc(config: GitDocConfig):
1615
data=retrieval.add_knowledge_by_doc(config)
1716
return data
1817

19-
@router.post("/rag/add_knowledge_by_issues")
18+
@router.post("/rag/add_knowledge_by_issues", dependencies=[Depends(verify_rate_limit)])
2019
def add_knowledge_by_issues(config: GitIssueConfig):
2120
data=retrieval.add_knowledge_by_issues(config)
2221
return data
2322

24-
@router.post("/rag/search_knowledge")
23+
@router.post("/rag/search_knowledge", dependencies=[Depends(verify_rate_limit)])
2524
def search_knowledge(query: str):
2625
data=retrieval.search_knowledge(query)
2726
return data

server/tools/bot_builder.py

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from typing import Optional
12
from langchain.tools import tool
23
from github import Github
34
from db.supabase.client import get_client
@@ -32,15 +33,65 @@ def create_bot(
3233
"uid": "u123456", # TODO get from auth
3334
"enable_img_generation": False,
3435
"label": "Assistant",
35-
"starters": starters,
36+
"starters": starters if isinstance(starters, list) and starters else ["介绍一下项目", "快速上手", "贡献指南"],
3637
"enable_img_generation": False,
3738
"public": False,
3839
}
39-
print('bot_data', bot_data)
40+
4041
supabase = get_client()
4142
response = supabase.table("bots").insert(bot_data).execute()
43+
print('bot_data_response', response)
4244
return response
4345
except Exception as e:
4446
print(f"An error occurred: {e}")
4547
return e
4648

49+
@tool
50+
def edit_bot(
51+
id: str,
52+
name: Optional[str] = None,
53+
description : Optional[str] = None,
54+
prompt: Optional[str] = None,
55+
starters: Optional[list[str]] = None,
56+
):
57+
"""
58+
edit a bot based on the given params
59+
60+
:param id: The id of the bot, e.g., "1234567890"
61+
:param name: The name of the bot, e.g., "Ant Design"
62+
:param description: The description of the bot, e.g., "A design system for enterprise-level products."
63+
:param prompt: The prompt of the bot, e.g., "You are an AI assistant designed to help users with their needs. You can answer questions about Ant Design, provide design resources, and offer advice on how to use Ant Design in your projects. Your responses should be clear, concise, and helpful."
64+
:param starters: The Opening Dialog, e.g., ["介绍一下项目", "快速上手", "贡献指南"]
65+
"""
66+
try:
67+
# Step1: Get the bot object
68+
supabase = get_client()
69+
bot = supabase.table("bots").select("*").eq("id", id).execute().data[0]
70+
if not bot:
71+
raise ValueError(f"Bot with ID {id} not found.")
72+
73+
# Step2: Update the bot data
74+
uid = "u123456" # TODO get from auth
75+
bot_data = {
76+
"name": name if name else bot["name"],
77+
"description": description if description else bot["description"],
78+
"avatar": bot["avatar"],
79+
"prompt": prompt if prompt else bot["prompt"],
80+
"uid": uid,
81+
"enable_img_generation": False,
82+
"label": "Assistant",
83+
"starters": starters if isinstance(starters, list) and starters else bot["starters"],
84+
"enable_img_generation": False,
85+
"public": False,
86+
}
87+
88+
89+
# Step3: Save the changes to the database
90+
response = supabase.table("bots").update(bot_data).eq("id", id).eq("uid", uid).execute()
91+
return response
92+
except Exception as e:
93+
print(f"An error occurred: {e}")
94+
return e
95+
96+
97+

0 commit comments

Comments
 (0)