feat(system):优化后端
1.新增后端测试 2.增加了后端的加密 3.增加了i18n(国际化)
This commit is contained in:
@@ -28,10 +28,10 @@ class ApprovalToolData(BaseToolData):
|
||||
"regulatory_node",
|
||||
"growth_node",
|
||||
"",
|
||||
"",
|
||||
]
|
||||
] = ["control_node", "consciousness_node"]
|
||||
config_args: Dict[str, str] = {}
|
||||
category: str = "system"
|
||||
|
||||
|
||||
async def approval(message: str, trace_id: str) -> str:
|
||||
|
||||
@@ -29,7 +29,8 @@ class BaseToolData(BaseModel):
|
||||
"regulatory_node",
|
||||
"growth_node",
|
||||
"",
|
||||
"",
|
||||
]
|
||||
] = []
|
||||
config_args: Dict[str, str] = {}
|
||||
category: str = "other"
|
||||
"""工具分类:system(系统内置)、search(搜索)、mcp(MCP 服务器)、other(其他)"""
|
||||
|
||||
@@ -12,6 +12,6 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from .file_reader import FileReaderData, file_reader
|
||||
from .file_reader import FileReaderToolData, file_reader
|
||||
|
||||
__all__ = ["FileReaderData", "file_reader"]
|
||||
__all__ = ["FileReaderToolData", "file_reader"]
|
||||
|
||||
@@ -12,36 +12,45 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pydantic_ai import RunContext
|
||||
"""File Reader Tool Plugin for KiloStar.
|
||||
|
||||
Reads the contents of a file from the local filesystem.
|
||||
"""
|
||||
|
||||
from kilostar.plugin.tool_plugin.base_tool import BaseToolData
|
||||
import os
|
||||
from typing import List, Literal, Dict
|
||||
|
||||
|
||||
class FileReaderData(BaseToolData):
|
||||
"""``file_reader`` 工具的元数据:声明工具的名称、描述与是否系统级别。"""
|
||||
class FileReaderToolData(BaseToolData):
|
||||
"""``file_reader`` 工具的元数据。"""
|
||||
|
||||
is_system: bool = True
|
||||
name: str = "file_reader"
|
||||
description: str = "读取本地文件的内容"
|
||||
action_scope: List[
|
||||
Literal[
|
||||
"control_node",
|
||||
"consciousness_node",
|
||||
"regulatory_node",
|
||||
"growth_node",
|
||||
"",
|
||||
]
|
||||
] = ["control_node"]
|
||||
config_args: Dict[str, str] = {}
|
||||
category: str = "system"
|
||||
|
||||
|
||||
def file_reader(ctx: RunContext, filepath: str) -> str:
|
||||
"""读取本地文件内容的工具。
|
||||
async def file_reader(file_path: str) -> str:
|
||||
"""读取本地文件的内容。
|
||||
|
||||
Args:
|
||||
filepath: 目标文件的绝对路径或相对路径。
|
||||
file_path: 文件的绝对路径或相对路径
|
||||
|
||||
Returns:
|
||||
如果文件存在并可读,返回文件内容;否则返回错误信息。
|
||||
文件内容文本,若文件不存在则返回错误信息
|
||||
"""
|
||||
if not os.path.exists(filepath):
|
||||
return f"Error: 文件 {filepath} 不存在。"
|
||||
if not os.path.isfile(filepath):
|
||||
return f"Error: {filepath} 不是一个文件。"
|
||||
|
||||
try:
|
||||
with open(filepath, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
return content
|
||||
with open(file_path, "r", encoding="utf-8") as f:
|
||||
return f.read()
|
||||
except FileNotFoundError:
|
||||
return f"[Error] File not found: {file_path}"
|
||||
except Exception as e:
|
||||
return f"Error: 读取文件失败,原因:{str(e)}"
|
||||
return f"[Error] Failed to read file: {str(e)}"
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
# Copyright 2026 zhaoxi826
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Tavily Web Search Tool Plugin for KiloStar.
|
||||
|
||||
Provides intelligent web search capabilities via Tavily API.
|
||||
API key 取值优先级:调用参数 > GlobalStateMachine 中 ``tavily_search`` 工具配置 >
|
||||
环境变量 ``TAVILY_API_KEY``。
|
||||
"""
|
||||
|
||||
import os
|
||||
from typing import List, Literal, Dict, Optional
|
||||
|
||||
from kilostar.plugin.tool_plugin.base_tool import BaseToolData
|
||||
from tavily import AsyncTavilyClient
|
||||
|
||||
|
||||
class TavilySearchToolData(BaseToolData):
|
||||
"""Tavily 搜索工具的元数据:面向所有节点开放。"""
|
||||
|
||||
is_system: bool = False
|
||||
action_scope: List[
|
||||
Literal[
|
||||
"control_node",
|
||||
"consciousness_node",
|
||||
"regulatory_node",
|
||||
"growth_node",
|
||||
]
|
||||
] = ["control_node", "consciousness_node", "regulatory_node"]
|
||||
config_args: Dict[str, str] = {
|
||||
"api_key": "",
|
||||
"max_results": "5",
|
||||
"search_depth": "basic",
|
||||
"include_answer": "true",
|
||||
}
|
||||
category: str = "search"
|
||||
|
||||
|
||||
async def _resolve_api_key(explicit: Optional[str]) -> Optional[str]:
|
||||
"""按优先级解析 Tavily API key:显式参数 > GSM 配置 > 环境变量。"""
|
||||
if explicit:
|
||||
return explicit
|
||||
try:
|
||||
from kilostar.core.global_state_machine.gsm_snapshot import fetch_snapshot
|
||||
|
||||
# 工具调用是高频热路径,走 Object Store 快照而不是 actor RPC
|
||||
snapshot = await fetch_snapshot()
|
||||
cfg = snapshot.tool_configs.get("tavily_search") or {}
|
||||
if isinstance(cfg, dict) and cfg.get("api_key"):
|
||||
return cfg["api_key"]
|
||||
except Exception:
|
||||
pass
|
||||
return os.environ.get("TAVILY_API_KEY")
|
||||
|
||||
|
||||
async def tavily_search(
|
||||
query: str,
|
||||
max_results: int = 5,
|
||||
search_depth: str = "basic",
|
||||
include_answer: bool = True,
|
||||
api_key: Optional[str] = None,
|
||||
) -> str:
|
||||
"""使用 Tavily 进行网络搜索,获取高质量的网络搜索结果。
|
||||
|
||||
Args:
|
||||
query: 搜索查询内容
|
||||
max_results: 返回的最大结果数量(1-10)
|
||||
search_depth: 搜索深度,"basic" 或 "advanced"
|
||||
include_answer: 是否包含 AI 生成的答案摘要
|
||||
api_key: 可选;不传则按 GSM 配置 → 环境变量顺序解析
|
||||
|
||||
Returns:
|
||||
格式化的搜索结果文本,包含标题、URL、摘要和可选的 AI 答案
|
||||
"""
|
||||
resolved_key = await _resolve_api_key(api_key)
|
||||
if not resolved_key:
|
||||
return (
|
||||
"[Error] Tavily API key 未配置。"
|
||||
"请在 ``/api/v1/resource/tool/config`` 写入或设置环境变量 ``TAVILY_API_KEY``。"
|
||||
)
|
||||
|
||||
try:
|
||||
client = AsyncTavilyClient(api_key=resolved_key)
|
||||
result = await client.search(
|
||||
query=query,
|
||||
max_results=min(max_results, 10),
|
||||
search_depth=search_depth,
|
||||
include_answer=include_answer,
|
||||
)
|
||||
|
||||
lines = []
|
||||
if include_answer and result.get("answer"):
|
||||
lines.append(f"【AI 摘要】{result['answer']}\n")
|
||||
|
||||
results = result.get("results", [])
|
||||
if not results:
|
||||
return "No results found for the query."
|
||||
|
||||
lines.append("【搜索结果】")
|
||||
for i, item in enumerate(results, 1):
|
||||
title = item.get("title", "Untitled")
|
||||
url = item.get("url", "")
|
||||
content = item.get("content", "").strip()
|
||||
lines.append(f"\n{i}. {title}")
|
||||
lines.append(f" URL: {url}")
|
||||
if content:
|
||||
lines.append(f" {content[:300]}{'...' if len(content) > 300 else ''}")
|
||||
|
||||
return "\n".join(lines)
|
||||
except Exception as e:
|
||||
return f"[Error] Tavily search failed: {str(e)}"
|
||||
Reference in New Issue
Block a user