From 57f4ee9ac0f97081999c19b2ece3c7719b92eed5 Mon Sep 17 00:00:00 2001 From: chiguyong Date: Wed, 1 Jul 2026 04:52:57 +0800 Subject: [PATCH 1/2] refactor: remove Any from tools/ + skills/ (220 sites) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wave 3 Any 治理: - tools/ 146 sites: bitable_tool(20), computer_use(16), schema_tools(14), shell(7), builtin(7), file_read(8), calendar_tool(8), web_crawl(6), computer_use_recorder(6), ask_human(5), baidu_search(5), web_search(5), base(5), memory_tool(3), skill_search(3), function_tool(3), skill_install(3), document_tool(4), mcp_tool(4), output_parser(2), advance_phase(2), search(2), composition(2), agent_tool(2), headroom_retrieve(2), terminal_session(1), registry(1) - skills/ 74 sites: geo_pipeline(23), base(18), pipeline(13), schema(8), skill_detail(6), skill_md(6) + 3 F401 fixes Strategy: dict[str,object] > object > TYPE_CHECKING Protocol Tests: 577 passed, 0 regressions ruff: 13 errors (all pre-existing, 0 new) --- src/agentkit/skills/base.py | 29 +++++++------ src/agentkit/skills/geo_pipeline.py | 45 ++++++++++----------- src/agentkit/skills/pipeline.py | 33 ++++++++------- src/agentkit/skills/registry.py | 2 +- src/agentkit/skills/schema.py | 17 ++++---- src/agentkit/skills/skill_detail.py | 11 +++-- src/agentkit/skills/skill_md.py | 11 +++-- src/agentkit/tools/advance_phase.py | 4 +- src/agentkit/tools/agent_tool.py | 4 +- src/agentkit/tools/ask_human.py | 9 ++--- src/agentkit/tools/baidu_search.py | 9 ++--- src/agentkit/tools/base.py | 9 ++--- src/agentkit/tools/bitable_tool.py | 37 +++++++++-------- src/agentkit/tools/builtin.py | 14 +++---- src/agentkit/tools/calendar_tool.py | 15 ++++--- src/agentkit/tools/composition.py | 3 +- src/agentkit/tools/computer_use.py | 30 +++++++------- src/agentkit/tools/computer_use_recorder.py | 11 +++-- src/agentkit/tools/document_tool.py | 7 ++-- src/agentkit/tools/file_read.py | 15 ++++--- src/agentkit/tools/function_tool.py | 6 +-- src/agentkit/tools/headroom_retrieve.py | 3 +- src/agentkit/tools/mcp_tool.py | 7 ++-- src/agentkit/tools/memory_tool.py | 5 +-- src/agentkit/tools/output_parser.py | 3 +- src/agentkit/tools/registry.py | 1 - src/agentkit/tools/schema_tools.py | 27 ++++++------- src/agentkit/tools/search.py | 3 +- src/agentkit/tools/shell.py | 14 +++---- src/agentkit/tools/skill_install.py | 6 +-- src/agentkit/tools/skill_search.py | 5 +-- src/agentkit/tools/terminal_session.py | 1 - src/agentkit/tools/web_crawl.py | 11 +++-- src/agentkit/tools/web_search.py | 9 ++--- 34 files changed, 194 insertions(+), 222 deletions(-) diff --git a/src/agentkit/skills/base.py b/src/agentkit/skills/base.py index 88b7d2b..8b7a838 100644 --- a/src/agentkit/skills/base.py +++ b/src/agentkit/skills/base.py @@ -4,7 +4,6 @@ from __future__ import annotations import logging from dataclasses import dataclass, field -from typing import Any from agentkit.core.config_driven import AgentConfig from agentkit.core.exceptions import ConfigValidationError @@ -66,27 +65,27 @@ class SkillConfig(AgentConfig): task_mode: str = "llm_generate", supported_tasks: list[str] | None = None, max_concurrency: int = 1, - input_schema: dict[str, Any] | None = None, - output_schema: dict[str, Any] | None = None, + input_schema: dict[str, object] | None = None, + output_schema: dict[str, object] | None = None, prompt: dict[str, str] | None = None, - llm: dict[str, Any] | None = None, + llm: dict[str, object] | None = None, tools: list[str] | None = None, - memory: dict[str, Any] | None = None, + memory: dict[str, object] | None = None, custom_handler: str | None = None, # v2 新增字段 - intent: dict[str, Any] | None = None, - quality_gate: dict[str, Any] | None = None, + intent: dict[str, object] | None = None, + quality_gate: dict[str, object] | None = None, execution_mode: str = "react", max_steps: int = 5, - evolution: dict[str, Any] | None = None, + evolution: dict[str, object] | None = None, # v3 新增字段:SKILL.md 支持 skill_md_path: str | None = None, disclosure_level: int = 1, # 默认全量加载,向后兼容;0=概要模式需显式指定 # v4 新增字段:依赖声明、能力标签 - dependencies: list[dict[str, Any] | DependencyDecl] | None = None, - capabilities: list[str | dict[str, Any] | CapabilityTag] | None = None, + dependencies: list[dict[str, object] | DependencyDecl] | None = None, + capabilities: list[str | dict[str, object] | CapabilityTag] | None = None, # v5 新增字段:对齐守卫 - alignment: dict[str, Any] | None = None, + alignment: dict[str, object] | None = None, # v6 新增字段:ReWOO fallback 策略(YAML 可配置) fallback_strategies: list[str] | None = None, # v7 新增字段:激活前置条件 + 来源标记(SkillHarness preconditions / provenance) @@ -163,7 +162,7 @@ class SkillConfig(AgentConfig): @staticmethod def _parse_dependencies( - raw: list[dict[str, Any] | DependencyDecl], + raw: list[dict[str, object] | DependencyDecl], ) -> list[DependencyDecl]: """解析依赖声明列表,支持 dict 或 DependencyDecl 实例""" result: list[DependencyDecl] = [] @@ -178,7 +177,7 @@ class SkillConfig(AgentConfig): @staticmethod def _parse_capabilities( - raw: list[str | dict[str, Any] | CapabilityTag], + raw: list[str | dict[str, object] | CapabilityTag], ) -> list[CapabilityTag]: """解析能力标签列表,支持 str / dict / CapabilityTag 实例""" result: list[CapabilityTag] = [] @@ -194,7 +193,7 @@ class SkillConfig(AgentConfig): return result @classmethod - def from_dict(cls, data: dict[str, Any]) -> "SkillConfig": + def from_dict(cls, data: dict[str, object]) -> "SkillConfig": """从字典创建配置""" return cls( name=data["name"], @@ -241,7 +240,7 @@ class SkillConfig(AgentConfig): ) return cls.from_dict(data) - def to_dict(self) -> dict[str, Any]: + def to_dict(self) -> dict[str, object]: """序列化为字典,包含 v2 字段""" d = super().to_dict() d["intent"] = { diff --git a/src/agentkit/skills/geo_pipeline.py b/src/agentkit/skills/geo_pipeline.py index d13dd1e..950c7d2 100644 --- a/src/agentkit/skills/geo_pipeline.py +++ b/src/agentkit/skills/geo_pipeline.py @@ -10,7 +10,6 @@ import asyncio import logging import uuid from dataclasses import dataclass, field -from typing import Any from agentkit.core.protocol import TaskMessage from agentkit.core.shared_workspace import SharedWorkspace @@ -42,7 +41,7 @@ class PipelineStepResult: step_name: str skill: str status: str # "success", "failed", "skipped" - output: dict[str, Any] | None = None + output: dict[str, object] | None = None error: str | None = None duration_ms: float = 0 @@ -54,7 +53,7 @@ class PipelineResult: pipeline_name: str execution_id: str steps: list[PipelineStepResult] - final_output: dict[str, Any] | None + final_output: dict[str, object] | None success: bool total_duration_ms: float @@ -79,7 +78,7 @@ class GEOPipeline: name: str, steps: list[PipelineStep], skill_registry: SkillRegistry | None = None, - agent_pool: Any = None, + agent_pool: object = None, workspace: SharedWorkspace | None = None, ): self.name = name @@ -92,9 +91,9 @@ class GEOPipeline: @classmethod def from_config( cls, - config: dict[str, Any], + config: dict[str, object], skill_registry: SkillRegistry | None = None, - agent_pool: Any = None, + agent_pool: object = None, workspace: SharedWorkspace | None = None, ) -> GEOPipeline: """从 YAML 配置创建 Pipeline @@ -136,7 +135,7 @@ class GEOPipeline: workspace=workspace, ) - async def execute(self, input_data: dict[str, Any]) -> PipelineResult: + async def execute(self, input_data: dict[str, object]) -> PipelineResult: """执行 Pipeline Args: @@ -150,7 +149,7 @@ class GEOPipeline: start_time = time.monotonic() execution_id = str(uuid.uuid4())[:8] step_results: list[PipelineStepResult] = [] - step_outputs: dict[str, dict[str, Any]] = {} + step_outputs: dict[str, dict[str, object]] = {} # Store initial input in workspace await self._workspace.write( @@ -227,8 +226,8 @@ class GEOPipeline: async def _execute_step( self, step: PipelineStep, - input_data: dict[str, Any], - step_outputs: dict[str, dict[str, Any]], + input_data: dict[str, object], + step_outputs: dict[str, dict[str, object]], execution_id: str, saga: SagaOrchestrator, ) -> PipelineStepResult: @@ -294,8 +293,8 @@ class GEOPipeline: ) async def _execute_skill( - self, skill_name: str, input_data: dict[str, Any] - ) -> dict[str, Any]: + self, skill_name: str, input_data: dict[str, object] + ) -> dict[str, object]: """执行 Skill""" if self._agent_pool: agent = self._agent_pool.get_agent(skill_name) @@ -361,9 +360,9 @@ class GEOPipeline: def _map_input( self, step: PipelineStep, - input_data: dict[str, Any], - step_outputs: dict[str, dict[str, Any]], - ) -> dict[str, Any]: + input_data: dict[str, object], + step_outputs: dict[str, dict[str, object]], + ) -> dict[str, object]: """根据 input_mapping 构建步骤输入 映射格式: {"target_key": "source_path"} @@ -379,7 +378,7 @@ class GEOPipeline: merged.update(step_outputs[dep]) return merged - mapped: dict[str, Any] = {} + mapped: dict[str, object] = {} for target_key, source_path in step.input_mapping.items(): value = self._resolve_mapping_path(source_path, input_data, step_outputs) if value is not None: @@ -390,9 +389,9 @@ class GEOPipeline: @staticmethod def _resolve_mapping_path( path: str, - input_data: dict[str, Any], - step_outputs: dict[str, dict[str, Any]], - ) -> Any: + input_data: dict[str, object], + step_outputs: dict[str, dict[str, object]], + ) -> object: """解析映射路径""" if path.startswith("$.input."): key = path[len("$.input."):] @@ -416,7 +415,7 @@ class GEOPipeline: return None def _evaluate_condition( - self, condition: str, input_data: dict[str, Any], step_outputs: dict[str, Any] + self, condition: str, input_data: dict[str, object], step_outputs: dict[str, object] ) -> bool: """评估条件表达式""" import re @@ -435,9 +434,9 @@ class GEOPipeline: def _build_final_output( self, - step_outputs: dict[str, dict[str, Any]], - input_data: dict[str, Any], - ) -> dict[str, Any]: + step_outputs: dict[str, dict[str, object]], + input_data: dict[str, object], + ) -> dict[str, object]: """构建最终输出""" final = {"input": input_data} for step_name, output in step_outputs.items(): diff --git a/src/agentkit/skills/pipeline.py b/src/agentkit/skills/pipeline.py index d5b7972..a7d2985 100644 --- a/src/agentkit/skills/pipeline.py +++ b/src/agentkit/skills/pipeline.py @@ -8,9 +8,8 @@ import logging import re -from typing import Any, Callable, Coroutine +from typing import Callable, Coroutine -from agentkit.skills.base import Skill, SkillConfig from agentkit.skills.registry import SkillRegistry logger = logging.getLogger(__name__) @@ -28,7 +27,7 @@ class SkillPipeline: def __init__( self, name: str, - steps: list[dict[str, Any]], + steps: list[dict[str, object]], skill_registry: SkillRegistry | None = None, ): """ @@ -43,9 +42,9 @@ class SkillPipeline: async def execute( self, - input_data: dict[str, Any], + input_data: dict[str, object], agent_factory: Callable[..., Coroutine] | None = None, - ) -> dict[str, Any]: + ) -> dict[str, object]: """顺序执行 Pipeline 中所有步骤 Args: @@ -57,8 +56,8 @@ class SkillPipeline: 包含 pipeline 名称、各步骤结果和最终输出的字典 """ success = True - current_input: dict[str, Any] = input_data - results: list[dict[str, Any]] = [] + current_input: dict[str, object] = input_data + results: list[dict[str, object]] = [] for i, step_def in enumerate(self._steps): skill_name = step_def["skill_name"] @@ -112,9 +111,9 @@ class SkillPipeline: async def _execute_skill( self, skill_name: str, - input_data: dict[str, Any], + input_data: dict[str, object], agent_factory: Callable[..., Coroutine] | None = None, - ) -> dict[str, Any]: + ) -> dict[str, object]: """执行单个 Skill 优先使用 agent_factory,其次通过 SkillRegistry 查找 Skill 并创建 Agent 执行。 @@ -152,8 +151,8 @@ class SkillPipeline: def _evaluate_condition( self, condition: str, - current_input: dict[str, Any], - results: list[dict[str, Any]], + current_input: dict[str, object], + results: list[dict[str, object]], ) -> bool: """评估简单条件表达式 @@ -180,10 +179,10 @@ class SkillPipeline: return False @staticmethod - def _resolve_path(path: str, data: dict[str, Any]) -> Any: + def _resolve_path(path: str, data: dict[str, object]) -> object: """解析点号路径,如 'output.score'""" parts = path.split(".") - obj: Any = data + obj: object = data for part in parts: if isinstance(obj, dict): obj = obj.get(part) @@ -193,15 +192,15 @@ class SkillPipeline: def _map_input( self, - current_input: dict[str, Any], + current_input: dict[str, object], mapping: dict[str, str], - results: list[dict[str, Any]], - ) -> dict[str, Any]: + results: list[dict[str, object]], + ) -> dict[str, object]: """根据映射规则将上一步输出映射到当前步骤输入 mapping 格式: {"target_key": "source.path"} """ - mapped: dict[str, Any] = {} + mapped: dict[str, object] = {} for target_key, source_path in mapping.items(): value = self._resolve_path(source_path, current_input) if value is not None: diff --git a/src/agentkit/skills/registry.py b/src/agentkit/skills/registry.py index 10aaf28..ef6b9a9 100644 --- a/src/agentkit/skills/registry.py +++ b/src/agentkit/skills/registry.py @@ -7,7 +7,7 @@ from typing import TYPE_CHECKING from agentkit.core.exceptions import SkillNotFoundError from agentkit.skills.base import Skill, SkillConfig -from agentkit.skills.schema import DependencyDecl, HealthCheckResult +from agentkit.skills.schema import HealthCheckResult if TYPE_CHECKING: from agentkit.skills.pipeline import SkillPipeline diff --git a/src/agentkit/skills/schema.py b/src/agentkit/skills/schema.py index 4b8ccb8..a6e3b73 100644 --- a/src/agentkit/skills/schema.py +++ b/src/agentkit/skills/schema.py @@ -8,7 +8,6 @@ from __future__ import annotations import logging from dataclasses import dataclass, field -from typing import Any logger = logging.getLogger(__name__) @@ -67,13 +66,13 @@ class SkillSpec: description: str = "" capabilities: list[CapabilityTag] = field(default_factory=list) dependencies: list[DependencyDecl] = field(default_factory=list) - input_schema: dict[str, Any] | None = None - output_schema: dict[str, Any] | None = None - quality_gate: dict[str, Any] | None = None - metadata: dict[str, Any] = field(default_factory=dict) + input_schema: dict[str, object] | None = None + output_schema: dict[str, object] | None = None + quality_gate: dict[str, object] | None = None + metadata: dict[str, object] = field(default_factory=dict) @classmethod - def from_dict(cls, data: dict[str, Any]) -> SkillSpec: + def from_dict(cls, data: dict[str, object]) -> SkillSpec: """从字典创建 SkillSpec""" capabilities = [ CapabilityTag(**cap) if isinstance(cap, dict) else cap @@ -95,9 +94,9 @@ class SkillSpec: metadata=data.get("metadata", {}), ) - def to_dict(self) -> dict[str, Any]: + def to_dict(self) -> dict[str, object]: """序列化为字典""" - d: dict[str, Any] = { + d: dict[str, object] = { "name": self.name, "version": self.version, "description": self.description, @@ -165,7 +164,7 @@ class HealthCheckResult: version_mismatches: list[str] = field(default_factory=list) warnings: list[str] = field(default_factory=list) - def to_dict(self) -> dict[str, Any]: + def to_dict(self) -> dict[str, object]: return { "skill_name": self.skill_name, "skill_version": self.skill_version, diff --git a/src/agentkit/skills/skill_detail.py b/src/agentkit/skills/skill_detail.py index f2882be..00b9c38 100644 --- a/src/agentkit/skills/skill_detail.py +++ b/src/agentkit/skills/skill_detail.py @@ -12,7 +12,6 @@ constraints/output_format),从本地 SkillRegistry 检索。 from __future__ import annotations import logging -from typing import Any from agentkit.chat.skill_routing import collect_prompt_parts, format_preconditions_block from agentkit.core.exceptions import SkillNotFoundError @@ -36,7 +35,7 @@ class SkillDetailTool(Tool): def __init__( self, - skill_registry: Any, + skill_registry: object, name: str = "skill_detail", description: str = ( "Load full instructions for a registered skill by name or keyword. " @@ -44,8 +43,8 @@ class SkillDetailTool(Tool): "and you need the complete instructions to execute the skill. " "Returns the skill's identity, context, instructions, constraints, and output format." ), - input_schema: dict[str, Any] | None = None, - output_schema: dict[str, Any] | None = None, + input_schema: dict[str, object] | None = None, + output_schema: dict[str, object] | None = None, version: str = "1.0.0", tags: list[str] | None = None, ): @@ -97,7 +96,7 @@ class SkillDetailTool(Tool): pass # 非精确匹配,降级到关键词搜索 # 关键词搜索:匹配 skill 名称和描述 - matches: list[Any] = [] + matches: list[object] = [] query_lower = query.lower() for skill in self._registry.list_skills(): name = skill.name.lower() @@ -117,7 +116,7 @@ class SkillDetailTool(Tool): return self._format_skill_full(matches[0]) @staticmethod - def _format_skill_full(skill: Any) -> dict[str, Any]: + def _format_skill_full(skill: object) -> dict[str, object]: """格式化 skill 的完整 instructions 供 LLM 使用。""" config = skill.config prompt_parts = collect_prompt_parts(config, with_headers=True) diff --git a/src/agentkit/skills/skill_md.py b/src/agentkit/skills/skill_md.py index c8d9c3d..bd6c49f 100644 --- a/src/agentkit/skills/skill_md.py +++ b/src/agentkit/skills/skill_md.py @@ -8,7 +8,6 @@ import logging import re -from typing import Any import yaml @@ -27,7 +26,7 @@ class SkillMdParser: """ @staticmethod - def parse(file_path: str) -> tuple[dict[str, Any], dict[str, str], str]: + def parse(file_path: str) -> tuple[dict[str, object], dict[str, str], str]: """解析 SKILL.md 文件 Note: Only H1 headings (# ) are treated as section delimiters. @@ -48,7 +47,7 @@ class SkillMdParser: content = f.read() # 提取 YAML frontmatter(--- 标记之间) - frontmatter: dict[str, Any] = {} + frontmatter: dict[str, object] = {} body = content if content.startswith("---"): parts = content.split("---", 2) @@ -81,7 +80,7 @@ class SkillMdParser: @staticmethod def to_skill_config( - frontmatter: dict[str, Any], + frontmatter: dict[str, object], sections: dict[str, str], file_path: str, disclosure_level: int = 1, @@ -99,7 +98,7 @@ class SkillMdParser: """ # 构建 IntentConfig intent_data = frontmatter.get("intent") or {} - intent_config_data: dict[str, Any] = { + intent_config_data: dict[str, object] = { "keywords": intent_data.get("keywords", []), "description": intent_data.get("description", ""), "examples": intent_data.get("examples", []), @@ -107,7 +106,7 @@ class SkillMdParser: # 构建 QualityGateConfig qg_data = frontmatter.get("quality_gate") or {} - quality_gate_config_data: dict[str, Any] = { + quality_gate_config_data: dict[str, object] = { "required_fields": qg_data.get("required_fields", []), "min_word_count": qg_data.get("min_word_count", 0), "max_retries": qg_data.get("max_retries", 0), diff --git a/src/agentkit/tools/advance_phase.py b/src/agentkit/tools/advance_phase.py index b750d3f..a1c0cdc 100644 --- a/src/agentkit/tools/advance_phase.py +++ b/src/agentkit/tools/advance_phase.py @@ -7,7 +7,7 @@ The LLM calls this tool to signal "I'm done planning, move to building". from __future__ import annotations import logging -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING from agentkit.tools.base import Tool @@ -55,7 +55,7 @@ class AdvancePhaseTool(Tool): ) self._engine = engine - async def execute(self, **kwargs) -> dict[str, Any]: + async def execute(self, **kwargs) -> dict[str, object]: # Capture previous phase before transition (engine is single-threaded per request). previous = self._engine.current_phase new_phase = self._engine.advance_phase() diff --git a/src/agentkit/tools/agent_tool.py b/src/agentkit/tools/agent_tool.py index bacf7e0..eec544f 100644 --- a/src/agentkit/tools/agent_tool.py +++ b/src/agentkit/tools/agent_tool.py @@ -1,7 +1,5 @@ """AgentTool - 将 Agent 包装为 Tool""" -from typing import Any - from agentkit.tools.base import Tool @@ -36,7 +34,7 @@ class AgentTool(Tool): self.timeout_seconds = timeout_seconds self._dispatcher = None - def set_dispatcher(self, dispatcher: Any) -> "AgentTool": + def set_dispatcher(self, dispatcher: object) -> "AgentTool": """注入 Dispatcher""" self._dispatcher = dispatcher return self diff --git a/src/agentkit/tools/ask_human.py b/src/agentkit/tools/ask_human.py index 0fc9a9b..34e6a88 100644 --- a/src/agentkit/tools/ask_human.py +++ b/src/agentkit/tools/ask_human.py @@ -10,7 +10,6 @@ from __future__ import annotations import asyncio import logging import uuid -from typing import Any from agentkit.tools.base import Tool @@ -42,12 +41,12 @@ class AskHumanTool(Tool): # request_id -> asyncio.Future self._pending_replies: dict[str, asyncio.Future] | None = None # Callback to push question to client - self._ask_callback: Any = None + self._ask_callback: object = None def configure( self, pending_replies: dict[str, asyncio.Future] | None = None, - ask_callback: Any = None, + ask_callback: object = None, ) -> None: """Configure the tool with WebSocket communication channels. @@ -61,7 +60,7 @@ class AskHumanTool(Tool): self._ask_callback = ask_callback @property - def parameters(self) -> dict[str, Any]: + def parameters(self) -> dict[str, object]: return { "type": "object", "properties": { @@ -78,7 +77,7 @@ class AskHumanTool(Tool): "required": ["question"], } - async def execute(self, **kwargs: Any) -> dict: + async def execute(self, **kwargs: object) -> dict: """Ask the user a question and wait for their reply. Args: diff --git a/src/agentkit/tools/baidu_search.py b/src/agentkit/tools/baidu_search.py index e3f76da..0f24de3 100644 --- a/src/agentkit/tools/baidu_search.py +++ b/src/agentkit/tools/baidu_search.py @@ -7,7 +7,6 @@ import json import logging import urllib.parse -from typing import Any import httpx @@ -30,8 +29,8 @@ class BaiduSearchTool(Tool): self, name: str = "baidu_search", description: str = "执行百度搜索,返回搜索结果列表", - input_schema: dict[str, Any] | None = None, - output_schema: dict[str, Any] | None = None, + input_schema: dict[str, object] | None = None, + output_schema: dict[str, object] | None = None, version: str = "1.0.0", tags: list[str] | None = None, api_key: str | None = None, @@ -49,7 +48,7 @@ class BaiduSearchTool(Tool): self._api_url = api_url @staticmethod - def _default_input_schema() -> dict[str, Any]: + def _default_input_schema() -> dict[str, object]: return { "type": "object", "properties": { @@ -67,7 +66,7 @@ class BaiduSearchTool(Tool): } @staticmethod - def _default_output_schema() -> dict[str, Any]: + def _default_output_schema() -> dict[str, object]: return { "type": "object", "properties": { diff --git a/src/agentkit/tools/base.py b/src/agentkit/tools/base.py index c54913e..65441e9 100644 --- a/src/agentkit/tools/base.py +++ b/src/agentkit/tools/base.py @@ -2,7 +2,6 @@ import time from abc import ABC, abstractmethod -from typing import Any import jsonschema @@ -23,7 +22,7 @@ class ToolValidationError(Exception): message: str, *, error_code: str = "schema_mismatch", - details: dict[str, Any] | None = None, + details: dict[str, object] | None = None, ) -> None: super().__init__(message) self.error_code = error_code @@ -40,8 +39,8 @@ class Tool(ABC): self, name: str, description: str, - input_schema: dict[str, Any] | None = None, - output_schema: dict[str, Any] | None = None, + input_schema: dict[str, object] | None = None, + output_schema: dict[str, object] | None = None, version: str = "1.0.0", tags: list[str] | None = None, ): @@ -99,7 +98,7 @@ class Tool(ABC): finally: _span_cm.__exit__(None, None, None) - def _validate_input(self, kwargs: dict[str, Any]) -> None: + def _validate_input(self, kwargs: dict[str, object]) -> None: """校验 kwargs 是否符合 self.input_schema。 - input_schema=None → 跳过(向后兼容,旧工具无 schema) diff --git a/src/agentkit/tools/bitable_tool.py b/src/agentkit/tools/bitable_tool.py index b8d44cb..4015107 100644 --- a/src/agentkit/tools/bitable_tool.py +++ b/src/agentkit/tools/bitable_tool.py @@ -16,7 +16,6 @@ from __future__ import annotations import asyncio import logging -from typing import Any import httpx @@ -140,7 +139,7 @@ class BitableTool(Tool): if self._client is not None and not self._client.is_closed: await self._client.aclose() - async def execute(self, **kwargs) -> dict[str, Any]: + async def execute(self, **kwargs) -> dict[str, object]: action = kwargs.get("action") handlers = { "create_table": self._create_table, @@ -169,7 +168,7 @@ class BitableTool(Tool): # create_table # ------------------------------------------------------------------ - async def _create_table(self, **kwargs) -> dict[str, Any]: + async def _create_table(self, **kwargs) -> dict[str, object]: table_name = kwargs.get("table_name") if not table_name: return {"success": False, "error": "Missing required field: table_name"} @@ -186,7 +185,7 @@ class BitableTool(Tool): # import_excel # ------------------------------------------------------------------ - async def _import_excel(self, **kwargs) -> dict[str, Any]: + async def _import_excel(self, **kwargs) -> dict[str, object]: file_path = kwargs.get("file_path") file_url = kwargs.get("file_url") if not file_path and not file_url: @@ -201,13 +200,13 @@ class BitableTool(Tool): if not sheets: return {"success": False, "error": "Excel file has no sheets with data"} - results: list[dict[str, Any]] = [] + results: list[dict[str, object]] = [] for sheet in sheets: result = await self._import_sheet(sheet) results.append(result) return {"success": True, "sheets": results} - async def _import_sheet(self, sheet: ParsedSheet) -> dict[str, Any]: + async def _import_sheet(self, sheet: ParsedSheet) -> dict[str, object]: """Create a bitable table from a parsed sheet and upsert all rows.""" client = await self._get_client() @@ -253,13 +252,13 @@ class BitableTool(Tool): } async def _batch_create_records( - self, table_id: str, records: list[dict[str, Any]] - ) -> dict[str, Any]: + self, table_id: str, records: list[dict[str, object]] + ) -> dict[str, object]: """Create records in batches via POST /tables/{id}/records.""" client = await self._get_client() total = len(records) successful = 0 - errors: list[dict[str, Any]] = [] + errors: list[dict[str, object]] = [] for start in range(0, total, BATCH_SIZE): batch = records[start : start + BATCH_SIZE] @@ -292,7 +291,7 @@ class BitableTool(Tool): # import_database # ------------------------------------------------------------------ - async def _import_database(self, **kwargs) -> dict[str, Any]: + async def _import_database(self, **kwargs) -> dict[str, object]: conn_str = kwargs.get("connection_string") table_names = kwargs.get("table_names") if not conn_str: @@ -300,7 +299,7 @@ class BitableTool(Tool): if not table_names: return {"success": False, "error": "Missing required field: table_names"} - results: list[dict[str, Any]] = [] + results: list[dict[str, object]] = [] for src_table in table_names: try: # Offload sync DB reflection to thread pool (P2 #21-23). @@ -313,7 +312,7 @@ class BitableTool(Tool): results.append({"table_name": src_table, "success": False, "error": str(e)}) return {"success": True, "tables": results} - async def _import_reflected_table(self, reflected: dict[str, Any]) -> dict[str, Any]: + async def _import_reflected_table(self, reflected: dict[str, object]) -> dict[str, object]: """Create a bitable table from reflected DB data and upsert rows.""" client = await self._get_client() table_name = reflected["table_name"] @@ -376,7 +375,7 @@ class BitableTool(Tool): # collect_api # ------------------------------------------------------------------ - async def _collect_api(self, **kwargs) -> dict[str, Any]: + async def _collect_api(self, **kwargs) -> dict[str, object]: table_id = kwargs.get("table_id") records = kwargs.get("records") field_mapping = kwargs.get("field_mapping") @@ -403,7 +402,7 @@ class BitableTool(Tool): # upsert_records # ------------------------------------------------------------------ - async def _upsert_records(self, **kwargs) -> dict[str, Any]: + async def _upsert_records(self, **kwargs) -> dict[str, object]: table_id = kwargs.get("table_id") records = kwargs.get("records") pk_field_id = kwargs.get("primary_key_field_id") @@ -421,13 +420,13 @@ class BitableTool(Tool): return {"success": True, **result} async def _batch_upsert( - self, table_id: str, records: list[dict[str, Any]], pk_field_id: str - ) -> dict[str, Any]: + self, table_id: str, records: list[dict[str, object]], pk_field_id: str + ) -> dict[str, object]: """Upsert records in batches of BATCH_SIZE via POST /tables/{id}/upsert.""" client = await self._get_client() total = len(records) successful = 0 - errors: list[dict[str, Any]] = [] + errors: list[dict[str, object]] = [] for start in range(0, total, BATCH_SIZE): batch = records[start : start + BATCH_SIZE] @@ -464,13 +463,13 @@ class BitableTool(Tool): # query_records # ------------------------------------------------------------------ - async def _query_records(self, **kwargs) -> dict[str, Any]: + async def _query_records(self, **kwargs) -> dict[str, object]: table_id = kwargs.get("table_id") if not table_id: return {"success": False, "error": "Missing required field: table_id"} client = await self._get_client() - params: dict[str, Any] = {} + params: dict[str, object] = {} if kwargs.get("cursor"): params["cursor"] = kwargs["cursor"] if kwargs.get("limit"): diff --git a/src/agentkit/tools/builtin.py b/src/agentkit/tools/builtin.py index 44cf1a6..0a86718 100644 --- a/src/agentkit/tools/builtin.py +++ b/src/agentkit/tools/builtin.py @@ -3,7 +3,7 @@ from __future__ import annotations import logging -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING from agentkit.tools.base import Tool @@ -24,8 +24,8 @@ class RunTestsTool(Tool): self, name: str = "run_tests", description: str = "Run project tests to verify code changes. Executes pytest and linting commands.", - input_schema: dict[str, Any] | None = None, - output_schema: dict[str, Any] | None = None, + input_schema: dict[str, object] | None = None, + output_schema: dict[str, object] | None = None, version: str = "1.0.0", tags: list[str] | None = None, working_dir: str | None = None, @@ -45,7 +45,7 @@ class RunTestsTool(Tool): self._max_retries = max_retries @staticmethod - def _default_input_schema() -> dict[str, Any]: + def _default_input_schema() -> dict[str, object]: return { "type": "object", "properties": { @@ -112,8 +112,8 @@ class ToolSearchTool(Tool): "Returns full descriptions (name, description, parameters) of matching tools. " "Use this when you need details about a tool that was only listed by name." ), - input_schema: dict[str, Any] | None = None, - output_schema: dict[str, Any] | None = None, + input_schema: dict[str, object] | None = None, + output_schema: dict[str, object] | None = None, version: str = "1.0.0", tags: list[str] | None = None, top_k: int = 5, @@ -176,7 +176,7 @@ class ToolSearchTool(Tool): } @staticmethod - def _format_tool_full(tool: Tool) -> dict[str, Any]: + def _format_tool_full(tool: Tool) -> dict[str, object]: """Format a tool's full description for the LLM.""" return { "name": tool.name, diff --git a/src/agentkit/tools/calendar_tool.py b/src/agentkit/tools/calendar_tool.py index 75a36c7..dcd0f9f 100644 --- a/src/agentkit/tools/calendar_tool.py +++ b/src/agentkit/tools/calendar_tool.py @@ -13,7 +13,6 @@ from __future__ import annotations import re from datetime import datetime, timedelta -from typing import Any from agentkit.calendar.models import ReminderRule from agentkit.calendar.service import CalendarService @@ -321,14 +320,14 @@ class CalendarTool(Tool): """ self._default_user_id = user_id - def _resolve_user_id(self, kwargs: dict[str, Any]) -> str | None: + def _resolve_user_id(self, kwargs: dict[str, object]) -> str | None: """Resolve user_id: prefer caller-provided, fall back to default.""" provided = kwargs.get("user_id") if provided and isinstance(provided, str) and provided.strip(): return provided return self._default_user_id - async def execute(self, **kwargs) -> dict[str, Any]: + async def execute(self, **kwargs) -> dict[str, object]: action = kwargs.get("action") if action == "create_event": @@ -345,7 +344,7 @@ class CalendarTool(Tool): # create_event # ------------------------------------------------------------------ - async def _create_event(self, **kwargs) -> dict[str, Any]: + async def _create_event(self, **kwargs) -> dict[str, object]: user_id = self._resolve_user_id(kwargs) title = kwargs.get("title") start_time = kwargs.get("start_time") @@ -494,7 +493,7 @@ class CalendarTool(Tool): # query_events # ------------------------------------------------------------------ - async def _query_events(self, **kwargs) -> dict[str, Any]: + async def _query_events(self, **kwargs) -> dict[str, object]: user_id = self._resolve_user_id(kwargs) if not user_id: return {"success": False, "error": "Missing required field: user_id"} @@ -519,7 +518,7 @@ class CalendarTool(Tool): # update_event # ------------------------------------------------------------------ - async def _update_event(self, **kwargs) -> dict[str, Any]: + async def _update_event(self, **kwargs) -> dict[str, object]: event_id = kwargs.get("event_id") user_id = self._resolve_user_id(kwargs) if not event_id: @@ -536,7 +535,7 @@ class CalendarTool(Tool): # Build fields dict from updatable params (only those explicitly provided) updatable = ["title", "description", "location", "is_all_day"] - fields: dict[str, Any] = {} + fields: dict[str, object] = {} for key in updatable: if key in kwargs and kwargs[key] is not None: fields[key] = kwargs[key] @@ -565,7 +564,7 @@ class CalendarTool(Tool): # delete_event # ------------------------------------------------------------------ - async def _delete_event(self, **kwargs) -> dict[str, Any]: + async def _delete_event(self, **kwargs) -> dict[str, object]: event_id = kwargs.get("event_id") user_id = self._resolve_user_id(kwargs) if not event_id: diff --git a/src/agentkit/tools/composition.py b/src/agentkit/tools/composition.py index a0a9e10..2ad7104 100644 --- a/src/agentkit/tools/composition.py +++ b/src/agentkit/tools/composition.py @@ -9,7 +9,6 @@ import asyncio import json import logging -from typing import Any from agentkit.tools.base import Tool @@ -137,7 +136,7 @@ class DynamicSelector(Tool): description: str, tools: list[Tool], mode: str = "keyword", - llm_client: Any = None, + llm_client: object = None, version: str = "1.0.0", tags: list[str] | None = None, ): diff --git a/src/agentkit/tools/computer_use.py b/src/agentkit/tools/computer_use.py index cacfc6c..5a7d28e 100644 --- a/src/agentkit/tools/computer_use.py +++ b/src/agentkit/tools/computer_use.py @@ -10,7 +10,7 @@ from __future__ import annotations import asyncio import base64 import logging -from typing import Any, Callable, Awaitable +from typing import Callable, Awaitable import httpx @@ -62,8 +62,8 @@ class ComputerUseTool(Tool): self, name: str = "computer_use", description: str = "Anthropic Computer Use API 集成,支持截屏识别和 UI 操作", - input_schema: dict[str, Any] | None = None, - output_schema: dict[str, Any] | None = None, + input_schema: dict[str, object] | None = None, + output_schema: dict[str, object] | None = None, version: str = "1.0.0", tags: list[str] | None = None, api_key: str | None = None, @@ -71,7 +71,7 @@ class ComputerUseTool(Tool): api_base_url: str = _ANTHROPIC_COMPUTER_USE_URL, session_factory: type[ComputerUseSession] | None = None, recorder: ComputerUseRecorder | None = None, - fallback_callback: Callable[[str, dict[str, Any]], Awaitable[dict[str, Any]]] | None = None, + fallback_callback: Callable[[str, dict[str, object]], Awaitable[dict[str, object]]] | None = None, max_retries: int = 1, request_timeout: float = 30.0, ): @@ -112,7 +112,7 @@ class ComputerUseTool(Tool): await self._http_client.aclose() @staticmethod - def _default_input_schema() -> dict[str, Any]: + def _default_input_schema() -> dict[str, object]: return { "type": "object", "properties": { @@ -177,7 +177,7 @@ class ComputerUseTool(Tool): } @staticmethod - def _default_output_schema() -> dict[str, Any]: + def _default_output_schema() -> dict[str, object]: return { "type": "object", "properties": { @@ -254,7 +254,7 @@ class ComputerUseTool(Tool): self, session: ComputerUseSession, action: str, - params: dict[str, Any], + params: dict[str, object], ) -> ActionResult: """带降级链的操作执行 @@ -318,14 +318,14 @@ class ComputerUseTool(Tool): self, session: ComputerUseSession, action: str, - params: dict[str, Any], + params: dict[str, object], ) -> ActionResult: """调用 Anthropic Computer Use API 通过 Anthropic Messages API 发送 computer_use_tool 请求。 """ # 构造 computer_use 工具调用参数 - tool_input: dict[str, Any] = {"action": action} + tool_input: dict[str, object] = {"action": action} if action == "click": tool_input["coordinate"] = [params.get("x", 0), params.get("y", 0)] elif action == "type": @@ -354,7 +354,7 @@ class ComputerUseTool(Tool): screenshot_b64 = screenshot_result.screenshot_base64 # 构造 API 请求 - content_blocks: list[dict[str, Any]] = [] + content_blocks: list[dict[str, object]] = [] if screenshot_b64: content_blocks.append({ "type": "image", @@ -437,7 +437,7 @@ class ComputerUseTool(Tool): metadata={"api_response": data}, ) - def _validate_params(self, action: str, kwargs: dict[str, Any]) -> str | None: + def _validate_params(self, action: str, kwargs: dict[str, object]) -> str | None: """验证操作参数 Returns: @@ -459,9 +459,9 @@ class ComputerUseTool(Tool): return f"drag 操作需要 {', '.join(missing)} 参数" return None - def _format_result(self, result: ActionResult, session_id: str) -> dict[str, Any]: + def _format_result(self, result: ActionResult, session_id: str) -> dict[str, object]: """格式化操作结果""" - formatted: dict[str, Any] = { + formatted: dict[str, object] = { "success": result.success, "action": result.action, "output": result.output, @@ -482,9 +482,9 @@ class ComputerUseTool(Tool): action: str, error: str, fallback: str = "", - ) -> dict[str, Any]: + ) -> dict[str, object]: """构造错误结果""" - result: dict[str, Any] = { + result: dict[str, object] = { "success": False, "action": action, "error": error, diff --git a/src/agentkit/tools/computer_use_recorder.py b/src/agentkit/tools/computer_use_recorder.py index 7b567c1..7215dc4 100644 --- a/src/agentkit/tools/computer_use_recorder.py +++ b/src/agentkit/tools/computer_use_recorder.py @@ -11,7 +11,6 @@ import logging import time from dataclasses import asdict, dataclass, field from pathlib import Path -from typing import Any from agentkit.tools.computer_use_session import ComputerUseSession, ActionResult @@ -27,17 +26,17 @@ class ActionRecord: timestamp: float action: str - params: dict[str, Any] = field(default_factory=dict) + params: dict[str, object] = field(default_factory=dict) success: bool = False output: str = "" error: str = "" screenshot_path: str = "" - def to_dict(self) -> dict[str, Any]: + def to_dict(self) -> dict[str, object]: return asdict(self) @classmethod - def from_dict(cls, data: dict[str, Any]) -> ActionRecord: + def from_dict(cls, data: dict[str, object]) -> ActionRecord: return cls(**data) @@ -69,7 +68,7 @@ class ComputerUseRecorder: def record( self, action: str, - params: dict[str, Any], + params: dict[str, object], result: ActionResult, screenshot_path: str = "", ) -> ActionRecord: @@ -220,7 +219,7 @@ class ComputerUseRecorder: """失败操作数""" return sum(1 for r in self._records if not r.success) - def summary(self) -> dict[str, Any]: + def summary(self) -> dict[str, object]: """生成录制摘要""" return { "total_actions": self.total_actions, diff --git a/src/agentkit/tools/document_tool.py b/src/agentkit/tools/document_tool.py index 06b1807..5756cc9 100644 --- a/src/agentkit/tools/document_tool.py +++ b/src/agentkit/tools/document_tool.py @@ -7,7 +7,6 @@ handle documents via function calling. U6 implements "create"; U9 adds "read". from __future__ import annotations from pathlib import Path -from typing import Any from agentkit.documents.service import DocumentService from agentkit.memory.document_loader import DocumentLoader @@ -81,7 +80,7 @@ class DocumentTool(Tool): self._service = service self._loader = loader or DocumentLoader() - async def execute(self, **kwargs) -> dict[str, Any]: + async def execute(self, **kwargs) -> dict[str, object]: action = kwargs.get("action", "create") if action == "read": @@ -90,7 +89,7 @@ class DocumentTool(Tool): return await self._execute_create(**kwargs) return {"success": False, "error": f"Unknown action: {action!r} (use 'create' or 'read')"} - async def _execute_create(self, **kwargs) -> dict[str, Any]: + async def _execute_create(self, **kwargs) -> dict[str, object]: format_key = kwargs.get("format", "") content = kwargs.get("content", "") conversation_id = kwargs.get("conversation_id", "") @@ -129,7 +128,7 @@ class DocumentTool(Tool): except Exception as e: return {"success": False, "error": f"Document creation failed: {e}"} - async def _execute_read(self, **kwargs) -> dict[str, Any]: + async def _execute_read(self, **kwargs) -> dict[str, object]: file_path = kwargs.get("filename") or kwargs.get("content") if not file_path: return {"success": False, "error": "filename (file path) is required for read"} diff --git a/src/agentkit/tools/file_read.py b/src/agentkit/tools/file_read.py index 1f70c96..ddf15c9 100644 --- a/src/agentkit/tools/file_read.py +++ b/src/agentkit/tools/file_read.py @@ -12,7 +12,6 @@ from __future__ import annotations import logging from pathlib import Path -from typing import Any from agentkit.tools.base import Tool from agentkit.tools.symbol_extractor import ( @@ -40,8 +39,8 @@ class ReadFileTool(Tool): self, name: str = "read_file", description: str | None = None, - input_schema: dict[str, Any] | None = None, - output_schema: dict[str, Any] | None = None, + input_schema: dict[str, object] | None = None, + output_schema: dict[str, object] | None = None, version: str = "1.0.0", tags: list[str] | None = None, ): @@ -63,7 +62,7 @@ class ReadFileTool(Tool): ) @staticmethod - def _default_input_schema() -> dict[str, Any]: + def _default_input_schema() -> dict[str, object]: return { "type": "object", "properties": { @@ -95,7 +94,7 @@ class ReadFileTool(Tool): } @staticmethod - def _default_output_schema() -> dict[str, Any]: + def _default_output_schema() -> dict[str, object]: return { "type": "object", "properties": { @@ -115,7 +114,7 @@ class ReadFileTool(Tool): }, } - async def execute(self, **kwargs) -> dict[str, Any]: + async def execute(self, **kwargs) -> dict[str, object]: raw_path = kwargs.get("path") if not raw_path: return self._error("`path` is required") @@ -235,8 +234,8 @@ class ReadFileTool(Tool): @staticmethod def _error( message: str, *, path: str | None = None, detail: str | None = None - ) -> dict[str, Any]: - result: dict[str, Any] = { + ) -> dict[str, object]: + result: dict[str, object] = { "content": "", "is_error": True, "error": message, diff --git a/src/agentkit/tools/function_tool.py b/src/agentkit/tools/function_tool.py index c1086d9..de4f2a7 100644 --- a/src/agentkit/tools/function_tool.py +++ b/src/agentkit/tools/function_tool.py @@ -1,7 +1,7 @@ """FunctionTool - 将普通 Python 函数包装为 Tool""" import inspect -from typing import Any, Callable, Awaitable +from typing import Callable, Awaitable from agentkit.tools.base import Tool @@ -17,8 +17,8 @@ class FunctionTool(Tool): name: str, description: str, func: Callable[..., Awaitable[dict]] | Callable[..., dict], - input_schema: dict[str, Any] | None = None, - output_schema: dict[str, Any] | None = None, + input_schema: dict[str, object] | None = None, + output_schema: dict[str, object] | None = None, version: str = "1.0.0", tags: list[str] | None = None, ): diff --git a/src/agentkit/tools/headroom_retrieve.py b/src/agentkit/tools/headroom_retrieve.py index 71c6bd3..7b4add5 100644 --- a/src/agentkit/tools/headroom_retrieve.py +++ b/src/agentkit/tools/headroom_retrieve.py @@ -6,7 +6,6 @@ """ import logging -from typing import Any from agentkit.tools.base import Tool @@ -20,7 +19,7 @@ class HeadroomRetrieveTool(Tool): 压缩内容中包含 标记,LLM 可使用该哈希值检索。 """ - def __init__(self, compressor: Any): + def __init__(self, compressor: object): super().__init__( name="headroom_retrieve", description=( diff --git a/src/agentkit/tools/mcp_tool.py b/src/agentkit/tools/mcp_tool.py index 67622d9..82a50b0 100644 --- a/src/agentkit/tools/mcp_tool.py +++ b/src/agentkit/tools/mcp_tool.py @@ -2,7 +2,6 @@ import json import logging -from typing import Any from agentkit.tools.base import Tool @@ -20,9 +19,9 @@ class MCPTool(Tool): self, name: str, description: str, - client: Any, - input_schema: dict[str, Any] | None = None, - output_schema: dict[str, Any] | None = None, + client: object, + input_schema: dict[str, object] | None = None, + output_schema: dict[str, object] | None = None, version: str = "1.0.0", tags: list[str] | None = None, ): diff --git a/src/agentkit/tools/memory_tool.py b/src/agentkit/tools/memory_tool.py index da69ee0..bc119e1 100644 --- a/src/agentkit/tools/memory_tool.py +++ b/src/agentkit/tools/memory_tool.py @@ -14,7 +14,6 @@ from __future__ import annotations import re from datetime import datetime, timezone -from typing import Any from agentkit.memory.profile import MemoryFile, MemoryStore from agentkit.tools.base import Tool @@ -75,7 +74,7 @@ class MemoryTool(Tool): ) self._store = memory_store - async def execute(self, **kwargs) -> dict[str, Any]: + async def execute(self, **kwargs) -> dict[str, object]: action = kwargs.get("action", "") file_key = kwargs.get("file", "") @@ -150,7 +149,7 @@ class MemoryTool(Tool): async def _update_soul( self, mf: MemoryFile, section: str, content: str, reason: str - ) -> dict[str, Any]: + ) -> dict[str, object]: """执行 SOUL 动态更新,带版本追踪和更新历史. 采用原子写入策略:先在内存中构建完整内容,再一次性写入文件, diff --git a/src/agentkit/tools/output_parser.py b/src/agentkit/tools/output_parser.py index b371712..cd0369e 100644 --- a/src/agentkit/tools/output_parser.py +++ b/src/agentkit/tools/output_parser.py @@ -8,7 +8,6 @@ from __future__ import annotations import re from dataclasses import dataclass, field from enum import Enum -from typing import Any class ErrorType(Enum): @@ -49,7 +48,7 @@ class ParsedOutput: raw_output: str = "" suggestions: list[str] = field(default_factory=list) - def to_dict(self) -> dict[str, Any]: + def to_dict(self) -> dict[str, object]: return { "exit_code": self.exit_code, "is_error": self.is_error, diff --git a/src/agentkit/tools/registry.py b/src/agentkit/tools/registry.py index bc0369f..3955bbd 100644 --- a/src/agentkit/tools/registry.py +++ b/src/agentkit/tools/registry.py @@ -1,7 +1,6 @@ """ToolRegistry - 工具注册中心""" import logging -from typing import Any from agentkit.core.exceptions import ToolNotFoundError from agentkit.tools.base import Tool diff --git a/src/agentkit/tools/schema_tools.py b/src/agentkit/tools/schema_tools.py index 451f132..52895be 100644 --- a/src/agentkit/tools/schema_tools.py +++ b/src/agentkit/tools/schema_tools.py @@ -6,7 +6,6 @@ SchemaGenerateTool: 生成 Schema.org JSON-LD 标记 import json import logging -from typing import Any import httpx @@ -47,8 +46,8 @@ class SchemaExtractTool(Tool): self, name: str = "schema_extract", description: str = "从网页 HTML 中提取结构化数据(JSON-LD、Microdata、RDFa 等)", - input_schema: dict[str, Any] | None = None, - output_schema: dict[str, Any] | None = None, + input_schema: dict[str, object] | None = None, + output_schema: dict[str, object] | None = None, version: str = "1.0.0", tags: list[str] | None = None, ): @@ -62,7 +61,7 @@ class SchemaExtractTool(Tool): ) @staticmethod - def _default_input_schema() -> dict[str, Any]: + def _default_input_schema() -> dict[str, object]: return { "type": "object", "properties": { @@ -81,7 +80,7 @@ class SchemaExtractTool(Tool): } @staticmethod - def _default_output_schema() -> dict[str, Any]: + def _default_output_schema() -> dict[str, object]: return { "type": "object", "properties": { @@ -164,7 +163,7 @@ class SchemaExtractTool(Tool): ) # 整理结果 - schemas: list[dict[str, Any]] = [] + schemas: list[dict[str, object]] = [] for fmt in formats: items = data.get(fmt, []) if items: @@ -206,8 +205,8 @@ class SchemaGenerateTool(Tool): self, name: str = "schema_generate", description: str = "生成 Schema.org JSON-LD 结构化数据标记", - input_schema: dict[str, Any] | None = None, - output_schema: dict[str, Any] | None = None, + input_schema: dict[str, object] | None = None, + output_schema: dict[str, object] | None = None, version: str = "1.0.0", tags: list[str] | None = None, ): @@ -221,7 +220,7 @@ class SchemaGenerateTool(Tool): ) @staticmethod - def _default_input_schema() -> dict[str, Any]: + def _default_input_schema() -> dict[str, object]: return { "type": "object", "properties": { @@ -238,7 +237,7 @@ class SchemaGenerateTool(Tool): } @staticmethod - def _default_output_schema() -> dict[str, Any]: + def _default_output_schema() -> dict[str, object]: return { "type": "object", "properties": { @@ -249,16 +248,16 @@ class SchemaGenerateTool(Tool): }, } - def _generate_manual(self, schema_type: str, properties: dict[str, Any]) -> str: + def _generate_manual(self, schema_type: str, properties: dict[str, object]) -> str: """手动构建 JSON-LD(无需外部依赖)""" - jsonld_obj: dict[str, Any] = { + jsonld_obj: dict[str, object] = { "@context": "https://schema.org", "@type": schema_type, } jsonld_obj.update(properties) return json.dumps(jsonld_obj, ensure_ascii=False, indent=2) - def _generate_with_schemaorg(self, schema_type: str, properties: dict[str, Any]) -> str | None: + def _generate_with_schemaorg(self, schema_type: str, properties: dict[str, object]) -> str | None: """使用 pydantic-schemaorg 生成 JSON-LD(带验证)""" if not _PYDANTIC_SCHEMAORG_AVAILABLE: return None @@ -278,7 +277,7 @@ class SchemaGenerateTool(Tool): else: return None - jsonld_obj: dict[str, Any] = { + jsonld_obj: dict[str, object] = { "@context": "https://schema.org", "@type": schema_type, } diff --git a/src/agentkit/tools/search.py b/src/agentkit/tools/search.py index 30c4140..bf73769 100644 --- a/src/agentkit/tools/search.py +++ b/src/agentkit/tools/search.py @@ -12,7 +12,6 @@ from __future__ import annotations import math import re from collections import Counter -from typing import Any from agentkit.tools.base import Tool @@ -92,7 +91,7 @@ class ToolSearchIndex: """Convert a tool's searchable metadata into a single text document.""" parts: list[str] = [str(tool.name), str(tool.description)] - schema: dict[str, Any] | None = tool.input_schema + schema: dict[str, object] | None = tool.input_schema if schema: props = schema.get("properties", {}) for pname, pinfo in props.items(): diff --git a/src/agentkit/tools/shell.py b/src/agentkit/tools/shell.py index c2a3aaf..5833965 100644 --- a/src/agentkit/tools/shell.py +++ b/src/agentkit/tools/shell.py @@ -14,7 +14,7 @@ import shlex import time import uuid from collections import deque -from typing import Any, Callable, Awaitable +from typing import Callable, Awaitable from agentkit.tools.base import Tool from agentkit.tools.output_parser import OutputParser, ParsedOutput @@ -232,8 +232,8 @@ class ShellTool(Tool): self, name: str = "shell", description: str = "执行 Shell 命令,支持会话模式保持跨命令状态", - input_schema: dict[str, Any] | None = None, - output_schema: dict[str, Any] | None = None, + input_schema: dict[str, object] | None = None, + output_schema: dict[str, object] | None = None, version: str = "1.0.0", tags: list[str] | None = None, confirm_callback: Callable[[str], Awaitable[bool]] | None = None, @@ -253,10 +253,10 @@ class ShellTool(Tool): self._confirm_callback = confirm_callback self._default_timeout = default_timeout self._max_output_length = max_output_length - self._audit_log: deque[dict[str, Any]] = deque(maxlen=10000) + self._audit_log: deque[dict[str, object]] = deque(maxlen=10000) @staticmethod - def _default_input_schema() -> dict[str, Any]: + def _default_input_schema() -> dict[str, object]: return { "type": "object", "properties": { @@ -287,7 +287,7 @@ class ShellTool(Tool): } @staticmethod - def _default_output_schema() -> dict[str, Any]: + def _default_output_schema() -> dict[str, object]: return { "type": "object", "properties": { @@ -613,6 +613,6 @@ class ShellTool(Tool): return self._session_manager @property - def audit_log(self) -> list[dict[str, Any]]: + def audit_log(self) -> list[dict[str, object]]: """获取审计日志(副本)""" return list(self._audit_log) diff --git a/src/agentkit/tools/skill_install.py b/src/agentkit/tools/skill_install.py index dfce86a..f8f5e63 100644 --- a/src/agentkit/tools/skill_install.py +++ b/src/agentkit/tools/skill_install.py @@ -4,7 +4,7 @@ import asyncio import logging import os import re -from typing import Any, Callable, Awaitable +from typing import Callable, Awaitable from agentkit.tools.base import Tool @@ -34,8 +34,8 @@ class SkillInstallTool(Tool): "重要:安装前应先用 skill_search 工具搜索确认技能名称和来源(source)。" "如果用户只提供了模糊名称,先用 skill_search 搜索,再根据搜索结果安装。" ), - input_schema: dict[str, Any] | None = None, - output_schema: dict[str, Any] | None = None, + input_schema: dict[str, object] | None = None, + output_schema: dict[str, object] | None = None, version: str = "1.0.0", tags: list[str] | None = None, confirm_callback: Callable[[str], Awaitable[bool]] | None = None, diff --git a/src/agentkit/tools/skill_search.py b/src/agentkit/tools/skill_search.py index 7a95b1b..5830723 100644 --- a/src/agentkit/tools/skill_search.py +++ b/src/agentkit/tools/skill_search.py @@ -2,7 +2,6 @@ import asyncio import logging -from typing import Any from agentkit.tools.base import Tool @@ -27,8 +26,8 @@ class SkillSearchTool(Tool): "搜索可用的 Agent 技能包。在安装技能之前,应先使用此工具搜索确认技能名称和来源。" "返回匹配的技能列表,包含名称、描述和安装来源。" ), - input_schema: dict[str, Any] | None = None, - output_schema: dict[str, Any] | None = None, + input_schema: dict[str, object] | None = None, + output_schema: dict[str, object] | None = None, version: str = "1.0.0", tags: list[str] | None = None, ): diff --git a/src/agentkit/tools/terminal_session.py b/src/agentkit/tools/terminal_session.py index b065877..674ab46 100644 --- a/src/agentkit/tools/terminal_session.py +++ b/src/agentkit/tools/terminal_session.py @@ -14,7 +14,6 @@ import shlex import time from collections import deque from dataclasses import dataclass, field -from typing import Any from agentkit.tools.output_parser import OutputParser, ParsedOutput diff --git a/src/agentkit/tools/web_crawl.py b/src/agentkit/tools/web_crawl.py index aa466b7..91e80bb 100644 --- a/src/agentkit/tools/web_crawl.py +++ b/src/agentkit/tools/web_crawl.py @@ -1,7 +1,6 @@ """WebCrawlTool - 基于 Crawl4AI 的网页抓取工具,支持优雅降级""" import logging -from typing import Any from agentkit.tools.base import Tool @@ -31,8 +30,8 @@ class WebCrawlTool(Tool): self, name: str = "web_crawl", description: str = "抓取网页内容,支持 Markdown/HTML 输出和 CSS 选择器提取", - input_schema: dict[str, Any] | None = None, - output_schema: dict[str, Any] | None = None, + input_schema: dict[str, object] | None = None, + output_schema: dict[str, object] | None = None, version: str = "1.0.0", tags: list[str] | None = None, ): @@ -46,7 +45,7 @@ class WebCrawlTool(Tool): ) @staticmethod - def _default_input_schema() -> dict[str, Any]: + def _default_input_schema() -> dict[str, object]: return { "type": "object", "properties": { @@ -74,7 +73,7 @@ class WebCrawlTool(Tool): } @staticmethod - def _default_output_schema() -> dict[str, Any]: + def _default_output_schema() -> dict[str, object]: return { "type": "object", "properties": { @@ -142,7 +141,7 @@ class WebCrawlTool(Tool): status_code = result.status_code if hasattr(result, "status_code") else 200 - response: dict[str, Any] = { + response: dict[str, object] = { "content": content, "status_code": status_code, "links": links, diff --git a/src/agentkit/tools/web_search.py b/src/agentkit/tools/web_search.py index 0c059d8..c665031 100644 --- a/src/agentkit/tools/web_search.py +++ b/src/agentkit/tools/web_search.py @@ -10,7 +10,6 @@ import json import logging import re import urllib.parse -from typing import Any import httpx @@ -32,8 +31,8 @@ class WebSearchTool(Tool): self, name: str = "web_search", description: str = "搜索互联网信息。返回搜索结果列表,包含标题、链接和摘要。", - input_schema: dict[str, Any] | None = None, - output_schema: dict[str, Any] | None = None, + input_schema: dict[str, object] | None = None, + output_schema: dict[str, object] | None = None, version: str = "1.0.0", tags: list[str] | None = None, tavily_api_key: str | None = None, @@ -53,7 +52,7 @@ class WebSearchTool(Tool): self._default_max_results = default_max_results @staticmethod - def _default_input_schema() -> dict[str, Any]: + def _default_input_schema() -> dict[str, object]: return { "type": "object", "properties": { @@ -71,7 +70,7 @@ class WebSearchTool(Tool): } @staticmethod - def _default_output_schema() -> dict[str, Any]: + def _default_output_schema() -> dict[str, object]: return { "type": "object", "properties": { -- 2.43.0 From 1f4f54b07312599e2866c11712cb084e44edc967 Mon Sep 17 00:00:00 2001 From: chiguyong Date: Wed, 1 Jul 2026 04:53:50 +0800 Subject: [PATCH 2/2] refactor: remove Any from Wave 4 modules (250 sites) --- src/agentkit/bus/memory_bus.py | 6 +-- src/agentkit/bus/message.py | 9 ++-- src/agentkit/bus/protocol.py | 2 +- src/agentkit/bus/redis_bus.py | 8 ++-- src/agentkit/calendar/sync/caldav_provider.py | 13 +++--- src/agentkit/calendar/sync/ics_provider.py | 5 +-- src/agentkit/calendar/sync/manager.py | 7 ++- .../calendar/sync/outlook_provider.py | 39 ++++++++--------- src/agentkit/channels/base.py | 3 +- src/agentkit/channels/dingtalk.py | 5 +-- src/agentkit/channels/feishu.py | 7 ++- src/agentkit/channels/slack.py | 5 +-- src/agentkit/cli/admin.py | 36 ++++++++-------- src/agentkit/cli/admin_client.py | 20 ++++----- src/agentkit/cli/onboarding.py | 9 ++-- .../documents/renderers/excel_renderer.py | 5 +-- .../documents/renderers/pdf_renderer.py | 3 +- .../documents/renderers/template_renderer.py | 3 +- src/agentkit/mcp/client.py | 16 +++---- src/agentkit/mcp/publisher.py | 8 ++-- src/agentkit/mcp/server.py | 16 +++---- src/agentkit/mcp/transport.py | 43 +++++++++---------- src/agentkit/quality/alignment.py | 15 +++---- src/agentkit/quality/cascade_state_store.py | 4 +- src/agentkit/quality/gate.py | 10 ++--- src/agentkit/quality/output.py | 5 +-- .../rag_platform/document_processor.py | 6 +-- src/agentkit/rag_platform/hit_processing.py | 3 +- src/agentkit/rag_platform/pipeline.py | 4 +- src/agentkit/rag_platform/preview.py | 4 +- src/agentkit/rag_platform/question_gen.py | 5 +-- src/agentkit/rag_platform/rerank.py | 14 +++--- src/agentkit/rag_platform/retrieval.py | 4 +- src/agentkit/rag_platform/sanitize.py | 4 +- src/agentkit/rag_platform/tasks.py | 22 +++++----- src/agentkit/server/auth/dependencies.py | 18 ++++---- src/agentkit/server/auth/jwt_utils.py | 9 ++-- src/agentkit/server/auth/middleware.py | 3 +- src/agentkit/server/auth/models.py | 11 +++-- src/agentkit/server/auth/permissions.py | 5 +-- src/agentkit/server/auth/providers/local.py | 3 +- src/agentkit/server/auth/session_service.py | 5 +-- src/agentkit/session/manager.py | 5 +-- src/agentkit/session/models.py | 13 +++--- src/agentkit/session/store.py | 4 +- src/agentkit/telemetry/tracer.py | 24 +++++------ src/agentkit/telemetry/tracing.py | 4 +- 47 files changed, 221 insertions(+), 251 deletions(-) diff --git a/src/agentkit/bus/memory_bus.py b/src/agentkit/bus/memory_bus.py index d720c52..b0bfe3c 100644 --- a/src/agentkit/bus/memory_bus.py +++ b/src/agentkit/bus/memory_bus.py @@ -8,7 +8,7 @@ from __future__ import annotations import asyncio import logging -from typing import Any, Callable, Awaitable +from typing import Callable, Awaitable from agentkit.bus.message import AgentMessage @@ -20,8 +20,8 @@ class InMemoryMessageBus: def __init__( self, - cascade_detector: Any = None, - alignment_guard: Any = None, + cascade_detector: object = None, + alignment_guard: object = None, ) -> None: self._subscribers: dict[str, list[Callable[[AgentMessage], Awaitable[None]]]] = {} self._pending_requests: dict[str, asyncio.Future[AgentMessage]] = {} diff --git a/src/agentkit/bus/message.py b/src/agentkit/bus/message.py index dc770c1..024f9fe 100644 --- a/src/agentkit/bus/message.py +++ b/src/agentkit/bus/message.py @@ -5,7 +5,6 @@ from __future__ import annotations import uuid from dataclasses import dataclass, field from datetime import datetime, timezone -from typing import Any @dataclass @@ -20,11 +19,11 @@ class AgentMessage: sender: str = "" recipient: str | None = None # None = broadcast topic: str = "" - payload: dict[str, Any] = field(default_factory=dict) + payload: dict[str, object] = field(default_factory=dict) timestamp: datetime = field(default_factory=lambda: datetime.now(timezone.utc)) correlation_id: str | None = None # 请求-响应关联 # --- 新增字段 --- - content: Any = None # 消息内容(与 payload 互补,payload 为 dict,content 可为任意类型) + content: object = None # 消息内容(与 payload 互补,payload 为 dict,content 可为任意类型) msg_type: str = "notify" # "request" | "response" | "notify" | "negotiate" ttl_seconds: int = 300 # 消息存活时间(秒) @@ -39,7 +38,7 @@ class AgentMessage: def is_broadcast(self) -> bool: return self.recipient is None - def to_dict(self) -> dict[str, Any]: + def to_dict(self) -> dict[str, object]: ts = self.timestamp if isinstance(ts, datetime): ts = ts.isoformat() @@ -57,7 +56,7 @@ class AgentMessage: } @classmethod - def from_dict(cls, data: dict[str, Any]) -> AgentMessage: + def from_dict(cls, data: dict[str, object]) -> AgentMessage: ts = data.get("timestamp", "") if isinstance(ts, str) and ts: try: diff --git a/src/agentkit/bus/protocol.py b/src/agentkit/bus/protocol.py index c7a2cb2..21f3e94 100644 --- a/src/agentkit/bus/protocol.py +++ b/src/agentkit/bus/protocol.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Any, Callable, Awaitable, Protocol as TypingProtocol, runtime_checkable +from typing import Callable, Awaitable, Protocol as TypingProtocol, runtime_checkable from agentkit.bus.message import AgentMessage diff --git a/src/agentkit/bus/redis_bus.py b/src/agentkit/bus/redis_bus.py index c846e0d..4017f25 100644 --- a/src/agentkit/bus/redis_bus.py +++ b/src/agentkit/bus/redis_bus.py @@ -9,7 +9,7 @@ from __future__ import annotations import asyncio import json import logging -from typing import Any, Callable, Awaitable +from typing import Callable, Awaitable from agentkit.bus.message import AgentMessage from agentkit.bus.memory_bus import InMemoryMessageBus @@ -32,12 +32,12 @@ class RedisMessageBus: self._redis_url = redis_url self._consumer_group = consumer_group self._max_retries = max_retries - self._redis: Any = None + self._redis: object = None self._subscribers: dict[str, list[Callable[[AgentMessage], Awaitable[None]]]] = {} self._pending_requests: dict[str, asyncio.Future[AgentMessage]] = {} self._consumer_tasks: dict[str, asyncio.Task] = {} - async def _get_redis(self) -> Any: + async def _get_redis(self) -> object: """获取 Redis 连接(懒初始化)。""" if self._redis is None: import redis.asyncio as aioredis @@ -151,7 +151,7 @@ class RedisMessageBus: async def _handle_failed_message( self, - redis: Any, + redis: object, stream_key: str, msg_id: str, fields: dict, diff --git a/src/agentkit/calendar/sync/caldav_provider.py b/src/agentkit/calendar/sync/caldav_provider.py index f1e280f..204cf6a 100644 --- a/src/agentkit/calendar/sync/caldav_provider.py +++ b/src/agentkit/calendar/sync/caldav_provider.py @@ -15,7 +15,6 @@ import uuid from collections.abc import Awaitable, Callable from datetime import date, datetime, timedelta, timezone from pathlib import Path -from typing import Any import caldav from icalendar import Calendar, Event @@ -33,7 +32,7 @@ from agentkit.calendar.sync.base import AbstractSyncProvider logger = logging.getLogger(__name__) # Async callback signature: (event_type: str, payload: dict) -> None -NotifyCallback = Callable[[str, dict[str, Any]], Awaitable[None]] +NotifyCallback = Callable[[str, dict[str, object]], Awaitable[None]] def _parse_iso(dt_str: str) -> datetime: @@ -51,7 +50,7 @@ def _to_iso_utc(dt: datetime) -> str: return dt.astimezone(timezone.utc).isoformat() -def _extract_dt(component: Any, key: str) -> tuple[str, bool]: +def _extract_dt(component: object, key: str) -> tuple[str, bool]: """Extract date/datetime from icalendar component. Returns (iso, is_all_day).""" prop = component.get(key) if prop is None: @@ -73,7 +72,7 @@ class CalDAVSyncProvider(AbstractSyncProvider): def __init__( self, db_path: str | Path | None = None, - client_factory: Callable[[ExternalCalendarConfig], Any] | None = None, + client_factory: Callable[[ExternalCalendarConfig], object] | None = None, notify_callback: NotifyCallback | None = None, ) -> None: self.db_path = Path(db_path) if db_path is not None else DEFAULT_CALENDAR_DB_PATH @@ -87,7 +86,7 @@ class CalDAVSyncProvider(AbstractSyncProvider): # Client construction # ------------------------------------------------------------------ - def _make_client(self, config: ExternalCalendarConfig) -> Any: + def _make_client(self, config: ExternalCalendarConfig) -> object: """Build a caldav.DAVClient from config credentials.""" if self._client_factory is not None: return self._client_factory(config) @@ -103,7 +102,7 @@ class CalDAVSyncProvider(AbstractSyncProvider): timeout=30, ) - def _get_calendar(self, config: ExternalCalendarConfig) -> Any: + def _get_calendar(self, config: ExternalCalendarConfig) -> object: """Connect and return the first calendar from the principal.""" client = self._make_client(config) principal = client.principal() @@ -166,7 +165,7 @@ class CalDAVSyncProvider(AbstractSyncProvider): events.append(parsed) return events - def _parse_caldav_event(self, caldav_event: Any, user_id: str) -> CalendarEvent | None: + def _parse_caldav_event(self, caldav_event: object, user_id: str) -> CalendarEvent | None: """Convert a caldav.Event to a CalendarEvent dataclass.""" try: ical_data = caldav_event.data diff --git a/src/agentkit/calendar/sync/ics_provider.py b/src/agentkit/calendar/sync/ics_provider.py index b54a335..e502a06 100644 --- a/src/agentkit/calendar/sync/ics_provider.py +++ b/src/agentkit/calendar/sync/ics_provider.py @@ -11,7 +11,6 @@ from __future__ import annotations import logging import uuid from datetime import date, datetime, timezone -from typing import Any from icalendar import Calendar, Event from icalendar.prop import vRecur @@ -38,7 +37,7 @@ def _parse_iso(dt_str: str) -> datetime: return dt.astimezone(timezone.utc) -def _extract_dt(component: Any, key: str) -> tuple[str, bool]: +def _extract_dt(component: object, key: str) -> tuple[str, bool]: """Extract a date/datetime property from an icalendar component. Returns ``(iso_string, is_all_day)``. ``is_all_day`` is True when the @@ -59,7 +58,7 @@ class ICSProvider: def __init__(self, service: CalendarService) -> None: self.service = service - async def import_ics(self, ics_bytes: bytes, user_id: str) -> dict[str, Any]: + async def import_ics(self, ics_bytes: bytes, user_id: str) -> dict[str, object]: """Parse ICS bytes and create events for *user_id*. Returns ``{"imported": N, "skipped": M, "errors": [...]}``. diff --git a/src/agentkit/calendar/sync/manager.py b/src/agentkit/calendar/sync/manager.py index c22053b..f911a46 100644 --- a/src/agentkit/calendar/sync/manager.py +++ b/src/agentkit/calendar/sync/manager.py @@ -16,7 +16,6 @@ import logging from collections.abc import Awaitable, Callable from datetime import datetime, timezone from pathlib import Path -from typing import Any from agentkit.calendar.db import ( DEFAULT_CALENDAR_DB_PATH, @@ -31,7 +30,7 @@ from agentkit.calendar.sync.caldav_provider import CalDAVSyncProvider logger = logging.getLogger(__name__) # Async callback signature: (event_type: str, payload: dict) -> None -NotifyCallback = Callable[[str, dict[str, Any]], Awaitable[None]] +NotifyCallback = Callable[[str, dict[str, object]], Awaitable[None]] class SyncManager: @@ -64,7 +63,7 @@ class SyncManager: # Sync orchestration # ------------------------------------------------------------------ - async def sync_all(self, user_id: str) -> dict[str, Any]: + async def sync_all(self, user_id: str) -> dict[str, object]: """Sync all external calendar configs for a user. Returns ``{"synced": N, "errors": [...]}``. @@ -81,7 +80,7 @@ class SyncManager: logger.warning("Sync failed for config %s: %s", config.id, e) return {"synced": synced, "errors": errors} - async def sync_provider(self, config_id: str) -> dict[str, Any]: + async def sync_provider(self, config_id: str) -> dict[str, object]: """Sync a single external calendar config by ID. Pulls remote changes, then pushes local changes modified since diff --git a/src/agentkit/calendar/sync/outlook_provider.py b/src/agentkit/calendar/sync/outlook_provider.py index b0053e8..5fc8d8b 100644 --- a/src/agentkit/calendar/sync/outlook_provider.py +++ b/src/agentkit/calendar/sync/outlook_provider.py @@ -19,7 +19,6 @@ import uuid from collections.abc import Awaitable, Callable from datetime import datetime, timedelta, timezone from pathlib import Path -from typing import Any from urllib.parse import parse_qs, quote, urlparse import httpx @@ -37,7 +36,7 @@ from agentkit.calendar.sync.base import AbstractSyncProvider logger = logging.getLogger(__name__) # Async callback signature: (event_type: str, payload: dict) -> None -NotifyCallback = Callable[[str, dict[str, Any]], Awaitable[None]] +NotifyCallback = Callable[[str, dict[str, object]], Awaitable[None]] GRAPH_BASE = "https://graph.microsoft.com/v1.0" TOKEN_URL = "https://login.microsoftonline.com/common/oauth2/v2.0/token" @@ -70,7 +69,7 @@ def _parse_iso(dt_str: str) -> datetime: return dt.astimezone(timezone.utc) -def _outlook_dt_to_iso(dt_obj: dict[str, Any]) -> str: +def _outlook_dt_to_iso(dt_obj: dict[str, object]) -> str: """Convert Outlook dateTimeTimeZone to ISO 8601 UTC. ponytail: assumes Graph returns UTC (no ``Prefer: outlook.timezone`` header @@ -102,7 +101,7 @@ def _iso_to_outlook_dt(iso_str: str, is_all_day: bool) -> dict[str, str]: return {"dateTime": dt.strftime("%Y-%m-%dT%H:%M:%S"), "timeZone": "UTC"} -def _outlook_recurrence_to_rrule(recurrence: dict[str, Any] | None) -> str | None: +def _outlook_recurrence_to_rrule(recurrence: dict[str, object] | None) -> str | None: """Convert Outlook recurrence pattern to RRULE string.""" if not recurrence: return None @@ -134,7 +133,7 @@ def _outlook_recurrence_to_rrule(recurrence: dict[str, Any] | None) -> str | Non return ";".join(parts) if parts else None -def _rrule_to_outlook_recurrence(rrule: str, start_date: str) -> dict[str, Any] | None: +def _rrule_to_outlook_recurrence(rrule: str, start_date: str) -> dict[str, object] | None: """Convert RRULE string to Outlook recurrence pattern. ``start_date`` is the event's start date in ``YYYY-MM-DD`` format (required @@ -151,7 +150,7 @@ def _rrule_to_outlook_recurrence(rrule: str, start_date: str) -> dict[str, Any] if not pattern_type: return None - pattern: dict[str, Any] = {"type": pattern_type} + pattern: dict[str, object] = {"type": pattern_type} interval = parts.get("INTERVAL") pattern["interval"] = int(interval) if interval else 1 @@ -167,7 +166,7 @@ def _rrule_to_outlook_recurrence(rrule: str, start_date: str) -> dict[str, Any] if count: pattern["numberOfOccurrences"] = int(count) - range_obj: dict[str, Any] = { + range_obj: dict[str, object] = { "type": "numbered", "startDate": start_date, "numberOfOccurrences": int(count), @@ -202,7 +201,7 @@ class OutlookSyncProvider(AbstractSyncProvider): def __init__( self, db_path: str | Path | None = None, - client_factory: Callable[[], Any] | None = None, + client_factory: Callable[[], object] | None = None, notify_callback: NotifyCallback | None = None, ) -> None: self.db_path = Path(db_path) if db_path is not None else DEFAULT_CALENDAR_DB_PATH @@ -216,19 +215,19 @@ class OutlookSyncProvider(AbstractSyncProvider): # Client / auth # ------------------------------------------------------------------ - def _get_client(self) -> Any: + def _get_client(self) -> object: """Return an httpx.AsyncClient (real or mock from factory).""" if self._client_factory is not None: return self._client_factory() return httpx.AsyncClient(timeout=30.0) - def _load_creds(self, config: ExternalCalendarConfig) -> dict[str, Any]: + def _load_creds(self, config: ExternalCalendarConfig) -> dict[str, object]: return json.loads(config.credentials) if config.credentials else {} - def _save_creds(self, config: ExternalCalendarConfig, creds: dict[str, Any]) -> None: + def _save_creds(self, config: ExternalCalendarConfig, creds: dict[str, object]) -> None: config.credentials = json.dumps(creds) - async def _refresh_token(self, client: Any, config: ExternalCalendarConfig) -> dict[str, Any]: + async def _refresh_token(self, client: object, config: ExternalCalendarConfig) -> dict[str, object]: """Refresh the access token using the refresh_token grant. Posts to the Azure AD token endpoint, updates ``config.credentials`` @@ -271,13 +270,13 @@ class OutlookSyncProvider(AbstractSyncProvider): async def _request( self, - client: Any, + client: object, config: ExternalCalendarConfig, method: str, url: str, *, - json_body: dict[str, Any] | None = None, - ) -> dict[str, Any]: + json_body: dict[str, object] | None = None, + ) -> dict[str, object]: """Make an authenticated Graph API request with 401 auto-refresh + retry.""" creds = self._load_creds(config) headers = {"Authorization": f"Bearer {creds.get('access_token', '')}"} @@ -330,7 +329,7 @@ class OutlookSyncProvider(AbstractSyncProvider): return result async def _pull_delta( - self, client: Any, config: ExternalCalendarConfig + self, client: object, config: ExternalCalendarConfig ) -> tuple[list[CalendarEvent], str | None]: """Call /me/calendarView/delta. Returns (events, delta_token).""" url = self._build_delta_url(config) @@ -356,7 +355,7 @@ class OutlookSyncProvider(AbstractSyncProvider): end = (datetime.now(timezone.utc) + timedelta(days=90)).strftime("%Y-%m-%dT%H:%M:%SZ") return f"{GRAPH_BASE}/me/calendarView/delta?startDateTime={start}&endDateTime={end}" - def _parse_outlook_event(self, raw: dict[str, Any], user_id: str) -> CalendarEvent | None: + def _parse_outlook_event(self, raw: dict[str, object], user_id: str) -> CalendarEvent | None: """Convert a Graph event JSON to a CalendarEvent dataclass.""" eid = raw.get("id") if not eid: @@ -459,7 +458,7 @@ class OutlookSyncProvider(AbstractSyncProvider): return result async def _push_single( - self, client: Any, config: ExternalCalendarConfig, event: CalendarEvent + self, client: object, config: ExternalCalendarConfig, event: CalendarEvent ) -> CalendarEvent: """Push a single event to Outlook, return event with external_id set.""" body = self._event_to_outlook(event) @@ -485,9 +484,9 @@ class OutlookSyncProvider(AbstractSyncProvider): event.external_provider = "outlook" return event - def _event_to_outlook(self, event: CalendarEvent) -> dict[str, Any]: + def _event_to_outlook(self, event: CalendarEvent) -> dict[str, object]: """Convert CalendarEvent to Outlook Graph event JSON.""" - body: dict[str, Any] = { + body: dict[str, object] = { "subject": event.title, "start": _iso_to_outlook_dt(event.start_time, event.is_all_day), "end": _iso_to_outlook_dt(event.end_time, event.is_all_day), diff --git a/src/agentkit/channels/base.py b/src/agentkit/channels/base.py index 450f307..6f3de3b 100644 --- a/src/agentkit/channels/base.py +++ b/src/agentkit/channels/base.py @@ -9,7 +9,6 @@ from __future__ import annotations import abc from dataclasses import dataclass, field from enum import Enum -from typing import Any import httpx @@ -39,7 +38,7 @@ class IncomingMessage: user_id: str # 平台用户 ID chat_id: str # 群组/会话 ID content: str # 消息文本 - raw_event: dict[str, Any] = field(default_factory=dict) # 原始事件 + raw_event: dict[str, object] = field(default_factory=dict) # 原始事件 timestamp: str = "" diff --git a/src/agentkit/channels/dingtalk.py b/src/agentkit/channels/dingtalk.py index 9b657b4..8f648f2 100644 --- a/src/agentkit/channels/dingtalk.py +++ b/src/agentkit/channels/dingtalk.py @@ -19,7 +19,6 @@ import json import logging import re import time -from typing import Any import httpx @@ -147,7 +146,7 @@ class DingTalkMessageAdapter(MessageAdapter): ValueError: 事件 body 不是合法 JSON。 """ try: - data: dict[str, Any] = json.loads(body) + data: dict[str, object] = json.loads(body) except json.JSONDecodeError as exc: raise ValueError(f"钉钉事件 body 不是合法 JSON: {exc}") from exc @@ -169,7 +168,7 @@ class DingTalkMessageAdapter(MessageAdapter): timestamp=timestamp, ) - def _extract_content(self, data: dict[str, Any]) -> str: + def _extract_content(self, data: dict[str, object]) -> str: """从钉钉事件提取文本内容。 - text 类型:解析 ``text.content``,剥离 @ 机器人前缀。 diff --git a/src/agentkit/channels/feishu.py b/src/agentkit/channels/feishu.py index cfac706..adc3730 100644 --- a/src/agentkit/channels/feishu.py +++ b/src/agentkit/channels/feishu.py @@ -19,7 +19,6 @@ import logging import re import time from datetime import datetime, timezone -from typing import Any import httpx @@ -142,7 +141,7 @@ class FeishuMessageAdapter(MessageAdapter): ValueError: 事件结构无法解析。 """ try: - data: dict[str, Any] = json.loads(body) + data: dict[str, object] = json.loads(body) except json.JSONDecodeError as exc: raise ValueError(f"飞书事件 body 不是合法 JSON: {exc}") from exc @@ -184,7 +183,7 @@ class FeishuMessageAdapter(MessageAdapter): timestamp=timestamp, ) - def _decrypt_event(self, encrypt_b64: str) -> dict[str, Any]: + def _decrypt_event(self, encrypt_b64: str) -> dict[str, object]: """AES-256-CBC 解密飞书加密事件。 飞书协议: @@ -216,7 +215,7 @@ class FeishuMessageAdapter(MessageAdapter): return json.loads(plaintext.decode("utf-8")) - def _extract_content(self, message: dict[str, Any]) -> str: + def _extract_content(self, message: dict[str, object]) -> str: """从飞书 message 字段提取文本内容。 - text 类型:解析 ``content`` JSON 中的 ``text`` 字段,剥离 @ 提及标记。 diff --git a/src/agentkit/channels/slack.py b/src/agentkit/channels/slack.py index 2087aee..8dfb141 100644 --- a/src/agentkit/channels/slack.py +++ b/src/agentkit/channels/slack.py @@ -19,7 +19,6 @@ import json import logging import re import time -from typing import Any from urllib.parse import parse_qs from uuid import uuid4 @@ -134,7 +133,7 @@ class SlackMessageAdapter(MessageAdapter): """ # 优先尝试 JSON 解析(Events API / URL 验证) try: - data: dict[str, Any] = json.loads(body) + data: dict[str, object] = json.loads(body) except (json.JSONDecodeError, UnicodeDecodeError): data = {} @@ -144,7 +143,7 @@ class SlackMessageAdapter(MessageAdapter): # 非 JSON — 视为 Slash Command(form-encoded) return self._parse_slash_command(body) - def _parse_event(self, data: dict[str, Any]) -> IncomingMessage: + def _parse_event(self, data: dict[str, object]) -> IncomingMessage: """解析 Events API 事件。""" # URL 验证流程 if data.get("type") == "url_verification": diff --git a/src/agentkit/cli/admin.py b/src/agentkit/cli/admin.py index 50ad19e..f16393a 100644 --- a/src/agentkit/cli/admin.py +++ b/src/agentkit/cli/admin.py @@ -13,7 +13,7 @@ from __future__ import annotations import json as _json from pathlib import Path -from typing import Any, Optional +from typing import Optional import httpx import typer @@ -87,13 +87,13 @@ def _build_client( ) -def _emit_json(data: Any) -> None: +def _emit_json(data: object) -> None: rprint(_json.dumps(data, indent=2, ensure_ascii=False, default=str)) def _safe_get( client: AdminHttpClient, path: str, server_url: str, params: dict | None = None -) -> Any: +) -> object: try: return client.get(path, params=params) except httpx.ConnectError: @@ -104,7 +104,7 @@ def _safe_get( def _safe_post( client: AdminHttpClient, path: str, server_url: str, body: dict | None = None -) -> Any: +) -> object: try: return client.post(path, json=body) except httpx.ConnectError: @@ -115,7 +115,7 @@ def _safe_post( def _safe_patch( client: AdminHttpClient, path: str, server_url: str, body: dict | None = None -) -> Any: +) -> object: try: return client.patch(path, json=body) except httpx.ConnectError: @@ -124,7 +124,7 @@ def _safe_patch( _handle_http_error(e, server_url) -def _safe_put(client: AdminHttpClient, path: str, server_url: str, body: dict | None = None) -> Any: +def _safe_put(client: AdminHttpClient, path: str, server_url: str, body: dict | None = None) -> object: try: return client.put(path, json=body) except httpx.ConnectError: @@ -135,7 +135,7 @@ def _safe_put(client: AdminHttpClient, path: str, server_url: str, body: dict | def _safe_delete( client: AdminHttpClient, path: str, server_url: str, params: dict | None = None -) -> Any: +) -> object: try: return client.delete(path, params=params) except httpx.ConnectError: @@ -252,7 +252,7 @@ def dept_update( json_output: bool = JsonFlag, ) -> None: """Update a department's name or description.""" - body: dict[str, Any] = {} + body: dict[str, object] = {} if name is not None: body["name"] = name if description is not None: @@ -576,7 +576,7 @@ def user_list( ) -> None: """List users, optionally filtered by department.""" client = _build_client(server_url, token, api_key) - params: dict[str, Any] = {} + params: dict[str, object] = {} if department_id: params["department_id"] = department_id users = _safe_get(client, "/api/v1/admin/users", client.base_url, params=params or None) @@ -620,7 +620,7 @@ def user_create( json_output: bool = JsonFlag, ) -> None: """Create a new user.""" - body: dict[str, Any] = { + body: dict[str, object] = { "username": username, "email": email, "password": password, @@ -653,7 +653,7 @@ def user_update( json_output: bool = JsonFlag, ) -> None: """Update a user's role or active flag.""" - body: dict[str, Any] = {} + body: dict[str, object] = {} if role is not None: body["role"] = role if is_active is not None: @@ -818,7 +818,7 @@ def llm_add_provider( json_output: bool = JsonFlag, ) -> None: """Create a new LLM provider.""" - body: dict[str, Any] = { + body: dict[str, object] = { "name": name, "type": type, "api_key": api_key, @@ -850,7 +850,7 @@ def llm_update_provider( json_output: bool = JsonFlag, ) -> None: """Update an LLM provider's configuration.""" - body: dict[str, Any] = {} + body: dict[str, object] = {} if type is not None: body["type"] = type if api_key is not None: @@ -1123,7 +1123,7 @@ def kb_list_documents( ) -> None: """List KB documents.""" client = _build_client(server_url, token, api_key) - params: dict[str, Any] = {} + params: dict[str, object] = {} if source_id: params["source_id"] = source_id if department_id: @@ -1176,7 +1176,7 @@ def kb_upload( rprint(f"[red]Error: File not found: {content_file}[/red]") raise typer.Exit(1) content = path.read_text(encoding="utf-8") - body: dict[str, Any] = { + body: dict[str, object] = { "filename": filename, "content": content, "source_id": source_id, @@ -1255,8 +1255,8 @@ def _usage_params( user_id: Optional[str], start: Optional[str], end: Optional[str], -) -> dict[str, Any]: - params: dict[str, Any] = {} +) -> dict[str, object]: + params: dict[str, object] = {} if department_id: params["department_id"] = department_id if user_id: @@ -1386,7 +1386,7 @@ def usage_top_users( ) -> None: """Show top-N users by token usage.""" client = _build_client(server_url, token, api_key) - params: dict[str, Any] = {"limit": limit} + params: dict[str, object] = {"limit": limit} if department_id: params["department_id"] = department_id if start: diff --git a/src/agentkit/cli/admin_client.py b/src/agentkit/cli/admin_client.py index 5dda624..c0bb805 100644 --- a/src/agentkit/cli/admin_client.py +++ b/src/agentkit/cli/admin_client.py @@ -11,8 +11,6 @@ from __future__ import annotations import os from pathlib import Path -from typing import Any - import httpx import yaml @@ -62,7 +60,7 @@ class AdminHttpClient: 4. Hard-coded defaults (server URL only) """ path = Path(config_path) if config_path else DEFAULT_CONFIG_PATH - file_cfg: dict[str, Any] = {} + file_cfg: dict[str, object] = {} if path.exists(): try: with path.open(encoding="utf-8") as f: @@ -106,8 +104,8 @@ class AdminHttpClient: method: str, path: str, *, - params: dict[str, Any] | None = None, - json: dict[str, Any] | None = None, + params: dict[str, object] | None = None, + json: dict[str, object] | None = None, timeout: float = DEFAULT_TIMEOUT, ) -> httpx.Response: url = f"{self._base_url}{path}" @@ -130,28 +128,28 @@ class AdminHttpClient: def base_url(self) -> str: return self._base_url - def get(self, path: str, params: dict[str, Any] | None = None) -> Any: + def get(self, path: str, params: dict[str, object] | None = None) -> object: resp = self._request("GET", path, params=params) return resp.json() - def get_text(self, path: str, params: dict[str, Any] | None = None) -> str: + def get_text(self, path: str, params: dict[str, object] | None = None) -> str: """GET returning response text (for CSV exports).""" resp = self._request("GET", path, params=params) return resp.text - def post(self, path: str, json: dict[str, Any] | None = None) -> Any: + def post(self, path: str, json: dict[str, object] | None = None) -> object: resp = self._request("POST", path, json=json) return resp.json() - def put(self, path: str, json: dict[str, Any] | None = None) -> Any: + def put(self, path: str, json: dict[str, object] | None = None) -> object: resp = self._request("PUT", path, json=json) return resp.json() - def patch(self, path: str, json: dict[str, Any] | None = None) -> Any: + def patch(self, path: str, json: dict[str, object] | None = None) -> object: resp = self._request("PATCH", path, json=json) return resp.json() - def delete(self, path: str, params: dict[str, Any] | None = None) -> Any: + def delete(self, path: str, params: dict[str, object] | None = None) -> object: resp = self._request("DELETE", path, params=params) return resp.json() diff --git a/src/agentkit/cli/onboarding.py b/src/agentkit/cli/onboarding.py index f40c329..f274ad8 100644 --- a/src/agentkit/cli/onboarding.py +++ b/src/agentkit/cli/onboarding.py @@ -10,7 +10,6 @@ When no agentkit.yaml exists, this wizard guides the user through: from __future__ import annotations from pathlib import Path -from typing import Any import yaml from rich.panel import Panel @@ -22,7 +21,7 @@ from agentkit.server.config import find_config_path # ── Provider presets ────────────────────────────────────────────────── -PROVIDER_PRESETS: dict[str, dict[str, Any]] = { +PROVIDER_PRESETS: dict[str, dict[str, object]] = { "deepseek": { "name": "DeepSeek", "env_key": "DEEPSEEK_API_KEY", @@ -144,7 +143,7 @@ def run_onboarding( output_path.mkdir(parents=True, exist_ok=True) existing_config_path = find_config_path(config_arg) - existing_config: dict[str, Any] | None = None + existing_config: dict[str, object] | None = None if existing_config_path: with open(existing_config_path, encoding="utf-8") as f: existing_config = yaml.safe_load(f) or {} @@ -220,7 +219,7 @@ def run_onboarding( ) selected_model = available_models[int(model_choice) - 1] # Rebuild models dict: selected model gets "default" alias - updated_models: dict[str, Any] = {} + updated_models: dict[str, object] = {} for model, conf in preset["models"].items(): if model == selected_model: updated_models[model] = {**conf, "alias": "default"} @@ -236,7 +235,7 @@ def run_onboarding( # ── Step 3: Optional — add a second provider ───────────────── env_vars: dict[str, str] = {preset["env_key"]: api_key.strip()} - providers_config: dict[str, Any] = { + providers_config: dict[str, object] = { selected_key: { "api_key": f"${{{preset['env_key']}}}", "base_url": preset["base_url"], diff --git a/src/agentkit/documents/renderers/excel_renderer.py b/src/agentkit/documents/renderers/excel_renderer.py index cc504af..e8d7c2c 100644 --- a/src/agentkit/documents/renderers/excel_renderer.py +++ b/src/agentkit/documents/renderers/excel_renderer.py @@ -12,7 +12,6 @@ from __future__ import annotations import json import re from pathlib import Path -from typing import Any from openpyxl import Workbook @@ -43,7 +42,7 @@ class ExcelRenderer: return self._render_markdown(markdown_content, output_path) - def _render_json(self, data: dict[str, list[list[Any]]], output_path: Path) -> Path: + def _render_json(self, data: dict[str, list[list[object]]], output_path: Path) -> Path: """Render JSON dict {sheet_name: rows} into a multi-sheet workbook.""" wb = Workbook() # Remove the default sheet — we'll create named ones @@ -108,7 +107,7 @@ class ExcelRenderer: wb.save(str(output_path)) return output_path - def _fill_sheet_from_table(self, ws: Any, table_lines: list[str]) -> None: + def _fill_sheet_from_table(self, ws: object, table_lines: list[str]) -> None: """Parse GFM table lines and write rows into a worksheet.""" for idx, line in enumerate(table_lines): if idx == 1: diff --git a/src/agentkit/documents/renderers/pdf_renderer.py b/src/agentkit/documents/renderers/pdf_renderer.py index 8e6bbdb..054cbcc 100644 --- a/src/agentkit/documents/renderers/pdf_renderer.py +++ b/src/agentkit/documents/renderers/pdf_renderer.py @@ -17,7 +17,6 @@ from __future__ import annotations import logging import re from pathlib import Path -from typing import Any from reportlab.lib import colors from reportlab.lib.pagesizes import A4 @@ -116,7 +115,7 @@ class PDFRenderer: ) styles = self._build_styles() - flowables: list[Any] = [] + flowables: list[object] = [] lines = markdown_content.splitlines() i = 0 diff --git a/src/agentkit/documents/renderers/template_renderer.py b/src/agentkit/documents/renderers/template_renderer.py index dfbeb8b..8f15410 100644 --- a/src/agentkit/documents/renderers/template_renderer.py +++ b/src/agentkit/documents/renderers/template_renderer.py @@ -10,7 +10,6 @@ from __future__ import annotations import logging from pathlib import Path -from typing import Any from docxtpl import DocxTemplate from jinja2.sandbox import SandboxedEnvironment @@ -40,7 +39,7 @@ class TemplateRenderer: ) def render_template( - self, template_path: str | Path, data: dict[str, Any], output_path: Path + self, template_path: str | Path, data: dict[str, object], output_path: Path ) -> Path: """Fill a .docx template with data using Jinja2 sandbox. diff --git a/src/agentkit/mcp/client.py b/src/agentkit/mcp/client.py index b912ab9..cce8585 100644 --- a/src/agentkit/mcp/client.py +++ b/src/agentkit/mcp/client.py @@ -14,7 +14,7 @@ from __future__ import annotations import json import logging import warnings -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING from urllib.parse import urlparse from agentkit.mcp.transport import HTTPTransport, SSETransport, StdioTransport, Transport @@ -65,7 +65,7 @@ class MCPClient: self._transport = transport # U10 — 懒构造并缓存的 langchain client,避免每次 list_tools/call_tool # 都新建 MultiServerMCPClient(stdio 传输下会反复 spawn 子进程)。 - self._lc_client: Any = None + self._lc_client: "MultiServerMCPClient | None" = None if transport is not None: # 旧 Transport 路径 — 发出 DeprecationWarning,但保持原有行为 @@ -76,13 +76,13 @@ class MCPClient: DeprecationWarning, stacklevel=2, ) - self._langchain_config: dict[str, Any] | None = None + self._langchain_config: dict[str, object] | None = None else: # 新 langchain 路径 — 解析 URL scheme 构建连接配置 self._langchain_config = self._build_langchain_config(self._server_url, timeout) @staticmethod - def _build_langchain_config(server_url: str, timeout: float) -> dict[str, Any]: + def _build_langchain_config(server_url: str, timeout: float) -> dict[str, object]: """根据 URL scheme 构建 langchain-mcp-adapters 连接配置。 Args: @@ -150,7 +150,7 @@ class MCPClient: server_url = "" return cls(server_url=server_url, transport=transport) - async def _get_lc_client(self) -> Any: + async def _get_lc_client(self) -> "MultiServerMCPClient": """懒构造并缓存 langchain ``MultiServerMCPClient`` 实例。 首次调用时创建,后续返回缓存,避免每次 list_tools/call_tool 都新建 @@ -202,7 +202,7 @@ class MCPClient: return tools @staticmethod - def _extract_schema(tool: Any) -> dict[str, Any]: + def _extract_schema(tool: object) -> dict[str, object]: """从 LangChain Tool 提取 inputSchema(JSON Schema 格式)。 LangChain 工具通常有 args_schema(pydantic model),回退到 tool.args dict。 @@ -273,8 +273,8 @@ class MCPTool(Tool): name: str, description: str, client: MCPClient, - input_schema: dict[str, Any] | None = None, - output_schema: dict[str, Any] | None = None, + input_schema: dict[str, object] | None = None, + output_schema: dict[str, object] | None = None, version: str = "1.0.0", tags: list[str] | None = None, ): diff --git a/src/agentkit/mcp/publisher.py b/src/agentkit/mcp/publisher.py index cc0ff97..f832a28 100644 --- a/src/agentkit/mcp/publisher.py +++ b/src/agentkit/mcp/publisher.py @@ -14,7 +14,7 @@ U14: 支持把已注册的 Skill 或专家团队封装成 ``Tool``,注册到 from __future__ import annotations import logging -from typing import Any, Awaitable, Callable +from collections.abc import Awaitable, Callable from agentkit.skills.base import Skill from agentkit.tools.base import Tool @@ -28,7 +28,7 @@ _DANGEROUS_TOOL_NAMES: frozenset[str] = frozenset( ) # 执行器签名:(skill_or_team_name, input_text) -> 结果 dict -SkillExecutor = Callable[[str, str], Awaitable[dict[str, Any]]] +SkillExecutor = Callable[[str, str], Awaitable[dict[str, object]]] class PublisherRegistry: @@ -90,7 +90,7 @@ class SkillMCPAdapter(Tool): self._skill = skill self._executor = executor - async def execute(self, **kwargs: Any) -> dict[str, Any]: + async def execute(self, **kwargs: object) -> dict[str, object]: """调用 executor 执行技能;未配置或异常时返回错误 dict。""" input_text = kwargs.get("input", "") if self._executor is None: @@ -126,7 +126,7 @@ class TeamMCPAdapter(Tool): self._team_name = team_name self._executor = executor - async def execute(self, **kwargs: Any) -> dict[str, Any]: + async def execute(self, **kwargs: object) -> dict[str, object]: """调用 executor 执行团队任务;未配置或异常时返回错误 dict。""" input_text = kwargs.get("input", "") if self._executor is None: diff --git a/src/agentkit/mcp/server.py b/src/agentkit/mcp/server.py index e8253ca..6c40e1c 100644 --- a/src/agentkit/mcp/server.py +++ b/src/agentkit/mcp/server.py @@ -11,7 +11,7 @@ U13: 重构为路由工厂 ``create_mcp_router()``,可挂载到主 FastAPI app from __future__ import annotations import logging -from typing import Any +from collections.abc import Callable from fastapi import APIRouter, Depends, HTTPException, Request @@ -41,7 +41,7 @@ _MCP_BLOCKED_TOOLS: frozenset[str] = frozenset( ) -def _serialize_tool(tool: Tool) -> dict[str, Any]: +def _serialize_tool(tool: Tool) -> dict[str, object]: """将 Tool 序列化为 MCP 协议响应字典。""" return { "name": tool.name, @@ -51,8 +51,8 @@ def _serialize_tool(tool: Tool) -> dict[str, Any]: def create_mcp_router( - tool_registry: Any = None, - published_tools_getter: Any = None, + tool_registry: object | None = None, + published_tools_getter: Callable[[], list[Tool]] | None = None, ) -> APIRouter: """构造 MCP 路由,挂载到主 app 的 ``/api/v1/mcp/`` 前缀下。 @@ -105,7 +105,7 @@ def create_mcp_router( return None @router.get("/tools/list") - async def list_tools(_user: dict = Depends(_mcp_member_auth)) -> dict[str, Any]: + async def list_tools(_user: dict = Depends(_mcp_member_auth)) -> dict[str, object]: """列出所有可用的 MCP 工具。""" tools = _all_tools() return {"tools": [_serialize_tool(t) for t in tools]} @@ -114,7 +114,7 @@ def create_mcp_router( async def call_tool( request: dict, _user: dict = Depends(_mcp_member_auth), - ) -> dict[str, Any]: + ) -> dict[str, object]: """调用指定的 MCP 工具。""" tool_name = request.get("name") arguments = request.get("arguments", {}) @@ -145,7 +145,7 @@ def create_mcp_router( async def jsonrpc_endpoint( request: Request, _user: dict = Depends(_mcp_member_auth), - ) -> dict[str, Any]: + ) -> dict[str, object]: """JSON-RPC 2.0 端点 — MCP 协议兼容。 支持 methods: initialize, tools/list, tools/call。 @@ -225,7 +225,7 @@ class MCPServer: 的 ``/api/v1/mcp/`` 前缀下,复用主 app 的 JWT + API Key 认证。 """ - def __init__(self, tool_registry: Any = None, host: str = "0.0.0.0", port: int = 8080): + def __init__(self, tool_registry: object | None = None, host: str = "0.0.0.0", port: int = 8080): self._tool_registry = tool_registry self._host = host self._port = port diff --git a/src/agentkit/mcp/transport.py b/src/agentkit/mcp/transport.py index 163181f..b81dda8 100644 --- a/src/agentkit/mcp/transport.py +++ b/src/agentkit/mcp/transport.py @@ -12,7 +12,6 @@ import logging import os import warnings from abc import ABC, abstractmethod -from typing import Any import httpx @@ -52,7 +51,7 @@ class Transport(ABC): ... @abstractmethod - async def send_request(self, method: str, params: dict[str, Any] | None = None) -> Any: + async def send_request(self, method: str, params: dict[str, object] | None = None) -> object: """发送 JSON-RPC 请求 Args: @@ -65,7 +64,7 @@ class Transport(ABC): ... @abstractmethod - async def receive_response(self) -> dict[str, Any]: + async def receive_response(self) -> dict[str, object]: """接收响应 Returns: @@ -92,7 +91,7 @@ class HTTPTransport(Transport): self._timeout = timeout self._client: httpx.AsyncClient | None = None self._request_id = 0 - self._pending: dict[int, asyncio.Future[dict[str, Any]]] = {} + self._pending: dict[int, asyncio.Future[dict[str, object]]] = {} @property def is_connected(self) -> bool: @@ -131,7 +130,7 @@ class HTTPTransport(Transport): self._request_id += 1 return self._request_id - async def send_request(self, method: str, params: dict[str, Any] | None = None) -> Any: + async def send_request(self, method: str, params: dict[str, object] | None = None) -> object: """发送 JSON-RPC 请求并等待响应 Args: @@ -148,7 +147,7 @@ class HTTPTransport(Transport): raise TransportError("Transport not connected") request_id = self._next_request_id() - message: dict[str, Any] = { + message: dict[str, object] = { "jsonrpc": "2.0", "id": request_id, "method": method, @@ -179,7 +178,7 @@ class HTTPTransport(Transport): return data.get("result") - async def receive_response(self) -> dict[str, Any]: + async def receive_response(self) -> dict[str, object]: """接收响应 对于 HTTPTransport,响应在 send_request 中同步返回。 @@ -218,7 +217,7 @@ class SSETransport(Transport): self._client: httpx.AsyncClient | None = None self._request_id = 0 self._sse_task: asyncio.Task[None] | None = None - self._response_queue: asyncio.Queue[dict[str, Any]] = asyncio.Queue() + self._response_queue: asyncio.Queue[dict[str, object]] = asyncio.Queue() self._connected = False @property @@ -304,7 +303,7 @@ class SSETransport(Transport): self._request_id += 1 return self._request_id - async def send_request(self, method: str, params: dict[str, Any] | None = None) -> Any: + async def send_request(self, method: str, params: dict[str, object] | None = None) -> object: """通过 HTTP POST 发送 JSON-RPC 请求 Args: @@ -321,7 +320,7 @@ class SSETransport(Transport): raise TransportError("Transport not connected") request_id = self._next_request_id() - message: dict[str, Any] = { + message: dict[str, object] = { "jsonrpc": "2.0", "id": request_id, "method": method, @@ -352,7 +351,7 @@ class SSETransport(Transport): return data.get("result") - async def receive_response(self) -> dict[str, Any]: + async def receive_response(self) -> dict[str, object]: """从 SSE 事件流接收响应 Returns: @@ -392,11 +391,11 @@ class StdioTransport(Transport): self._timeout = timeout self._process: asyncio.subprocess.Process | None = None self._request_id = 0 - self._pending: dict[int, asyncio.Future[Any]] = {} + self._pending: dict[int, asyncio.Future[object]] = {} self._reader_task: asyncio.Task[None] | None = None self._stderr_task: asyncio.Task[None] | None = None self._connected = False - self._notifications: asyncio.Queue[dict[str, Any]] = asyncio.Queue() + self._notifications: asyncio.Queue[dict[str, object]] = asyncio.Queue() @property def is_connected(self) -> bool: @@ -513,7 +512,7 @@ class StdioTransport(Transport): logger.info("StdioTransport disconnected") - async def send_request(self, method: str, params: dict[str, Any] | None = None) -> Any: + async def send_request(self, method: str, params: dict[str, object] | None = None) -> object: """发送 JSON-RPC 请求并等待响应 Args: @@ -531,11 +530,11 @@ class StdioTransport(Transport): return await self._send_request_internal(method, params) async def _send_request_internal( - self, method: str, params: dict[str, Any] | None = None - ) -> Any: + self, method: str, params: dict[str, object] | None = None + ) -> object: """内部请求发送方法(connect 时也可调用)""" request_id = self._next_request_id() - message: dict[str, Any] = { + message: dict[str, object] = { "jsonrpc": "2.0", "id": request_id, "method": method, @@ -546,7 +545,7 @@ class StdioTransport(Transport): await self._write_message(message) loop = asyncio.get_running_loop() - future: asyncio.Future[Any] = loop.create_future() + future: asyncio.Future[object] = loop.create_future() self._pending[request_id] = future try: @@ -558,9 +557,9 @@ class StdioTransport(Transport): self._pending.pop(request_id, None) raise - async def _send_notification(self, method: str, params: dict[str, Any] | None = None) -> None: + async def _send_notification(self, method: str, params: dict[str, object] | None = None) -> None: """发送 JSON-RPC 通知(无 id,不期待响应)""" - message: dict[str, Any] = { + message: dict[str, object] = { "jsonrpc": "2.0", "method": method, } @@ -568,7 +567,7 @@ class StdioTransport(Transport): message["params"] = params await self._write_message(message) - async def _write_message(self, message: dict[str, Any]) -> None: + async def _write_message(self, message: dict[str, object]) -> None: """将 JSON-RPC 消息写入子进程 stdin""" if self._process is None or self._process.stdin is None: raise TransportError("Process stdin not available") @@ -576,7 +575,7 @@ class StdioTransport(Transport): self._process.stdin.write(data) await self._process.stdin.drain() - async def receive_response(self) -> dict[str, Any]: + async def receive_response(self) -> dict[str, object]: """接收通知消息 对于 StdioTransport,请求响应通过 _pending Future 异步返回。 diff --git a/src/agentkit/quality/alignment.py b/src/agentkit/quality/alignment.py index 9316e10..aebd1d4 100644 --- a/src/agentkit/quality/alignment.py +++ b/src/agentkit/quality/alignment.py @@ -4,7 +4,6 @@ from __future__ import annotations import logging from dataclasses import dataclass, field -from typing import Any from agentkit.telemetry.tracer import get_tracer @@ -23,7 +22,7 @@ class AlignmentConfig: audit_sample_rate: float = 1.0 # 审计采样率 0.0-1.0,1.0=每次都审计 @classmethod - def from_dict(cls, data: dict[str, Any]) -> "AlignmentConfig": + def from_dict(cls, data: dict[str, object]) -> "AlignmentConfig": """从字典创建,忽略未知键""" known_fields = {f.name for f in cls.__dataclass_fields__.values()} filtered = {k: v for k, v in data.items() if k in known_fields} @@ -56,7 +55,7 @@ class ConstraintInjector: def __init__(self, config: AlignmentConfig): self._config = config - def inject(self, input_data: dict[str, Any]) -> dict[str, Any]: + def inject(self, input_data: dict[str, object]) -> dict[str, object]: """注入约束指令到 input_data 在 input_data 中添加 'alignment_constraints' 键,值为约束列表。 @@ -76,13 +75,13 @@ class AlignmentGuard: self._interaction_counts: dict[str, int] = {} self._loop_depths: dict[str, int] = {} - def inject_constraints(self, input_data: dict[str, Any]) -> dict[str, Any]: + def inject_constraints(self, input_data: dict[str, object]) -> dict[str, object]: """委托给 ConstraintInjector""" return self._injector.inject(input_data) async def check_output( self, - output: dict[str, Any], + output: dict[str, object], constraints: list[str] | None = None, ) -> AlignmentCheckResult: """检查输出是否符合约束 @@ -127,7 +126,7 @@ class AlignmentGuard: return result def _rule_check( - self, output: dict[str, Any], constraints: list[str] + self, output: dict[str, object], constraints: list[str] ) -> list[str]: """基于规则的约束检查:方向性判断,区分'禁止X'和'提及X' @@ -206,7 +205,7 @@ class AlignmentGuard: start = idx + len(keyword) @staticmethod - def _extract_text(output: dict[str, Any]) -> str: + def _extract_text(output: dict[str, object]) -> str: """从 output dict 中提取所有文本内容""" parts: list[str] = [] for value in output.values(): @@ -217,7 +216,7 @@ class AlignmentGuard: return " ".join(parts) async def _llm_check( - self, output: dict[str, Any], constraints: list[str] + self, output: dict[str, object], constraints: list[str] ) -> AlignmentCheckResult: """LLM 语义检查""" content = self._extract_text(output) diff --git a/src/agentkit/quality/cascade_state_store.py b/src/agentkit/quality/cascade_state_store.py index b45fc43..04199ea 100644 --- a/src/agentkit/quality/cascade_state_store.py +++ b/src/agentkit/quality/cascade_state_store.py @@ -11,7 +11,7 @@ Key schema (Redis): import logging import time -from typing import Any, Protocol, runtime_checkable +from typing import Protocol, runtime_checkable # redis 可选依赖;未安装时回退为 Exception 以保留原 catch-all 语义(降级到 fallback) try: @@ -132,7 +132,7 @@ class RedisCascadeStateStore: def __init__(self, redis_url: str = "redis://localhost:6379", session_ttl: int = 86400): self._redis_url = redis_url self._session_ttl = session_ttl - self._sync_redis: Any = None + self._sync_redis: object = None self._fallback: InMemoryCascadeStateStore | None = None self._degraded = False diff --git a/src/agentkit/quality/gate.py b/src/agentkit/quality/gate.py index 93684ee..f7f52b9 100644 --- a/src/agentkit/quality/gate.py +++ b/src/agentkit/quality/gate.py @@ -6,7 +6,7 @@ import importlib import logging from dataclasses import dataclass -from typing import Any, Callable +from typing import Callable from agentkit.skills.base import Skill @@ -36,9 +36,9 @@ class QualityGate: async def validate( self, - output: dict[str, Any], + output: dict[str, object], skill: Skill, - skill_context: dict[str, Any] | None = None, + skill_context: dict[str, object] | None = None, ) -> QualityResult: """对产出执行多维度质量检查 @@ -150,8 +150,8 @@ class QualityGate: @staticmethod def _check_skill_match( - output: dict[str, Any], - skill_context: dict[str, Any] | None, + output: dict[str, object], + skill_context: dict[str, object] | None, ) -> QualityCheck | None: """第五维度:技能匹配验证 diff --git a/src/agentkit/quality/output.py b/src/agentkit/quality/output.py index ba55562..6c17007 100644 --- a/src/agentkit/quality/output.py +++ b/src/agentkit/quality/output.py @@ -6,7 +6,6 @@ Schema 验证、字段类型归一化、元数据附加。 import logging from dataclasses import dataclass from datetime import datetime, timezone -from typing import Any from agentkit.quality.gate import QualityResult from agentkit.skills.base import Skill @@ -28,7 +27,7 @@ class StandardOutput: """标准化输出""" skill_name: str - data: dict[str, Any] + data: dict[str, object] metadata: OutputMetadata @@ -37,7 +36,7 @@ class OutputStandardizer: async def standardize( self, - raw_output: dict[str, Any], + raw_output: dict[str, object], skill: Skill, quality_result: QualityResult | None = None, ) -> StandardOutput: diff --git a/src/agentkit/rag_platform/document_processor.py b/src/agentkit/rag_platform/document_processor.py index be55200..273a118 100644 --- a/src/agentkit/rag_platform/document_processor.py +++ b/src/agentkit/rag_platform/document_processor.py @@ -10,7 +10,7 @@ PGVectorStore(向量化存储)。 from __future__ import annotations import logging -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING from agentkit.memory.document_loader import DocumentLoader from agentkit.rag_platform.models import DocumentStatus @@ -106,7 +106,7 @@ class DocumentProcessor: file_type: str, chunk_size: int | None = None, chunk_overlap: int | None = None, - ) -> list[dict[str, Any]]: + ) -> list[dict[str, object]]: """解析 + 分段,返回 chunk 用于只读预览(不向量化)。 Args: @@ -128,7 +128,7 @@ class DocumentProcessor: async def vectorize( self, - chunks: list[str] | list[dict[str, Any]], + chunks: list[str] | list[dict[str, object]], kb_id: str, document_id: str, vector_store: "PGVectorStore", diff --git a/src/agentkit/rag_platform/hit_processing.py b/src/agentkit/rag_platform/hit_processing.py index 1505a01..57fe24c 100644 --- a/src/agentkit/rag_platform/hit_processing.py +++ b/src/agentkit/rag_platform/hit_processing.py @@ -11,7 +11,6 @@ from __future__ import annotations import hashlib import logging -from typing import Any from pydantic import BaseModel, ConfigDict @@ -56,7 +55,7 @@ class HitProcessor: def __init__( self, - llm_gateway: Any = None, + llm_gateway: object | None = None, cache_enabled: bool = True, model: str = _DEFAULT_LLM_MODEL, ) -> None: diff --git a/src/agentkit/rag_platform/pipeline.py b/src/agentkit/rag_platform/pipeline.py index 78863d2..39ec9fd 100644 --- a/src/agentkit/rag_platform/pipeline.py +++ b/src/agentkit/rag_platform/pipeline.py @@ -7,7 +7,7 @@ U3 将扩展为完整 IngestionPipeline(含解析、预览、净化)。 from __future__ import annotations import logging -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING from agentkit.rag_platform.indexing import KB_CHUNKS_TABLE from agentkit.rag_platform.models import QueryResult @@ -64,7 +64,7 @@ class RAGPipeline: KB_CHUNKS_TABLE, ) - async def ingest(self, text: str, metadata: dict[str, Any] | None = None) -> list["TextNode"]: + async def ingest(self, text: str, metadata: dict[str, object] | None = None) -> list["TextNode"]: """将文本摄入向量存储。 Args: diff --git a/src/agentkit/rag_platform/preview.py b/src/agentkit/rag_platform/preview.py index 2dea820..76dab3b 100644 --- a/src/agentkit/rag_platform/preview.py +++ b/src/agentkit/rag_platform/preview.py @@ -5,8 +5,6 @@ from __future__ import annotations -from typing import Any - from pydantic import BaseModel, ConfigDict, Field from agentkit.rag_platform.document_processor import ( @@ -23,7 +21,7 @@ class PreviewChunk(BaseModel): index: int content: str - metadata: dict[str, Any] = Field(default_factory=dict) + metadata: dict[str, object] = Field(default_factory=dict) class PreviewResult(BaseModel): diff --git a/src/agentkit/rag_platform/question_gen.py b/src/agentkit/rag_platform/question_gen.py index 001bf4c..eb040fb 100644 --- a/src/agentkit/rag_platform/question_gen.py +++ b/src/agentkit/rag_platform/question_gen.py @@ -14,7 +14,6 @@ from __future__ import annotations import hashlib import logging import re -from typing import Any from pydantic import BaseModel, ConfigDict @@ -63,7 +62,7 @@ class QuestionGenerator: def __init__( self, - llm_gateway: Any = None, + llm_gateway: object | None = None, max_questions_per_chunk: int = 3, model: str = "default", cache: bool = True, @@ -76,7 +75,7 @@ class QuestionGenerator: async def generate( self, - chunks: list[dict[str, Any]], + chunks: list[dict[str, object]], document_context: str = "", ) -> list[GeneratedQuestion]: """为每个 chunk 生成相关问题。 diff --git a/src/agentkit/rag_platform/rerank.py b/src/agentkit/rag_platform/rerank.py index 3186c03..7957120 100644 --- a/src/agentkit/rag_platform/rerank.py +++ b/src/agentkit/rag_platform/rerank.py @@ -7,7 +7,7 @@ from __future__ import annotations import logging -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING from pydantic import BaseModel, ConfigDict @@ -50,9 +50,9 @@ class Reranker: def __init__(self, config: RerankConfig) -> None: self._config = config - self._reranker: Any = None # 延迟初始化,避免 import 失败 + self._reranker: object | None = None # 延迟初始化,避免 import 失败 - def _get_reranker(self) -> Any: + def _get_reranker(self) -> object | None: """延迟加载 reranker 实例 — 避免在 import 时失败。""" if self._reranker is not None: return self._reranker @@ -69,7 +69,7 @@ class Reranker: return self._reranker - def _build_cohere_reranker(self, cfg: RerankConfig) -> Any: + def _build_cohere_reranker(self, cfg: RerankConfig) -> object: """构建 CohereRerank — 数据出境,需 api_key。""" if not cfg.api_key: raise ValueError("Cohere rerank requires api_key") @@ -81,7 +81,7 @@ class Reranker: "Install: pip install llama-index-postprocessor-cohere-rerank" ) from e - kwargs: dict[str, Any] = { + kwargs: dict[str, object] = { "api_key": cfg.api_key, "top_n": cfg.top_n, } @@ -89,7 +89,7 @@ class Reranker: kwargs["model"] = cfg.model_name return CohereRerank(**kwargs) - def _build_bge_reranker(self, cfg: RerankConfig) -> Any: + def _build_bge_reranker(self, cfg: RerankConfig) -> object: """构建 BGE-Reranker via Xinference(本地部署,无数据出境)。 使用 SentenceTransformerRerank 作为本地 BGE-Reranker 的封装。 @@ -107,7 +107,7 @@ class Reranker: ) from e model = cfg.model_name or "BAAI/bge-reranker-base" - kwargs: dict[str, Any] = { + kwargs: dict[str, object] = { "model": model, "top_n": cfg.top_n, } diff --git a/src/agentkit/rag_platform/retrieval.py b/src/agentkit/rag_platform/retrieval.py index 41c06f9..426b864 100644 --- a/src/agentkit/rag_platform/retrieval.py +++ b/src/agentkit/rag_platform/retrieval.py @@ -12,7 +12,7 @@ from __future__ import annotations import asyncio import logging -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING from sqlalchemy import text @@ -184,7 +184,7 @@ class RetrievalEngine: out: list[QueryResult] = [] for row in rows: chunk_id, content, metadata, document_id, score = row - meta: dict[str, Any] = metadata if isinstance(metadata, dict) else {} + meta: dict[str, object] = metadata if isinstance(metadata, dict) else {} kb_id = meta.get("kb_id", "") out.append( QueryResult( diff --git a/src/agentkit/rag_platform/sanitize.py b/src/agentkit/rag_platform/sanitize.py index 99a47e9..2e246bc 100644 --- a/src/agentkit/rag_platform/sanitize.py +++ b/src/agentkit/rag_platform/sanitize.py @@ -17,7 +17,7 @@ import re import struct import zipfile from pathlib import Path -from typing import Any +from xml.etree.ElementTree import Element # 文件类型白名单:扩展名 → MIME 类型 ALLOWED_FILE_TYPES: dict[str, str] = { @@ -311,7 +311,7 @@ def sanitize_content(content: str, file_type: str) -> str: return content -def parse_xml_safe(content: bytes) -> Any: +def parse_xml_safe(content: bytes) -> Element: """安全解析 XML — 禁止 DTD/实体以防止 XXE 攻击。 Args: diff --git a/src/agentkit/rag_platform/tasks.py b/src/agentkit/rag_platform/tasks.py index 1db0aca..a1e3277 100644 --- a/src/agentkit/rag_platform/tasks.py +++ b/src/agentkit/rag_platform/tasks.py @@ -23,7 +23,7 @@ import logging import re import uuid from datetime import datetime, timedelta, timezone -from typing import TYPE_CHECKING, Any, Protocol +from typing import TYPE_CHECKING, Protocol from pydantic import BaseModel, ConfigDict, Field, field_validator @@ -54,7 +54,7 @@ TASKIQ_REDIS_DB = 1 TASKIQ_KEY_PREFIX = "taskiq:" -async def _maybe_await(result: Any) -> Any: +async def _maybe_await(result: object) -> object: """统一处理 sync/async 调用结果 — InMemoryTaskStore 方法为 sync,RedisTaskStore 为 async。""" if inspect.isawaitable(result): return await result @@ -81,7 +81,7 @@ class VectorizeTaskParams(BaseModel): @field_validator("chunk_overlap") @classmethod - def _overlap_less_than_size(cls, v: int, info: Any) -> int: + def _overlap_less_than_size(cls, v: int, info: object) -> int: """chunk_overlap 必须小于 chunk_size。""" size = info.data.get("chunk_size", DEFAULT_CHUNK_SIZE) if v >= size: @@ -161,7 +161,7 @@ class TaskStoreProtocol(Protocol): def get(self, task_id: str) -> TaskRecord | None: ... async def update_status( - self, task_id: str, status: TaskStatus, **kwargs: Any + self, task_id: str, status: TaskStatus, **kwargs: object ) -> TaskRecord: ... def list_tasks( @@ -265,7 +265,7 @@ class TaskManager: def __init__( self, - broker: Any = None, + broker: object | None = None, task_store: TaskStoreProtocol | None = None, max_concurrent_per_user: int = DEFAULT_MAX_CONCURRENT_PER_USER, task_ttl_seconds: int = DEFAULT_TASK_TTL_SECONDS, @@ -284,7 +284,7 @@ class TaskManager: return self._store @property - def broker(self) -> Any: + def broker(self) -> object: """暴露底层 broker(供 startup/shutdown 集成)。""" return self._broker @@ -293,7 +293,7 @@ class TaskManager: async def submit_vectorize( self, params: VectorizeTaskParams, - dependencies: dict[str, Any] | None = None, + dependencies: dict[str, object] | None = None, ) -> str: """提交向量化任务,返回 task_id。 @@ -344,7 +344,7 @@ class TaskManager: async def submit_batch_index( self, params: BatchIndexTaskParams, - dependencies: dict[str, Any] | None = None, + dependencies: dict[str, object] | None = None, ) -> str: """提交批量索引任务,返回 task_id。""" await self._check_concurrency(params.user_id) @@ -466,7 +466,7 @@ class TaskManager: self, task_id: str, params: VectorizeTaskParams, - deps: dict[str, Any], + deps: dict[str, object], ) -> None: """降级模式 — 同步执行向量化任务(在 asyncio 任务中)。""" now = datetime.now(timezone.utc) @@ -504,7 +504,7 @@ class TaskManager: self, task_id: str, params: BatchIndexTaskParams, - deps: dict[str, Any], + deps: dict[str, object], ) -> None: """降级模式 — 同步执行批量索引任务。""" now = datetime.now(timezone.utc) @@ -655,7 +655,7 @@ async def run_batch_index_task( # --------------------------------------------------------------------------- -def create_broker(redis_url: str) -> Any: +def create_broker(redis_url: str) -> object: """创建 TaskIQ Redis broker — 独立 db=1 隔离。 Args: diff --git a/src/agentkit/server/auth/dependencies.py b/src/agentkit/server/auth/dependencies.py index 13ca67a..fe4ca93 100644 --- a/src/agentkit/server/auth/dependencies.py +++ b/src/agentkit/server/auth/dependencies.py @@ -14,7 +14,7 @@ These dependencies read the ``current_user`` payload that from __future__ import annotations import logging -from typing import Any, Callable +from typing import Callable import aiosqlite from fastapi import HTTPException, Request @@ -25,7 +25,7 @@ from agentkit.server.auth.permissions import Permission, has_permission, is_dev_ logger = logging.getLogger(__name__) -async def get_current_user(request: Request) -> dict[str, Any] | None: +async def get_current_user(request: Request) -> dict[str, object] | None: """Return the current user payload, or ``None`` in dev mode. The payload is set by :class:`AuthMiddleware` and contains @@ -43,7 +43,7 @@ async def get_current_user(request: Request) -> dict[str, Any] | None: return getattr(request.state, "current_user", None) -async def require_authenticated(request: Request) -> dict[str, Any]: +async def require_authenticated(request: Request) -> dict[str, object]: """Require an authenticated user. Raises 401 if not authenticated. Use this as a FastAPI dependency for endpoints that need *any* @@ -67,7 +67,7 @@ async def require_authenticated(request: Request) -> dict[str, Any]: return user -def require_permission(*permissions: Permission) -> Callable[..., Any]: +def require_permission(*permissions: Permission) -> Callable[..., object]: """Build a FastAPI dependency that requires the given permissions. The user must have *all* of the given permissions (AND semantics). @@ -96,7 +96,7 @@ def require_permission(*permissions: Permission) -> Callable[..., Any]: A FastAPI dependency function. """ - async def _dependency(request: Request) -> dict[str, Any]: + async def _dependency(request: Request) -> dict[str, object]: user = await get_current_user(request) # Dev mode: no authenticated user @@ -132,13 +132,13 @@ def require_permission(*permissions: Permission) -> Callable[..., Any]: return _dependency -def require_any_permission(*permissions: Permission) -> Callable[..., Any]: +def require_any_permission(*permissions: Permission) -> Callable[..., object]: """Build a FastAPI dependency that requires at least one of the permissions. Similar to :func:`require_permission` but uses OR semantics. """ - async def _dependency(request: Request) -> dict[str, Any]: + async def _dependency(request: Request) -> dict[str, object]: user = await get_current_user(request) if is_dev_mode(user): @@ -181,7 +181,7 @@ async def _resolve_db_path(request: Request): return DEFAULT_AUTH_DB_PATH -async def require_terminal_authorized(request: Request) -> dict[str, Any]: +async def require_terminal_authorized(request: Request) -> dict[str, object]: """Require a user authorized to use the local terminal. This checks both: @@ -224,7 +224,7 @@ async def require_terminal_authorized(request: Request) -> dict[str, Any]: return user -async def require_server_terminal_authorized(request: Request) -> dict[str, Any]: +async def require_server_terminal_authorized(request: Request) -> dict[str, object]: """Require a user authorized to use the server terminal. Checks: diff --git a/src/agentkit/server/auth/jwt_utils.py b/src/agentkit/server/auth/jwt_utils.py index 77244da..2c391fa 100644 --- a/src/agentkit/server/auth/jwt_utils.py +++ b/src/agentkit/server/auth/jwt_utils.py @@ -35,7 +35,6 @@ import secrets import uuid from dataclasses import dataclass from datetime import datetime, timedelta, timezone -from typing import Any import jwt @@ -165,7 +164,7 @@ def create_token_pair( # the JWT. jti = str(uuid.uuid4()) if effective_session_id else None - access_payload: dict[str, Any] = { + access_payload: dict[str, object] = { "sub": user_id, "username": username, "role": role, @@ -173,7 +172,7 @@ def create_token_pair( "iat": int(issued_at.timestamp()), "exp": int(access_exp.timestamp()), } - refresh_payload: dict[str, Any] = { + refresh_payload: dict[str, object] = { "sub": user_id, "username": username, "role": role, @@ -238,7 +237,7 @@ def create_access_token( access_exp = issued_at + ACCESS_TOKEN_TTL jti = str(uuid.uuid4()) if session_id else None - access_payload: dict[str, Any] = { + access_payload: dict[str, object] = { "sub": user_id, "username": username, "role": role, @@ -261,7 +260,7 @@ def verify_token( secret: str, *, expected_type: str | None = None, -) -> dict[str, Any]: +) -> dict[str, object]: """Verify a JWT and return its payload. Args: diff --git a/src/agentkit/server/auth/middleware.py b/src/agentkit/server/auth/middleware.py index de6397f..2424652 100644 --- a/src/agentkit/server/auth/middleware.py +++ b/src/agentkit/server/auth/middleware.py @@ -20,7 +20,6 @@ from __future__ import annotations import hmac import logging -from typing import Any import jwt from starlette.middleware.base import BaseHTTPMiddleware @@ -93,7 +92,7 @@ class AuthMiddleware(BaseHTTPMiddleware): """Dev mode = no JWT secret, no global API key, no client keys.""" return not self._jwt_secret and self._api_key is None and not self._client_keys - def _verify_jwt(self, token: str) -> dict[str, Any] | None: + def _verify_jwt(self, token: str) -> dict[str, object] | None: """Verify a JWT bearer token. Returns payload or None. V2 tokens carry a ``sid`` claim. The middleware does NOT diff --git a/src/agentkit/server/auth/models.py b/src/agentkit/server/auth/models.py index b958044..f53920a 100644 --- a/src/agentkit/server/auth/models.py +++ b/src/agentkit/server/auth/models.py @@ -27,7 +27,6 @@ import os from collections.abc import Mapping from datetime import datetime, timezone from pathlib import Path -from typing import Any import aiosqlite from sqlalchemy import String @@ -774,7 +773,7 @@ async def init_auth_db(db_path: str | Path | None = None) -> Path: # --------------------------------------------------------------------------- -def user_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> dict[str, Any]: +def user_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> dict[str, object]: """Convert a ``users`` row into a JSON-safe dict.""" return { "id": row["id"], @@ -791,7 +790,7 @@ def user_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> dict[str, Any } -def auth_session_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> dict[str, Any]: +def auth_session_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> dict[str, object]: """Convert an ``auth_sessions`` row into a JSON-safe dict. The ``revoked`` field is normalized to a Python ``bool`` (the DB stores @@ -816,7 +815,7 @@ def auth_session_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> dict[ } -def department_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> dict[str, Any]: +def department_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> dict[str, object]: """Convert a ``departments`` row into a JSON-safe dict. The ``is_active`` field is normalized to a Python ``bool`` (the DB @@ -831,7 +830,7 @@ def department_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> dict[st } -def user_department_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> dict[str, Any]: +def user_department_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> dict[str, object]: """Convert a ``user_departments`` row into a JSON-safe dict.""" return { "user_id": row["user_id"], @@ -840,7 +839,7 @@ def user_department_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> di } -def skill_state_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> dict[str, Any]: +def skill_state_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> dict[str, object]: """Convert a ``skill_states`` row into a JSON-safe dict. The ``is_disabled`` field is normalized to a Python ``bool`` (the DB diff --git a/src/agentkit/server/auth/permissions.py b/src/agentkit/server/auth/permissions.py index 8303aa8..2b5917e 100644 --- a/src/agentkit/server/auth/permissions.py +++ b/src/agentkit/server/auth/permissions.py @@ -22,7 +22,6 @@ terminal access on a per-user basis without changing the user's role. from __future__ import annotations from enum import Enum -from typing import Any class Permission(str, Enum): @@ -108,7 +107,7 @@ def get_role_permissions(role: str | None) -> frozenset[Permission]: return ROLE_PERMISSIONS.get(role, frozenset()) -def has_permission(user: dict[str, Any] | None, permission: Permission) -> bool: +def has_permission(user: dict[str, object] | None, permission: Permission) -> bool: """Check if a user payload has a specific permission. Args: @@ -132,7 +131,7 @@ def has_permission(user: dict[str, Any] | None, permission: Permission) -> bool: return permission in get_role_permissions(role) -def is_dev_mode(user: dict[str, Any] | None) -> bool: +def is_dev_mode(user: dict[str, object] | None) -> bool: """Return True if the request is in dev mode (no authenticated user). Dev mode is when ``AuthMiddleware`` passes through requests without diff --git a/src/agentkit/server/auth/providers/local.py b/src/agentkit/server/auth/providers/local.py index 706b61c..6bf0de8 100644 --- a/src/agentkit/server/auth/providers/local.py +++ b/src/agentkit/server/auth/providers/local.py @@ -24,7 +24,6 @@ import logging import os import uuid from pathlib import Path -from typing import Any import aiosqlite @@ -149,7 +148,7 @@ class LocalAuthProvider: is_terminal_authorized: bool = False, is_server_terminal_authorized: bool = False, created_by: str | None = None, - ) -> dict[str, Any]: + ) -> dict[str, object]: """Create a new user in the local ``users`` table. Args: diff --git a/src/agentkit/server/auth/session_service.py b/src/agentkit/server/auth/session_service.py index 83046bb..87f58c0 100644 --- a/src/agentkit/server/auth/session_service.py +++ b/src/agentkit/server/auth/session_service.py @@ -29,7 +29,6 @@ import uuid from dataclasses import dataclass from datetime import datetime, timezone from pathlib import Path -from typing import Any import aiosqlite @@ -179,7 +178,7 @@ class SessionService: admin "list all sessions" view passes ``include_revoked=True``. """ sql = "SELECT * FROM auth_sessions WHERE user_id = ?" - args: tuple[Any, ...] = (user_id,) + args: tuple[object, ...] = (user_id,) if not include_revoked: sql += " AND revoked = 0" sql += " ORDER BY created_at DESC" @@ -437,7 +436,7 @@ class SessionService: "SET revoked = 1, revoked_reason = ? " "WHERE user_id = ? AND revoked = 0" ) - args: list[Any] = [reason, user_id] + args: list[object] = [reason, user_id] if except_sid is not None: sql += " AND id != ?" args.append(except_sid) diff --git a/src/agentkit/session/manager.py b/src/agentkit/session/manager.py index ac4ab4f..297ed63 100644 --- a/src/agentkit/session/manager.py +++ b/src/agentkit/session/manager.py @@ -6,7 +6,6 @@ import asyncio import datetime import logging from collections import defaultdict, deque -from typing import Any from agentkit.session.models import Message, MessageRole, Session, SessionStatus from agentkit.session.store import InMemorySessionStore, SessionStore @@ -155,7 +154,7 @@ class SessionManager: async def create_session( self, agent_name: str, - metadata: dict[str, Any] | None = None, + metadata: dict[str, object] | None = None, ) -> Session: """Create a new conversation session bound to an Agent. @@ -216,7 +215,7 @@ class SessionManager: content: str, tool_call_id: str | None = None, agent_name: str | None = None, - metadata: dict[str, Any] | None = None, + metadata: dict[str, object] | None = None, ) -> Message: """Append a message to a session. diff --git a/src/agentkit/session/models.py b/src/agentkit/session/models.py index 74a32b1..5521e12 100644 --- a/src/agentkit/session/models.py +++ b/src/agentkit/session/models.py @@ -6,7 +6,6 @@ import uuid from dataclasses import dataclass, field from datetime import datetime, timezone from enum import Enum -from typing import Any class SessionStatus(str, Enum): @@ -40,9 +39,9 @@ class Message: tool_call_id: str | None = None agent_name: str | None = None created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc)) - metadata: dict[str, Any] = field(default_factory=dict) + metadata: dict[str, object] = field(default_factory=dict) - def to_dict(self) -> dict[str, Any]: + def to_dict(self) -> dict[str, object]: return { "message_id": self.message_id, "session_id": self.session_id, @@ -55,7 +54,7 @@ class Message: } @classmethod - def from_dict(cls, data: dict[str, Any]) -> Message: + def from_dict(cls, data: dict[str, object]) -> Message: return cls( message_id=data["message_id"], session_id=data["session_id"], @@ -89,11 +88,11 @@ class Session: session_id: str agent_name: str status: SessionStatus = SessionStatus.ACTIVE - metadata: dict[str, Any] = field(default_factory=dict) + metadata: dict[str, object] = field(default_factory=dict) created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc)) updated_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc)) - def to_dict(self) -> dict[str, Any]: + def to_dict(self) -> dict[str, object]: return { "session_id": self.session_id, "agent_name": self.agent_name, @@ -104,7 +103,7 @@ class Session: } @classmethod - def from_dict(cls, data: dict[str, Any]) -> Session: + def from_dict(cls, data: dict[str, object]) -> Session: return cls( session_id=data["session_id"], agent_name=data["agent_name"], diff --git a/src/agentkit/session/store.py b/src/agentkit/session/store.py index 8f6c306..23b5f2e 100644 --- a/src/agentkit/session/store.py +++ b/src/agentkit/session/store.py @@ -5,7 +5,7 @@ from __future__ import annotations import json import logging import os -from typing import Any, Protocol, runtime_checkable +from typing import Protocol, runtime_checkable # redis 可选依赖;未安装时回退为 Exception 以保留原 catch-all 语义 try: @@ -119,7 +119,7 @@ class RedisSessionStore: def __init__(self, redis_url: str = "redis://localhost:6379/0", ttl_seconds: int = 86400): self._redis_url = redis_url self._ttl_seconds = ttl_seconds - self._redis: Any = None + self._redis: object = None async def _get_redis(self): if self._redis is None: diff --git a/src/agentkit/telemetry/tracer.py b/src/agentkit/telemetry/tracer.py index 666ccd0..ad808ef 100644 --- a/src/agentkit/telemetry/tracer.py +++ b/src/agentkit/telemetry/tracer.py @@ -2,8 +2,8 @@ from __future__ import annotations import logging -from dataclasses import dataclass, field -from typing import Any, ContextManager +from dataclasses import dataclass +from typing import ContextManager logger = logging.getLogger(__name__) @@ -27,10 +27,10 @@ class NoOpSpan: def __exit__(self, *args): pass - def set_attribute(self, key: str, value: Any) -> None: + def set_attribute(self, key: str, value: object) -> None: pass - def add_event(self, name: str, attributes: dict[str, Any] | None = None) -> None: + def add_event(self, name: str, attributes: dict[str, object] | None = None) -> None: pass def record_exception(self, exception: Exception) -> None: @@ -43,17 +43,17 @@ class NoOpSpan: class NoOpTracer: """No-op tracer when telemetry is disabled.""" - def start_span(self, name: str, attributes: dict[str, Any] | None = None) -> ContextManager[NoOpSpan]: + def start_span(self, name: str, attributes: dict[str, object] | None = None) -> ContextManager[NoOpSpan]: return NoOpSpan() - def start_as_current_span(self, name: str, attributes: dict[str, Any] | None = None) -> ContextManager[NoOpSpan]: + def start_as_current_span(self, name: str, attributes: dict[str, object] | None = None) -> ContextManager[NoOpSpan]: return NoOpSpan() class OTelSpan: """Wrapper around OpenTelemetry Span.""" - def __init__(self, span: Any): + def __init__(self, span: object): self._span = span def __enter__(self): @@ -63,10 +63,10 @@ class OTelSpan: def __exit__(self, *args): self._span.__exit__(*args) - def set_attribute(self, key: str, value: Any) -> None: + def set_attribute(self, key: str, value: object) -> None: self._span.set_attribute(key, value) - def add_event(self, name: str, attributes: dict[str, Any] | None = None) -> None: + def add_event(self, name: str, attributes: dict[str, object] | None = None) -> None: self._span.add_event(name, attributes or {}) def record_exception(self, exception: Exception) -> None: @@ -79,14 +79,14 @@ class OTelSpan: class OTelTracer: """Wrapper around OpenTelemetry Tracer.""" - def __init__(self, tracer: Any): + def __init__(self, tracer: object): self._tracer = tracer - def start_span(self, name: str, attributes: dict[str, Any] | None = None) -> OTelSpan: + def start_span(self, name: str, attributes: dict[str, object] | None = None) -> OTelSpan: span = self._tracer.start_span(name, attributes=attributes) return OTelSpan(span) - def start_as_current_span(self, name: str, attributes: dict[str, Any] | None = None) -> OTelSpan: + def start_as_current_span(self, name: str, attributes: dict[str, object] | None = None) -> OTelSpan: span = self._tracer.start_as_current_span(name, attributes=attributes) return OTelSpan(span) diff --git a/src/agentkit/telemetry/tracing.py b/src/agentkit/telemetry/tracing.py index 531eb66..4f3c65c 100644 --- a/src/agentkit/telemetry/tracing.py +++ b/src/agentkit/telemetry/tracing.py @@ -3,7 +3,7 @@ import logging import time from functools import wraps -from typing import Any, Callable +from typing import Callable logger = logging.getLogger(__name__) @@ -67,7 +67,7 @@ def get_tracer(name: str = "fischer.agentkit"): def start_span( name: str, - kind: Any = None, + kind: object = None, attributes: dict | None = None, ): """Start a span — returns no-op span if OTel not installed. -- 2.43.0