005ce566a8
- Provider model_settings (Provider+Model 级别参数配置): DB JSONB → API → GSM → AgentFactory.resolve → 三节点 agent.run 注入 - 新增 data/toolset/regulatory_toolset/: 监管节点专属工具(query_workflow_status / query_task_list / send_file) - send_file 从 interactive_toolset 迁移至 regulatory_toolset,interactive 仅保留 approval - mcp_helper 合入 GlobalPluginManager dispatch tools - 前端 Provider 弹窗参数设置区加 JSON 编辑器(model_settings) - 前端 Plugin 页面新增"重型插件"Tab(HeavyPluginList 占位) - .gitignore 精简:去除系统默认项,修复 data/ 子目录追踪 - data/toolset/ 与 data/plugin/ 首次纳入版本控制 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
145 lines
5.5 KiB
Python
145 lines
5.5 KiB
Python
# 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.
|
||
|
||
from typing import Sequence, Any, Dict
|
||
|
||
from pydantic_ai import Agent
|
||
from pydantic_ai.models.openai import OpenAIChatModel
|
||
from pydantic_ai.models.anthropic import AnthropicModel
|
||
from pydantic_ai.models.gemini import GeminiModel
|
||
from pydantic_ai.providers.openai import OpenAIProvider
|
||
from pydantic_ai.providers.anthropic import AnthropicProvider
|
||
from pydantic_ai.providers.deepseek import DeepSeekProvider
|
||
from pydantic_ai.providers.google import GoogleProvider
|
||
from pydantic_ai.toolsets import AbstractToolset
|
||
|
||
from kilostar.core.global_state_machine.model_provider import Provider
|
||
from kilostar.utils.agent_model import ResponseModel, DepsModel
|
||
from kilostar.utils.error import ModelNotExistError
|
||
|
||
|
||
class AgentFactory:
|
||
"""模型工厂:把内部的 ``Provider`` 元数据翻译成 pydantic-ai 的 ``Agent``。
|
||
|
||
支持 openai / claude / deepseek / gemini 四类后端,差异通过
|
||
``_models_mapping`` 中的 ``model_class`` + ``provider_class`` 键值对屏蔽。
|
||
同时支持传入本地工具(tools)和外部工具集(toolsets),包括 MCP 服务器。
|
||
"""
|
||
|
||
def __init__(self):
|
||
self._models_mapping = {
|
||
"openai": {
|
||
"model_class": OpenAIChatModel,
|
||
"provider_class": OpenAIProvider,
|
||
"provider_kwargs": {"base_url": True, "api_key": True},
|
||
},
|
||
"claude": {
|
||
"model_class": AnthropicModel,
|
||
"provider_class": AnthropicProvider,
|
||
"provider_kwargs": {"api_key": True},
|
||
},
|
||
"deepseek": {
|
||
"model_class": OpenAIChatModel,
|
||
"provider_class": DeepSeekProvider,
|
||
"provider_kwargs": {"api_key": True},
|
||
},
|
||
"gemini": {
|
||
"model_class": GeminiModel,
|
||
"provider_class": GoogleProvider,
|
||
"provider_kwargs": {"api_key": True},
|
||
},
|
||
}
|
||
|
||
def create_agent(
|
||
self,
|
||
provider: Provider,
|
||
model_id: str,
|
||
output_type: ResponseModel,
|
||
system_prompt: str,
|
||
deps_type: DepsModel,
|
||
agent_name: str,
|
||
tools: list = None,
|
||
toolsets: Sequence[AbstractToolset[Any]] = None,
|
||
) -> Agent:
|
||
"""将输入的 provider 对象实例化为一个 pydantic-ai 的 agent 对象。
|
||
|
||
Args:
|
||
provider: Provider 对象,从 global_state_machine 中获取
|
||
model_id: 模型名
|
||
output_type: 输出格式
|
||
system_prompt: 系统提示词
|
||
deps_type: 依赖类型,在 agent 运行时动态输入的格式化消息
|
||
agent_name: agent 的名字
|
||
tools: 本地工具函数列表
|
||
toolsets: 外部工具集列表(包括 MCP 服务器等 AbstractToolset 实例)
|
||
|
||
Returns:
|
||
被实例化的 pydantic-ai 的 Agent 对象
|
||
"""
|
||
if model_id not in provider.provider_models:
|
||
raise ModelNotExistError("模型不存在")
|
||
if provider.provider_type not in self._models_mapping:
|
||
raise ValueError(f"不支持的协议类型: {provider.provider_type}")
|
||
|
||
config = self._models_mapping[provider.provider_type]
|
||
model_class = config["model_class"]
|
||
provider_class = config["provider_class"]
|
||
provider_kwargs = config["provider_kwargs"]
|
||
|
||
# 构建 provider 实例化参数
|
||
init_kwargs = {}
|
||
if provider_kwargs.get("api_key"):
|
||
init_kwargs["api_key"] = provider.provider_apikey
|
||
if provider_kwargs.get("base_url"):
|
||
init_kwargs["base_url"] = provider.provider_url
|
||
|
||
model_provider = provider_class(**init_kwargs)
|
||
|
||
# 对于 Gemini,provider 需要传递给 model
|
||
if provider.provider_type == "gemini":
|
||
model = model_class(
|
||
model_name=model_id,
|
||
provider=model_provider,
|
||
)
|
||
else:
|
||
model = model_class(model_id, provider=model_provider)
|
||
|
||
# 创建 Agent,同时传入 tools 和 toolsets
|
||
agent = Agent(
|
||
model=model,
|
||
name=agent_name,
|
||
system_prompt=system_prompt,
|
||
output_type=output_type,
|
||
deps_type=deps_type,
|
||
tools=tools or [],
|
||
toolsets=toolsets or [],
|
||
)
|
||
return agent
|
||
|
||
@staticmethod
|
||
def resolve_model_settings(provider: Provider, model_id: str) -> Dict[str, Any]:
|
||
"""合并 provider.model_settings 中 ``__default__`` 与具体 model_id 的参数。
|
||
|
||
- ``__default__`` 是全 Provider 的兜底参数
|
||
- ``model_id`` 键覆盖 default 中相同 key
|
||
- 都缺省时返回空 dict(``agent.run(model_settings={})`` 等效于不传)
|
||
"""
|
||
settings = getattr(provider, "model_settings", None) or {}
|
||
if not isinstance(settings, dict):
|
||
return {}
|
||
default = settings.get("__default__", {}) or {}
|
||
specific = settings.get(model_id, {}) or {}
|
||
merged: Dict[str, Any] = {**default, **specific}
|
||
return merged
|