update a lot
This commit is contained in:
parent
b7533a0111
commit
dbf5000ac7
@ -1,53 +1,39 @@
|
||||
from src.agents.registry import BaseAgent
|
||||
from src.agents.chatbot.graph import ChatbotAgent
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
from langchain_core.messages import AIMessageChunk, ToolMessage
|
||||
|
||||
from src.agents.chatbot import ChatbotAgent, ChatbotConfiguration
|
||||
|
||||
class AgentManager:
|
||||
def __init__(self):
|
||||
self.agents = {}
|
||||
|
||||
def add_agent(self, agent_id, agent):
|
||||
def add_agent(self, agent_id, agent_class, configuration_class):
|
||||
self.agents[agent_id] = {
|
||||
"agent": agent,
|
||||
"configuration": agent.configuration
|
||||
"agent_class": agent_class,
|
||||
"configuration_class": configuration_class
|
||||
}
|
||||
|
||||
def agent_cli(agent: BaseAgent, config: RunnableConfig = None):
|
||||
config = config or {}
|
||||
if "configurable" not in config:
|
||||
config["configurable"] = {}
|
||||
def get_runnable_agent(self, agent_id, **kwargs):
|
||||
agent_class = self.get_agent(agent_id)
|
||||
configuration_class = self.get_configuration(agent_id)
|
||||
configuration = configuration_class(**kwargs)
|
||||
return agent_class(configuration)
|
||||
|
||||
while True:
|
||||
user_input = input("\nUser: ")
|
||||
if user_input.lower() in ["quit", "exit", "q"]:
|
||||
print("Goodbye!")
|
||||
break
|
||||
def get_agent(self, agent_id):
|
||||
return self.agents[agent_id]["agent_class"]
|
||||
|
||||
stream_flag = False
|
||||
for msg, metadata in agent.stream_messages([{"role": "user", "content": user_input}], config):
|
||||
if isinstance(msg, AIMessageChunk):
|
||||
content = msg.content or msg.tool_calls
|
||||
def get_configuration(self, agent_id):
|
||||
return self.agents[agent_id]["configuration_class"]
|
||||
|
||||
if not content:
|
||||
if stream_flag == True:
|
||||
print()
|
||||
stream_flag = False
|
||||
continue
|
||||
|
||||
if stream_flag == False and content:
|
||||
print(f"AI: {content}", end="", flush=True)
|
||||
stream_flag = True
|
||||
continue
|
||||
agent_manager = AgentManager()
|
||||
agent_manager.add_agent("chatbot", ChatbotAgent, ChatbotConfiguration)
|
||||
|
||||
elif content:
|
||||
print(f"{content}", end="", flush=True)
|
||||
__all__ = ["agent_manager"]
|
||||
|
||||
if isinstance(msg, ToolMessage):
|
||||
print(f"Tool: {msg.content}")
|
||||
|
||||
def get_agents():
|
||||
agent_manager = AgentManager()
|
||||
agent_manager.add_agent("chatbot", ChatbotAgent())
|
||||
return agent_manager.agents
|
||||
if __name__ == "__main__":
|
||||
agent = agent_manager.get_agent("chatbot")
|
||||
conf = agent_manager.get_configuration("chatbot")
|
||||
agent_info = {
|
||||
"name": agent.name,
|
||||
"description": agent.description,
|
||||
}
|
||||
print(agent_info)
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
from .graph import ChatbotAgent
|
||||
from .configuration import ChatbotConfiguration
|
||||
|
||||
__all__ = ["ChatbotAgent"]
|
||||
__all__ = ["ChatbotAgent", "ChatbotConfiguration"]
|
||||
@ -1,7 +1,5 @@
|
||||
from dataclasses import dataclass, field
|
||||
from typing import List
|
||||
|
||||
from langchain_core.messages import SystemMessage
|
||||
from langchain_core.tools import Tool
|
||||
from langchain_openai import ChatOpenAI
|
||||
|
||||
@ -9,20 +7,19 @@ from src import config
|
||||
from src.models import select_model
|
||||
from src.agents.registry import Configuration
|
||||
|
||||
def get_default_llm():
|
||||
return select_model(config).chat_open_ai
|
||||
|
||||
def get_default_tools():
|
||||
from langchain_community.tools.tavily_search import TavilySearchResults
|
||||
return [TavilySearchResults(max_results=10)]
|
||||
|
||||
def get_default_requirements():
|
||||
return ["TAVILY_API_KEY"]
|
||||
|
||||
@dataclass(kw_only=True)
|
||||
class ChatbotConfiguration(Configuration):
|
||||
name: str = "chatbot"
|
||||
description: str = "A chatbot that can answer questions and help with tasks."
|
||||
requirements: list[str] = field(default_factory=get_default_requirements)
|
||||
tools: list[Tool] = field(default_factory=get_default_tools)
|
||||
llm: ChatOpenAI = field(default_factory=get_default_llm)
|
||||
llm: ChatOpenAI | None = None
|
||||
model_provider: str = "siliconflow"
|
||||
model_name: str = "Qwen/Qwen2.5-72B-Instruct"
|
||||
|
||||
def __post_init__(self):
|
||||
# TODO 需要确保这里的模型是支持 tools 的
|
||||
if self.llm is None:
|
||||
self.llm = select_model(config=config,
|
||||
model_provider=self.model_provider,
|
||||
model_name=self.model_name).chat_open_ai
|
||||
|
||||
@ -3,7 +3,6 @@ import uuid
|
||||
from typing import Any
|
||||
from datetime import datetime
|
||||
|
||||
from langchain_core.messages import AIMessageChunk, ToolMessage
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
from langgraph.graph import StateGraph, START, END
|
||||
from langgraph.prebuilt import ToolNode, tools_condition
|
||||
@ -13,53 +12,56 @@ from src.agents.registry import State, BaseAgent
|
||||
from src.agents.chatbot.configuration import ChatbotConfiguration
|
||||
|
||||
class ChatbotAgent(BaseAgent):
|
||||
name = "chatbot"
|
||||
description = "A chatbot that can answer questions and help with tasks."
|
||||
_graph_cache = None
|
||||
|
||||
def __init__(self, configuration: ChatbotConfiguration = None):
|
||||
super().__init__(configuration)
|
||||
self.configuration = configuration or ChatbotConfiguration()
|
||||
self.llm = configuration.llm
|
||||
self.llm = self.configuration.llm
|
||||
|
||||
def llm_call(self, state: State) -> dict[str, Any]:
|
||||
tools = self.configuration.tools
|
||||
system_prompt = {
|
||||
"role": "system",
|
||||
"content": (
|
||||
f"Current time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
|
||||
)
|
||||
}
|
||||
messages = [system_prompt] + state["messages"]
|
||||
model = self.llm.bind_tools(tools)
|
||||
def _get_tools(self, config: RunnableConfig):
|
||||
"""根据配置获取工具"""
|
||||
tools = []
|
||||
if not config:
|
||||
return tools
|
||||
|
||||
res = model.invoke(messages)
|
||||
if config.get("configurable", {}).get("use_web", None):
|
||||
from langchain_community.tools.tavily_search import TavilySearchResults
|
||||
tools.append(TavilySearchResults(max_results=10))
|
||||
|
||||
return tools
|
||||
|
||||
def llm_call(self, state: State, config: RunnableConfig) -> dict[str, Any]:
|
||||
model = self.llm.bind_tools(self._get_tools(config))
|
||||
|
||||
res = model.invoke(state["messages"])
|
||||
return {"messages": [res]}
|
||||
|
||||
def get_graph(self):
|
||||
def get_graph(self, config: RunnableConfig = None):
|
||||
"""构建图"""
|
||||
if ChatbotAgent._graph_cache is None:
|
||||
workflow = StateGraph(State)
|
||||
workflow.add_node("chatbot", self.llm_call)
|
||||
workflow.add_node("tools", ToolNode(tools=self.configuration.tools))
|
||||
workflow.add_edge(START, "chatbot")
|
||||
workflow.add_conditional_edges(
|
||||
"chatbot",
|
||||
tools_condition,
|
||||
)
|
||||
workflow.add_edge("tools", "chatbot")
|
||||
workflow.add_edge("chatbot", END)
|
||||
workflow = StateGraph(State)
|
||||
workflow.add_node("chatbot", self.llm_call)
|
||||
workflow.add_node("tools", ToolNode(tools=self._get_tools(config)))
|
||||
workflow.add_edge(START, "chatbot")
|
||||
workflow.add_conditional_edges(
|
||||
"chatbot",
|
||||
tools_condition,
|
||||
)
|
||||
workflow.add_edge("tools", "chatbot")
|
||||
workflow.add_edge("chatbot", END)
|
||||
|
||||
graph = workflow.compile(checkpointer=MemorySaver())
|
||||
ChatbotAgent._graph_cache = graph
|
||||
|
||||
return ChatbotAgent._graph_cache
|
||||
graph = workflow.compile(checkpointer=MemorySaver())
|
||||
return graph
|
||||
|
||||
def stream_values(self, messages: list[str], config: RunnableConfig = None):
|
||||
graph = self.get_graph()
|
||||
graph = self.get_graph(config)
|
||||
for event in graph.stream({"messages": messages}, stream_mode="values", config=config):
|
||||
yield event["messages"]
|
||||
|
||||
def stream_messages(self, messages: list[str], config: RunnableConfig = None):
|
||||
graph = self.get_graph()
|
||||
graph = self.get_graph(config)
|
||||
for msg, metadata in graph.stream({"messages": messages}, stream_mode="messages", config=config):
|
||||
msg_type = msg.type
|
||||
|
||||
@ -67,14 +69,13 @@ class ChatbotAgent(BaseAgent):
|
||||
if not return_keys or msg_type in return_keys:
|
||||
yield msg, metadata
|
||||
|
||||
|
||||
def main():
|
||||
agent = ChatbotAgent(ChatbotConfiguration())
|
||||
|
||||
thread_id = str(uuid.uuid4())
|
||||
config = {"configurable": {"thread_id": thread_id}}
|
||||
|
||||
from src.agents import agent_cli
|
||||
from src.agents.utils import agent_cli
|
||||
agent_cli(agent, config)
|
||||
|
||||
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
from typing import Type, Annotated, Optional, TypedDict
|
||||
from abc import abstractmethod
|
||||
|
||||
from langchain_core.tools import BaseTool
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langgraph.checkpoint.base import BaseCheckpointSaver
|
||||
from langgraph.graph.message import add_messages
|
||||
from langchain_core.messages import BaseMessage, ToolMessage, AIMessage, filter_messages
|
||||
|
||||
from langchain_core.messages import BaseMessage
|
||||
from langgraph.graph.state import CompiledStateGraph
|
||||
from langgraph.graph.message import add_messages
|
||||
|
||||
|
||||
from dataclasses import dataclass
|
||||
@ -26,7 +25,8 @@ class Configuration:
|
||||
"""
|
||||
定义一个基础 Configuration 供 各类 graph 继承
|
||||
"""
|
||||
llm: ChatOpenAI
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class BaseAgent():
|
||||
|
||||
37
src/agents/utils.py
Normal file
37
src/agents/utils.py
Normal file
@ -0,0 +1,37 @@
|
||||
from src.agents.registry import BaseAgent
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
from langchain_core.messages import AIMessageChunk, ToolMessage
|
||||
|
||||
|
||||
def agent_cli(agent: BaseAgent, config: RunnableConfig = None):
|
||||
config = config or {}
|
||||
if "configurable" not in config:
|
||||
config["configurable"] = {}
|
||||
|
||||
while True:
|
||||
user_input = input("\nUser: ")
|
||||
if user_input.lower() in ["quit", "exit", "q"]:
|
||||
print("Goodbye!")
|
||||
break
|
||||
|
||||
stream_flag = False
|
||||
for msg, metadata in agent.stream_messages([{"role": "user", "content": user_input}], config):
|
||||
if isinstance(msg, AIMessageChunk):
|
||||
content = msg.content or msg.tool_calls
|
||||
|
||||
if not content:
|
||||
if stream_flag == True:
|
||||
print()
|
||||
stream_flag = False
|
||||
continue
|
||||
|
||||
if stream_flag == False and content:
|
||||
print(f"AI: {content}", end="", flush=True)
|
||||
stream_flag = True
|
||||
continue
|
||||
|
||||
elif content:
|
||||
print(f"{content}", end="", flush=True)
|
||||
|
||||
if isinstance(msg, ToolMessage):
|
||||
print(f"Tool: {msg.content}")
|
||||
@ -1,10 +1,14 @@
|
||||
import json
|
||||
import asyncio
|
||||
import traceback
|
||||
import uuid
|
||||
from fastapi import APIRouter, Body
|
||||
from fastapi.responses import StreamingResponse, Response
|
||||
from src.core import HistoryManager
|
||||
from fastapi.responses import StreamingResponse
|
||||
from langchain_core.messages import AIMessageChunk
|
||||
|
||||
from src import executor, config, retriever
|
||||
from src.core import HistoryManager
|
||||
from src.agents import agent_manager
|
||||
from src.models import select_model
|
||||
from src.utils.logging_config import logger
|
||||
|
||||
@ -20,8 +24,8 @@ async def chat_get():
|
||||
def chat_post(
|
||||
query: str = Body(...),
|
||||
meta: dict = Body(None),
|
||||
history: list = Body(...),
|
||||
cur_res_id: str = Body(...)):
|
||||
history: list | None = Body(None),
|
||||
thread_id: str | None = Body(None)):
|
||||
|
||||
model = select_model(config)
|
||||
meta["server_model_name"] = model.model_name
|
||||
@ -31,7 +35,6 @@ def chat_post(
|
||||
def make_chunk(content=None, **kwargs):
|
||||
return json.dumps({
|
||||
"response": content,
|
||||
"model_name": config.model_name,
|
||||
"meta": meta,
|
||||
**kwargs
|
||||
}, ensure_ascii=False).encode('utf-8') + b"\n"
|
||||
@ -76,7 +79,7 @@ def chat_post(
|
||||
else:
|
||||
content += delta.content or ""
|
||||
|
||||
chunk = make_chunk(content=content, status="loading")
|
||||
chunk = make_chunk(content=delta.content, status="loading")
|
||||
yield chunk
|
||||
|
||||
logger.debug(f"Final response: {content}")
|
||||
@ -117,4 +120,62 @@ async def call(query: str = Body(...), meta: dict = Body(None)):
|
||||
response = await predict_async(query)
|
||||
logger.debug({"query": query, "response": response.content})
|
||||
|
||||
return {"response": response.content}
|
||||
return {"response": response.content}
|
||||
|
||||
@chat.get("/agent")
|
||||
async def get_agent():
|
||||
agents = [{
|
||||
"name": agent["agent_class"].name,
|
||||
"description": agent["agent_class"].description
|
||||
} for agent in agent_manager.agents.values()]
|
||||
return {"agents": agents}
|
||||
|
||||
@chat.post("/agent/{agent_name}")
|
||||
def chat_agent(agent_name: str,
|
||||
query: str = Body(...),
|
||||
meta: dict = Body({}),
|
||||
history: list = Body(...),
|
||||
thread_id: str | None = Body(None)):
|
||||
|
||||
meta["server_model_name"] = agent_name
|
||||
agent = agent_manager.get_runnable_agent(agent_name)
|
||||
|
||||
history_manager = HistoryManager(history)
|
||||
messages = history_manager.get_history_with_msg(query, max_rounds=meta.get('history_round'))
|
||||
history_manager.add_user(query) # 注意这里使用原始查询
|
||||
|
||||
runnable_config = {
|
||||
"configurable": {
|
||||
"thread_id": thread_id or str(uuid.uuid4()),
|
||||
"use_web": meta.get("use_web", False),
|
||||
"return_keys": []
|
||||
}
|
||||
}
|
||||
|
||||
def make_chunk(content=None, **kwargs):
|
||||
return json.dumps({
|
||||
"response": content,
|
||||
"model_name": agent_name,
|
||||
"meta": meta,
|
||||
**kwargs
|
||||
}, ensure_ascii=False).encode('utf-8') + b"\n"
|
||||
|
||||
def stream_messages():
|
||||
content = ""
|
||||
yield make_chunk(status="waiting")
|
||||
for msg, metadata in agent.stream_messages(messages, runnable_config):
|
||||
# logger.debug(f"msg: {msg.model_dump()}, {metadata=}")
|
||||
if isinstance(msg, AIMessageChunk) and msg.content != "<tool_call>":
|
||||
content += msg.content
|
||||
yield make_chunk(content=msg.content,
|
||||
msg=msg.model_dump(),
|
||||
metadata=metadata,
|
||||
status="loading")
|
||||
else:
|
||||
yield make_chunk(msg=msg.model_dump(),
|
||||
metadata=metadata,
|
||||
status="loading")
|
||||
|
||||
yield make_chunk(status="finished", history=history_manager.update_ai(content))
|
||||
|
||||
return StreamingResponse(stream_messages(), media_type='application/json')
|
||||
|
||||
@ -370,7 +370,7 @@ const updateMessage = (info) => {
|
||||
try {
|
||||
// 只有在 text 不为空时更新
|
||||
if (info.text !== null && info.text !== undefined && info.text !== '') {
|
||||
msg.text = info.text;
|
||||
msg.text += info.text;
|
||||
}
|
||||
|
||||
if (info.reasoning_content !== null && info.reasoning_content !== undefined && info.reasoning_content !== '') {
|
||||
@ -463,6 +463,7 @@ const fetchChatResponse = (user_input, cur_res_id) => {
|
||||
history: conv.value.history,
|
||||
meta: meta,
|
||||
cur_res_id: cur_res_id,
|
||||
thread_id: conv.value.id.toString(),
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
@ -639,6 +640,12 @@ watch(
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header__left {
|
||||
.close {
|
||||
margin-right: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
@ -652,6 +659,7 @@ watch(
|
||||
font-size: 1rem;
|
||||
width: auto;
|
||||
padding: 0.5rem 1rem;
|
||||
transition: background-color 0.3s;
|
||||
|
||||
.text {
|
||||
margin-left: 10px;
|
||||
@ -678,6 +686,7 @@ watch(
|
||||
padding: 12px;
|
||||
z-index: 11;
|
||||
width: 280px;
|
||||
transition: transform 0.3s ease, opacity 0.3s ease;
|
||||
|
||||
.flex-center {
|
||||
display: flex;
|
||||
@ -1051,9 +1060,27 @@ watch(
|
||||
@keyframes loading {0%,80%,100%{transform:scale(0.5);}40%{transform:scale(1);}}
|
||||
|
||||
.slide-out-left{-webkit-animation:slide-out-left .2s cubic-bezier(.55,.085,.68,.53) both;animation:slide-out-left .5s cubic-bezier(.55,.085,.68,.53) both}
|
||||
.swing-in-top-fwd{-webkit-animation:swing-in-top-fwd .2s ease-out both;animation:swing-in-top-fwd .2s ease-out both}
|
||||
@-webkit-keyframes swing-in-top-fwd{0%{-webkit-transform:rotateX(-100deg);transform:rotateX(-100deg);-webkit-transform-origin:top;transform-origin:top;opacity:0}100%{-webkit-transform:rotateX(0deg);transform:rotateX(0deg);-webkit-transform-origin:top;transform-origin:top;opacity:1}}@keyframes swing-in-top-fwd{0%{-webkit-transform:rotateX(-100deg);transform:rotateX(-100deg);-webkit-transform-origin:top;transform-origin:top;opacity:0}100%{-webkit-transform:rotateX(0deg);transform:rotateX(0deg);-webkit-transform-origin:top;transform-origin:top;opacity:1}}
|
||||
@-webkit-keyframes slide-out-left{0%{-webkit-transform:translateX(0);transform:translateX(0);opacity:1}100%{-webkit-transform:translateX(-1000px);transform:translateX(-1000px);opacity:0}}@keyframes slide-out-left{0%{-webkit-transform:translateX(0);transform:translateX(0);opacity:1}100%{-webkit-transform:translateX(-1000px);transform:translateX(-1000px);opacity:0}}
|
||||
.swing-in-top-fwd {
|
||||
-webkit-animation: swing-in-top-fwd 0.3s cubic-bezier(0.175, 0.885, 0.320, 1.275) both;
|
||||
animation: swing-in-top-fwd 0.3s cubic-bezier(0.175, 0.885, 0.320, 1.275) both;
|
||||
}
|
||||
|
||||
@keyframes swing-in-top-fwd {
|
||||
0% {
|
||||
-webkit-transform: rotateX(-100deg);
|
||||
transform: rotateX(-100deg);
|
||||
-webkit-transform-origin: top;
|
||||
transform-origin: top;
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotateX(0deg);
|
||||
transform: rotateX(0deg);
|
||||
-webkit-transform-origin: top;
|
||||
transform-origin: top;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
||||
@keyframes slideInUp { from { transform: translateY(20px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
|
||||
|
||||
@ -21,6 +21,8 @@ import {
|
||||
StarFilled,
|
||||
StarOutlined,
|
||||
ExclamationCircleOutlined,
|
||||
RobotOutlined,
|
||||
RobotFilled,
|
||||
} from '@ant-design/icons-vue'
|
||||
import { themeConfig } from '@/assets/theme'
|
||||
import { useConfigStore } from '@/stores/config'
|
||||
@ -73,6 +75,37 @@ onMounted(() => {
|
||||
// 打印当前页面的路由信息,使用 vue3 的 setup composition API
|
||||
const route = useRoute()
|
||||
console.log(route)
|
||||
|
||||
// 下面是导航菜单部分,添加智能体项
|
||||
const mainList = [{
|
||||
name: '对话',
|
||||
path: '/chat',
|
||||
icon: MessageOutlined,
|
||||
activeIcon: MessageFilled,
|
||||
}, {
|
||||
name: '智能体',
|
||||
path: '/agent',
|
||||
icon: RobotOutlined,
|
||||
activeIcon: RobotFilled,
|
||||
}, {
|
||||
name: '图谱',
|
||||
path: '/graph',
|
||||
icon: ProjectOutlined,
|
||||
activeIcon: ProjectFilled,
|
||||
// hidden: !configStore.config.enable_knowledge_graph,
|
||||
}, {
|
||||
name: '知识库',
|
||||
path: '/database',
|
||||
icon: BookOutlined,
|
||||
activeIcon: BookFilled,
|
||||
// hidden: !configStore.config.enable_knowledge_base,
|
||||
}, {
|
||||
name: '工具',
|
||||
path: '/tools',
|
||||
icon: ToolOutlined,
|
||||
activeIcon: ToolFilled,
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -107,22 +140,18 @@ console.log(route)
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="nav">
|
||||
<RouterLink to="/chat" class="nav-item" active-class="active">
|
||||
<component class="icon" :is="route.path === '/chat' ? MessageFilled : MessageOutlined" />
|
||||
<span class="text">对话</span>
|
||||
</RouterLink>
|
||||
<RouterLink to="/database" class="nav-item" active-class="active">
|
||||
<component class="icon" :is="route.path.startsWith('/database') ? FolderFilled : FolderOutlined" />
|
||||
<span class="text">知识</span>
|
||||
</RouterLink>
|
||||
<RouterLink to="/graph" class="nav-item" active-class="active">
|
||||
<component class="icon" :is="route.path.startsWith('/graph') ? ProjectFilled: ProjectOutlined" />
|
||||
<span class="text">图谱</span>
|
||||
</RouterLink>
|
||||
<RouterLink to="/tools" class="nav-item" active-class="active">
|
||||
<component class="icon" :is="route.path.startsWith('/tools') ? ToolFilled: ToolOutlined" />
|
||||
<span class="text">工具</span>
|
||||
<!-- 使用mainList渲染导航项 -->
|
||||
<RouterLink
|
||||
v-for="(item, index) in mainList"
|
||||
:key="index"
|
||||
:to="item.path"
|
||||
v-show="!item.hidden"
|
||||
class="nav-item"
|
||||
active-class="active">
|
||||
<component class="icon" :is="route.path.startsWith(item.path) ? item.activeIcon : item.icon" />
|
||||
<span class="text">{{item.name}}</span>
|
||||
</RouterLink>
|
||||
|
||||
<a-tooltip placement="right">
|
||||
<template #title>后端疑似没有正常启动或者正在繁忙中,请刷新一下或者检查 docker logs api-dev</template>
|
||||
<div class="nav-item warning" v-if="!configStore.config._config_items">
|
||||
@ -140,7 +169,7 @@ console.log(route)
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<RouterLink class="nav-item setting" to="/setting" active-class="active">
|
||||
<RouterLink class="nav-item setting" to="/setting" active-class="active">
|
||||
<component class="icon" :is="route.path === '/setting' ? SettingFilled : SettingOutlined" />
|
||||
</RouterLink>
|
||||
</div>
|
||||
|
||||
@ -30,6 +30,19 @@ const router = createRouter({
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/agent',
|
||||
name: 'agent',
|
||||
component: AppLayout,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'AgentComp',
|
||||
component: () => import('../views/AgentView.vue'),
|
||||
meta: { keepAlive: true }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/graph',
|
||||
name: 'graph',
|
||||
|
||||
1694
web/src/views/AgentView.vue
Normal file
1694
web/src/views/AgentView.vue
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="chat-container">
|
||||
<div class="conversations" :class="['conversations', { 'is-open': state.isSidebarOpen }]">
|
||||
<div class="conversations" :class="{ 'is-open': state.isSidebarOpen }">
|
||||
<div class="actions">
|
||||
<!-- <div class="action new" @click="addNewConv"><FormOutlined /></div> -->
|
||||
<span class="header-title">对话历史</span>
|
||||
@ -129,29 +129,24 @@ onMounted(() => {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.chat-container .conversations {
|
||||
transition: width 0.3s ease, opacity 0.3s ease, flex 0.3s ease;
|
||||
white-space: nowrap; /* 防止文本换行 */
|
||||
overflow: hidden; /* 确保内容不溢出 */
|
||||
}
|
||||
|
||||
.chat-container .conversations:not(.is-open) {
|
||||
width: 0;
|
||||
opacity: 0;
|
||||
flex: 0 0 0;
|
||||
}
|
||||
|
||||
.chat-container .conversations.is-open {
|
||||
flex: 1 1 auto; /* 当侧边栏打开时,占据可用空间 */
|
||||
width: 230px;
|
||||
opacity: 1;
|
||||
}
|
||||
.conversations {
|
||||
width: 230px; /* 初始宽度 */
|
||||
width: 230px;
|
||||
max-width: 230px;
|
||||
border-right: 1px solid var(--main-light-3);
|
||||
max-height: 100%;
|
||||
background-color: var(--bg-sider);
|
||||
transition: all 0.3s ease;
|
||||
white-space: nowrap; /* 防止文本换行 */
|
||||
overflow: hidden; /* 确保内容不溢出 */
|
||||
|
||||
&.is-open {
|
||||
width: 230px;
|
||||
}
|
||||
|
||||
&:not(.is-open) {
|
||||
width: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
& .actions {
|
||||
height: var(--header-height);
|
||||
@ -238,7 +233,6 @@ onMounted(() => {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -275,6 +269,12 @@ onMounted(() => {
|
||||
height: 100%;
|
||||
border-radius: 0 16px 16px 0;
|
||||
box-shadow: 0 0 10px 1px rgba(0, 0, 0, 0.05);
|
||||
|
||||
&:not(.is-open) {
|
||||
width: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user