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 import ChatbotAgent, ChatbotConfiguration
|
||||||
from src.agents.chatbot.graph import ChatbotAgent
|
|
||||||
from langchain_core.runnables import RunnableConfig
|
|
||||||
from langchain_core.messages import AIMessageChunk, ToolMessage
|
|
||||||
|
|
||||||
|
|
||||||
class AgentManager:
|
class AgentManager:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.agents = {}
|
self.agents = {}
|
||||||
|
|
||||||
def add_agent(self, agent_id, agent):
|
def add_agent(self, agent_id, agent_class, configuration_class):
|
||||||
self.agents[agent_id] = {
|
self.agents[agent_id] = {
|
||||||
"agent": agent,
|
"agent_class": agent_class,
|
||||||
"configuration": agent.configuration
|
"configuration_class": configuration_class
|
||||||
}
|
}
|
||||||
|
|
||||||
def agent_cli(agent: BaseAgent, config: RunnableConfig = None):
|
def get_runnable_agent(self, agent_id, **kwargs):
|
||||||
config = config or {}
|
agent_class = self.get_agent(agent_id)
|
||||||
if "configurable" not in config:
|
configuration_class = self.get_configuration(agent_id)
|
||||||
config["configurable"] = {}
|
configuration = configuration_class(**kwargs)
|
||||||
|
return agent_class(configuration)
|
||||||
|
|
||||||
while True:
|
def get_agent(self, agent_id):
|
||||||
user_input = input("\nUser: ")
|
return self.agents[agent_id]["agent_class"]
|
||||||
if user_input.lower() in ["quit", "exit", "q"]:
|
|
||||||
print("Goodbye!")
|
|
||||||
break
|
|
||||||
|
|
||||||
stream_flag = False
|
def get_configuration(self, agent_id):
|
||||||
for msg, metadata in agent.stream_messages([{"role": "user", "content": user_input}], config):
|
return self.agents[agent_id]["configuration_class"]
|
||||||
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:
|
agent_manager = AgentManager()
|
||||||
print(f"AI: {content}", end="", flush=True)
|
agent_manager.add_agent("chatbot", ChatbotAgent, ChatbotConfiguration)
|
||||||
stream_flag = True
|
|
||||||
continue
|
|
||||||
|
|
||||||
elif content:
|
__all__ = ["agent_manager"]
|
||||||
print(f"{content}", end="", flush=True)
|
|
||||||
|
|
||||||
if isinstance(msg, ToolMessage):
|
|
||||||
print(f"Tool: {msg.content}")
|
|
||||||
|
|
||||||
def get_agents():
|
if __name__ == "__main__":
|
||||||
agent_manager = AgentManager()
|
agent = agent_manager.get_agent("chatbot")
|
||||||
agent_manager.add_agent("chatbot", ChatbotAgent())
|
conf = agent_manager.get_configuration("chatbot")
|
||||||
return agent_manager.agents
|
agent_info = {
|
||||||
|
"name": agent.name,
|
||||||
|
"description": agent.description,
|
||||||
|
}
|
||||||
|
print(agent_info)
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
from .graph import ChatbotAgent
|
from .graph import ChatbotAgent
|
||||||
|
from .configuration import ChatbotConfiguration
|
||||||
|
|
||||||
__all__ = ["ChatbotAgent"]
|
__all__ = ["ChatbotAgent", "ChatbotConfiguration"]
|
||||||
@ -1,7 +1,5 @@
|
|||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from langchain_core.messages import SystemMessage
|
|
||||||
from langchain_core.tools import Tool
|
from langchain_core.tools import Tool
|
||||||
from langchain_openai import ChatOpenAI
|
from langchain_openai import ChatOpenAI
|
||||||
|
|
||||||
@ -9,20 +7,19 @@ from src import config
|
|||||||
from src.models import select_model
|
from src.models import select_model
|
||||||
from src.agents.registry import Configuration
|
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():
|
def get_default_requirements():
|
||||||
return ["TAVILY_API_KEY"]
|
return ["TAVILY_API_KEY"]
|
||||||
|
|
||||||
@dataclass(kw_only=True)
|
@dataclass(kw_only=True)
|
||||||
class ChatbotConfiguration(Configuration):
|
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)
|
requirements: list[str] = field(default_factory=get_default_requirements)
|
||||||
tools: list[Tool] = field(default_factory=get_default_tools)
|
llm: ChatOpenAI | None = None
|
||||||
llm: ChatOpenAI = field(default_factory=get_default_llm)
|
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 typing import Any
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from langchain_core.messages import AIMessageChunk, ToolMessage
|
|
||||||
from langchain_core.runnables import RunnableConfig
|
from langchain_core.runnables import RunnableConfig
|
||||||
from langgraph.graph import StateGraph, START, END
|
from langgraph.graph import StateGraph, START, END
|
||||||
from langgraph.prebuilt import ToolNode, tools_condition
|
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
|
from src.agents.chatbot.configuration import ChatbotConfiguration
|
||||||
|
|
||||||
class ChatbotAgent(BaseAgent):
|
class ChatbotAgent(BaseAgent):
|
||||||
|
name = "chatbot"
|
||||||
|
description = "A chatbot that can answer questions and help with tasks."
|
||||||
_graph_cache = None
|
_graph_cache = None
|
||||||
|
|
||||||
def __init__(self, configuration: ChatbotConfiguration = None):
|
def __init__(self, configuration: ChatbotConfiguration = None):
|
||||||
super().__init__(configuration)
|
super().__init__(configuration)
|
||||||
self.configuration = configuration or ChatbotConfiguration()
|
self.configuration = configuration or ChatbotConfiguration()
|
||||||
self.llm = configuration.llm
|
self.llm = self.configuration.llm
|
||||||
|
|
||||||
def llm_call(self, state: State) -> dict[str, Any]:
|
def _get_tools(self, config: RunnableConfig):
|
||||||
tools = self.configuration.tools
|
"""根据配置获取工具"""
|
||||||
system_prompt = {
|
tools = []
|
||||||
"role": "system",
|
if not config:
|
||||||
"content": (
|
return tools
|
||||||
f"Current time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
messages = [system_prompt] + state["messages"]
|
|
||||||
model = self.llm.bind_tools(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]}
|
return {"messages": [res]}
|
||||||
|
|
||||||
def get_graph(self):
|
def get_graph(self, config: RunnableConfig = None):
|
||||||
"""构建图"""
|
"""构建图"""
|
||||||
if ChatbotAgent._graph_cache is None:
|
workflow = StateGraph(State)
|
||||||
workflow = StateGraph(State)
|
workflow.add_node("chatbot", self.llm_call)
|
||||||
workflow.add_node("chatbot", self.llm_call)
|
workflow.add_node("tools", ToolNode(tools=self._get_tools(config)))
|
||||||
workflow.add_node("tools", ToolNode(tools=self.configuration.tools))
|
workflow.add_edge(START, "chatbot")
|
||||||
workflow.add_edge(START, "chatbot")
|
workflow.add_conditional_edges(
|
||||||
workflow.add_conditional_edges(
|
"chatbot",
|
||||||
"chatbot",
|
tools_condition,
|
||||||
tools_condition,
|
)
|
||||||
)
|
workflow.add_edge("tools", "chatbot")
|
||||||
workflow.add_edge("tools", "chatbot")
|
workflow.add_edge("chatbot", END)
|
||||||
workflow.add_edge("chatbot", END)
|
|
||||||
|
|
||||||
graph = workflow.compile(checkpointer=MemorySaver())
|
graph = workflow.compile(checkpointer=MemorySaver())
|
||||||
ChatbotAgent._graph_cache = graph
|
return graph
|
||||||
|
|
||||||
return ChatbotAgent._graph_cache
|
|
||||||
|
|
||||||
def stream_values(self, messages: list[str], config: RunnableConfig = None):
|
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):
|
for event in graph.stream({"messages": messages}, stream_mode="values", config=config):
|
||||||
yield event["messages"]
|
yield event["messages"]
|
||||||
|
|
||||||
def stream_messages(self, messages: list[str], config: RunnableConfig = None):
|
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):
|
for msg, metadata in graph.stream({"messages": messages}, stream_mode="messages", config=config):
|
||||||
msg_type = msg.type
|
msg_type = msg.type
|
||||||
|
|
||||||
@ -67,14 +69,13 @@ class ChatbotAgent(BaseAgent):
|
|||||||
if not return_keys or msg_type in return_keys:
|
if not return_keys or msg_type in return_keys:
|
||||||
yield msg, metadata
|
yield msg, metadata
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
agent = ChatbotAgent(ChatbotConfiguration())
|
agent = ChatbotAgent(ChatbotConfiguration())
|
||||||
|
|
||||||
thread_id = str(uuid.uuid4())
|
thread_id = str(uuid.uuid4())
|
||||||
config = {"configurable": {"thread_id": thread_id}}
|
config = {"configurable": {"thread_id": thread_id}}
|
||||||
|
|
||||||
from src.agents import agent_cli
|
from src.agents.utils import agent_cli
|
||||||
agent_cli(agent, config)
|
agent_cli(agent, config)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
from typing import Type, Annotated, Optional, TypedDict
|
from typing import Type, Annotated, Optional, TypedDict
|
||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
|
|
||||||
from langchain_core.tools import BaseTool
|
|
||||||
from langchain_openai import ChatOpenAI
|
from langchain_openai import ChatOpenAI
|
||||||
from langgraph.checkpoint.base import BaseCheckpointSaver
|
|
||||||
from langgraph.graph.message import add_messages
|
from langchain_core.messages import BaseMessage
|
||||||
from langchain_core.messages import BaseMessage, ToolMessage, AIMessage, filter_messages
|
|
||||||
from langgraph.graph.state import CompiledStateGraph
|
from langgraph.graph.state import CompiledStateGraph
|
||||||
|
from langgraph.graph.message import add_messages
|
||||||
|
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
@ -26,7 +25,8 @@ class Configuration:
|
|||||||
"""
|
"""
|
||||||
定义一个基础 Configuration 供 各类 graph 继承
|
定义一个基础 Configuration 供 各类 graph 继承
|
||||||
"""
|
"""
|
||||||
llm: ChatOpenAI
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class BaseAgent():
|
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 json
|
||||||
import asyncio
|
import asyncio
|
||||||
import traceback
|
import traceback
|
||||||
|
import uuid
|
||||||
from fastapi import APIRouter, Body
|
from fastapi import APIRouter, Body
|
||||||
from fastapi.responses import StreamingResponse, Response
|
from fastapi.responses import StreamingResponse
|
||||||
from src.core import HistoryManager
|
from langchain_core.messages import AIMessageChunk
|
||||||
|
|
||||||
from src import executor, config, retriever
|
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.models import select_model
|
||||||
from src.utils.logging_config import logger
|
from src.utils.logging_config import logger
|
||||||
|
|
||||||
@ -20,8 +24,8 @@ async def chat_get():
|
|||||||
def chat_post(
|
def chat_post(
|
||||||
query: str = Body(...),
|
query: str = Body(...),
|
||||||
meta: dict = Body(None),
|
meta: dict = Body(None),
|
||||||
history: list = Body(...),
|
history: list | None = Body(None),
|
||||||
cur_res_id: str = Body(...)):
|
thread_id: str | None = Body(None)):
|
||||||
|
|
||||||
model = select_model(config)
|
model = select_model(config)
|
||||||
meta["server_model_name"] = model.model_name
|
meta["server_model_name"] = model.model_name
|
||||||
@ -31,7 +35,6 @@ def chat_post(
|
|||||||
def make_chunk(content=None, **kwargs):
|
def make_chunk(content=None, **kwargs):
|
||||||
return json.dumps({
|
return json.dumps({
|
||||||
"response": content,
|
"response": content,
|
||||||
"model_name": config.model_name,
|
|
||||||
"meta": meta,
|
"meta": meta,
|
||||||
**kwargs
|
**kwargs
|
||||||
}, ensure_ascii=False).encode('utf-8') + b"\n"
|
}, ensure_ascii=False).encode('utf-8') + b"\n"
|
||||||
@ -76,7 +79,7 @@ def chat_post(
|
|||||||
else:
|
else:
|
||||||
content += delta.content or ""
|
content += delta.content or ""
|
||||||
|
|
||||||
chunk = make_chunk(content=content, status="loading")
|
chunk = make_chunk(content=delta.content, status="loading")
|
||||||
yield chunk
|
yield chunk
|
||||||
|
|
||||||
logger.debug(f"Final response: {content}")
|
logger.debug(f"Final response: {content}")
|
||||||
@ -118,3 +121,61 @@ async def call(query: str = Body(...), meta: dict = Body(None)):
|
|||||||
logger.debug({"query": query, "response": response.content})
|
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 {
|
try {
|
||||||
// 只有在 text 不为空时更新
|
// 只有在 text 不为空时更新
|
||||||
if (info.text !== null && info.text !== undefined && info.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 !== '') {
|
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,
|
history: conv.value.history,
|
||||||
meta: meta,
|
meta: meta,
|
||||||
cur_res_id: cur_res_id,
|
cur_res_id: cur_res_id,
|
||||||
|
thread_id: conv.value.id.toString(),
|
||||||
}),
|
}),
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
@ -639,6 +640,12 @@ watch(
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header__left {
|
||||||
|
.close {
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-btn {
|
.nav-btn {
|
||||||
@ -652,6 +659,7 @@ watch(
|
|||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
width: auto;
|
width: auto;
|
||||||
padding: 0.5rem 1rem;
|
padding: 0.5rem 1rem;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
@ -678,6 +686,7 @@ watch(
|
|||||||
padding: 12px;
|
padding: 12px;
|
||||||
z-index: 11;
|
z-index: 11;
|
||||||
width: 280px;
|
width: 280px;
|
||||||
|
transition: transform 0.3s ease, opacity 0.3s ease;
|
||||||
|
|
||||||
.flex-center {
|
.flex-center {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -1051,9 +1060,27 @@ watch(
|
|||||||
@keyframes loading {0%,80%,100%{transform:scale(0.5);}40%{transform:scale(1);}}
|
@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}
|
.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}
|
.swing-in-top-fwd {
|
||||||
@-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-animation: swing-in-top-fwd 0.3s cubic-bezier(0.175, 0.885, 0.320, 1.275) both;
|
||||||
@-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}}
|
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 fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
||||||
@keyframes slideInUp { from { transform: translateY(20px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
|
@keyframes slideInUp { from { transform: translateY(20px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
|
||||||
|
|||||||
@ -21,6 +21,8 @@ import {
|
|||||||
StarFilled,
|
StarFilled,
|
||||||
StarOutlined,
|
StarOutlined,
|
||||||
ExclamationCircleOutlined,
|
ExclamationCircleOutlined,
|
||||||
|
RobotOutlined,
|
||||||
|
RobotFilled,
|
||||||
} from '@ant-design/icons-vue'
|
} from '@ant-design/icons-vue'
|
||||||
import { themeConfig } from '@/assets/theme'
|
import { themeConfig } from '@/assets/theme'
|
||||||
import { useConfigStore } from '@/stores/config'
|
import { useConfigStore } from '@/stores/config'
|
||||||
@ -73,6 +75,37 @@ onMounted(() => {
|
|||||||
// 打印当前页面的路由信息,使用 vue3 的 setup composition API
|
// 打印当前页面的路由信息,使用 vue3 的 setup composition API
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
console.log(route)
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -107,22 +140,18 @@ console.log(route)
|
|||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
<div class="nav">
|
<div class="nav">
|
||||||
<RouterLink to="/chat" class="nav-item" active-class="active">
|
<!-- 使用mainList渲染导航项 -->
|
||||||
<component class="icon" :is="route.path === '/chat' ? MessageFilled : MessageOutlined" />
|
<RouterLink
|
||||||
<span class="text">对话</span>
|
v-for="(item, index) in mainList"
|
||||||
</RouterLink>
|
:key="index"
|
||||||
<RouterLink to="/database" class="nav-item" active-class="active">
|
:to="item.path"
|
||||||
<component class="icon" :is="route.path.startsWith('/database') ? FolderFilled : FolderOutlined" />
|
v-show="!item.hidden"
|
||||||
<span class="text">知识</span>
|
class="nav-item"
|
||||||
</RouterLink>
|
active-class="active">
|
||||||
<RouterLink to="/graph" class="nav-item" active-class="active">
|
<component class="icon" :is="route.path.startsWith(item.path) ? item.activeIcon : item.icon" />
|
||||||
<component class="icon" :is="route.path.startsWith('/graph') ? ProjectFilled: ProjectOutlined" />
|
<span class="text">{{item.name}}</span>
|
||||||
<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>
|
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
|
|
||||||
<a-tooltip placement="right">
|
<a-tooltip placement="right">
|
||||||
<template #title>后端疑似没有正常启动或者正在繁忙中,请刷新一下或者检查 docker logs api-dev</template>
|
<template #title>后端疑似没有正常启动或者正在繁忙中,请刷新一下或者检查 docker logs api-dev</template>
|
||||||
<div class="nav-item warning" v-if="!configStore.config._config_items">
|
<div class="nav-item warning" v-if="!configStore.config._config_items">
|
||||||
@ -140,7 +169,7 @@ console.log(route)
|
|||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</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" />
|
<component class="icon" :is="route.path === '/setting' ? SettingFilled : SettingOutlined" />
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
</div>
|
</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',
|
path: '/graph',
|
||||||
name: '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>
|
<template>
|
||||||
<div class="chat-container">
|
<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="actions">
|
||||||
<!-- <div class="action new" @click="addNewConv"><FormOutlined /></div> -->
|
<!-- <div class="action new" @click="addNewConv"><FormOutlined /></div> -->
|
||||||
<span class="header-title">对话历史</span>
|
<span class="header-title">对话历史</span>
|
||||||
@ -129,29 +129,24 @@ onMounted(() => {
|
|||||||
position: relative;
|
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 {
|
.conversations {
|
||||||
width: 230px; /* 初始宽度 */
|
width: 230px;
|
||||||
max-width: 230px;
|
max-width: 230px;
|
||||||
border-right: 1px solid var(--main-light-3);
|
border-right: 1px solid var(--main-light-3);
|
||||||
max-height: 100%;
|
|
||||||
background-color: var(--bg-sider);
|
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 {
|
& .actions {
|
||||||
height: var(--header-height);
|
height: var(--header-height);
|
||||||
@ -238,7 +233,6 @@ onMounted(() => {
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,6 +269,12 @@ onMounted(() => {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 0 16px 16px 0;
|
border-radius: 0 16px 16px 0;
|
||||||
box-shadow: 0 0 10px 1px rgba(0, 0, 0, 0.05);
|
box-shadow: 0 0 10px 1px rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
&:not(.is-open) {
|
||||||
|
width: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user