refactor(tools_factory): 重构工具工厂模块,优化工具管理和错误处理
- 重命名工具获取函数为 get_buildin_tools 和 get_buildin_tools_info - 改进工具ID生成和描述构建逻辑 - 添加详细的日志记录和错误处理 - 优化知识库工具创建流程,避免闭包问题 - 分离静态工具和知识库工具获取逻辑 - 增强计算器和知识图谱查询工具的错误处理
This commit is contained in:
parent
fbe85702a9
commit
402ff52739
@ -25,6 +25,8 @@
|
|||||||
|
|
||||||
下面的功能会放在后续版本实现,暂时未定
|
下面的功能会放在后续版本实现,暂时未定
|
||||||
|
|
||||||
|
|
||||||
|
- [ ] 使用其他的聊天记录管理方法(现在是基于 LangGraph 的 Memory 实现的)
|
||||||
- [ ] 封装现有工具为 mcp(stdio)调用
|
- [ ] 封装现有工具为 mcp(stdio)调用
|
||||||
- [ ] 支持额外 mcp 配置(代码端)
|
- [ ] 支持额外 mcp 配置(代码端)
|
||||||
- [ ] 添加用户日志与用户反馈模块,可以在 AgentView 中查看信息
|
- [ ] 添加用户日志与用户反馈模块,可以在 AgentView 中查看信息
|
||||||
|
|||||||
40
docs/vibe/AGENT.md
Normal file
40
docs/vibe/AGENT.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
|
||||||
|
# 项目目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
Yuxi-Know/
|
||||||
|
├── docker/ # Docker 配置文件
|
||||||
|
├── docs/ # 项目文档
|
||||||
|
├── scripts/ # 脚本文件,如批量上传等
|
||||||
|
├── server/ # 服务端代码(部分)
|
||||||
|
├── src/ # 主要源代码目录
|
||||||
|
│ ├── agents/ # 智能体应用
|
||||||
|
│ ├── config/ # 配置文件
|
||||||
|
│ ├── knowledge/ # 知识库相关
|
||||||
|
│ ├── models/ # 数据模型
|
||||||
|
│ ├── plugins/ # 插件
|
||||||
|
│ ├── static/ # 静态资源
|
||||||
|
│ └── utils/ # 工具函数
|
||||||
|
├── web/ # 前端代码
|
||||||
|
└── docker-compose.yml # Docker Compose 配置
|
||||||
|
```
|
||||||
|
|
||||||
|
# 主要容器名称
|
||||||
|
|
||||||
|
项目使用 Docker Compose 管理多个服务,主要容器名称如下:
|
||||||
|
- `api-dev` - FastAPI 后端服务
|
||||||
|
- `web-dev` - Vue.js 前端开发服务器
|
||||||
|
- `graph` - Neo4j 图数据库
|
||||||
|
- `milvus` - 向量数据库,包含 etcd 和 MinIO 依赖
|
||||||
|
- `mineru` - 可选的 MinerU OCR 服务(需要 GPU)
|
||||||
|
- `paddlex` - 可选的 PaddleX OCR 服务(需要 GPU)
|
||||||
|
|
||||||
|
## 项目调试
|
||||||
|
|
||||||
|
此项目是使用 Docker 进行部署的,使用 `docker compose up -d` 命令进行构建和启动。因此当进行任何修改的时候,不要尝试启动这个项目,应该先检查项目是否已经在后台启动(`docker ps`),具体的可以阅读 [docker-compose.yml](docker-compose.yml).
|
||||||
|
|
||||||
|
前端和后端都是配置了自动启动的,因此当修改完成后,会自动更新,可以使用 docker logs 查看日志。对于部分场景可以创建一个 test_router.py 在 [server/routers](server/routers) 中,然后通过 API 测试功能场景。对于前端的 UI 修改,则不用测试。
|
||||||
|
|
||||||
|
## 风格说明
|
||||||
|
|
||||||
|
UI风格要简洁,同时要保持一致性,颜色要尽量参考 [base.css](web/src/assets/css/base.css) 中的颜色。不要悬停位移,不要过度使用阴影以及渐变色。
|
||||||
@ -14,7 +14,7 @@ from src import executor, config
|
|||||||
from src.agents import agent_manager
|
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
|
||||||
from src.agents.tools_factory import get_runnable_tools
|
from src.agents.tools_factory import get_buildin_tools
|
||||||
from server.routers.auth_router import get_admin_user
|
from server.routers.auth_router import get_admin_user
|
||||||
from server.utils.auth_middleware import get_required_user, get_db
|
from server.utils.auth_middleware import get_required_user, get_db
|
||||||
from server.models.user_model import User
|
from server.models.user_model import User
|
||||||
@ -181,7 +181,7 @@ async def update_chat_models(model_provider: str, model_names: list[str], curren
|
|||||||
@chat.get("/tools")
|
@chat.get("/tools")
|
||||||
async def get_tools(current_user: User = Depends(get_admin_user)):
|
async def get_tools(current_user: User = Depends(get_admin_user)):
|
||||||
"""获取所有可用工具(需要登录)"""
|
"""获取所有可用工具(需要登录)"""
|
||||||
return {"tools": list(get_runnable_tools().keys())}
|
return {"tools": list(get_buildin_tools().keys())}
|
||||||
|
|
||||||
@chat.post("/agent/{agent_id}/config")
|
@chat.post("/agent/{agent_id}/config")
|
||||||
async def save_agent_config(
|
async def save_agent_config(
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends
|
||||||
from src.agents.tools_factory import get_all_tools_info
|
from src.agents.tools_factory import get_buildin_tools_info
|
||||||
from server.models.user_model import User
|
from server.models.user_model import User
|
||||||
from server.utils.auth_middleware import get_required_user
|
from server.utils.auth_middleware import get_required_user
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ tool = chat = APIRouter(prefix="/tool", tags=["tool"])
|
|||||||
async def get_tools(current_user: User = Depends(get_required_user)):
|
async def get_tools(current_user: User = Depends(get_required_user)):
|
||||||
"""获取所有可用工具的信息"""
|
"""获取所有可用工具的信息"""
|
||||||
try:
|
try:
|
||||||
tools_info = get_all_tools_info()
|
tools_info = get_buildin_tools_info()
|
||||||
return {"tools": tools_info}
|
return {"tools": tools_info}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {"error": str(e)}
|
return {"error": str(e)}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import uuid
|
|||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
from src.agents.registry import Configuration
|
from src.agents.registry import Configuration
|
||||||
from src.agents.tools_factory import get_runnable_tools
|
from src.agents.tools_factory import get_buildin_tools
|
||||||
|
|
||||||
@dataclass(kw_only=True)
|
@dataclass(kw_only=True)
|
||||||
class ChatbotConfiguration(Configuration):
|
class ChatbotConfiguration(Configuration):
|
||||||
@ -37,7 +37,7 @@ class ChatbotConfiguration(Configuration):
|
|||||||
default_factory=list,
|
default_factory=list,
|
||||||
metadata={
|
metadata={
|
||||||
"name": "工具",
|
"name": "工具",
|
||||||
"options": list(get_runnable_tools().keys()), # 这里的选择是所有的工具
|
"options": list(get_buildin_tools().keys()), # 这里的选择是所有的工具
|
||||||
"description": "工具列表"
|
"description": "工具列表"
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@ -15,7 +15,7 @@ from src.utils import logger
|
|||||||
from src.agents.registry import State, BaseAgent
|
from src.agents.registry import State, BaseAgent
|
||||||
from src.agents.utils import load_chat_model, get_cur_time_with_utc
|
from src.agents.utils import load_chat_model, get_cur_time_with_utc
|
||||||
from src.agents.chatbot.configuration import ChatbotConfiguration
|
from src.agents.chatbot.configuration import ChatbotConfiguration
|
||||||
from src.agents.tools_factory import get_runnable_tools
|
from src.agents.tools_factory import get_buildin_tools
|
||||||
|
|
||||||
class ChatbotAgent(BaseAgent):
|
class ChatbotAgent(BaseAgent):
|
||||||
name = "智能体助手"
|
name = "智能体助手"
|
||||||
@ -33,7 +33,7 @@ class ChatbotAgent(BaseAgent):
|
|||||||
默认不使用任何工具。
|
默认不使用任何工具。
|
||||||
如果配置为列表,则使用列表中的工具。
|
如果配置为列表,则使用列表中的工具。
|
||||||
"""
|
"""
|
||||||
platform_tools = get_runnable_tools()
|
platform_tools = get_buildin_tools()
|
||||||
if tools is None or not isinstance(tools, list) or len(tools) == 0:
|
if tools is None or not isinstance(tools, list) or len(tools) == 0:
|
||||||
# 默认不使用任何工具
|
# 默认不使用任何工具
|
||||||
logger.info("未配置工具或配置为空,不使用任何工具")
|
logger.info("未配置工具或配置为空,不使用任何工具")
|
||||||
@ -65,7 +65,7 @@ class ChatbotAgent(BaseAgent):
|
|||||||
if self.graph:
|
if self.graph:
|
||||||
return self.graph
|
return self.graph
|
||||||
|
|
||||||
runnable_tools = get_runnable_tools()
|
runnable_tools = get_buildin_tools()
|
||||||
tools = list(runnable_tools.values())
|
tools = list(runnable_tools.values())
|
||||||
tools_name = list(runnable_tools.keys())
|
tools_name = list(runnable_tools.keys())
|
||||||
logger.debug(f"build graph `{self.id}` with tools: {tools_name}")
|
logger.debug(f"build graph `{self.id}` with tools: {tools_name}")
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
import json
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import inspect
|
import hashlib
|
||||||
import types
|
import os
|
||||||
from collections.abc import Callable
|
|
||||||
from typing import Annotated, Any
|
from typing import Annotated, Any
|
||||||
|
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
@ -13,10 +11,6 @@ from src import config, graph_base, knowledge_base
|
|||||||
from src.utils import logger
|
from src.utils import logger
|
||||||
|
|
||||||
|
|
||||||
# 工具注册表 - 移到前面以避免NameError
|
|
||||||
_TOOLS_REGISTRY = {}
|
|
||||||
|
|
||||||
|
|
||||||
class KnowledgeRetrieverModel(BaseModel):
|
class KnowledgeRetrieverModel(BaseModel):
|
||||||
query_text: str = Field(
|
query_text: str = Field(
|
||||||
description=(
|
description=(
|
||||||
@ -25,128 +19,176 @@ class KnowledgeRetrieverModel(BaseModel):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_runnable_tools():
|
|
||||||
|
def _create_retriever_wrapper(db_id: str, retriever_info: dict[str, Any]):
|
||||||
|
"""创建检索器包装函数的工厂函数,避免闭包变量捕获问题"""
|
||||||
|
async def async_retriever_wrapper(query_text: str) -> Any:
|
||||||
|
"""异步检索器包装函数"""
|
||||||
|
retriever = retriever_info["retriever"]
|
||||||
|
try:
|
||||||
|
logger.debug(f"Retrieving from database {db_id} with query: {query_text}")
|
||||||
|
if asyncio.iscoroutinefunction(retriever):
|
||||||
|
result = await retriever(query_text)
|
||||||
|
else:
|
||||||
|
result = retriever(query_text)
|
||||||
|
logger.debug(f"Retrieved {len(result) if isinstance(result, list) else 'N/A'} results from {db_id}")
|
||||||
|
return result
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in retriever {db_id}: {e}")
|
||||||
|
return f"检索失败: {str(e)}"
|
||||||
|
|
||||||
|
return async_retriever_wrapper
|
||||||
|
|
||||||
|
def get_buildin_tools() -> dict[str, Any]:
|
||||||
"""获取所有可运行的工具(给大模型使用)"""
|
"""获取所有可运行的工具(给大模型使用)"""
|
||||||
tools = _TOOLS_REGISTRY.copy()
|
tools = {}
|
||||||
|
|
||||||
# 获取所有知识库
|
try:
|
||||||
for db_Id, retrieve_info in knowledge_base.get_retrievers().items():
|
# 获取所有知识库基于的工具
|
||||||
_id = f"retrieve_{db_Id[:8]}" # Deepseek does not support non-alphanumeric characters in tool names
|
kb_tools = get_kb_based_tools()
|
||||||
description = (
|
static_tools = get_static_tools()
|
||||||
f"使用 {retrieve_info['name']} 知识库进行检索。\n"
|
|
||||||
f"下面是这个知识库的描述:\n{retrieve_info['description']}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# 创建异步工具,确保正确处理异步检索器
|
tools.update(kb_tools)
|
||||||
async def async_retriever_wrapper(query_text: str, db_id=db_Id, retriever_info=retrieve_info):
|
tools.update(static_tools)
|
||||||
"""异步检索器包装函数"""
|
|
||||||
retriever = retriever_info["retriever"]
|
|
||||||
try:
|
|
||||||
if asyncio.iscoroutinefunction(retriever):
|
|
||||||
result = await retriever(query_text)
|
|
||||||
else:
|
|
||||||
result = retriever(query_text)
|
|
||||||
return result
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error in retriever {db_id}: {e}")
|
|
||||||
return f"检索失败: {str(e)}"
|
|
||||||
|
|
||||||
# 使用 StructuredTool.from_function 创建异步工具
|
except Exception as e:
|
||||||
tools[_id] = StructuredTool.from_function(
|
logger.error(f"Failed to get knowledge base retrievers: {e}")
|
||||||
coroutine=async_retriever_wrapper, # 指定为协程
|
|
||||||
name=_id,
|
|
||||||
description=description,
|
|
||||||
args_schema=KnowledgeRetrieverModel,
|
|
||||||
metadata=retrieve_info
|
|
||||||
)
|
|
||||||
|
|
||||||
|
logger.info(f"Total tools available: {len(tools)}")
|
||||||
return tools
|
return tools
|
||||||
|
|
||||||
def get_all_tools_info():
|
def get_kb_based_tools() -> dict[str, Any]:
|
||||||
|
"""获取所有知识库基于的工具"""
|
||||||
|
# 获取所有知识库
|
||||||
|
kb_tools = {}
|
||||||
|
retrievers = knowledge_base.get_retrievers()
|
||||||
|
logger.debug(f"Found {len(retrievers)} knowledge base retrievers")
|
||||||
|
|
||||||
|
for db_id, retrieve_info in retrievers.items():
|
||||||
|
try:
|
||||||
|
# 使用改进的工具ID生成策略
|
||||||
|
tool_id = f"query_{db_id[:8]}"
|
||||||
|
|
||||||
|
# 构建工具描述
|
||||||
|
description = (
|
||||||
|
f"使用 {retrieve_info['name']} 知识库进行检索。\n"
|
||||||
|
f"下面是这个知识库的描述:\n{retrieve_info['description'] or '没有描述。'}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 使用工厂函数创建检索器包装函数,避免闭包问题
|
||||||
|
retriever_wrapper = _create_retriever_wrapper(db_id, retrieve_info)
|
||||||
|
|
||||||
|
# 使用 StructuredTool.from_function 创建异步工具
|
||||||
|
tool = StructuredTool.from_function(
|
||||||
|
coroutine=retriever_wrapper,
|
||||||
|
name=tool_id,
|
||||||
|
description=description,
|
||||||
|
args_schema=KnowledgeRetrieverModel,
|
||||||
|
metadata=retrieve_info | {
|
||||||
|
"tag": "knowledgebase"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
kb_tools[tool_id] = tool
|
||||||
|
logger.debug(f"Successfully created tool {tool_id} for database {db_id}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to create tool for database {db_id}: {e}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
return kb_tools
|
||||||
|
|
||||||
|
def get_buildin_tools_info() -> dict[str, dict[str, Any]]:
|
||||||
"""获取所有工具的信息(用于前端展示)"""
|
"""获取所有工具的信息(用于前端展示)"""
|
||||||
tools_info = {}
|
tools_info = {}
|
||||||
|
|
||||||
tools = get_runnable_tools()
|
try:
|
||||||
|
tools = get_buildin_tools()
|
||||||
|
logger.debug(f"Processing {len(tools)} tools for info extraction")
|
||||||
|
|
||||||
# 获取注册的工具信息
|
# 获取注册的工具信息
|
||||||
for _id, tool_obj in tools.items():
|
for tool_id, tool_obj in tools.items():
|
||||||
|
try:
|
||||||
|
metadata = getattr(tool_obj, 'metadata', {}) or {}
|
||||||
|
info = {
|
||||||
|
"id": tool_id,
|
||||||
|
"name": metadata.get('name', tool_id),
|
||||||
|
"description": metadata.get('description') or getattr(tool_obj, 'description', ''),
|
||||||
|
'metadata': metadata,
|
||||||
|
"args": []
|
||||||
|
}
|
||||||
|
|
||||||
metadata = getattr(tool_obj, 'metadata', {}) or {}
|
# 获取工具参数信息
|
||||||
info = {
|
try:
|
||||||
"id": _id,
|
if hasattr(tool_obj, 'args_schema') and tool_obj.args_schema:
|
||||||
"name": metadata.get('name', _id),
|
schema = tool_obj.args_schema.schema()
|
||||||
"description": metadata.get('description') or getattr(tool_obj, 'description', ''),
|
if 'properties' in schema:
|
||||||
'metadata': metadata,
|
for arg_name, arg_info in schema['properties'].items():
|
||||||
"args": []
|
info["args"].append({
|
||||||
}
|
"name": arg_name,
|
||||||
|
"type": arg_info.get('type', ''),
|
||||||
|
"description": arg_info.get('description', '')
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Failed to extract args schema for tool {tool_id}: {e}")
|
||||||
|
|
||||||
# 获取工具参数信息
|
tools_info[tool_id] = info
|
||||||
if hasattr(tool_obj, 'args_schema') and tool_obj.args_schema:
|
logger.debug(f"Successfully processed tool info for {tool_id}")
|
||||||
schema = tool_obj.args_schema.schema()
|
|
||||||
if 'properties' in schema:
|
|
||||||
for arg_name, arg_info in schema['properties'].items():
|
|
||||||
info["args"].append({
|
|
||||||
"name": arg_name,
|
|
||||||
"type": arg_info.get('type', ''),
|
|
||||||
"description": arg_info.get('description', '')
|
|
||||||
})
|
|
||||||
tools_info[info['id']] = info
|
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to process tool {tool_id}: {e}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to get tools info: {e}")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
logger.info(f"Successfully extracted info for {len(tools_info)} tools")
|
||||||
return tools_info
|
return tools_info
|
||||||
|
|
||||||
class BaseToolOutput:
|
|
||||||
"""
|
|
||||||
LLM 要求 Tool 的输出为 str,但 Tool 用在别处时希望它正常返回结构化数据。
|
|
||||||
只需要将 Tool 返回值用该类封装,能同时满足两者的需要。
|
|
||||||
基类简单的将返回值字符串化,或指定 format="json" 将其转为 json。
|
|
||||||
用户也可以继承该类定义自己的转换方法。
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
data: Any,
|
|
||||||
format: str | Callable | None = None,
|
|
||||||
data_alias: str = "",
|
|
||||||
**extras: Any,
|
|
||||||
) -> None:
|
|
||||||
self.data = data
|
|
||||||
self.format = format
|
|
||||||
self.extras = extras
|
|
||||||
if data_alias:
|
|
||||||
setattr(self, data_alias, property(lambda obj: obj.data))
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
|
||||||
if self.format == "json":
|
|
||||||
return json.dumps(self.data, ensure_ascii=False, indent=2)
|
|
||||||
elif callable(self.format):
|
|
||||||
return self.format(self)
|
|
||||||
else:
|
|
||||||
return str(self.data)
|
|
||||||
|
|
||||||
@tool
|
@tool
|
||||||
def calculator(a: float, b: float, operation: str) -> float:
|
def calculator(a: float, b: float, operation: str) -> float:
|
||||||
"""Calculate two numbers. operation: add, subtract, multiply, divide"""
|
"""Calculate two numbers. operation: add, subtract, multiply, divide"""
|
||||||
if operation == "add":
|
try:
|
||||||
return a + b
|
if operation == "add":
|
||||||
elif operation == "subtract":
|
return a + b
|
||||||
return a - b
|
elif operation == "subtract":
|
||||||
elif operation == "multiply":
|
return a - b
|
||||||
return a * b
|
elif operation == "multiply":
|
||||||
elif operation == "divide":
|
return a * b
|
||||||
return a / b
|
elif operation == "divide":
|
||||||
else:
|
if b == 0:
|
||||||
raise ValueError(f"Invalid operation: {operation}, only support add, subtract, multiply, divide")
|
raise ZeroDivisionError("除数不能为零")
|
||||||
|
return a / b
|
||||||
|
else:
|
||||||
|
raise ValueError(f"不支持的运算类型: {operation},仅支持 add, subtract, multiply, divide")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Calculator error: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
@tool
|
@tool
|
||||||
def query_knowledge_graph(query: Annotated[str, "The keyword to query knowledge graph."]):
|
def query_knowledge_graph(query: Annotated[str, "The keyword to query knowledge graph."]) -> Any:
|
||||||
"""Use this to query knowledge graph, which include some food domain knowledge."""
|
"""Use this to query knowledge graph, which include some food domain knowledge."""
|
||||||
return graph_base.query_node(query, hops=2, return_format='triples')
|
try:
|
||||||
|
logger.debug(f"Querying knowledge graph with: {query}")
|
||||||
|
result = graph_base.query_node(query, hops=2, return_format='triples')
|
||||||
|
logger.debug(f"Knowledge graph query returned {len(result.get('triples', [])) if isinstance(result, d) else 'N/A'} triples")
|
||||||
|
return result
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Knowledge graph query error: {e}")
|
||||||
|
return f"知识图谱查询失败: {str(e)}"
|
||||||
|
|
||||||
# 更新工具注册表
|
def get_static_tools() -> dict[str, Any]:
|
||||||
_TOOLS_REGISTRY.update({
|
"""注册静态工具"""
|
||||||
"Calculator": calculator,
|
static_tools = {
|
||||||
"QueryKnowledgeGraph": query_knowledge_graph,
|
"Calculator": calculator,
|
||||||
})
|
"QueryKnowledgeGraph": query_knowledge_graph,
|
||||||
|
}
|
||||||
|
|
||||||
if config.enable_web_search:
|
# 检查是否启用网页搜索
|
||||||
_TOOLS_REGISTRY["WebSearchWithTavily"] = TavilySearch(max_results=10)
|
if config.enable_web_search:
|
||||||
|
static_tools["WebSearchWithTavily"] = TavilySearch(max_results=10)
|
||||||
|
|
||||||
|
|
||||||
|
return static_tools
|
||||||
|
|||||||
@ -894,7 +894,7 @@ watch(() => props.isOpen, (newVal) => {
|
|||||||
&:focus-within {
|
&:focus-within {
|
||||||
border-color: var(--main-color);
|
border-color: var(--main-color);
|
||||||
box-shadow: 0 0 0 2px rgba(var(--main-color-rgb), 0.1);
|
box-shadow: 0 0 0 2px rgba(var(--main-color-rgb), 0.1);
|
||||||
|
|
||||||
.search-icon {
|
.search-icon {
|
||||||
color: var(--main-color);
|
color: var(--main-color);
|
||||||
}
|
}
|
||||||
@ -907,13 +907,19 @@ watch(() => props.isOpen, (newVal) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tools-list {
|
.tools-list {
|
||||||
max-height: 300px;
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
max-height: max(60vh, 800px);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
border: 1px solid var(--gray-200);
|
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
background: white;
|
background: white;
|
||||||
padding: 4px;
|
|
||||||
|
// 在小屏幕下调整为单列布局
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
width: 6px;
|
width: 6px;
|
||||||
@ -943,38 +949,14 @@ watch(() => props.isOpen, (newVal) => {
|
|||||||
background: white;
|
background: white;
|
||||||
border: 1px solid var(--gray-200);
|
border: 1px solid var(--gray-200);
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: var(--main-color);
|
border-color: var(--gray-300);
|
||||||
background: var(--gray-50);
|
background: var(--gray-20);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.selected {
|
|
||||||
background: var(--main-10);
|
|
||||||
border-color: var(--main-color);
|
|
||||||
|
|
||||||
.tool-name {
|
|
||||||
color: var(--main-color);
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tool-description {
|
|
||||||
color: var(--gray-700);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tool-indicator {
|
|
||||||
color: var(--main-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tool-content {
|
.tool-content {
|
||||||
.tool-header {
|
.tool-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
align-items: center;
|
||||||
align-items: flex-start;
|
|
||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|
||||||
@ -1005,6 +987,25 @@ watch(() => props.isOpen, (newVal) => {
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
background: var(--main-30);
|
||||||
|
// border-color: var(--main-color);
|
||||||
|
|
||||||
|
.tool-content {
|
||||||
|
.tool-name {
|
||||||
|
color: var(--main-color);
|
||||||
|
}
|
||||||
|
.tool-indicator {
|
||||||
|
color: var(--main-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.tool-description {
|
||||||
|
color: var(--gray-700);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user