mirror of
https://github.com/csunny/DB-GPT.git
synced 2025-09-07 20:10:08 +00:00
feat: (0.6)New UI (#1855)
Co-authored-by: 夏姜 <wenfengjiang.jwf@digital-engine.com> Co-authored-by: aries_ckt <916701291@qq.com> Co-authored-by: wb-lh513319 <wb-lh513319@alibaba-inc.com> Co-authored-by: csunny <cfqsunny@163.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
"""Base memory interface for agents."""
|
||||
"""Gpts memory define."""
|
||||
from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
from abc import ABC, abstractmethod
|
||||
@@ -58,11 +59,15 @@ class GptsMessage:
|
||||
role: str
|
||||
content: str
|
||||
rounds: Optional[int]
|
||||
is_success: bool = True
|
||||
app_code: Optional[str] = None
|
||||
app_name: Optional[str] = None
|
||||
current_goal: Optional[str] = None
|
||||
context: Optional[str] = None
|
||||
review_info: Optional[str] = None
|
||||
action_report: Optional[str] = None
|
||||
model_name: Optional[str] = None
|
||||
resource_info: Optional[str] = None
|
||||
created_at: datetime = dataclasses.field(default_factory=datetime.utcnow)
|
||||
updated_at: datetime = dataclasses.field(default_factory=datetime.utcnow)
|
||||
|
||||
@@ -76,11 +81,15 @@ class GptsMessage:
|
||||
role=d["role"],
|
||||
content=d["content"],
|
||||
rounds=d["rounds"],
|
||||
is_success=d["is_success"],
|
||||
app_code=d["app_code"],
|
||||
app_name=d["app_name"],
|
||||
model_name=d["model_name"],
|
||||
current_goal=d["current_goal"],
|
||||
context=d["context"],
|
||||
review_info=d["review_info"],
|
||||
action_report=d["action_report"],
|
||||
resource_info=d["resource_info"],
|
||||
created_at=d["created_at"],
|
||||
updated_at=d["updated_at"],
|
||||
)
|
||||
|
@@ -1,17 +1,23 @@
|
||||
"""GPTs memory."""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
from collections import OrderedDict, defaultdict
|
||||
from typing import Dict, List, Optional
|
||||
import logging
|
||||
from asyncio import Queue
|
||||
from collections import defaultdict
|
||||
from typing import Dict, List, Optional, Union
|
||||
|
||||
from dbgpt.vis.client import VisAgentMessages, VisAgentPlans, vis_client
|
||||
from dbgpt.vis.client import VisAgentMessages, VisAgentPlans, VisAppLink, vis_client
|
||||
|
||||
from ...action.base import ActionOutput
|
||||
from ...schema import Status
|
||||
from .base import GptsMessage, GptsMessageMemory, GptsPlansMemory
|
||||
from .default_gpts_memory import DefaultGptsMessageMemory, DefaultGptsPlansMemory
|
||||
|
||||
NONE_GOAL_PREFIX: str = "none_goal_count_"
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class GptsMemory:
|
||||
"""GPTs memory."""
|
||||
@@ -29,6 +35,11 @@ class GptsMemory:
|
||||
message_memory if message_memory is not None else DefaultGptsMessageMemory()
|
||||
)
|
||||
|
||||
self.messages_cache: defaultdict = defaultdict(List[GptsMessage])
|
||||
self.channels: defaultdict = defaultdict(Queue)
|
||||
self.enable_vis_map: defaultdict = defaultdict(bool)
|
||||
self.start_round_map: defaultdict = defaultdict(int)
|
||||
|
||||
@property
|
||||
def plans_memory(self) -> GptsPlansMemory:
|
||||
"""Return the plans memory."""
|
||||
@@ -39,41 +50,191 @@ class GptsMemory:
|
||||
"""Return the message memory."""
|
||||
return self._message_memory
|
||||
|
||||
async def _message_group_vis_build(self, message_group):
|
||||
if not message_group:
|
||||
return ""
|
||||
def init(
|
||||
self,
|
||||
conv_id: str,
|
||||
enable_vis_message: bool = True,
|
||||
history_messages: Optional[List[GptsMessage]] = None,
|
||||
start_round: int = 0,
|
||||
):
|
||||
"""Gpt memory init."""
|
||||
self.channels[conv_id] = asyncio.Queue()
|
||||
self.enable_vis_map[conv_id] = enable_vis_message
|
||||
self.messages_cache[conv_id] = history_messages if history_messages else []
|
||||
self.start_round_map[conv_id] = start_round
|
||||
|
||||
def enable_vis_message(self, conv_id):
|
||||
"""Enable conversation message vis tag."""
|
||||
return self.enable_vis_map[conv_id] if conv_id in self.enable_vis_map else True
|
||||
|
||||
def queue(self, conv_id: str):
|
||||
"""Get conversation message queue."""
|
||||
return self.channels[conv_id] if conv_id in self.channels else None
|
||||
|
||||
def clear(self, conv_id: str):
|
||||
"""Clear gpt memory."""
|
||||
# clear last message queue
|
||||
queue = self.channels.pop(conv_id) # noqa
|
||||
del queue
|
||||
# clear messages cache'
|
||||
if self.messages_cache.get(conv_id):
|
||||
cache = self.messages_cache.pop(conv_id) # noqa
|
||||
del cache
|
||||
|
||||
# clear vis_enable_tag
|
||||
vis_enable_tag = self.enable_vis_map.pop(conv_id) # noqa
|
||||
del vis_enable_tag
|
||||
|
||||
# clear start_roun
|
||||
start_round = self.start_round_map.pop(conv_id) # noqa
|
||||
del start_round
|
||||
|
||||
async def push_message(self, conv_id: str, temp_msg: Optional[str] = None):
|
||||
"""Push conversation message."""
|
||||
queue = self.queue(conv_id)
|
||||
enable_vis_tag = self.enable_vis_message(conv_id=conv_id)
|
||||
if enable_vis_tag:
|
||||
# 如果有临时消息内容需要push 拼接再最末尾,否则直接从短期记忆中发布最后消息
|
||||
message_view = await self.app_link_chat_message(conv_id)
|
||||
if temp_msg:
|
||||
temp_view = await self.agent_stream_message(temp_msg)
|
||||
message_view = message_view + "\n" + temp_view
|
||||
await queue.put(message_view)
|
||||
|
||||
else:
|
||||
# 非VIS消息模式,直接推送简单消息列表即可,不做任何处理
|
||||
message_views = await self.simple_message(conv_id)
|
||||
if temp_msg:
|
||||
temp_view = await self.agent_stream_message(temp_msg, False)
|
||||
if temp_view and len(temp_view) > 0:
|
||||
message_views.extend(temp_view)
|
||||
await queue.put(message_views)
|
||||
|
||||
async def complete(self, conv_id: str):
|
||||
"""Complete conversation message."""
|
||||
queue = self.queue(conv_id)
|
||||
|
||||
await queue.put("[DONE]")
|
||||
|
||||
async def append_message(self, conv_id: str, message: GptsMessage):
|
||||
"""Append message."""
|
||||
# 中期记忆
|
||||
self.messages_cache[conv_id].append(message)
|
||||
# 长期记忆
|
||||
self.message_memory.append(message)
|
||||
|
||||
# 消息记忆后发布消息
|
||||
await self.push_message(conv_id)
|
||||
|
||||
async def get_messages(self, conv_id: str) -> List[GptsMessage]:
|
||||
"""Get conversation message."""
|
||||
return self.messages_cache[conv_id]
|
||||
|
||||
async def get_agent_messages(
|
||||
self, conv_id: str, agent_role: str
|
||||
) -> List[GptsMessage]:
|
||||
"""Get agent messages."""
|
||||
gpt_messages = self.messages_cache[conv_id]
|
||||
result = []
|
||||
for gpt_message in gpt_messages:
|
||||
if gpt_message.sender == agent_role or gpt_messages.receiver == agent_role:
|
||||
result.append(gpt_message)
|
||||
return result
|
||||
|
||||
async def get_agent_history_memory(self, conv_id: str, agent_role: str) -> List:
|
||||
"""Get agent history memory."""
|
||||
gpt_messages = self.messages_cache[conv_id]
|
||||
|
||||
agent_messages = []
|
||||
for gpt_message in gpt_messages:
|
||||
if gpt_message.sender == agent_role or gpt_message.receiver == agent_role:
|
||||
agent_messages.append(gpt_message)
|
||||
|
||||
new_list = [
|
||||
{
|
||||
"question": agent_messages[i].content,
|
||||
"ai_message": agent_messages[i + 1].content,
|
||||
"action_output": ActionOutput.from_dict(
|
||||
json.loads(agent_messages[i + 1].action_report)
|
||||
),
|
||||
"check_pass": agent_messages[i + 1].is_success,
|
||||
}
|
||||
for i in range(0, len(agent_messages), 2)
|
||||
]
|
||||
|
||||
return new_list
|
||||
|
||||
async def _message_group_vis_build(self, message_group, vis_items: list):
|
||||
num: int = 0
|
||||
last_goal = next(reversed(message_group))
|
||||
last_goal_messages = message_group[last_goal]
|
||||
if message_group:
|
||||
last_goal = next(reversed(message_group))
|
||||
last_goal_message = None
|
||||
if not last_goal.startswith(NONE_GOAL_PREFIX):
|
||||
last_goal_messages = message_group[last_goal]
|
||||
last_goal_message = last_goal_messages[-1]
|
||||
|
||||
last_goal_message = last_goal_messages[-1]
|
||||
vis_items = []
|
||||
plan_temps: List[dict] = []
|
||||
need_show_singe_last_message = False
|
||||
for key, value in message_group.items():
|
||||
num = num + 1
|
||||
if key.startswith(NONE_GOAL_PREFIX):
|
||||
vis_items.append(await self._messages_to_plan_vis(plan_temps))
|
||||
plan_temps = []
|
||||
num = 0
|
||||
vis_items.append(await self._messages_to_agents_vis(value))
|
||||
else:
|
||||
num += 1
|
||||
plan_temps.append(
|
||||
{
|
||||
"name": key,
|
||||
"num": num,
|
||||
"status": "complete",
|
||||
"agent": value[0].receiver if value else "",
|
||||
"markdown": await self._messages_to_agents_vis(value),
|
||||
}
|
||||
)
|
||||
need_show_singe_last_message = True
|
||||
|
||||
plan_temps = []
|
||||
for key, value in message_group.items():
|
||||
num = num + 1
|
||||
if key.startswith(NONE_GOAL_PREFIX):
|
||||
if len(plan_temps) > 0:
|
||||
vis_items.append(await self._messages_to_plan_vis(plan_temps))
|
||||
plan_temps = []
|
||||
num = 0
|
||||
vis_items.append(await self._messages_to_agents_vis(value))
|
||||
else:
|
||||
num += 1
|
||||
plan_temps.append(
|
||||
{
|
||||
"name": key,
|
||||
"num": num,
|
||||
"status": "complete",
|
||||
"agent": value[0].receiver if value else "",
|
||||
"markdown": await self._messages_to_agents_vis(value),
|
||||
}
|
||||
if need_show_singe_last_message and last_goal_message:
|
||||
vis_items.append(
|
||||
await self._messages_to_agents_vis([last_goal_message], True)
|
||||
)
|
||||
|
||||
if len(plan_temps) > 0:
|
||||
vis_items.append(await self._messages_to_plan_vis(plan_temps))
|
||||
vis_items.append(await self._messages_to_agents_vis([last_goal_message]))
|
||||
return "\n".join(vis_items)
|
||||
|
||||
async def agent_stream_message(
|
||||
self,
|
||||
message: Union[Dict, str],
|
||||
enable_vis_message: bool = True,
|
||||
):
|
||||
"""Get agent stream message."""
|
||||
messages_view = []
|
||||
if isinstance(message, dict):
|
||||
messages_view.append(
|
||||
{
|
||||
"sender": message["sender"],
|
||||
"receiver": message["receiver"],
|
||||
"model": message["model"],
|
||||
"markdown": message["markdown"],
|
||||
}
|
||||
)
|
||||
else:
|
||||
messages_view.append(
|
||||
{
|
||||
"sender": "?",
|
||||
"receiver": "?",
|
||||
"model": "?",
|
||||
"markdown": message,
|
||||
}
|
||||
)
|
||||
if enable_vis_message:
|
||||
return await vis_client.get(VisAgentMessages.vis_tag()).display(
|
||||
content=messages_view
|
||||
)
|
||||
else:
|
||||
return messages_view
|
||||
|
||||
async def _plan_vis_build(self, plan_group: dict[str, list]):
|
||||
num: int = 0
|
||||
plan_items = []
|
||||
@@ -90,71 +251,106 @@ class GptsMemory:
|
||||
)
|
||||
return await self._messages_to_plan_vis(plan_items)
|
||||
|
||||
async def one_chat_completions_v2(self, conv_id: str):
|
||||
"""Generate a visualization of the conversation."""
|
||||
messages = self.message_memory.get_by_conv_id(conv_id=conv_id)
|
||||
temp_group: Dict[str, List[GptsMessage]] = OrderedDict()
|
||||
none_goal_count = 1
|
||||
count: int = 0
|
||||
async def simple_message(self, conv_id: str):
|
||||
"""Get agent simple message."""
|
||||
messages_cache = self.messages_cache[conv_id]
|
||||
if messages_cache and len(messages_cache) > 0:
|
||||
messages = messages_cache
|
||||
else:
|
||||
messages = self.message_memory.get_by_conv_id(conv_id=conv_id)
|
||||
|
||||
simple_message_list = []
|
||||
for message in messages:
|
||||
count = count + 1
|
||||
if count == 1:
|
||||
if message.sender == "Human":
|
||||
continue
|
||||
current_goal = message.current_goal
|
||||
|
||||
action_report_str = message.action_report
|
||||
view_info = message.content
|
||||
action_out = None
|
||||
if action_report_str and len(action_report_str) > 0:
|
||||
action_out = ActionOutput.from_dict(json.loads(action_report_str))
|
||||
if action_out is not None:
|
||||
view_info = action_out.content
|
||||
|
||||
simple_message_list.append(
|
||||
{
|
||||
"sender": message.sender,
|
||||
"receiver": message.receiver,
|
||||
"model": message.model_name,
|
||||
"markdown": view_info,
|
||||
}
|
||||
)
|
||||
|
||||
return simple_message_list
|
||||
|
||||
async def app_link_chat_message(self, conv_id: str):
|
||||
"""Get app link chat message."""
|
||||
messages = []
|
||||
if conv_id in self.messages_cache:
|
||||
messages_cache = self.messages_cache[conv_id]
|
||||
if messages_cache and len(messages_cache) > 0:
|
||||
start_round = (
|
||||
self.start_round_map[conv_id]
|
||||
if conv_id in self.start_round_map
|
||||
else 0
|
||||
)
|
||||
messages = messages_cache[start_round:]
|
||||
else:
|
||||
messages = self.message_memory.get_by_conv_id(conv_id=conv_id)
|
||||
|
||||
# VIS消息组装
|
||||
temp_group: Dict = {}
|
||||
app_link_message: Optional[GptsMessage] = None
|
||||
app_lanucher_message: Optional[GptsMessage] = None
|
||||
|
||||
none_goal_count = 1
|
||||
for message in messages:
|
||||
if message.sender in [
|
||||
"Intent Recognition Expert",
|
||||
"App Link",
|
||||
] or message.receiver in ["Intent Recognition Expert", "App Link"]:
|
||||
if (
|
||||
message.sender in ["Intent Recognition Expert", "App Link"]
|
||||
and message.receiver == "AppLauncher"
|
||||
):
|
||||
app_link_message = message
|
||||
if message.receiver != "Human":
|
||||
continue
|
||||
|
||||
if message.sender == "AppLauncher":
|
||||
if message.receiver == "Human":
|
||||
app_lanucher_message = message
|
||||
continue
|
||||
|
||||
current_gogal = message.current_goal
|
||||
|
||||
last_goal = next(reversed(temp_group)) if temp_group else None
|
||||
if last_goal:
|
||||
last_goal_messages = temp_group[last_goal]
|
||||
if current_goal:
|
||||
if current_goal == last_goal:
|
||||
if current_gogal:
|
||||
if current_gogal == last_goal:
|
||||
last_goal_messages.append(message)
|
||||
else:
|
||||
temp_group[current_goal] = [message]
|
||||
temp_group[current_gogal] = [message]
|
||||
else:
|
||||
temp_group[f"{NONE_GOAL_PREFIX}{none_goal_count}"] = [message]
|
||||
none_goal_count += 1
|
||||
else:
|
||||
if current_goal:
|
||||
temp_group[current_goal] = [message]
|
||||
if current_gogal:
|
||||
temp_group[current_gogal] = [message]
|
||||
else:
|
||||
temp_group[f"{NONE_GOAL_PREFIX}{none_goal_count}"] = [message]
|
||||
none_goal_count += 1
|
||||
|
||||
return await self._message_group_vis_build(temp_group)
|
||||
vis_items: list = []
|
||||
if app_link_message:
|
||||
vis_items.append(
|
||||
await self._messages_to_app_link_vis(
|
||||
app_link_message, app_lanucher_message
|
||||
)
|
||||
)
|
||||
|
||||
async def one_chat_completions(self, conv_id: str):
|
||||
"""Generate a visualization of the conversation."""
|
||||
messages = self.message_memory.get_by_conv_id(conv_id=conv_id)
|
||||
temp_group: Dict[str, List[GptsMessage]] = defaultdict(list)
|
||||
temp_messages = []
|
||||
vis_items = []
|
||||
count: int = 0
|
||||
for message in messages:
|
||||
count = count + 1
|
||||
if count == 1:
|
||||
continue
|
||||
if not message.current_goal or len(message.current_goal) <= 0:
|
||||
if len(temp_group) > 0:
|
||||
vis_items.append(await self._plan_vis_build(temp_group))
|
||||
temp_group.clear()
|
||||
|
||||
temp_messages.append(message)
|
||||
else:
|
||||
if len(temp_messages) > 0:
|
||||
vis_items.append(await self._messages_to_agents_vis(temp_messages))
|
||||
temp_messages.clear()
|
||||
|
||||
last_goal = message.current_goal
|
||||
temp_group[last_goal].append(message)
|
||||
|
||||
if len(temp_group) > 0:
|
||||
vis_items.append(await self._plan_vis_build(temp_group))
|
||||
temp_group.clear()
|
||||
if len(temp_messages) > 0:
|
||||
vis_items.append(await self._messages_to_agents_vis(temp_messages, True))
|
||||
temp_messages.clear()
|
||||
|
||||
return "\n".join(vis_items)
|
||||
return await self._message_group_vis_build(temp_group, vis_items)
|
||||
|
||||
async def _messages_to_agents_vis(
|
||||
self, messages: List[GptsMessage], is_last_message: bool = False
|
||||
@@ -167,11 +363,10 @@ class GptsMemory:
|
||||
view_info = message.content
|
||||
if action_report_str and len(action_report_str) > 0:
|
||||
action_out = ActionOutput.from_dict(json.loads(action_report_str))
|
||||
if action_out is not None and (
|
||||
action_out.is_exe_success or is_last_message
|
||||
):
|
||||
view = action_out.view
|
||||
view_info = view if view else action_out.content
|
||||
if action_out is not None: # noqa
|
||||
if action_out.is_exe_success or is_last_message: # noqa
|
||||
view = action_out.view
|
||||
view_info = view if view else action_out.content
|
||||
|
||||
messages_view.append(
|
||||
{
|
||||
@@ -179,12 +374,56 @@ class GptsMemory:
|
||||
"receiver": message.receiver,
|
||||
"model": message.model_name,
|
||||
"markdown": view_info,
|
||||
"resource": message.resource_info
|
||||
if message.resource_info
|
||||
else None,
|
||||
}
|
||||
)
|
||||
vis_compent = vis_client.get(VisAgentMessages.vis_tag())
|
||||
return await vis_compent.display(content=messages_view)
|
||||
return await vis_client.get(VisAgentMessages.vis_tag()).display(
|
||||
content=messages_view
|
||||
)
|
||||
|
||||
async def _messages_to_plan_vis(self, messages: List[Dict]):
|
||||
if messages is None or len(messages) <= 0:
|
||||
return ""
|
||||
return await vis_client.get(VisAgentPlans.vis_tag()).display(content=messages)
|
||||
|
||||
async def _messages_to_app_link_vis(
|
||||
self, link_message: GptsMessage, lanucher_message: Optional[GptsMessage] = None
|
||||
):
|
||||
logger.info("app link vis build")
|
||||
if link_message is None:
|
||||
return ""
|
||||
param = {}
|
||||
link_report_str = link_message.action_report
|
||||
if link_report_str and len(link_report_str) > 0:
|
||||
action_out = ActionOutput.from_dict(json.loads(link_report_str))
|
||||
if action_out is not None:
|
||||
if action_out.is_exe_success:
|
||||
temp = json.loads(action_out.content)
|
||||
|
||||
param["app_code"] = temp["app_code"]
|
||||
param["app_name"] = temp["app_name"]
|
||||
param["app_desc"] = temp.get("app_desc", "")
|
||||
param["app_logo"] = ""
|
||||
param["status"] = Status.RUNNING.value
|
||||
|
||||
else:
|
||||
param["status"] = Status.FAILED.value
|
||||
param["msg"] = action_out.content
|
||||
|
||||
if lanucher_message:
|
||||
lanucher_report_str = lanucher_message.action_report
|
||||
if lanucher_report_str and len(lanucher_report_str) > 0:
|
||||
lanucher_action_out = ActionOutput.from_dict(
|
||||
json.loads(lanucher_report_str)
|
||||
)
|
||||
if lanucher_action_out is not None:
|
||||
if lanucher_action_out.is_exe_success:
|
||||
param["status"] = Status.COMPLETE.value
|
||||
else:
|
||||
param["status"] = Status.FAILED.value
|
||||
param["msg"] = lanucher_action_out.content
|
||||
else:
|
||||
param["status"] = Status.COMPLETE.value
|
||||
return await vis_client.get(VisAppLink.vis_tag()).display(content=param)
|
||||
|
Reference in New Issue
Block a user