refactor: tech debt Wave 3+4 (tools/skills/mcp/rag/calendar/auth/cli/quality/channels/telemetry/session/bus/documents Any 治理) #11

Merged
fischer merged 2 commits from refactor/tech-debt-wave-3-4 into main 2026-07-01 08:08:36 +08:00
81 changed files with 415 additions and 473 deletions

View File

@ -8,7 +8,7 @@ from __future__ import annotations
import asyncio import asyncio
import logging import logging
from typing import Any, Callable, Awaitable from typing import Callable, Awaitable
from agentkit.bus.message import AgentMessage from agentkit.bus.message import AgentMessage
@ -20,8 +20,8 @@ class InMemoryMessageBus:
def __init__( def __init__(
self, self,
cascade_detector: Any = None, cascade_detector: object = None,
alignment_guard: Any = None, alignment_guard: object = None,
) -> None: ) -> None:
self._subscribers: dict[str, list[Callable[[AgentMessage], Awaitable[None]]]] = {} self._subscribers: dict[str, list[Callable[[AgentMessage], Awaitable[None]]]] = {}
self._pending_requests: dict[str, asyncio.Future[AgentMessage]] = {} self._pending_requests: dict[str, asyncio.Future[AgentMessage]] = {}

View File

@ -5,7 +5,6 @@ from __future__ import annotations
import uuid import uuid
from dataclasses import dataclass, field from dataclasses import dataclass, field
from datetime import datetime, timezone from datetime import datetime, timezone
from typing import Any
@dataclass @dataclass
@ -20,11 +19,11 @@ class AgentMessage:
sender: str = "" sender: str = ""
recipient: str | None = None # None = broadcast recipient: str | None = None # None = broadcast
topic: str = "" 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)) timestamp: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
correlation_id: str | None = None # 请求-响应关联 correlation_id: str | None = None # 请求-响应关联
# --- 新增字段 --- # --- 新增字段 ---
content: Any = None # 消息内容(与 payload 互补payload 为 dictcontent 可为任意类型) content: object = None # 消息内容(与 payload 互补payload 为 dictcontent 可为任意类型)
msg_type: str = "notify" # "request" | "response" | "notify" | "negotiate" msg_type: str = "notify" # "request" | "response" | "notify" | "negotiate"
ttl_seconds: int = 300 # 消息存活时间(秒) ttl_seconds: int = 300 # 消息存活时间(秒)
@ -39,7 +38,7 @@ class AgentMessage:
def is_broadcast(self) -> bool: def is_broadcast(self) -> bool:
return self.recipient is None return self.recipient is None
def to_dict(self) -> dict[str, Any]: def to_dict(self) -> dict[str, object]:
ts = self.timestamp ts = self.timestamp
if isinstance(ts, datetime): if isinstance(ts, datetime):
ts = ts.isoformat() ts = ts.isoformat()
@ -57,7 +56,7 @@ class AgentMessage:
} }
@classmethod @classmethod
def from_dict(cls, data: dict[str, Any]) -> AgentMessage: def from_dict(cls, data: dict[str, object]) -> AgentMessage:
ts = data.get("timestamp", "") ts = data.get("timestamp", "")
if isinstance(ts, str) and ts: if isinstance(ts, str) and ts:
try: try:

View File

@ -2,7 +2,7 @@
from __future__ import annotations 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 from agentkit.bus.message import AgentMessage

View File

@ -9,7 +9,7 @@ from __future__ import annotations
import asyncio import asyncio
import json import json
import logging import logging
from typing import Any, Callable, Awaitable from typing import Callable, Awaitable
from agentkit.bus.message import AgentMessage from agentkit.bus.message import AgentMessage
from agentkit.bus.memory_bus import InMemoryMessageBus from agentkit.bus.memory_bus import InMemoryMessageBus
@ -32,12 +32,12 @@ class RedisMessageBus:
self._redis_url = redis_url self._redis_url = redis_url
self._consumer_group = consumer_group self._consumer_group = consumer_group
self._max_retries = max_retries self._max_retries = max_retries
self._redis: Any = None self._redis: object = None
self._subscribers: dict[str, list[Callable[[AgentMessage], Awaitable[None]]]] = {} self._subscribers: dict[str, list[Callable[[AgentMessage], Awaitable[None]]]] = {}
self._pending_requests: dict[str, asyncio.Future[AgentMessage]] = {} self._pending_requests: dict[str, asyncio.Future[AgentMessage]] = {}
self._consumer_tasks: dict[str, asyncio.Task] = {} self._consumer_tasks: dict[str, asyncio.Task] = {}
async def _get_redis(self) -> Any: async def _get_redis(self) -> object:
"""获取 Redis 连接(懒初始化)。""" """获取 Redis 连接(懒初始化)。"""
if self._redis is None: if self._redis is None:
import redis.asyncio as aioredis import redis.asyncio as aioredis
@ -151,7 +151,7 @@ class RedisMessageBus:
async def _handle_failed_message( async def _handle_failed_message(
self, self,
redis: Any, redis: object,
stream_key: str, stream_key: str,
msg_id: str, msg_id: str,
fields: dict, fields: dict,

View File

@ -15,7 +15,6 @@ import uuid
from collections.abc import Awaitable, Callable from collections.abc import Awaitable, Callable
from datetime import date, datetime, timedelta, timezone from datetime import date, datetime, timedelta, timezone
from pathlib import Path from pathlib import Path
from typing import Any
import caldav import caldav
from icalendar import Calendar, Event from icalendar import Calendar, Event
@ -33,7 +32,7 @@ from agentkit.calendar.sync.base import AbstractSyncProvider
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Async callback signature: (event_type: str, payload: dict) -> None # 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: def _parse_iso(dt_str: str) -> datetime:
@ -51,7 +50,7 @@ def _to_iso_utc(dt: datetime) -> str:
return dt.astimezone(timezone.utc).isoformat() 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).""" """Extract date/datetime from icalendar component. Returns (iso, is_all_day)."""
prop = component.get(key) prop = component.get(key)
if prop is None: if prop is None:
@ -73,7 +72,7 @@ class CalDAVSyncProvider(AbstractSyncProvider):
def __init__( def __init__(
self, self,
db_path: str | Path | None = None, 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, notify_callback: NotifyCallback | None = None,
) -> None: ) -> None:
self.db_path = Path(db_path) if db_path is not None else DEFAULT_CALENDAR_DB_PATH 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 # Client construction
# ------------------------------------------------------------------ # ------------------------------------------------------------------
def _make_client(self, config: ExternalCalendarConfig) -> Any: def _make_client(self, config: ExternalCalendarConfig) -> object:
"""Build a caldav.DAVClient from config credentials.""" """Build a caldav.DAVClient from config credentials."""
if self._client_factory is not None: if self._client_factory is not None:
return self._client_factory(config) return self._client_factory(config)
@ -103,7 +102,7 @@ class CalDAVSyncProvider(AbstractSyncProvider):
timeout=30, 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.""" """Connect and return the first calendar from the principal."""
client = self._make_client(config) client = self._make_client(config)
principal = client.principal() principal = client.principal()
@ -166,7 +165,7 @@ class CalDAVSyncProvider(AbstractSyncProvider):
events.append(parsed) events.append(parsed)
return events 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.""" """Convert a caldav.Event to a CalendarEvent dataclass."""
try: try:
ical_data = caldav_event.data ical_data = caldav_event.data

View File

@ -11,7 +11,6 @@ from __future__ import annotations
import logging import logging
import uuid import uuid
from datetime import date, datetime, timezone from datetime import date, datetime, timezone
from typing import Any
from icalendar import Calendar, Event from icalendar import Calendar, Event
from icalendar.prop import vRecur from icalendar.prop import vRecur
@ -38,7 +37,7 @@ def _parse_iso(dt_str: str) -> datetime:
return dt.astimezone(timezone.utc) 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. """Extract a date/datetime property from an icalendar component.
Returns ``(iso_string, is_all_day)``. ``is_all_day`` is True when the 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: def __init__(self, service: CalendarService) -> None:
self.service = service 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*. """Parse ICS bytes and create events for *user_id*.
Returns ``{"imported": N, "skipped": M, "errors": [...]}``. Returns ``{"imported": N, "skipped": M, "errors": [...]}``.

View File

@ -16,7 +16,6 @@ import logging
from collections.abc import Awaitable, Callable from collections.abc import Awaitable, Callable
from datetime import datetime, timezone from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
from typing import Any
from agentkit.calendar.db import ( from agentkit.calendar.db import (
DEFAULT_CALENDAR_DB_PATH, DEFAULT_CALENDAR_DB_PATH,
@ -31,7 +30,7 @@ from agentkit.calendar.sync.caldav_provider import CalDAVSyncProvider
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Async callback signature: (event_type: str, payload: dict) -> None # 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: class SyncManager:
@ -64,7 +63,7 @@ class SyncManager:
# Sync orchestration # 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. """Sync all external calendar configs for a user.
Returns ``{"synced": N, "errors": [...]}``. Returns ``{"synced": N, "errors": [...]}``.
@ -81,7 +80,7 @@ class SyncManager:
logger.warning("Sync failed for config %s: %s", config.id, e) logger.warning("Sync failed for config %s: %s", config.id, e)
return {"synced": synced, "errors": errors} 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. """Sync a single external calendar config by ID.
Pulls remote changes, then pushes local changes modified since Pulls remote changes, then pushes local changes modified since

View File

@ -19,7 +19,6 @@ import uuid
from collections.abc import Awaitable, Callable from collections.abc import Awaitable, Callable
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from pathlib import Path from pathlib import Path
from typing import Any
from urllib.parse import parse_qs, quote, urlparse from urllib.parse import parse_qs, quote, urlparse
import httpx import httpx
@ -37,7 +36,7 @@ from agentkit.calendar.sync.base import AbstractSyncProvider
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Async callback signature: (event_type: str, payload: dict) -> None # 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" GRAPH_BASE = "https://graph.microsoft.com/v1.0"
TOKEN_URL = "https://login.microsoftonline.com/common/oauth2/v2.0/token" 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) 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. """Convert Outlook dateTimeTimeZone to ISO 8601 UTC.
ponytail: assumes Graph returns UTC (no ``Prefer: outlook.timezone`` header 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"} 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.""" """Convert Outlook recurrence pattern to RRULE string."""
if not recurrence: if not recurrence:
return None 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 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. """Convert RRULE string to Outlook recurrence pattern.
``start_date`` is the event's start date in ``YYYY-MM-DD`` format (required ``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: if not pattern_type:
return None return None
pattern: dict[str, Any] = {"type": pattern_type} pattern: dict[str, object] = {"type": pattern_type}
interval = parts.get("INTERVAL") interval = parts.get("INTERVAL")
pattern["interval"] = int(interval) if interval else 1 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: if count:
pattern["numberOfOccurrences"] = int(count) pattern["numberOfOccurrences"] = int(count)
range_obj: dict[str, Any] = { range_obj: dict[str, object] = {
"type": "numbered", "type": "numbered",
"startDate": start_date, "startDate": start_date,
"numberOfOccurrences": int(count), "numberOfOccurrences": int(count),
@ -202,7 +201,7 @@ class OutlookSyncProvider(AbstractSyncProvider):
def __init__( def __init__(
self, self,
db_path: str | Path | None = None, db_path: str | Path | None = None,
client_factory: Callable[[], Any] | None = None, client_factory: Callable[[], object] | None = None,
notify_callback: NotifyCallback | None = None, notify_callback: NotifyCallback | None = None,
) -> None: ) -> None:
self.db_path = Path(db_path) if db_path is not None else DEFAULT_CALENDAR_DB_PATH 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 # Client / auth
# ------------------------------------------------------------------ # ------------------------------------------------------------------
def _get_client(self) -> Any: def _get_client(self) -> object:
"""Return an httpx.AsyncClient (real or mock from factory).""" """Return an httpx.AsyncClient (real or mock from factory)."""
if self._client_factory is not None: if self._client_factory is not None:
return self._client_factory() return self._client_factory()
return httpx.AsyncClient(timeout=30.0) 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 {} 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) 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. """Refresh the access token using the refresh_token grant.
Posts to the Azure AD token endpoint, updates ``config.credentials`` Posts to the Azure AD token endpoint, updates ``config.credentials``
@ -271,13 +270,13 @@ class OutlookSyncProvider(AbstractSyncProvider):
async def _request( async def _request(
self, self,
client: Any, client: object,
config: ExternalCalendarConfig, config: ExternalCalendarConfig,
method: str, method: str,
url: str, url: str,
*, *,
json_body: dict[str, Any] | None = None, json_body: dict[str, object] | None = None,
) -> dict[str, Any]: ) -> dict[str, object]:
"""Make an authenticated Graph API request with 401 auto-refresh + retry.""" """Make an authenticated Graph API request with 401 auto-refresh + retry."""
creds = self._load_creds(config) creds = self._load_creds(config)
headers = {"Authorization": f"Bearer {creds.get('access_token', '')}"} headers = {"Authorization": f"Bearer {creds.get('access_token', '')}"}
@ -330,7 +329,7 @@ class OutlookSyncProvider(AbstractSyncProvider):
return result return result
async def _pull_delta( async def _pull_delta(
self, client: Any, config: ExternalCalendarConfig self, client: object, config: ExternalCalendarConfig
) -> tuple[list[CalendarEvent], str | None]: ) -> tuple[list[CalendarEvent], str | None]:
"""Call /me/calendarView/delta. Returns (events, delta_token).""" """Call /me/calendarView/delta. Returns (events, delta_token)."""
url = self._build_delta_url(config) 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") 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}" 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.""" """Convert a Graph event JSON to a CalendarEvent dataclass."""
eid = raw.get("id") eid = raw.get("id")
if not eid: if not eid:
@ -459,7 +458,7 @@ class OutlookSyncProvider(AbstractSyncProvider):
return result return result
async def _push_single( async def _push_single(
self, client: Any, config: ExternalCalendarConfig, event: CalendarEvent self, client: object, config: ExternalCalendarConfig, event: CalendarEvent
) -> CalendarEvent: ) -> CalendarEvent:
"""Push a single event to Outlook, return event with external_id set.""" """Push a single event to Outlook, return event with external_id set."""
body = self._event_to_outlook(event) body = self._event_to_outlook(event)
@ -485,9 +484,9 @@ class OutlookSyncProvider(AbstractSyncProvider):
event.external_provider = "outlook" event.external_provider = "outlook"
return event 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.""" """Convert CalendarEvent to Outlook Graph event JSON."""
body: dict[str, Any] = { body: dict[str, object] = {
"subject": event.title, "subject": event.title,
"start": _iso_to_outlook_dt(event.start_time, event.is_all_day), "start": _iso_to_outlook_dt(event.start_time, event.is_all_day),
"end": _iso_to_outlook_dt(event.end_time, event.is_all_day), "end": _iso_to_outlook_dt(event.end_time, event.is_all_day),

View File

@ -9,7 +9,6 @@ from __future__ import annotations
import abc import abc
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import Enum from enum import Enum
from typing import Any
import httpx import httpx
@ -39,7 +38,7 @@ class IncomingMessage:
user_id: str # 平台用户 ID user_id: str # 平台用户 ID
chat_id: str # 群组/会话 ID chat_id: str # 群组/会话 ID
content: str # 消息文本 content: str # 消息文本
raw_event: dict[str, Any] = field(default_factory=dict) # 原始事件 raw_event: dict[str, object] = field(default_factory=dict) # 原始事件
timestamp: str = "" timestamp: str = ""

View File

@ -19,7 +19,6 @@ import json
import logging import logging
import re import re
import time import time
from typing import Any
import httpx import httpx
@ -147,7 +146,7 @@ class DingTalkMessageAdapter(MessageAdapter):
ValueError: 事件 body 不是合法 JSON ValueError: 事件 body 不是合法 JSON
""" """
try: try:
data: dict[str, Any] = json.loads(body) data: dict[str, object] = json.loads(body)
except json.JSONDecodeError as exc: except json.JSONDecodeError as exc:
raise ValueError(f"钉钉事件 body 不是合法 JSON: {exc}") from exc raise ValueError(f"钉钉事件 body 不是合法 JSON: {exc}") from exc
@ -169,7 +168,7 @@ class DingTalkMessageAdapter(MessageAdapter):
timestamp=timestamp, timestamp=timestamp,
) )
def _extract_content(self, data: dict[str, Any]) -> str: def _extract_content(self, data: dict[str, object]) -> str:
"""从钉钉事件提取文本内容。 """从钉钉事件提取文本内容。
- text 类型解析 ``text.content``剥离 @ 机器人前缀 - text 类型解析 ``text.content``剥离 @ 机器人前缀

View File

@ -19,7 +19,6 @@ import logging
import re import re
import time import time
from datetime import datetime, timezone from datetime import datetime, timezone
from typing import Any
import httpx import httpx
@ -142,7 +141,7 @@ class FeishuMessageAdapter(MessageAdapter):
ValueError: 事件结构无法解析 ValueError: 事件结构无法解析
""" """
try: try:
data: dict[str, Any] = json.loads(body) data: dict[str, object] = json.loads(body)
except json.JSONDecodeError as exc: except json.JSONDecodeError as exc:
raise ValueError(f"飞书事件 body 不是合法 JSON: {exc}") from exc raise ValueError(f"飞书事件 body 不是合法 JSON: {exc}") from exc
@ -184,7 +183,7 @@ class FeishuMessageAdapter(MessageAdapter):
timestamp=timestamp, 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 解密飞书加密事件。 """AES-256-CBC 解密飞书加密事件。
飞书协议 飞书协议
@ -216,7 +215,7 @@ class FeishuMessageAdapter(MessageAdapter):
return json.loads(plaintext.decode("utf-8")) 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 字段提取文本内容。 """从飞书 message 字段提取文本内容。
- text 类型解析 ``content`` JSON 中的 ``text`` 字段剥离 @ 提及标记 - text 类型解析 ``content`` JSON 中的 ``text`` 字段剥离 @ 提及标记

View File

@ -19,7 +19,6 @@ import json
import logging import logging
import re import re
import time import time
from typing import Any
from urllib.parse import parse_qs from urllib.parse import parse_qs
from uuid import uuid4 from uuid import uuid4
@ -134,7 +133,7 @@ class SlackMessageAdapter(MessageAdapter):
""" """
# 优先尝试 JSON 解析Events API / URL 验证) # 优先尝试 JSON 解析Events API / URL 验证)
try: try:
data: dict[str, Any] = json.loads(body) data: dict[str, object] = json.loads(body)
except (json.JSONDecodeError, UnicodeDecodeError): except (json.JSONDecodeError, UnicodeDecodeError):
data = {} data = {}
@ -144,7 +143,7 @@ class SlackMessageAdapter(MessageAdapter):
# 非 JSON — 视为 Slash Commandform-encoded # 非 JSON — 视为 Slash Commandform-encoded
return self._parse_slash_command(body) 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 事件。""" """解析 Events API 事件。"""
# URL 验证流程 # URL 验证流程
if data.get("type") == "url_verification": if data.get("type") == "url_verification":

View File

@ -13,7 +13,7 @@ from __future__ import annotations
import json as _json import json as _json
from pathlib import Path from pathlib import Path
from typing import Any, Optional from typing import Optional
import httpx import httpx
import typer 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)) rprint(_json.dumps(data, indent=2, ensure_ascii=False, default=str))
def _safe_get( def _safe_get(
client: AdminHttpClient, path: str, server_url: str, params: dict | None = None client: AdminHttpClient, path: str, server_url: str, params: dict | None = None
) -> Any: ) -> object:
try: try:
return client.get(path, params=params) return client.get(path, params=params)
except httpx.ConnectError: except httpx.ConnectError:
@ -104,7 +104,7 @@ def _safe_get(
def _safe_post( def _safe_post(
client: AdminHttpClient, path: str, server_url: str, body: dict | None = None client: AdminHttpClient, path: str, server_url: str, body: dict | None = None
) -> Any: ) -> object:
try: try:
return client.post(path, json=body) return client.post(path, json=body)
except httpx.ConnectError: except httpx.ConnectError:
@ -115,7 +115,7 @@ def _safe_post(
def _safe_patch( def _safe_patch(
client: AdminHttpClient, path: str, server_url: str, body: dict | None = None client: AdminHttpClient, path: str, server_url: str, body: dict | None = None
) -> Any: ) -> object:
try: try:
return client.patch(path, json=body) return client.patch(path, json=body)
except httpx.ConnectError: except httpx.ConnectError:
@ -124,7 +124,7 @@ def _safe_patch(
_handle_http_error(e, server_url) _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: try:
return client.put(path, json=body) return client.put(path, json=body)
except httpx.ConnectError: except httpx.ConnectError:
@ -135,7 +135,7 @@ def _safe_put(client: AdminHttpClient, path: str, server_url: str, body: dict |
def _safe_delete( def _safe_delete(
client: AdminHttpClient, path: str, server_url: str, params: dict | None = None client: AdminHttpClient, path: str, server_url: str, params: dict | None = None
) -> Any: ) -> object:
try: try:
return client.delete(path, params=params) return client.delete(path, params=params)
except httpx.ConnectError: except httpx.ConnectError:
@ -252,7 +252,7 @@ def dept_update(
json_output: bool = JsonFlag, json_output: bool = JsonFlag,
) -> None: ) -> None:
"""Update a department's name or description.""" """Update a department's name or description."""
body: dict[str, Any] = {} body: dict[str, object] = {}
if name is not None: if name is not None:
body["name"] = name body["name"] = name
if description is not None: if description is not None:
@ -576,7 +576,7 @@ def user_list(
) -> None: ) -> None:
"""List users, optionally filtered by department.""" """List users, optionally filtered by department."""
client = _build_client(server_url, token, api_key) client = _build_client(server_url, token, api_key)
params: dict[str, Any] = {} params: dict[str, object] = {}
if department_id: if department_id:
params["department_id"] = department_id params["department_id"] = department_id
users = _safe_get(client, "/api/v1/admin/users", client.base_url, params=params or None) 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, json_output: bool = JsonFlag,
) -> None: ) -> None:
"""Create a new user.""" """Create a new user."""
body: dict[str, Any] = { body: dict[str, object] = {
"username": username, "username": username,
"email": email, "email": email,
"password": password, "password": password,
@ -653,7 +653,7 @@ def user_update(
json_output: bool = JsonFlag, json_output: bool = JsonFlag,
) -> None: ) -> None:
"""Update a user's role or active flag.""" """Update a user's role or active flag."""
body: dict[str, Any] = {} body: dict[str, object] = {}
if role is not None: if role is not None:
body["role"] = role body["role"] = role
if is_active is not None: if is_active is not None:
@ -818,7 +818,7 @@ def llm_add_provider(
json_output: bool = JsonFlag, json_output: bool = JsonFlag,
) -> None: ) -> None:
"""Create a new LLM provider.""" """Create a new LLM provider."""
body: dict[str, Any] = { body: dict[str, object] = {
"name": name, "name": name,
"type": type, "type": type,
"api_key": api_key, "api_key": api_key,
@ -850,7 +850,7 @@ def llm_update_provider(
json_output: bool = JsonFlag, json_output: bool = JsonFlag,
) -> None: ) -> None:
"""Update an LLM provider's configuration.""" """Update an LLM provider's configuration."""
body: dict[str, Any] = {} body: dict[str, object] = {}
if type is not None: if type is not None:
body["type"] = type body["type"] = type
if api_key is not None: if api_key is not None:
@ -1123,7 +1123,7 @@ def kb_list_documents(
) -> None: ) -> None:
"""List KB documents.""" """List KB documents."""
client = _build_client(server_url, token, api_key) client = _build_client(server_url, token, api_key)
params: dict[str, Any] = {} params: dict[str, object] = {}
if source_id: if source_id:
params["source_id"] = source_id params["source_id"] = source_id
if department_id: if department_id:
@ -1176,7 +1176,7 @@ def kb_upload(
rprint(f"[red]Error: File not found: {content_file}[/red]") rprint(f"[red]Error: File not found: {content_file}[/red]")
raise typer.Exit(1) raise typer.Exit(1)
content = path.read_text(encoding="utf-8") content = path.read_text(encoding="utf-8")
body: dict[str, Any] = { body: dict[str, object] = {
"filename": filename, "filename": filename,
"content": content, "content": content,
"source_id": source_id, "source_id": source_id,
@ -1255,8 +1255,8 @@ def _usage_params(
user_id: Optional[str], user_id: Optional[str],
start: Optional[str], start: Optional[str],
end: Optional[str], end: Optional[str],
) -> dict[str, Any]: ) -> dict[str, object]:
params: dict[str, Any] = {} params: dict[str, object] = {}
if department_id: if department_id:
params["department_id"] = department_id params["department_id"] = department_id
if user_id: if user_id:
@ -1386,7 +1386,7 @@ def usage_top_users(
) -> None: ) -> None:
"""Show top-N users by token usage.""" """Show top-N users by token usage."""
client = _build_client(server_url, token, api_key) client = _build_client(server_url, token, api_key)
params: dict[str, Any] = {"limit": limit} params: dict[str, object] = {"limit": limit}
if department_id: if department_id:
params["department_id"] = department_id params["department_id"] = department_id
if start: if start:

View File

@ -11,8 +11,6 @@ from __future__ import annotations
import os import os
from pathlib import Path from pathlib import Path
from typing import Any
import httpx import httpx
import yaml import yaml
@ -62,7 +60,7 @@ class AdminHttpClient:
4. Hard-coded defaults (server URL only) 4. Hard-coded defaults (server URL only)
""" """
path = Path(config_path) if config_path else DEFAULT_CONFIG_PATH path = Path(config_path) if config_path else DEFAULT_CONFIG_PATH
file_cfg: dict[str, Any] = {} file_cfg: dict[str, object] = {}
if path.exists(): if path.exists():
try: try:
with path.open(encoding="utf-8") as f: with path.open(encoding="utf-8") as f:
@ -106,8 +104,8 @@ class AdminHttpClient:
method: str, method: str,
path: str, path: str,
*, *,
params: dict[str, Any] | None = None, params: dict[str, object] | None = None,
json: dict[str, Any] | None = None, json: dict[str, object] | None = None,
timeout: float = DEFAULT_TIMEOUT, timeout: float = DEFAULT_TIMEOUT,
) -> httpx.Response: ) -> httpx.Response:
url = f"{self._base_url}{path}" url = f"{self._base_url}{path}"
@ -130,28 +128,28 @@ class AdminHttpClient:
def base_url(self) -> str: def base_url(self) -> str:
return self._base_url 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) resp = self._request("GET", path, params=params)
return resp.json() 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).""" """GET returning response text (for CSV exports)."""
resp = self._request("GET", path, params=params) resp = self._request("GET", path, params=params)
return resp.text 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) resp = self._request("POST", path, json=json)
return resp.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) resp = self._request("PUT", path, json=json)
return resp.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) resp = self._request("PATCH", path, json=json)
return resp.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) resp = self._request("DELETE", path, params=params)
return resp.json() return resp.json()

View File

@ -10,7 +10,6 @@ When no agentkit.yaml exists, this wizard guides the user through:
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from typing import Any
import yaml import yaml
from rich.panel import Panel from rich.panel import Panel
@ -22,7 +21,7 @@ from agentkit.server.config import find_config_path
# ── Provider presets ────────────────────────────────────────────────── # ── Provider presets ──────────────────────────────────────────────────
PROVIDER_PRESETS: dict[str, dict[str, Any]] = { PROVIDER_PRESETS: dict[str, dict[str, object]] = {
"deepseek": { "deepseek": {
"name": "DeepSeek", "name": "DeepSeek",
"env_key": "DEEPSEEK_API_KEY", "env_key": "DEEPSEEK_API_KEY",
@ -144,7 +143,7 @@ def run_onboarding(
output_path.mkdir(parents=True, exist_ok=True) output_path.mkdir(parents=True, exist_ok=True)
existing_config_path = find_config_path(config_arg) 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: if existing_config_path:
with open(existing_config_path, encoding="utf-8") as f: with open(existing_config_path, encoding="utf-8") as f:
existing_config = yaml.safe_load(f) or {} existing_config = yaml.safe_load(f) or {}
@ -220,7 +219,7 @@ def run_onboarding(
) )
selected_model = available_models[int(model_choice) - 1] selected_model = available_models[int(model_choice) - 1]
# Rebuild models dict: selected model gets "default" alias # 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(): for model, conf in preset["models"].items():
if model == selected_model: if model == selected_model:
updated_models[model] = {**conf, "alias": "default"} updated_models[model] = {**conf, "alias": "default"}
@ -236,7 +235,7 @@ def run_onboarding(
# ── Step 3: Optional — add a second provider ───────────────── # ── Step 3: Optional — add a second provider ─────────────────
env_vars: dict[str, str] = {preset["env_key"]: api_key.strip()} env_vars: dict[str, str] = {preset["env_key"]: api_key.strip()}
providers_config: dict[str, Any] = { providers_config: dict[str, object] = {
selected_key: { selected_key: {
"api_key": f"${{{preset['env_key']}}}", "api_key": f"${{{preset['env_key']}}}",
"base_url": preset["base_url"], "base_url": preset["base_url"],

View File

@ -12,7 +12,6 @@ from __future__ import annotations
import json import json
import re import re
from pathlib import Path from pathlib import Path
from typing import Any
from openpyxl import Workbook from openpyxl import Workbook
@ -43,7 +42,7 @@ class ExcelRenderer:
return self._render_markdown(markdown_content, output_path) 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.""" """Render JSON dict {sheet_name: rows} into a multi-sheet workbook."""
wb = Workbook() wb = Workbook()
# Remove the default sheet — we'll create named ones # Remove the default sheet — we'll create named ones
@ -108,7 +107,7 @@ class ExcelRenderer:
wb.save(str(output_path)) wb.save(str(output_path))
return 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.""" """Parse GFM table lines and write rows into a worksheet."""
for idx, line in enumerate(table_lines): for idx, line in enumerate(table_lines):
if idx == 1: if idx == 1:

View File

@ -17,7 +17,6 @@ from __future__ import annotations
import logging import logging
import re import re
from pathlib import Path from pathlib import Path
from typing import Any
from reportlab.lib import colors from reportlab.lib import colors
from reportlab.lib.pagesizes import A4 from reportlab.lib.pagesizes import A4
@ -116,7 +115,7 @@ class PDFRenderer:
) )
styles = self._build_styles() styles = self._build_styles()
flowables: list[Any] = [] flowables: list[object] = []
lines = markdown_content.splitlines() lines = markdown_content.splitlines()
i = 0 i = 0

View File

@ -10,7 +10,6 @@ from __future__ import annotations
import logging import logging
from pathlib import Path from pathlib import Path
from typing import Any
from docxtpl import DocxTemplate from docxtpl import DocxTemplate
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
@ -40,7 +39,7 @@ class TemplateRenderer:
) )
def render_template( 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: ) -> Path:
"""Fill a .docx template with data using Jinja2 sandbox. """Fill a .docx template with data using Jinja2 sandbox.

View File

@ -14,7 +14,7 @@ from __future__ import annotations
import json import json
import logging import logging
import warnings import warnings
from typing import TYPE_CHECKING, Any from typing import TYPE_CHECKING
from urllib.parse import urlparse from urllib.parse import urlparse
from agentkit.mcp.transport import HTTPTransport, SSETransport, StdioTransport, Transport from agentkit.mcp.transport import HTTPTransport, SSETransport, StdioTransport, Transport
@ -65,7 +65,7 @@ class MCPClient:
self._transport = transport self._transport = transport
# U10 — 懒构造并缓存的 langchain client避免每次 list_tools/call_tool # U10 — 懒构造并缓存的 langchain client避免每次 list_tools/call_tool
# 都新建 MultiServerMCPClientstdio 传输下会反复 spawn 子进程)。 # 都新建 MultiServerMCPClientstdio 传输下会反复 spawn 子进程)。
self._lc_client: Any = None self._lc_client: "MultiServerMCPClient | None" = None
if transport is not None: if transport is not None:
# 旧 Transport 路径 — 发出 DeprecationWarning但保持原有行为 # 旧 Transport 路径 — 发出 DeprecationWarning但保持原有行为
@ -76,13 +76,13 @@ class MCPClient:
DeprecationWarning, DeprecationWarning,
stacklevel=2, stacklevel=2,
) )
self._langchain_config: dict[str, Any] | None = None self._langchain_config: dict[str, object] | None = None
else: else:
# 新 langchain 路径 — 解析 URL scheme 构建连接配置 # 新 langchain 路径 — 解析 URL scheme 构建连接配置
self._langchain_config = self._build_langchain_config(self._server_url, timeout) self._langchain_config = self._build_langchain_config(self._server_url, timeout)
@staticmethod @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 连接配置。 """根据 URL scheme 构建 langchain-mcp-adapters 连接配置。
Args: Args:
@ -150,7 +150,7 @@ class MCPClient:
server_url = "" server_url = ""
return cls(server_url=server_url, transport=transport) return cls(server_url=server_url, transport=transport)
async def _get_lc_client(self) -> Any: async def _get_lc_client(self) -> "MultiServerMCPClient":
"""懒构造并缓存 langchain ``MultiServerMCPClient`` 实例。 """懒构造并缓存 langchain ``MultiServerMCPClient`` 实例。
首次调用时创建后续返回缓存避免每次 list_tools/call_tool 都新建 首次调用时创建后续返回缓存避免每次 list_tools/call_tool 都新建
@ -202,7 +202,7 @@ class MCPClient:
return tools return tools
@staticmethod @staticmethod
def _extract_schema(tool: Any) -> dict[str, Any]: def _extract_schema(tool: object) -> dict[str, object]:
"""从 LangChain Tool 提取 inputSchemaJSON Schema 格式)。 """从 LangChain Tool 提取 inputSchemaJSON Schema 格式)。
LangChain 工具通常有 args_schemapydantic model回退到 tool.args dict LangChain 工具通常有 args_schemapydantic model回退到 tool.args dict
@ -273,8 +273,8 @@ class MCPTool(Tool):
name: str, name: str,
description: str, description: str,
client: MCPClient, client: MCPClient,
input_schema: dict[str, Any] | None = None, input_schema: dict[str, object] | None = None,
output_schema: dict[str, Any] | None = None, output_schema: dict[str, object] | None = None,
version: str = "1.0.0", version: str = "1.0.0",
tags: list[str] | None = None, tags: list[str] | None = None,
): ):

View File

@ -14,7 +14,7 @@ U14: 支持把已注册的 Skill 或专家团队封装成 ``Tool``,注册到
from __future__ import annotations from __future__ import annotations
import logging import logging
from typing import Any, Awaitable, Callable from collections.abc import Awaitable, Callable
from agentkit.skills.base import Skill from agentkit.skills.base import Skill
from agentkit.tools.base import Tool from agentkit.tools.base import Tool
@ -28,7 +28,7 @@ _DANGEROUS_TOOL_NAMES: frozenset[str] = frozenset(
) )
# 执行器签名:(skill_or_team_name, input_text) -> 结果 dict # 执行器签名:(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: class PublisherRegistry:
@ -90,7 +90,7 @@ class SkillMCPAdapter(Tool):
self._skill = skill self._skill = skill
self._executor = executor self._executor = executor
async def execute(self, **kwargs: Any) -> dict[str, Any]: async def execute(self, **kwargs: object) -> dict[str, object]:
"""调用 executor 执行技能;未配置或异常时返回错误 dict。""" """调用 executor 执行技能;未配置或异常时返回错误 dict。"""
input_text = kwargs.get("input", "") input_text = kwargs.get("input", "")
if self._executor is None: if self._executor is None:
@ -126,7 +126,7 @@ class TeamMCPAdapter(Tool):
self._team_name = team_name self._team_name = team_name
self._executor = executor self._executor = executor
async def execute(self, **kwargs: Any) -> dict[str, Any]: async def execute(self, **kwargs: object) -> dict[str, object]:
"""调用 executor 执行团队任务;未配置或异常时返回错误 dict。""" """调用 executor 执行团队任务;未配置或异常时返回错误 dict。"""
input_text = kwargs.get("input", "") input_text = kwargs.get("input", "")
if self._executor is None: if self._executor is None:

View File

@ -11,7 +11,7 @@ U13: 重构为路由工厂 ``create_mcp_router()``,可挂载到主 FastAPI app
from __future__ import annotations from __future__ import annotations
import logging import logging
from typing import Any from collections.abc import Callable
from fastapi import APIRouter, Depends, HTTPException, Request 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 协议响应字典。""" """将 Tool 序列化为 MCP 协议响应字典。"""
return { return {
"name": tool.name, "name": tool.name,
@ -51,8 +51,8 @@ def _serialize_tool(tool: Tool) -> dict[str, Any]:
def create_mcp_router( def create_mcp_router(
tool_registry: Any = None, tool_registry: object | None = None,
published_tools_getter: Any = None, published_tools_getter: Callable[[], list[Tool]] | None = None,
) -> APIRouter: ) -> APIRouter:
"""构造 MCP 路由,挂载到主 app 的 ``/api/v1/mcp/`` 前缀下。 """构造 MCP 路由,挂载到主 app 的 ``/api/v1/mcp/`` 前缀下。
@ -105,7 +105,7 @@ def create_mcp_router(
return None return None
@router.get("/tools/list") @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 工具。""" """列出所有可用的 MCP 工具。"""
tools = _all_tools() tools = _all_tools()
return {"tools": [_serialize_tool(t) for t in tools]} return {"tools": [_serialize_tool(t) for t in tools]}
@ -114,7 +114,7 @@ def create_mcp_router(
async def call_tool( async def call_tool(
request: dict, request: dict,
_user: dict = Depends(_mcp_member_auth), _user: dict = Depends(_mcp_member_auth),
) -> dict[str, Any]: ) -> dict[str, object]:
"""调用指定的 MCP 工具。""" """调用指定的 MCP 工具。"""
tool_name = request.get("name") tool_name = request.get("name")
arguments = request.get("arguments", {}) arguments = request.get("arguments", {})
@ -145,7 +145,7 @@ def create_mcp_router(
async def jsonrpc_endpoint( async def jsonrpc_endpoint(
request: Request, request: Request,
_user: dict = Depends(_mcp_member_auth), _user: dict = Depends(_mcp_member_auth),
) -> dict[str, Any]: ) -> dict[str, object]:
"""JSON-RPC 2.0 端点 — MCP 协议兼容。 """JSON-RPC 2.0 端点 — MCP 协议兼容。
支持 methods: initialize, tools/list, tools/call 支持 methods: initialize, tools/list, tools/call
@ -225,7 +225,7 @@ class MCPServer:
``/api/v1/mcp/`` 前缀下复用主 app JWT + API Key 认证 ``/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._tool_registry = tool_registry
self._host = host self._host = host
self._port = port self._port = port

View File

@ -12,7 +12,6 @@ import logging
import os import os
import warnings import warnings
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import Any
import httpx import httpx
@ -52,7 +51,7 @@ class Transport(ABC):
... ...
@abstractmethod @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 请求 """发送 JSON-RPC 请求
Args: Args:
@ -65,7 +64,7 @@ class Transport(ABC):
... ...
@abstractmethod @abstractmethod
async def receive_response(self) -> dict[str, Any]: async def receive_response(self) -> dict[str, object]:
"""接收响应 """接收响应
Returns: Returns:
@ -92,7 +91,7 @@ class HTTPTransport(Transport):
self._timeout = timeout self._timeout = timeout
self._client: httpx.AsyncClient | None = None self._client: httpx.AsyncClient | None = None
self._request_id = 0 self._request_id = 0
self._pending: dict[int, asyncio.Future[dict[str, Any]]] = {} self._pending: dict[int, asyncio.Future[dict[str, object]]] = {}
@property @property
def is_connected(self) -> bool: def is_connected(self) -> bool:
@ -131,7 +130,7 @@ class HTTPTransport(Transport):
self._request_id += 1 self._request_id += 1
return self._request_id 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 请求并等待响应 """发送 JSON-RPC 请求并等待响应
Args: Args:
@ -148,7 +147,7 @@ class HTTPTransport(Transport):
raise TransportError("Transport not connected") raise TransportError("Transport not connected")
request_id = self._next_request_id() request_id = self._next_request_id()
message: dict[str, Any] = { message: dict[str, object] = {
"jsonrpc": "2.0", "jsonrpc": "2.0",
"id": request_id, "id": request_id,
"method": method, "method": method,
@ -179,7 +178,7 @@ class HTTPTransport(Transport):
return data.get("result") return data.get("result")
async def receive_response(self) -> dict[str, Any]: async def receive_response(self) -> dict[str, object]:
"""接收响应 """接收响应
对于 HTTPTransport响应在 send_request 中同步返回 对于 HTTPTransport响应在 send_request 中同步返回
@ -218,7 +217,7 @@ class SSETransport(Transport):
self._client: httpx.AsyncClient | None = None self._client: httpx.AsyncClient | None = None
self._request_id = 0 self._request_id = 0
self._sse_task: asyncio.Task[None] | None = None 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 self._connected = False
@property @property
@ -304,7 +303,7 @@ class SSETransport(Transport):
self._request_id += 1 self._request_id += 1
return self._request_id 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 请求 """通过 HTTP POST 发送 JSON-RPC 请求
Args: Args:
@ -321,7 +320,7 @@ class SSETransport(Transport):
raise TransportError("Transport not connected") raise TransportError("Transport not connected")
request_id = self._next_request_id() request_id = self._next_request_id()
message: dict[str, Any] = { message: dict[str, object] = {
"jsonrpc": "2.0", "jsonrpc": "2.0",
"id": request_id, "id": request_id,
"method": method, "method": method,
@ -352,7 +351,7 @@ class SSETransport(Transport):
return data.get("result") return data.get("result")
async def receive_response(self) -> dict[str, Any]: async def receive_response(self) -> dict[str, object]:
"""从 SSE 事件流接收响应 """从 SSE 事件流接收响应
Returns: Returns:
@ -392,11 +391,11 @@ class StdioTransport(Transport):
self._timeout = timeout self._timeout = timeout
self._process: asyncio.subprocess.Process | None = None self._process: asyncio.subprocess.Process | None = None
self._request_id = 0 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._reader_task: asyncio.Task[None] | None = None
self._stderr_task: asyncio.Task[None] | None = None self._stderr_task: asyncio.Task[None] | None = None
self._connected = False self._connected = False
self._notifications: asyncio.Queue[dict[str, Any]] = asyncio.Queue() self._notifications: asyncio.Queue[dict[str, object]] = asyncio.Queue()
@property @property
def is_connected(self) -> bool: def is_connected(self) -> bool:
@ -513,7 +512,7 @@ class StdioTransport(Transport):
logger.info("StdioTransport disconnected") 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 请求并等待响应 """发送 JSON-RPC 请求并等待响应
Args: Args:
@ -531,11 +530,11 @@ class StdioTransport(Transport):
return await self._send_request_internal(method, params) return await self._send_request_internal(method, params)
async def _send_request_internal( async def _send_request_internal(
self, method: str, params: dict[str, Any] | None = None self, method: str, params: dict[str, object] | None = None
) -> Any: ) -> object:
"""内部请求发送方法connect 时也可调用)""" """内部请求发送方法connect 时也可调用)"""
request_id = self._next_request_id() request_id = self._next_request_id()
message: dict[str, Any] = { message: dict[str, object] = {
"jsonrpc": "2.0", "jsonrpc": "2.0",
"id": request_id, "id": request_id,
"method": method, "method": method,
@ -546,7 +545,7 @@ class StdioTransport(Transport):
await self._write_message(message) await self._write_message(message)
loop = asyncio.get_running_loop() loop = asyncio.get_running_loop()
future: asyncio.Future[Any] = loop.create_future() future: asyncio.Future[object] = loop.create_future()
self._pending[request_id] = future self._pending[request_id] = future
try: try:
@ -558,9 +557,9 @@ class StdioTransport(Transport):
self._pending.pop(request_id, None) self._pending.pop(request_id, None)
raise 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不期待响应""" """发送 JSON-RPC 通知(无 id不期待响应"""
message: dict[str, Any] = { message: dict[str, object] = {
"jsonrpc": "2.0", "jsonrpc": "2.0",
"method": method, "method": method,
} }
@ -568,7 +567,7 @@ class StdioTransport(Transport):
message["params"] = params message["params"] = params
await self._write_message(message) 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""" """将 JSON-RPC 消息写入子进程 stdin"""
if self._process is None or self._process.stdin is None: if self._process is None or self._process.stdin is None:
raise TransportError("Process stdin not available") raise TransportError("Process stdin not available")
@ -576,7 +575,7 @@ class StdioTransport(Transport):
self._process.stdin.write(data) self._process.stdin.write(data)
await self._process.stdin.drain() await self._process.stdin.drain()
async def receive_response(self) -> dict[str, Any]: async def receive_response(self) -> dict[str, object]:
"""接收通知消息 """接收通知消息
对于 StdioTransport请求响应通过 _pending Future 异步返回 对于 StdioTransport请求响应通过 _pending Future 异步返回

View File

@ -4,7 +4,6 @@ from __future__ import annotations
import logging import logging
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Any
from agentkit.telemetry.tracer import get_tracer from agentkit.telemetry.tracer import get_tracer
@ -23,7 +22,7 @@ class AlignmentConfig:
audit_sample_rate: float = 1.0 # 审计采样率 0.0-1.01.0=每次都审计 audit_sample_rate: float = 1.0 # 审计采样率 0.0-1.01.0=每次都审计
@classmethod @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()} 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} 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): def __init__(self, config: AlignmentConfig):
self._config = config 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
input_data 中添加 'alignment_constraints' 值为约束列表 input_data 中添加 'alignment_constraints' 值为约束列表
@ -76,13 +75,13 @@ class AlignmentGuard:
self._interaction_counts: dict[str, int] = {} self._interaction_counts: dict[str, int] = {}
self._loop_depths: 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""" """委托给 ConstraintInjector"""
return self._injector.inject(input_data) return self._injector.inject(input_data)
async def check_output( async def check_output(
self, self,
output: dict[str, Any], output: dict[str, object],
constraints: list[str] | None = None, constraints: list[str] | None = None,
) -> AlignmentCheckResult: ) -> AlignmentCheckResult:
"""检查输出是否符合约束 """检查输出是否符合约束
@ -127,7 +126,7 @@ class AlignmentGuard:
return result return result
def _rule_check( def _rule_check(
self, output: dict[str, Any], constraints: list[str] self, output: dict[str, object], constraints: list[str]
) -> list[str]: ) -> list[str]:
"""基于规则的约束检查:方向性判断,区分'禁止X''提及X' """基于规则的约束检查:方向性判断,区分'禁止X''提及X'
@ -206,7 +205,7 @@ class AlignmentGuard:
start = idx + len(keyword) start = idx + len(keyword)
@staticmethod @staticmethod
def _extract_text(output: dict[str, Any]) -> str: def _extract_text(output: dict[str, object]) -> str:
"""从 output dict 中提取所有文本内容""" """从 output dict 中提取所有文本内容"""
parts: list[str] = [] parts: list[str] = []
for value in output.values(): for value in output.values():
@ -217,7 +216,7 @@ class AlignmentGuard:
return " ".join(parts) return " ".join(parts)
async def _llm_check( async def _llm_check(
self, output: dict[str, Any], constraints: list[str] self, output: dict[str, object], constraints: list[str]
) -> AlignmentCheckResult: ) -> AlignmentCheckResult:
"""LLM 语义检查""" """LLM 语义检查"""
content = self._extract_text(output) content = self._extract_text(output)

View File

@ -11,7 +11,7 @@ Key schema (Redis):
import logging import logging
import time import time
from typing import Any, Protocol, runtime_checkable from typing import Protocol, runtime_checkable
# redis 可选依赖;未安装时回退为 Exception 以保留原 catch-all 语义(降级到 fallback # redis 可选依赖;未安装时回退为 Exception 以保留原 catch-all 语义(降级到 fallback
try: try:
@ -132,7 +132,7 @@ class RedisCascadeStateStore:
def __init__(self, redis_url: str = "redis://localhost:6379", session_ttl: int = 86400): def __init__(self, redis_url: str = "redis://localhost:6379", session_ttl: int = 86400):
self._redis_url = redis_url self._redis_url = redis_url
self._session_ttl = session_ttl self._session_ttl = session_ttl
self._sync_redis: Any = None self._sync_redis: object = None
self._fallback: InMemoryCascadeStateStore | None = None self._fallback: InMemoryCascadeStateStore | None = None
self._degraded = False self._degraded = False

View File

@ -6,7 +6,7 @@
import importlib import importlib
import logging import logging
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any, Callable from typing import Callable
from agentkit.skills.base import Skill from agentkit.skills.base import Skill
@ -36,9 +36,9 @@ class QualityGate:
async def validate( async def validate(
self, self,
output: dict[str, Any], output: dict[str, object],
skill: Skill, skill: Skill,
skill_context: dict[str, Any] | None = None, skill_context: dict[str, object] | None = None,
) -> QualityResult: ) -> QualityResult:
"""对产出执行多维度质量检查 """对产出执行多维度质量检查
@ -150,8 +150,8 @@ class QualityGate:
@staticmethod @staticmethod
def _check_skill_match( def _check_skill_match(
output: dict[str, Any], output: dict[str, object],
skill_context: dict[str, Any] | None, skill_context: dict[str, object] | None,
) -> QualityCheck | None: ) -> QualityCheck | None:
"""第五维度:技能匹配验证 """第五维度:技能匹配验证

View File

@ -6,7 +6,6 @@ Schema 验证、字段类型归一化、元数据附加。
import logging import logging
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime, timezone from datetime import datetime, timezone
from typing import Any
from agentkit.quality.gate import QualityResult from agentkit.quality.gate import QualityResult
from agentkit.skills.base import Skill from agentkit.skills.base import Skill
@ -28,7 +27,7 @@ class StandardOutput:
"""标准化输出""" """标准化输出"""
skill_name: str skill_name: str
data: dict[str, Any] data: dict[str, object]
metadata: OutputMetadata metadata: OutputMetadata
@ -37,7 +36,7 @@ class OutputStandardizer:
async def standardize( async def standardize(
self, self,
raw_output: dict[str, Any], raw_output: dict[str, object],
skill: Skill, skill: Skill,
quality_result: QualityResult | None = None, quality_result: QualityResult | None = None,
) -> StandardOutput: ) -> StandardOutput:

View File

@ -10,7 +10,7 @@ PGVectorStore向量化存储
from __future__ import annotations from __future__ import annotations
import logging import logging
from typing import TYPE_CHECKING, Any from typing import TYPE_CHECKING
from agentkit.memory.document_loader import DocumentLoader from agentkit.memory.document_loader import DocumentLoader
from agentkit.rag_platform.models import DocumentStatus from agentkit.rag_platform.models import DocumentStatus
@ -106,7 +106,7 @@ class DocumentProcessor:
file_type: str, file_type: str,
chunk_size: int | None = None, chunk_size: int | None = None,
chunk_overlap: int | None = None, chunk_overlap: int | None = None,
) -> list[dict[str, Any]]: ) -> list[dict[str, object]]:
"""解析 + 分段,返回 chunk 用于只读预览(不向量化)。 """解析 + 分段,返回 chunk 用于只读预览(不向量化)。
Args: Args:
@ -128,7 +128,7 @@ class DocumentProcessor:
async def vectorize( async def vectorize(
self, self,
chunks: list[str] | list[dict[str, Any]], chunks: list[str] | list[dict[str, object]],
kb_id: str, kb_id: str,
document_id: str, document_id: str,
vector_store: "PGVectorStore", vector_store: "PGVectorStore",

View File

@ -11,7 +11,6 @@ from __future__ import annotations
import hashlib import hashlib
import logging import logging
from typing import Any
from pydantic import BaseModel, ConfigDict from pydantic import BaseModel, ConfigDict
@ -56,7 +55,7 @@ class HitProcessor:
def __init__( def __init__(
self, self,
llm_gateway: Any = None, llm_gateway: object | None = None,
cache_enabled: bool = True, cache_enabled: bool = True,
model: str = _DEFAULT_LLM_MODEL, model: str = _DEFAULT_LLM_MODEL,
) -> None: ) -> None:

View File

@ -7,7 +7,7 @@ U3 将扩展为完整 IngestionPipeline含解析、预览、净化
from __future__ import annotations from __future__ import annotations
import logging 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.indexing import KB_CHUNKS_TABLE
from agentkit.rag_platform.models import QueryResult from agentkit.rag_platform.models import QueryResult
@ -64,7 +64,7 @@ class RAGPipeline:
KB_CHUNKS_TABLE, 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: Args:

View File

@ -5,8 +5,6 @@
from __future__ import annotations from __future__ import annotations
from typing import Any
from pydantic import BaseModel, ConfigDict, Field from pydantic import BaseModel, ConfigDict, Field
from agentkit.rag_platform.document_processor import ( from agentkit.rag_platform.document_processor import (
@ -23,7 +21,7 @@ class PreviewChunk(BaseModel):
index: int index: int
content: str content: str
metadata: dict[str, Any] = Field(default_factory=dict) metadata: dict[str, object] = Field(default_factory=dict)
class PreviewResult(BaseModel): class PreviewResult(BaseModel):

View File

@ -14,7 +14,6 @@ from __future__ import annotations
import hashlib import hashlib
import logging import logging
import re import re
from typing import Any
from pydantic import BaseModel, ConfigDict from pydantic import BaseModel, ConfigDict
@ -63,7 +62,7 @@ class QuestionGenerator:
def __init__( def __init__(
self, self,
llm_gateway: Any = None, llm_gateway: object | None = None,
max_questions_per_chunk: int = 3, max_questions_per_chunk: int = 3,
model: str = "default", model: str = "default",
cache: bool = True, cache: bool = True,
@ -76,7 +75,7 @@ class QuestionGenerator:
async def generate( async def generate(
self, self,
chunks: list[dict[str, Any]], chunks: list[dict[str, object]],
document_context: str = "", document_context: str = "",
) -> list[GeneratedQuestion]: ) -> list[GeneratedQuestion]:
"""为每个 chunk 生成相关问题。 """为每个 chunk 生成相关问题。

View File

@ -7,7 +7,7 @@
from __future__ import annotations from __future__ import annotations
import logging import logging
from typing import TYPE_CHECKING, Any from typing import TYPE_CHECKING
from pydantic import BaseModel, ConfigDict from pydantic import BaseModel, ConfigDict
@ -50,9 +50,9 @@ class Reranker:
def __init__(self, config: RerankConfig) -> None: def __init__(self, config: RerankConfig) -> None:
self._config = config 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 时失败。""" """延迟加载 reranker 实例 — 避免在 import 时失败。"""
if self._reranker is not None: if self._reranker is not None:
return self._reranker return self._reranker
@ -69,7 +69,7 @@ class Reranker:
return self._reranker return self._reranker
def _build_cohere_reranker(self, cfg: RerankConfig) -> Any: def _build_cohere_reranker(self, cfg: RerankConfig) -> object:
"""构建 CohereRerank — 数据出境,需 api_key。""" """构建 CohereRerank — 数据出境,需 api_key。"""
if not cfg.api_key: if not cfg.api_key:
raise ValueError("Cohere rerank requires api_key") raise ValueError("Cohere rerank requires api_key")
@ -81,7 +81,7 @@ class Reranker:
"Install: pip install llama-index-postprocessor-cohere-rerank" "Install: pip install llama-index-postprocessor-cohere-rerank"
) from e ) from e
kwargs: dict[str, Any] = { kwargs: dict[str, object] = {
"api_key": cfg.api_key, "api_key": cfg.api_key,
"top_n": cfg.top_n, "top_n": cfg.top_n,
} }
@ -89,7 +89,7 @@ class Reranker:
kwargs["model"] = cfg.model_name kwargs["model"] = cfg.model_name
return CohereRerank(**kwargs) return CohereRerank(**kwargs)
def _build_bge_reranker(self, cfg: RerankConfig) -> Any: def _build_bge_reranker(self, cfg: RerankConfig) -> object:
"""构建 BGE-Reranker via Xinference本地部署无数据出境 """构建 BGE-Reranker via Xinference本地部署无数据出境
使用 SentenceTransformerRerank 作为本地 BGE-Reranker 的封装 使用 SentenceTransformerRerank 作为本地 BGE-Reranker 的封装
@ -107,7 +107,7 @@ class Reranker:
) from e ) from e
model = cfg.model_name or "BAAI/bge-reranker-base" model = cfg.model_name or "BAAI/bge-reranker-base"
kwargs: dict[str, Any] = { kwargs: dict[str, object] = {
"model": model, "model": model,
"top_n": cfg.top_n, "top_n": cfg.top_n,
} }

View File

@ -12,7 +12,7 @@ from __future__ import annotations
import asyncio import asyncio
import logging import logging
from typing import TYPE_CHECKING, Any from typing import TYPE_CHECKING
from sqlalchemy import text from sqlalchemy import text
@ -184,7 +184,7 @@ class RetrievalEngine:
out: list[QueryResult] = [] out: list[QueryResult] = []
for row in rows: for row in rows:
chunk_id, content, metadata, document_id, score = row 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", "") kb_id = meta.get("kb_id", "")
out.append( out.append(
QueryResult( QueryResult(

View File

@ -17,7 +17,7 @@ import re
import struct import struct
import zipfile import zipfile
from pathlib import Path from pathlib import Path
from typing import Any from xml.etree.ElementTree import Element
# 文件类型白名单:扩展名 → MIME 类型 # 文件类型白名单:扩展名 → MIME 类型
ALLOWED_FILE_TYPES: dict[str, str] = { ALLOWED_FILE_TYPES: dict[str, str] = {
@ -311,7 +311,7 @@ def sanitize_content(content: str, file_type: str) -> str:
return content return content
def parse_xml_safe(content: bytes) -> Any: def parse_xml_safe(content: bytes) -> Element:
"""安全解析 XML — 禁止 DTD/实体以防止 XXE 攻击。 """安全解析 XML — 禁止 DTD/实体以防止 XXE 攻击。
Args: Args:

View File

@ -23,7 +23,7 @@ import logging
import re import re
import uuid import uuid
from datetime import datetime, timedelta, timezone 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 from pydantic import BaseModel, ConfigDict, Field, field_validator
@ -54,7 +54,7 @@ TASKIQ_REDIS_DB = 1
TASKIQ_KEY_PREFIX = "taskiq:" TASKIQ_KEY_PREFIX = "taskiq:"
async def _maybe_await(result: Any) -> Any: async def _maybe_await(result: object) -> object:
"""统一处理 sync/async 调用结果 — InMemoryTaskStore 方法为 syncRedisTaskStore 为 async。""" """统一处理 sync/async 调用结果 — InMemoryTaskStore 方法为 syncRedisTaskStore 为 async。"""
if inspect.isawaitable(result): if inspect.isawaitable(result):
return await result return await result
@ -81,7 +81,7 @@ class VectorizeTaskParams(BaseModel):
@field_validator("chunk_overlap") @field_validator("chunk_overlap")
@classmethod @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。""" """chunk_overlap 必须小于 chunk_size。"""
size = info.data.get("chunk_size", DEFAULT_CHUNK_SIZE) size = info.data.get("chunk_size", DEFAULT_CHUNK_SIZE)
if v >= size: if v >= size:
@ -161,7 +161,7 @@ class TaskStoreProtocol(Protocol):
def get(self, task_id: str) -> TaskRecord | None: ... def get(self, task_id: str) -> TaskRecord | None: ...
async def update_status( async def update_status(
self, task_id: str, status: TaskStatus, **kwargs: Any self, task_id: str, status: TaskStatus, **kwargs: object
) -> TaskRecord: ... ) -> TaskRecord: ...
def list_tasks( def list_tasks(
@ -265,7 +265,7 @@ class TaskManager:
def __init__( def __init__(
self, self,
broker: Any = None, broker: object | None = None,
task_store: TaskStoreProtocol | None = None, task_store: TaskStoreProtocol | None = None,
max_concurrent_per_user: int = DEFAULT_MAX_CONCURRENT_PER_USER, max_concurrent_per_user: int = DEFAULT_MAX_CONCURRENT_PER_USER,
task_ttl_seconds: int = DEFAULT_TASK_TTL_SECONDS, task_ttl_seconds: int = DEFAULT_TASK_TTL_SECONDS,
@ -284,7 +284,7 @@ class TaskManager:
return self._store return self._store
@property @property
def broker(self) -> Any: def broker(self) -> object:
"""暴露底层 broker供 startup/shutdown 集成)。""" """暴露底层 broker供 startup/shutdown 集成)。"""
return self._broker return self._broker
@ -293,7 +293,7 @@ class TaskManager:
async def submit_vectorize( async def submit_vectorize(
self, self,
params: VectorizeTaskParams, params: VectorizeTaskParams,
dependencies: dict[str, Any] | None = None, dependencies: dict[str, object] | None = None,
) -> str: ) -> str:
"""提交向量化任务,返回 task_id。 """提交向量化任务,返回 task_id。
@ -344,7 +344,7 @@ class TaskManager:
async def submit_batch_index( async def submit_batch_index(
self, self,
params: BatchIndexTaskParams, params: BatchIndexTaskParams,
dependencies: dict[str, Any] | None = None, dependencies: dict[str, object] | None = None,
) -> str: ) -> str:
"""提交批量索引任务,返回 task_id。""" """提交批量索引任务,返回 task_id。"""
await self._check_concurrency(params.user_id) await self._check_concurrency(params.user_id)
@ -466,7 +466,7 @@ class TaskManager:
self, self,
task_id: str, task_id: str,
params: VectorizeTaskParams, params: VectorizeTaskParams,
deps: dict[str, Any], deps: dict[str, object],
) -> None: ) -> None:
"""降级模式 — 同步执行向量化任务(在 asyncio 任务中)。""" """降级模式 — 同步执行向量化任务(在 asyncio 任务中)。"""
now = datetime.now(timezone.utc) now = datetime.now(timezone.utc)
@ -504,7 +504,7 @@ class TaskManager:
self, self,
task_id: str, task_id: str,
params: BatchIndexTaskParams, params: BatchIndexTaskParams,
deps: dict[str, Any], deps: dict[str, object],
) -> None: ) -> None:
"""降级模式 — 同步执行批量索引任务。""" """降级模式 — 同步执行批量索引任务。"""
now = datetime.now(timezone.utc) 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 隔离。 """创建 TaskIQ Redis broker — 独立 db=1 隔离。
Args: Args:

View File

@ -14,7 +14,7 @@ These dependencies read the ``current_user`` payload that
from __future__ import annotations from __future__ import annotations
import logging import logging
from typing import Any, Callable from typing import Callable
import aiosqlite import aiosqlite
from fastapi import HTTPException, Request from fastapi import HTTPException, Request
@ -25,7 +25,7 @@ from agentkit.server.auth.permissions import Permission, has_permission, is_dev_
logger = logging.getLogger(__name__) 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. """Return the current user payload, or ``None`` in dev mode.
The payload is set by :class:`AuthMiddleware` and contains 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) 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. """Require an authenticated user. Raises 401 if not authenticated.
Use this as a FastAPI dependency for endpoints that need *any* 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 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. """Build a FastAPI dependency that requires the given permissions.
The user must have *all* of the given permissions (AND semantics). 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. 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) user = await get_current_user(request)
# Dev mode: no authenticated user # Dev mode: no authenticated user
@ -132,13 +132,13 @@ def require_permission(*permissions: Permission) -> Callable[..., Any]:
return _dependency 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. """Build a FastAPI dependency that requires at least one of the permissions.
Similar to :func:`require_permission` but uses OR semantics. 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) user = await get_current_user(request)
if is_dev_mode(user): if is_dev_mode(user):
@ -181,7 +181,7 @@ async def _resolve_db_path(request: Request):
return DEFAULT_AUTH_DB_PATH 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. """Require a user authorized to use the local terminal.
This checks both: This checks both:
@ -224,7 +224,7 @@ async def require_terminal_authorized(request: Request) -> dict[str, Any]:
return user 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. """Require a user authorized to use the server terminal.
Checks: Checks:

View File

@ -35,7 +35,6 @@ import secrets
import uuid import uuid
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from typing import Any
import jwt import jwt
@ -165,7 +164,7 @@ def create_token_pair(
# the JWT. # the JWT.
jti = str(uuid.uuid4()) if effective_session_id else None jti = str(uuid.uuid4()) if effective_session_id else None
access_payload: dict[str, Any] = { access_payload: dict[str, object] = {
"sub": user_id, "sub": user_id,
"username": username, "username": username,
"role": role, "role": role,
@ -173,7 +172,7 @@ def create_token_pair(
"iat": int(issued_at.timestamp()), "iat": int(issued_at.timestamp()),
"exp": int(access_exp.timestamp()), "exp": int(access_exp.timestamp()),
} }
refresh_payload: dict[str, Any] = { refresh_payload: dict[str, object] = {
"sub": user_id, "sub": user_id,
"username": username, "username": username,
"role": role, "role": role,
@ -238,7 +237,7 @@ def create_access_token(
access_exp = issued_at + ACCESS_TOKEN_TTL access_exp = issued_at + ACCESS_TOKEN_TTL
jti = str(uuid.uuid4()) if session_id else None jti = str(uuid.uuid4()) if session_id else None
access_payload: dict[str, Any] = { access_payload: dict[str, object] = {
"sub": user_id, "sub": user_id,
"username": username, "username": username,
"role": role, "role": role,
@ -261,7 +260,7 @@ def verify_token(
secret: str, secret: str,
*, *,
expected_type: str | None = None, expected_type: str | None = None,
) -> dict[str, Any]: ) -> dict[str, object]:
"""Verify a JWT and return its payload. """Verify a JWT and return its payload.
Args: Args:

View File

@ -20,7 +20,6 @@ from __future__ import annotations
import hmac import hmac
import logging import logging
from typing import Any
import jwt import jwt
from starlette.middleware.base import BaseHTTPMiddleware 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.""" """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 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. """Verify a JWT bearer token. Returns payload or None.
V2 tokens carry a ``sid`` claim. The middleware does NOT V2 tokens carry a ``sid`` claim. The middleware does NOT

View File

@ -27,7 +27,6 @@ import os
from collections.abc import Mapping from collections.abc import Mapping
from datetime import datetime, timezone from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
from typing import Any
import aiosqlite import aiosqlite
from sqlalchemy import String 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.""" """Convert a ``users`` row into a JSON-safe dict."""
return { return {
"id": row["id"], "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. """Convert an ``auth_sessions`` row into a JSON-safe dict.
The ``revoked`` field is normalized to a Python ``bool`` (the DB stores 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. """Convert a ``departments`` row into a JSON-safe dict.
The ``is_active`` field is normalized to a Python ``bool`` (the DB 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.""" """Convert a ``user_departments`` row into a JSON-safe dict."""
return { return {
"user_id": row["user_id"], "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. """Convert a ``skill_states`` row into a JSON-safe dict.
The ``is_disabled`` field is normalized to a Python ``bool`` (the DB The ``is_disabled`` field is normalized to a Python ``bool`` (the DB

View File

@ -22,7 +22,6 @@ terminal access on a per-user basis without changing the user's role.
from __future__ import annotations from __future__ import annotations
from enum import Enum from enum import Enum
from typing import Any
class Permission(str, Enum): class Permission(str, Enum):
@ -108,7 +107,7 @@ def get_role_permissions(role: str | None) -> frozenset[Permission]:
return ROLE_PERMISSIONS.get(role, frozenset()) 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. """Check if a user payload has a specific permission.
Args: Args:
@ -132,7 +131,7 @@ def has_permission(user: dict[str, Any] | None, permission: Permission) -> bool:
return permission in get_role_permissions(role) 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). """Return True if the request is in dev mode (no authenticated user).
Dev mode is when ``AuthMiddleware`` passes through requests without Dev mode is when ``AuthMiddleware`` passes through requests without

View File

@ -24,7 +24,6 @@ import logging
import os import os
import uuid import uuid
from pathlib import Path from pathlib import Path
from typing import Any
import aiosqlite import aiosqlite
@ -149,7 +148,7 @@ class LocalAuthProvider:
is_terminal_authorized: bool = False, is_terminal_authorized: bool = False,
is_server_terminal_authorized: bool = False, is_server_terminal_authorized: bool = False,
created_by: str | None = None, created_by: str | None = None,
) -> dict[str, Any]: ) -> dict[str, object]:
"""Create a new user in the local ``users`` table. """Create a new user in the local ``users`` table.
Args: Args:

View File

@ -29,7 +29,6 @@ import uuid
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime, timezone from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
from typing import Any
import aiosqlite import aiosqlite
@ -179,7 +178,7 @@ class SessionService:
admin "list all sessions" view passes ``include_revoked=True``. admin "list all sessions" view passes ``include_revoked=True``.
""" """
sql = "SELECT * FROM auth_sessions WHERE user_id = ?" sql = "SELECT * FROM auth_sessions WHERE user_id = ?"
args: tuple[Any, ...] = (user_id,) args: tuple[object, ...] = (user_id,)
if not include_revoked: if not include_revoked:
sql += " AND revoked = 0" sql += " AND revoked = 0"
sql += " ORDER BY created_at DESC" sql += " ORDER BY created_at DESC"
@ -437,7 +436,7 @@ class SessionService:
"SET revoked = 1, revoked_reason = ? " "SET revoked = 1, revoked_reason = ? "
"WHERE user_id = ? AND revoked = 0" "WHERE user_id = ? AND revoked = 0"
) )
args: list[Any] = [reason, user_id] args: list[object] = [reason, user_id]
if except_sid is not None: if except_sid is not None:
sql += " AND id != ?" sql += " AND id != ?"
args.append(except_sid) args.append(except_sid)

View File

@ -6,7 +6,6 @@ import asyncio
import datetime import datetime
import logging import logging
from collections import defaultdict, deque from collections import defaultdict, deque
from typing import Any
from agentkit.session.models import Message, MessageRole, Session, SessionStatus from agentkit.session.models import Message, MessageRole, Session, SessionStatus
from agentkit.session.store import InMemorySessionStore, SessionStore from agentkit.session.store import InMemorySessionStore, SessionStore
@ -155,7 +154,7 @@ class SessionManager:
async def create_session( async def create_session(
self, self,
agent_name: str, agent_name: str,
metadata: dict[str, Any] | None = None, metadata: dict[str, object] | None = None,
) -> Session: ) -> Session:
"""Create a new conversation session bound to an Agent. """Create a new conversation session bound to an Agent.
@ -216,7 +215,7 @@ class SessionManager:
content: str, content: str,
tool_call_id: str | None = None, tool_call_id: str | None = None,
agent_name: str | None = None, agent_name: str | None = None,
metadata: dict[str, Any] | None = None, metadata: dict[str, object] | None = None,
) -> Message: ) -> Message:
"""Append a message to a session. """Append a message to a session.

View File

@ -6,7 +6,6 @@ import uuid
from dataclasses import dataclass, field from dataclasses import dataclass, field
from datetime import datetime, timezone from datetime import datetime, timezone
from enum import Enum from enum import Enum
from typing import Any
class SessionStatus(str, Enum): class SessionStatus(str, Enum):
@ -40,9 +39,9 @@ class Message:
tool_call_id: str | None = None tool_call_id: str | None = None
agent_name: str | None = None agent_name: str | None = None
created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc)) 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 { return {
"message_id": self.message_id, "message_id": self.message_id,
"session_id": self.session_id, "session_id": self.session_id,
@ -55,7 +54,7 @@ class Message:
} }
@classmethod @classmethod
def from_dict(cls, data: dict[str, Any]) -> Message: def from_dict(cls, data: dict[str, object]) -> Message:
return cls( return cls(
message_id=data["message_id"], message_id=data["message_id"],
session_id=data["session_id"], session_id=data["session_id"],
@ -89,11 +88,11 @@ class Session:
session_id: str session_id: str
agent_name: str agent_name: str
status: SessionStatus = SessionStatus.ACTIVE 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)) created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
updated_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 { return {
"session_id": self.session_id, "session_id": self.session_id,
"agent_name": self.agent_name, "agent_name": self.agent_name,
@ -104,7 +103,7 @@ class Session:
} }
@classmethod @classmethod
def from_dict(cls, data: dict[str, Any]) -> Session: def from_dict(cls, data: dict[str, object]) -> Session:
return cls( return cls(
session_id=data["session_id"], session_id=data["session_id"],
agent_name=data["agent_name"], agent_name=data["agent_name"],

View File

@ -5,7 +5,7 @@ from __future__ import annotations
import json import json
import logging import logging
import os import os
from typing import Any, Protocol, runtime_checkable from typing import Protocol, runtime_checkable
# redis 可选依赖;未安装时回退为 Exception 以保留原 catch-all 语义 # redis 可选依赖;未安装时回退为 Exception 以保留原 catch-all 语义
try: try:
@ -119,7 +119,7 @@ class RedisSessionStore:
def __init__(self, redis_url: str = "redis://localhost:6379/0", ttl_seconds: int = 86400): def __init__(self, redis_url: str = "redis://localhost:6379/0", ttl_seconds: int = 86400):
self._redis_url = redis_url self._redis_url = redis_url
self._ttl_seconds = ttl_seconds self._ttl_seconds = ttl_seconds
self._redis: Any = None self._redis: object = None
async def _get_redis(self): async def _get_redis(self):
if self._redis is None: if self._redis is None:

View File

@ -4,7 +4,6 @@ from __future__ import annotations
import logging import logging
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Any
from agentkit.core.config_driven import AgentConfig from agentkit.core.config_driven import AgentConfig
from agentkit.core.exceptions import ConfigValidationError from agentkit.core.exceptions import ConfigValidationError
@ -66,27 +65,27 @@ class SkillConfig(AgentConfig):
task_mode: str = "llm_generate", task_mode: str = "llm_generate",
supported_tasks: list[str] | None = None, supported_tasks: list[str] | None = None,
max_concurrency: int = 1, max_concurrency: int = 1,
input_schema: dict[str, Any] | None = None, input_schema: dict[str, object] | None = None,
output_schema: dict[str, Any] | None = None, output_schema: dict[str, object] | None = None,
prompt: dict[str, str] | 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, tools: list[str] | None = None,
memory: dict[str, Any] | None = None, memory: dict[str, object] | None = None,
custom_handler: str | None = None, custom_handler: str | None = None,
# v2 新增字段 # v2 新增字段
intent: dict[str, Any] | None = None, intent: dict[str, object] | None = None,
quality_gate: dict[str, Any] | None = None, quality_gate: dict[str, object] | None = None,
execution_mode: str = "react", execution_mode: str = "react",
max_steps: int = 5, max_steps: int = 5,
evolution: dict[str, Any] | None = None, evolution: dict[str, object] | None = None,
# v3 新增字段SKILL.md 支持 # v3 新增字段SKILL.md 支持
skill_md_path: str | None = None, skill_md_path: str | None = None,
disclosure_level: int = 1, # 默认全量加载向后兼容0=概要模式需显式指定 disclosure_level: int = 1, # 默认全量加载向后兼容0=概要模式需显式指定
# v4 新增字段:依赖声明、能力标签 # v4 新增字段:依赖声明、能力标签
dependencies: list[dict[str, Any] | DependencyDecl] | None = None, dependencies: list[dict[str, object] | DependencyDecl] | None = None,
capabilities: list[str | dict[str, Any] | CapabilityTag] | None = None, capabilities: list[str | dict[str, object] | CapabilityTag] | None = None,
# v5 新增字段:对齐守卫 # v5 新增字段:对齐守卫
alignment: dict[str, Any] | None = None, alignment: dict[str, object] | None = None,
# v6 新增字段ReWOO fallback 策略YAML 可配置) # v6 新增字段ReWOO fallback 策略YAML 可配置)
fallback_strategies: list[str] | None = None, fallback_strategies: list[str] | None = None,
# v7 新增字段:激活前置条件 + 来源标记SkillHarness preconditions / provenance # v7 新增字段:激活前置条件 + 来源标记SkillHarness preconditions / provenance
@ -163,7 +162,7 @@ class SkillConfig(AgentConfig):
@staticmethod @staticmethod
def _parse_dependencies( def _parse_dependencies(
raw: list[dict[str, Any] | DependencyDecl], raw: list[dict[str, object] | DependencyDecl],
) -> list[DependencyDecl]: ) -> list[DependencyDecl]:
"""解析依赖声明列表,支持 dict 或 DependencyDecl 实例""" """解析依赖声明列表,支持 dict 或 DependencyDecl 实例"""
result: list[DependencyDecl] = [] result: list[DependencyDecl] = []
@ -178,7 +177,7 @@ class SkillConfig(AgentConfig):
@staticmethod @staticmethod
def _parse_capabilities( def _parse_capabilities(
raw: list[str | dict[str, Any] | CapabilityTag], raw: list[str | dict[str, object] | CapabilityTag],
) -> list[CapabilityTag]: ) -> list[CapabilityTag]:
"""解析能力标签列表,支持 str / dict / CapabilityTag 实例""" """解析能力标签列表,支持 str / dict / CapabilityTag 实例"""
result: list[CapabilityTag] = [] result: list[CapabilityTag] = []
@ -194,7 +193,7 @@ class SkillConfig(AgentConfig):
return result return result
@classmethod @classmethod
def from_dict(cls, data: dict[str, Any]) -> "SkillConfig": def from_dict(cls, data: dict[str, object]) -> "SkillConfig":
"""从字典创建配置""" """从字典创建配置"""
return cls( return cls(
name=data["name"], name=data["name"],
@ -241,7 +240,7 @@ class SkillConfig(AgentConfig):
) )
return cls.from_dict(data) return cls.from_dict(data)
def to_dict(self) -> dict[str, Any]: def to_dict(self) -> dict[str, object]:
"""序列化为字典,包含 v2 字段""" """序列化为字典,包含 v2 字段"""
d = super().to_dict() d = super().to_dict()
d["intent"] = { d["intent"] = {

View File

@ -10,7 +10,6 @@ import asyncio
import logging import logging
import uuid import uuid
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Any
from agentkit.core.protocol import TaskMessage from agentkit.core.protocol import TaskMessage
from agentkit.core.shared_workspace import SharedWorkspace from agentkit.core.shared_workspace import SharedWorkspace
@ -42,7 +41,7 @@ class PipelineStepResult:
step_name: str step_name: str
skill: str skill: str
status: str # "success", "failed", "skipped" status: str # "success", "failed", "skipped"
output: dict[str, Any] | None = None output: dict[str, object] | None = None
error: str | None = None error: str | None = None
duration_ms: float = 0 duration_ms: float = 0
@ -54,7 +53,7 @@ class PipelineResult:
pipeline_name: str pipeline_name: str
execution_id: str execution_id: str
steps: list[PipelineStepResult] steps: list[PipelineStepResult]
final_output: dict[str, Any] | None final_output: dict[str, object] | None
success: bool success: bool
total_duration_ms: float total_duration_ms: float
@ -79,7 +78,7 @@ class GEOPipeline:
name: str, name: str,
steps: list[PipelineStep], steps: list[PipelineStep],
skill_registry: SkillRegistry | None = None, skill_registry: SkillRegistry | None = None,
agent_pool: Any = None, agent_pool: object = None,
workspace: SharedWorkspace | None = None, workspace: SharedWorkspace | None = None,
): ):
self.name = name self.name = name
@ -92,9 +91,9 @@ class GEOPipeline:
@classmethod @classmethod
def from_config( def from_config(
cls, cls,
config: dict[str, Any], config: dict[str, object],
skill_registry: SkillRegistry | None = None, skill_registry: SkillRegistry | None = None,
agent_pool: Any = None, agent_pool: object = None,
workspace: SharedWorkspace | None = None, workspace: SharedWorkspace | None = None,
) -> GEOPipeline: ) -> GEOPipeline:
"""从 YAML 配置创建 Pipeline """从 YAML 配置创建 Pipeline
@ -136,7 +135,7 @@ class GEOPipeline:
workspace=workspace, workspace=workspace,
) )
async def execute(self, input_data: dict[str, Any]) -> PipelineResult: async def execute(self, input_data: dict[str, object]) -> PipelineResult:
"""执行 Pipeline """执行 Pipeline
Args: Args:
@ -150,7 +149,7 @@ class GEOPipeline:
start_time = time.monotonic() start_time = time.monotonic()
execution_id = str(uuid.uuid4())[:8] execution_id = str(uuid.uuid4())[:8]
step_results: list[PipelineStepResult] = [] step_results: list[PipelineStepResult] = []
step_outputs: dict[str, dict[str, Any]] = {} step_outputs: dict[str, dict[str, object]] = {}
# Store initial input in workspace # Store initial input in workspace
await self._workspace.write( await self._workspace.write(
@ -227,8 +226,8 @@ class GEOPipeline:
async def _execute_step( async def _execute_step(
self, self,
step: PipelineStep, step: PipelineStep,
input_data: dict[str, Any], input_data: dict[str, object],
step_outputs: dict[str, dict[str, Any]], step_outputs: dict[str, dict[str, object]],
execution_id: str, execution_id: str,
saga: SagaOrchestrator, saga: SagaOrchestrator,
) -> PipelineStepResult: ) -> PipelineStepResult:
@ -294,8 +293,8 @@ class GEOPipeline:
) )
async def _execute_skill( async def _execute_skill(
self, skill_name: str, input_data: dict[str, Any] self, skill_name: str, input_data: dict[str, object]
) -> dict[str, Any]: ) -> dict[str, object]:
"""执行 Skill""" """执行 Skill"""
if self._agent_pool: if self._agent_pool:
agent = self._agent_pool.get_agent(skill_name) agent = self._agent_pool.get_agent(skill_name)
@ -361,9 +360,9 @@ class GEOPipeline:
def _map_input( def _map_input(
self, self,
step: PipelineStep, step: PipelineStep,
input_data: dict[str, Any], input_data: dict[str, object],
step_outputs: dict[str, dict[str, Any]], step_outputs: dict[str, dict[str, object]],
) -> dict[str, Any]: ) -> dict[str, object]:
"""根据 input_mapping 构建步骤输入 """根据 input_mapping 构建步骤输入
映射格式: {"target_key": "source_path"} 映射格式: {"target_key": "source_path"}
@ -379,7 +378,7 @@ class GEOPipeline:
merged.update(step_outputs[dep]) merged.update(step_outputs[dep])
return merged return merged
mapped: dict[str, Any] = {} mapped: dict[str, object] = {}
for target_key, source_path in step.input_mapping.items(): for target_key, source_path in step.input_mapping.items():
value = self._resolve_mapping_path(source_path, input_data, step_outputs) value = self._resolve_mapping_path(source_path, input_data, step_outputs)
if value is not None: if value is not None:
@ -390,9 +389,9 @@ class GEOPipeline:
@staticmethod @staticmethod
def _resolve_mapping_path( def _resolve_mapping_path(
path: str, path: str,
input_data: dict[str, Any], input_data: dict[str, object],
step_outputs: dict[str, dict[str, Any]], step_outputs: dict[str, dict[str, object]],
) -> Any: ) -> object:
"""解析映射路径""" """解析映射路径"""
if path.startswith("$.input."): if path.startswith("$.input."):
key = path[len("$.input."):] key = path[len("$.input."):]
@ -416,7 +415,7 @@ class GEOPipeline:
return None return None
def _evaluate_condition( 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: ) -> bool:
"""评估条件表达式""" """评估条件表达式"""
import re import re
@ -435,9 +434,9 @@ class GEOPipeline:
def _build_final_output( def _build_final_output(
self, self,
step_outputs: dict[str, dict[str, Any]], step_outputs: dict[str, dict[str, object]],
input_data: dict[str, Any], input_data: dict[str, object],
) -> dict[str, Any]: ) -> dict[str, object]:
"""构建最终输出""" """构建最终输出"""
final = {"input": input_data} final = {"input": input_data}
for step_name, output in step_outputs.items(): for step_name, output in step_outputs.items():

View File

@ -8,9 +8,8 @@
import logging import logging
import re 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 from agentkit.skills.registry import SkillRegistry
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -28,7 +27,7 @@ class SkillPipeline:
def __init__( def __init__(
self, self,
name: str, name: str,
steps: list[dict[str, Any]], steps: list[dict[str, object]],
skill_registry: SkillRegistry | None = None, skill_registry: SkillRegistry | None = None,
): ):
""" """
@ -43,9 +42,9 @@ class SkillPipeline:
async def execute( async def execute(
self, self,
input_data: dict[str, Any], input_data: dict[str, object],
agent_factory: Callable[..., Coroutine] | None = None, agent_factory: Callable[..., Coroutine] | None = None,
) -> dict[str, Any]: ) -> dict[str, object]:
"""顺序执行 Pipeline 中所有步骤 """顺序执行 Pipeline 中所有步骤
Args: Args:
@ -57,8 +56,8 @@ class SkillPipeline:
包含 pipeline 名称各步骤结果和最终输出的字典 包含 pipeline 名称各步骤结果和最终输出的字典
""" """
success = True success = True
current_input: dict[str, Any] = input_data current_input: dict[str, object] = input_data
results: list[dict[str, Any]] = [] results: list[dict[str, object]] = []
for i, step_def in enumerate(self._steps): for i, step_def in enumerate(self._steps):
skill_name = step_def["skill_name"] skill_name = step_def["skill_name"]
@ -112,9 +111,9 @@ class SkillPipeline:
async def _execute_skill( async def _execute_skill(
self, self,
skill_name: str, skill_name: str,
input_data: dict[str, Any], input_data: dict[str, object],
agent_factory: Callable[..., Coroutine] | None = None, agent_factory: Callable[..., Coroutine] | None = None,
) -> dict[str, Any]: ) -> dict[str, object]:
"""执行单个 Skill """执行单个 Skill
优先使用 agent_factory其次通过 SkillRegistry 查找 Skill 并创建 Agent 执行 优先使用 agent_factory其次通过 SkillRegistry 查找 Skill 并创建 Agent 执行
@ -152,8 +151,8 @@ class SkillPipeline:
def _evaluate_condition( def _evaluate_condition(
self, self,
condition: str, condition: str,
current_input: dict[str, Any], current_input: dict[str, object],
results: list[dict[str, Any]], results: list[dict[str, object]],
) -> bool: ) -> bool:
"""评估简单条件表达式 """评估简单条件表达式
@ -180,10 +179,10 @@ class SkillPipeline:
return False return False
@staticmethod @staticmethod
def _resolve_path(path: str, data: dict[str, Any]) -> Any: def _resolve_path(path: str, data: dict[str, object]) -> object:
"""解析点号路径,如 'output.score'""" """解析点号路径,如 'output.score'"""
parts = path.split(".") parts = path.split(".")
obj: Any = data obj: object = data
for part in parts: for part in parts:
if isinstance(obj, dict): if isinstance(obj, dict):
obj = obj.get(part) obj = obj.get(part)
@ -193,15 +192,15 @@ class SkillPipeline:
def _map_input( def _map_input(
self, self,
current_input: dict[str, Any], current_input: dict[str, object],
mapping: dict[str, str], mapping: dict[str, str],
results: list[dict[str, Any]], results: list[dict[str, object]],
) -> dict[str, Any]: ) -> dict[str, object]:
"""根据映射规则将上一步输出映射到当前步骤输入 """根据映射规则将上一步输出映射到当前步骤输入
mapping 格式: {"target_key": "source.path"} mapping 格式: {"target_key": "source.path"}
""" """
mapped: dict[str, Any] = {} mapped: dict[str, object] = {}
for target_key, source_path in mapping.items(): for target_key, source_path in mapping.items():
value = self._resolve_path(source_path, current_input) value = self._resolve_path(source_path, current_input)
if value is not None: if value is not None:

View File

@ -7,7 +7,7 @@ from typing import TYPE_CHECKING
from agentkit.core.exceptions import SkillNotFoundError from agentkit.core.exceptions import SkillNotFoundError
from agentkit.skills.base import Skill, SkillConfig from agentkit.skills.base import Skill, SkillConfig
from agentkit.skills.schema import DependencyDecl, HealthCheckResult from agentkit.skills.schema import HealthCheckResult
if TYPE_CHECKING: if TYPE_CHECKING:
from agentkit.skills.pipeline import SkillPipeline from agentkit.skills.pipeline import SkillPipeline

View File

@ -8,7 +8,6 @@ from __future__ import annotations
import logging import logging
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Any
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -67,13 +66,13 @@ class SkillSpec:
description: str = "" description: str = ""
capabilities: list[CapabilityTag] = field(default_factory=list) capabilities: list[CapabilityTag] = field(default_factory=list)
dependencies: list[DependencyDecl] = field(default_factory=list) dependencies: list[DependencyDecl] = field(default_factory=list)
input_schema: dict[str, Any] | None = None input_schema: dict[str, object] | None = None
output_schema: dict[str, Any] | None = None output_schema: dict[str, object] | None = None
quality_gate: dict[str, Any] | None = None quality_gate: dict[str, object] | None = None
metadata: dict[str, Any] = field(default_factory=dict) metadata: dict[str, object] = field(default_factory=dict)
@classmethod @classmethod
def from_dict(cls, data: dict[str, Any]) -> SkillSpec: def from_dict(cls, data: dict[str, object]) -> SkillSpec:
"""从字典创建 SkillSpec""" """从字典创建 SkillSpec"""
capabilities = [ capabilities = [
CapabilityTag(**cap) if isinstance(cap, dict) else cap CapabilityTag(**cap) if isinstance(cap, dict) else cap
@ -95,9 +94,9 @@ class SkillSpec:
metadata=data.get("metadata", {}), 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, "name": self.name,
"version": self.version, "version": self.version,
"description": self.description, "description": self.description,
@ -165,7 +164,7 @@ class HealthCheckResult:
version_mismatches: list[str] = field(default_factory=list) version_mismatches: list[str] = field(default_factory=list)
warnings: 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 { return {
"skill_name": self.skill_name, "skill_name": self.skill_name,
"skill_version": self.skill_version, "skill_version": self.skill_version,

View File

@ -12,7 +12,6 @@ constraints/output_format从本地 SkillRegistry 检索。
from __future__ import annotations from __future__ import annotations
import logging import logging
from typing import Any
from agentkit.chat.skill_routing import collect_prompt_parts, format_preconditions_block from agentkit.chat.skill_routing import collect_prompt_parts, format_preconditions_block
from agentkit.core.exceptions import SkillNotFoundError from agentkit.core.exceptions import SkillNotFoundError
@ -36,7 +35,7 @@ class SkillDetailTool(Tool):
def __init__( def __init__(
self, self,
skill_registry: Any, skill_registry: object,
name: str = "skill_detail", name: str = "skill_detail",
description: str = ( description: str = (
"Load full instructions for a registered skill by name or keyword. " "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. " "and you need the complete instructions to execute the skill. "
"Returns the skill's identity, context, instructions, constraints, and output format." "Returns the skill's identity, context, instructions, constraints, and output format."
), ),
input_schema: dict[str, Any] | None = None, input_schema: dict[str, object] | None = None,
output_schema: dict[str, Any] | None = None, output_schema: dict[str, object] | None = None,
version: str = "1.0.0", version: str = "1.0.0",
tags: list[str] | None = None, tags: list[str] | None = None,
): ):
@ -97,7 +96,7 @@ class SkillDetailTool(Tool):
pass # 非精确匹配,降级到关键词搜索 pass # 非精确匹配,降级到关键词搜索
# 关键词搜索:匹配 skill 名称和描述 # 关键词搜索:匹配 skill 名称和描述
matches: list[Any] = [] matches: list[object] = []
query_lower = query.lower() query_lower = query.lower()
for skill in self._registry.list_skills(): for skill in self._registry.list_skills():
name = skill.name.lower() name = skill.name.lower()
@ -117,7 +116,7 @@ class SkillDetailTool(Tool):
return self._format_skill_full(matches[0]) return self._format_skill_full(matches[0])
@staticmethod @staticmethod
def _format_skill_full(skill: Any) -> dict[str, Any]: def _format_skill_full(skill: object) -> dict[str, object]:
"""格式化 skill 的完整 instructions 供 LLM 使用。""" """格式化 skill 的完整 instructions 供 LLM 使用。"""
config = skill.config config = skill.config
prompt_parts = collect_prompt_parts(config, with_headers=True) prompt_parts = collect_prompt_parts(config, with_headers=True)

View File

@ -8,7 +8,6 @@
import logging import logging
import re import re
from typing import Any
import yaml import yaml
@ -27,7 +26,7 @@ class SkillMdParser:
""" """
@staticmethod @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 文件 """解析 SKILL.md 文件
Note: Only H1 headings (# ) are treated as section delimiters. Note: Only H1 headings (# ) are treated as section delimiters.
@ -48,7 +47,7 @@ class SkillMdParser:
content = f.read() content = f.read()
# 提取 YAML frontmatter--- 标记之间) # 提取 YAML frontmatter--- 标记之间)
frontmatter: dict[str, Any] = {} frontmatter: dict[str, object] = {}
body = content body = content
if content.startswith("---"): if content.startswith("---"):
parts = content.split("---", 2) parts = content.split("---", 2)
@ -81,7 +80,7 @@ class SkillMdParser:
@staticmethod @staticmethod
def to_skill_config( def to_skill_config(
frontmatter: dict[str, Any], frontmatter: dict[str, object],
sections: dict[str, str], sections: dict[str, str],
file_path: str, file_path: str,
disclosure_level: int = 1, disclosure_level: int = 1,
@ -99,7 +98,7 @@ class SkillMdParser:
""" """
# 构建 IntentConfig # 构建 IntentConfig
intent_data = frontmatter.get("intent") or {} intent_data = frontmatter.get("intent") or {}
intent_config_data: dict[str, Any] = { intent_config_data: dict[str, object] = {
"keywords": intent_data.get("keywords", []), "keywords": intent_data.get("keywords", []),
"description": intent_data.get("description", ""), "description": intent_data.get("description", ""),
"examples": intent_data.get("examples", []), "examples": intent_data.get("examples", []),
@ -107,7 +106,7 @@ class SkillMdParser:
# 构建 QualityGateConfig # 构建 QualityGateConfig
qg_data = frontmatter.get("quality_gate") or {} 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", []), "required_fields": qg_data.get("required_fields", []),
"min_word_count": qg_data.get("min_word_count", 0), "min_word_count": qg_data.get("min_word_count", 0),
"max_retries": qg_data.get("max_retries", 0), "max_retries": qg_data.get("max_retries", 0),

View File

@ -2,8 +2,8 @@
from __future__ import annotations from __future__ import annotations
import logging import logging
from dataclasses import dataclass, field from dataclasses import dataclass
from typing import Any, ContextManager from typing import ContextManager
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -27,10 +27,10 @@ class NoOpSpan:
def __exit__(self, *args): def __exit__(self, *args):
pass pass
def set_attribute(self, key: str, value: Any) -> None: def set_attribute(self, key: str, value: object) -> None:
pass 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 pass
def record_exception(self, exception: Exception) -> None: def record_exception(self, exception: Exception) -> None:
@ -43,17 +43,17 @@ class NoOpSpan:
class NoOpTracer: class NoOpTracer:
"""No-op tracer when telemetry is disabled.""" """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() 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() return NoOpSpan()
class OTelSpan: class OTelSpan:
"""Wrapper around OpenTelemetry Span.""" """Wrapper around OpenTelemetry Span."""
def __init__(self, span: Any): def __init__(self, span: object):
self._span = span self._span = span
def __enter__(self): def __enter__(self):
@ -63,10 +63,10 @@ class OTelSpan:
def __exit__(self, *args): def __exit__(self, *args):
self._span.__exit__(*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) 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 {}) self._span.add_event(name, attributes or {})
def record_exception(self, exception: Exception) -> None: def record_exception(self, exception: Exception) -> None:
@ -79,14 +79,14 @@ class OTelSpan:
class OTelTracer: class OTelTracer:
"""Wrapper around OpenTelemetry Tracer.""" """Wrapper around OpenTelemetry Tracer."""
def __init__(self, tracer: Any): def __init__(self, tracer: object):
self._tracer = tracer 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) span = self._tracer.start_span(name, attributes=attributes)
return OTelSpan(span) 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) span = self._tracer.start_as_current_span(name, attributes=attributes)
return OTelSpan(span) return OTelSpan(span)

View File

@ -3,7 +3,7 @@
import logging import logging
import time import time
from functools import wraps from functools import wraps
from typing import Any, Callable from typing import Callable
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -67,7 +67,7 @@ def get_tracer(name: str = "fischer.agentkit"):
def start_span( def start_span(
name: str, name: str,
kind: Any = None, kind: object = None,
attributes: dict | None = None, attributes: dict | None = None,
): ):
"""Start a span — returns no-op span if OTel not installed. """Start a span — returns no-op span if OTel not installed.

View File

@ -7,7 +7,7 @@ The LLM calls this tool to signal "I'm done planning, move to building".
from __future__ import annotations from __future__ import annotations
import logging import logging
from typing import TYPE_CHECKING, Any from typing import TYPE_CHECKING
from agentkit.tools.base import Tool from agentkit.tools.base import Tool
@ -55,7 +55,7 @@ class AdvancePhaseTool(Tool):
) )
self._engine = engine 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). # Capture previous phase before transition (engine is single-threaded per request).
previous = self._engine.current_phase previous = self._engine.current_phase
new_phase = self._engine.advance_phase() new_phase = self._engine.advance_phase()

View File

@ -1,7 +1,5 @@
"""AgentTool - 将 Agent 包装为 Tool""" """AgentTool - 将 Agent 包装为 Tool"""
from typing import Any
from agentkit.tools.base import Tool from agentkit.tools.base import Tool
@ -36,7 +34,7 @@ class AgentTool(Tool):
self.timeout_seconds = timeout_seconds self.timeout_seconds = timeout_seconds
self._dispatcher = None self._dispatcher = None
def set_dispatcher(self, dispatcher: Any) -> "AgentTool": def set_dispatcher(self, dispatcher: object) -> "AgentTool":
"""注入 Dispatcher""" """注入 Dispatcher"""
self._dispatcher = dispatcher self._dispatcher = dispatcher
return self return self

View File

@ -10,7 +10,6 @@ from __future__ import annotations
import asyncio import asyncio
import logging import logging
import uuid import uuid
from typing import Any
from agentkit.tools.base import Tool from agentkit.tools.base import Tool
@ -42,12 +41,12 @@ class AskHumanTool(Tool):
# request_id -> asyncio.Future # request_id -> asyncio.Future
self._pending_replies: dict[str, asyncio.Future] | None = None self._pending_replies: dict[str, asyncio.Future] | None = None
# Callback to push question to client # Callback to push question to client
self._ask_callback: Any = None self._ask_callback: object = None
def configure( def configure(
self, self,
pending_replies: dict[str, asyncio.Future] | None = None, pending_replies: dict[str, asyncio.Future] | None = None,
ask_callback: Any = None, ask_callback: object = None,
) -> None: ) -> None:
"""Configure the tool with WebSocket communication channels. """Configure the tool with WebSocket communication channels.
@ -61,7 +60,7 @@ class AskHumanTool(Tool):
self._ask_callback = ask_callback self._ask_callback = ask_callback
@property @property
def parameters(self) -> dict[str, Any]: def parameters(self) -> dict[str, object]:
return { return {
"type": "object", "type": "object",
"properties": { "properties": {
@ -78,7 +77,7 @@ class AskHumanTool(Tool):
"required": ["question"], "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. """Ask the user a question and wait for their reply.
Args: Args:

View File

@ -7,7 +7,6 @@
import json import json
import logging import logging
import urllib.parse import urllib.parse
from typing import Any
import httpx import httpx
@ -30,8 +29,8 @@ class BaiduSearchTool(Tool):
self, self,
name: str = "baidu_search", name: str = "baidu_search",
description: str = "执行百度搜索,返回搜索结果列表", description: str = "执行百度搜索,返回搜索结果列表",
input_schema: dict[str, Any] | None = None, input_schema: dict[str, object] | None = None,
output_schema: dict[str, Any] | None = None, output_schema: dict[str, object] | None = None,
version: str = "1.0.0", version: str = "1.0.0",
tags: list[str] | None = None, tags: list[str] | None = None,
api_key: str | None = None, api_key: str | None = None,
@ -49,7 +48,7 @@ class BaiduSearchTool(Tool):
self._api_url = api_url self._api_url = api_url
@staticmethod @staticmethod
def _default_input_schema() -> dict[str, Any]: def _default_input_schema() -> dict[str, object]:
return { return {
"type": "object", "type": "object",
"properties": { "properties": {
@ -67,7 +66,7 @@ class BaiduSearchTool(Tool):
} }
@staticmethod @staticmethod
def _default_output_schema() -> dict[str, Any]: def _default_output_schema() -> dict[str, object]:
return { return {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@ -2,7 +2,6 @@
import time import time
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import Any
import jsonschema import jsonschema
@ -23,7 +22,7 @@ class ToolValidationError(Exception):
message: str, message: str,
*, *,
error_code: str = "schema_mismatch", error_code: str = "schema_mismatch",
details: dict[str, Any] | None = None, details: dict[str, object] | None = None,
) -> None: ) -> None:
super().__init__(message) super().__init__(message)
self.error_code = error_code self.error_code = error_code
@ -40,8 +39,8 @@ class Tool(ABC):
self, self,
name: str, name: str,
description: str, description: str,
input_schema: dict[str, Any] | None = None, input_schema: dict[str, object] | None = None,
output_schema: dict[str, Any] | None = None, output_schema: dict[str, object] | None = None,
version: str = "1.0.0", version: str = "1.0.0",
tags: list[str] | None = None, tags: list[str] | None = None,
): ):
@ -99,7 +98,7 @@ class Tool(ABC):
finally: finally:
_span_cm.__exit__(None, None, None) _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。 """校验 kwargs 是否符合 self.input_schema。
- input_schema=None 跳过(向后兼容,旧工具无 schema) - input_schema=None 跳过(向后兼容,旧工具无 schema)

View File

@ -16,7 +16,6 @@ from __future__ import annotations
import asyncio import asyncio
import logging import logging
from typing import Any
import httpx import httpx
@ -140,7 +139,7 @@ class BitableTool(Tool):
if self._client is not None and not self._client.is_closed: if self._client is not None and not self._client.is_closed:
await self._client.aclose() await self._client.aclose()
async def execute(self, **kwargs) -> dict[str, Any]: async def execute(self, **kwargs) -> dict[str, object]:
action = kwargs.get("action") action = kwargs.get("action")
handlers = { handlers = {
"create_table": self._create_table, "create_table": self._create_table,
@ -169,7 +168,7 @@ class BitableTool(Tool):
# create_table # 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") table_name = kwargs.get("table_name")
if not table_name: if not table_name:
return {"success": False, "error": "Missing required field: table_name"} return {"success": False, "error": "Missing required field: table_name"}
@ -186,7 +185,7 @@ class BitableTool(Tool):
# import_excel # 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_path = kwargs.get("file_path")
file_url = kwargs.get("file_url") file_url = kwargs.get("file_url")
if not file_path and not file_url: if not file_path and not file_url:
@ -201,13 +200,13 @@ class BitableTool(Tool):
if not sheets: if not sheets:
return {"success": False, "error": "Excel file has no sheets with data"} 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: for sheet in sheets:
result = await self._import_sheet(sheet) result = await self._import_sheet(sheet)
results.append(result) results.append(result)
return {"success": True, "sheets": results} 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.""" """Create a bitable table from a parsed sheet and upsert all rows."""
client = await self._get_client() client = await self._get_client()
@ -253,13 +252,13 @@ class BitableTool(Tool):
} }
async def _batch_create_records( async def _batch_create_records(
self, table_id: str, records: list[dict[str, Any]] self, table_id: str, records: list[dict[str, object]]
) -> dict[str, Any]: ) -> dict[str, object]:
"""Create records in batches via POST /tables/{id}/records.""" """Create records in batches via POST /tables/{id}/records."""
client = await self._get_client() client = await self._get_client()
total = len(records) total = len(records)
successful = 0 successful = 0
errors: list[dict[str, Any]] = [] errors: list[dict[str, object]] = []
for start in range(0, total, BATCH_SIZE): for start in range(0, total, BATCH_SIZE):
batch = records[start : start + BATCH_SIZE] batch = records[start : start + BATCH_SIZE]
@ -292,7 +291,7 @@ class BitableTool(Tool):
# import_database # 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") conn_str = kwargs.get("connection_string")
table_names = kwargs.get("table_names") table_names = kwargs.get("table_names")
if not conn_str: if not conn_str:
@ -300,7 +299,7 @@ class BitableTool(Tool):
if not table_names: if not table_names:
return {"success": False, "error": "Missing required field: 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: for src_table in table_names:
try: try:
# Offload sync DB reflection to thread pool (P2 #21-23). # 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)}) results.append({"table_name": src_table, "success": False, "error": str(e)})
return {"success": True, "tables": results} 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.""" """Create a bitable table from reflected DB data and upsert rows."""
client = await self._get_client() client = await self._get_client()
table_name = reflected["table_name"] table_name = reflected["table_name"]
@ -376,7 +375,7 @@ class BitableTool(Tool):
# collect_api # 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") table_id = kwargs.get("table_id")
records = kwargs.get("records") records = kwargs.get("records")
field_mapping = kwargs.get("field_mapping") field_mapping = kwargs.get("field_mapping")
@ -403,7 +402,7 @@ class BitableTool(Tool):
# upsert_records # 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") table_id = kwargs.get("table_id")
records = kwargs.get("records") records = kwargs.get("records")
pk_field_id = kwargs.get("primary_key_field_id") pk_field_id = kwargs.get("primary_key_field_id")
@ -421,13 +420,13 @@ class BitableTool(Tool):
return {"success": True, **result} return {"success": True, **result}
async def _batch_upsert( async def _batch_upsert(
self, table_id: str, records: list[dict[str, Any]], pk_field_id: str self, table_id: str, records: list[dict[str, object]], pk_field_id: str
) -> dict[str, Any]: ) -> dict[str, object]:
"""Upsert records in batches of BATCH_SIZE via POST /tables/{id}/upsert.""" """Upsert records in batches of BATCH_SIZE via POST /tables/{id}/upsert."""
client = await self._get_client() client = await self._get_client()
total = len(records) total = len(records)
successful = 0 successful = 0
errors: list[dict[str, Any]] = [] errors: list[dict[str, object]] = []
for start in range(0, total, BATCH_SIZE): for start in range(0, total, BATCH_SIZE):
batch = records[start : start + BATCH_SIZE] batch = records[start : start + BATCH_SIZE]
@ -464,13 +463,13 @@ class BitableTool(Tool):
# query_records # 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") table_id = kwargs.get("table_id")
if not table_id: if not table_id:
return {"success": False, "error": "Missing required field: table_id"} return {"success": False, "error": "Missing required field: table_id"}
client = await self._get_client() client = await self._get_client()
params: dict[str, Any] = {} params: dict[str, object] = {}
if kwargs.get("cursor"): if kwargs.get("cursor"):
params["cursor"] = kwargs["cursor"] params["cursor"] = kwargs["cursor"]
if kwargs.get("limit"): if kwargs.get("limit"):

View File

@ -3,7 +3,7 @@
from __future__ import annotations from __future__ import annotations
import logging import logging
from typing import TYPE_CHECKING, Any from typing import TYPE_CHECKING
from agentkit.tools.base import Tool from agentkit.tools.base import Tool
@ -24,8 +24,8 @@ class RunTestsTool(Tool):
self, self,
name: str = "run_tests", name: str = "run_tests",
description: str = "Run project tests to verify code changes. Executes pytest and linting commands.", description: str = "Run project tests to verify code changes. Executes pytest and linting commands.",
input_schema: dict[str, Any] | None = None, input_schema: dict[str, object] | None = None,
output_schema: dict[str, Any] | None = None, output_schema: dict[str, object] | None = None,
version: str = "1.0.0", version: str = "1.0.0",
tags: list[str] | None = None, tags: list[str] | None = None,
working_dir: str | None = None, working_dir: str | None = None,
@ -45,7 +45,7 @@ class RunTestsTool(Tool):
self._max_retries = max_retries self._max_retries = max_retries
@staticmethod @staticmethod
def _default_input_schema() -> dict[str, Any]: def _default_input_schema() -> dict[str, object]:
return { return {
"type": "object", "type": "object",
"properties": { "properties": {
@ -112,8 +112,8 @@ class ToolSearchTool(Tool):
"Returns full descriptions (name, description, parameters) of matching tools. " "Returns full descriptions (name, description, parameters) of matching tools. "
"Use this when you need details about a tool that was only listed by name." "Use this when you need details about a tool that was only listed by name."
), ),
input_schema: dict[str, Any] | None = None, input_schema: dict[str, object] | None = None,
output_schema: dict[str, Any] | None = None, output_schema: dict[str, object] | None = None,
version: str = "1.0.0", version: str = "1.0.0",
tags: list[str] | None = None, tags: list[str] | None = None,
top_k: int = 5, top_k: int = 5,
@ -176,7 +176,7 @@ class ToolSearchTool(Tool):
} }
@staticmethod @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.""" """Format a tool's full description for the LLM."""
return { return {
"name": tool.name, "name": tool.name,

View File

@ -13,7 +13,6 @@ from __future__ import annotations
import re import re
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Any
from agentkit.calendar.models import ReminderRule from agentkit.calendar.models import ReminderRule
from agentkit.calendar.service import CalendarService from agentkit.calendar.service import CalendarService
@ -321,14 +320,14 @@ class CalendarTool(Tool):
""" """
self._default_user_id = user_id 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.""" """Resolve user_id: prefer caller-provided, fall back to default."""
provided = kwargs.get("user_id") provided = kwargs.get("user_id")
if provided and isinstance(provided, str) and provided.strip(): if provided and isinstance(provided, str) and provided.strip():
return provided return provided
return self._default_user_id 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") action = kwargs.get("action")
if action == "create_event": if action == "create_event":
@ -345,7 +344,7 @@ class CalendarTool(Tool):
# create_event # 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) user_id = self._resolve_user_id(kwargs)
title = kwargs.get("title") title = kwargs.get("title")
start_time = kwargs.get("start_time") start_time = kwargs.get("start_time")
@ -494,7 +493,7 @@ class CalendarTool(Tool):
# query_events # 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) user_id = self._resolve_user_id(kwargs)
if not user_id: if not user_id:
return {"success": False, "error": "Missing required field: user_id"} return {"success": False, "error": "Missing required field: user_id"}
@ -519,7 +518,7 @@ class CalendarTool(Tool):
# update_event # 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") event_id = kwargs.get("event_id")
user_id = self._resolve_user_id(kwargs) user_id = self._resolve_user_id(kwargs)
if not event_id: if not event_id:
@ -536,7 +535,7 @@ class CalendarTool(Tool):
# Build fields dict from updatable params (only those explicitly provided) # Build fields dict from updatable params (only those explicitly provided)
updatable = ["title", "description", "location", "is_all_day"] updatable = ["title", "description", "location", "is_all_day"]
fields: dict[str, Any] = {} fields: dict[str, object] = {}
for key in updatable: for key in updatable:
if key in kwargs and kwargs[key] is not None: if key in kwargs and kwargs[key] is not None:
fields[key] = kwargs[key] fields[key] = kwargs[key]
@ -565,7 +564,7 @@ class CalendarTool(Tool):
# delete_event # 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") event_id = kwargs.get("event_id")
user_id = self._resolve_user_id(kwargs) user_id = self._resolve_user_id(kwargs)
if not event_id: if not event_id:

View File

@ -9,7 +9,6 @@
import asyncio import asyncio
import json import json
import logging import logging
from typing import Any
from agentkit.tools.base import Tool from agentkit.tools.base import Tool
@ -137,7 +136,7 @@ class DynamicSelector(Tool):
description: str, description: str,
tools: list[Tool], tools: list[Tool],
mode: str = "keyword", mode: str = "keyword",
llm_client: Any = None, llm_client: object = None,
version: str = "1.0.0", version: str = "1.0.0",
tags: list[str] | None = None, tags: list[str] | None = None,
): ):

View File

@ -10,7 +10,7 @@ from __future__ import annotations
import asyncio import asyncio
import base64 import base64
import logging import logging
from typing import Any, Callable, Awaitable from typing import Callable, Awaitable
import httpx import httpx
@ -62,8 +62,8 @@ class ComputerUseTool(Tool):
self, self,
name: str = "computer_use", name: str = "computer_use",
description: str = "Anthropic Computer Use API 集成,支持截屏识别和 UI 操作", description: str = "Anthropic Computer Use API 集成,支持截屏识别和 UI 操作",
input_schema: dict[str, Any] | None = None, input_schema: dict[str, object] | None = None,
output_schema: dict[str, Any] | None = None, output_schema: dict[str, object] | None = None,
version: str = "1.0.0", version: str = "1.0.0",
tags: list[str] | None = None, tags: list[str] | None = None,
api_key: str | None = None, api_key: str | None = None,
@ -71,7 +71,7 @@ class ComputerUseTool(Tool):
api_base_url: str = _ANTHROPIC_COMPUTER_USE_URL, api_base_url: str = _ANTHROPIC_COMPUTER_USE_URL,
session_factory: type[ComputerUseSession] | None = None, session_factory: type[ComputerUseSession] | None = None,
recorder: ComputerUseRecorder | 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, max_retries: int = 1,
request_timeout: float = 30.0, request_timeout: float = 30.0,
): ):
@ -112,7 +112,7 @@ class ComputerUseTool(Tool):
await self._http_client.aclose() await self._http_client.aclose()
@staticmethod @staticmethod
def _default_input_schema() -> dict[str, Any]: def _default_input_schema() -> dict[str, object]:
return { return {
"type": "object", "type": "object",
"properties": { "properties": {
@ -177,7 +177,7 @@ class ComputerUseTool(Tool):
} }
@staticmethod @staticmethod
def _default_output_schema() -> dict[str, Any]: def _default_output_schema() -> dict[str, object]:
return { return {
"type": "object", "type": "object",
"properties": { "properties": {
@ -254,7 +254,7 @@ class ComputerUseTool(Tool):
self, self,
session: ComputerUseSession, session: ComputerUseSession,
action: str, action: str,
params: dict[str, Any], params: dict[str, object],
) -> ActionResult: ) -> ActionResult:
"""带降级链的操作执行 """带降级链的操作执行
@ -318,14 +318,14 @@ class ComputerUseTool(Tool):
self, self,
session: ComputerUseSession, session: ComputerUseSession,
action: str, action: str,
params: dict[str, Any], params: dict[str, object],
) -> ActionResult: ) -> ActionResult:
"""调用 Anthropic Computer Use API """调用 Anthropic Computer Use API
通过 Anthropic Messages API 发送 computer_use_tool 请求 通过 Anthropic Messages API 发送 computer_use_tool 请求
""" """
# 构造 computer_use 工具调用参数 # 构造 computer_use 工具调用参数
tool_input: dict[str, Any] = {"action": action} tool_input: dict[str, object] = {"action": action}
if action == "click": if action == "click":
tool_input["coordinate"] = [params.get("x", 0), params.get("y", 0)] tool_input["coordinate"] = [params.get("x", 0), params.get("y", 0)]
elif action == "type": elif action == "type":
@ -354,7 +354,7 @@ class ComputerUseTool(Tool):
screenshot_b64 = screenshot_result.screenshot_base64 screenshot_b64 = screenshot_result.screenshot_base64
# 构造 API 请求 # 构造 API 请求
content_blocks: list[dict[str, Any]] = [] content_blocks: list[dict[str, object]] = []
if screenshot_b64: if screenshot_b64:
content_blocks.append({ content_blocks.append({
"type": "image", "type": "image",
@ -437,7 +437,7 @@ class ComputerUseTool(Tool):
metadata={"api_response": data}, 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: Returns:
@ -459,9 +459,9 @@ class ComputerUseTool(Tool):
return f"drag 操作需要 {', '.join(missing)} 参数" return f"drag 操作需要 {', '.join(missing)} 参数"
return None 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, "success": result.success,
"action": result.action, "action": result.action,
"output": result.output, "output": result.output,
@ -482,9 +482,9 @@ class ComputerUseTool(Tool):
action: str, action: str,
error: str, error: str,
fallback: str = "", fallback: str = "",
) -> dict[str, Any]: ) -> dict[str, object]:
"""构造错误结果""" """构造错误结果"""
result: dict[str, Any] = { result: dict[str, object] = {
"success": False, "success": False,
"action": action, "action": action,
"error": error, "error": error,

View File

@ -11,7 +11,6 @@ import logging
import time import time
from dataclasses import asdict, dataclass, field from dataclasses import asdict, dataclass, field
from pathlib import Path from pathlib import Path
from typing import Any
from agentkit.tools.computer_use_session import ComputerUseSession, ActionResult from agentkit.tools.computer_use_session import ComputerUseSession, ActionResult
@ -27,17 +26,17 @@ class ActionRecord:
timestamp: float timestamp: float
action: str action: str
params: dict[str, Any] = field(default_factory=dict) params: dict[str, object] = field(default_factory=dict)
success: bool = False success: bool = False
output: str = "" output: str = ""
error: str = "" error: str = ""
screenshot_path: str = "" screenshot_path: str = ""
def to_dict(self) -> dict[str, Any]: def to_dict(self) -> dict[str, object]:
return asdict(self) return asdict(self)
@classmethod @classmethod
def from_dict(cls, data: dict[str, Any]) -> ActionRecord: def from_dict(cls, data: dict[str, object]) -> ActionRecord:
return cls(**data) return cls(**data)
@ -69,7 +68,7 @@ class ComputerUseRecorder:
def record( def record(
self, self,
action: str, action: str,
params: dict[str, Any], params: dict[str, object],
result: ActionResult, result: ActionResult,
screenshot_path: str = "", screenshot_path: str = "",
) -> ActionRecord: ) -> ActionRecord:
@ -220,7 +219,7 @@ class ComputerUseRecorder:
"""失败操作数""" """失败操作数"""
return sum(1 for r in self._records if not r.success) 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 { return {
"total_actions": self.total_actions, "total_actions": self.total_actions,

View File

@ -7,7 +7,6 @@ handle documents via function calling. U6 implements "create"; U9 adds "read".
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from typing import Any
from agentkit.documents.service import DocumentService from agentkit.documents.service import DocumentService
from agentkit.memory.document_loader import DocumentLoader from agentkit.memory.document_loader import DocumentLoader
@ -81,7 +80,7 @@ class DocumentTool(Tool):
self._service = service self._service = service
self._loader = loader or DocumentLoader() 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") action = kwargs.get("action", "create")
if action == "read": if action == "read":
@ -90,7 +89,7 @@ class DocumentTool(Tool):
return await self._execute_create(**kwargs) return await self._execute_create(**kwargs)
return {"success": False, "error": f"Unknown action: {action!r} (use 'create' or 'read')"} 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", "") format_key = kwargs.get("format", "")
content = kwargs.get("content", "") content = kwargs.get("content", "")
conversation_id = kwargs.get("conversation_id", "") conversation_id = kwargs.get("conversation_id", "")
@ -129,7 +128,7 @@ class DocumentTool(Tool):
except Exception as e: except Exception as e:
return {"success": False, "error": f"Document creation failed: {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") file_path = kwargs.get("filename") or kwargs.get("content")
if not file_path: if not file_path:
return {"success": False, "error": "filename (file path) is required for read"} return {"success": False, "error": "filename (file path) is required for read"}

View File

@ -12,7 +12,6 @@ from __future__ import annotations
import logging import logging
from pathlib import Path from pathlib import Path
from typing import Any
from agentkit.tools.base import Tool from agentkit.tools.base import Tool
from agentkit.tools.symbol_extractor import ( from agentkit.tools.symbol_extractor import (
@ -40,8 +39,8 @@ class ReadFileTool(Tool):
self, self,
name: str = "read_file", name: str = "read_file",
description: str | None = None, description: str | None = None,
input_schema: dict[str, Any] | None = None, input_schema: dict[str, object] | None = None,
output_schema: dict[str, Any] | None = None, output_schema: dict[str, object] | None = None,
version: str = "1.0.0", version: str = "1.0.0",
tags: list[str] | None = None, tags: list[str] | None = None,
): ):
@ -63,7 +62,7 @@ class ReadFileTool(Tool):
) )
@staticmethod @staticmethod
def _default_input_schema() -> dict[str, Any]: def _default_input_schema() -> dict[str, object]:
return { return {
"type": "object", "type": "object",
"properties": { "properties": {
@ -95,7 +94,7 @@ class ReadFileTool(Tool):
} }
@staticmethod @staticmethod
def _default_output_schema() -> dict[str, Any]: def _default_output_schema() -> dict[str, object]:
return { return {
"type": "object", "type": "object",
"properties": { "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") raw_path = kwargs.get("path")
if not raw_path: if not raw_path:
return self._error("`path` is required") return self._error("`path` is required")
@ -235,8 +234,8 @@ class ReadFileTool(Tool):
@staticmethod @staticmethod
def _error( def _error(
message: str, *, path: str | None = None, detail: str | None = None message: str, *, path: str | None = None, detail: str | None = None
) -> dict[str, Any]: ) -> dict[str, object]:
result: dict[str, Any] = { result: dict[str, object] = {
"content": "", "content": "",
"is_error": True, "is_error": True,
"error": message, "error": message,

View File

@ -1,7 +1,7 @@
"""FunctionTool - 将普通 Python 函数包装为 Tool""" """FunctionTool - 将普通 Python 函数包装为 Tool"""
import inspect import inspect
from typing import Any, Callable, Awaitable from typing import Callable, Awaitable
from agentkit.tools.base import Tool from agentkit.tools.base import Tool
@ -17,8 +17,8 @@ class FunctionTool(Tool):
name: str, name: str,
description: str, description: str,
func: Callable[..., Awaitable[dict]] | Callable[..., dict], func: Callable[..., Awaitable[dict]] | Callable[..., dict],
input_schema: dict[str, Any] | None = None, input_schema: dict[str, object] | None = None,
output_schema: dict[str, Any] | None = None, output_schema: dict[str, object] | None = None,
version: str = "1.0.0", version: str = "1.0.0",
tags: list[str] | None = None, tags: list[str] | None = None,
): ):

View File

@ -6,7 +6,6 @@
""" """
import logging import logging
from typing import Any
from agentkit.tools.base import Tool from agentkit.tools.base import Tool
@ -20,7 +19,7 @@ class HeadroomRetrieveTool(Tool):
压缩内容中包含 <!-- CCR:hash=xxx --> 标记LLM 可使用该哈希值检索 压缩内容中包含 <!-- CCR:hash=xxx --> 标记LLM 可使用该哈希值检索
""" """
def __init__(self, compressor: Any): def __init__(self, compressor: object):
super().__init__( super().__init__(
name="headroom_retrieve", name="headroom_retrieve",
description=( description=(

View File

@ -2,7 +2,6 @@
import json import json
import logging import logging
from typing import Any
from agentkit.tools.base import Tool from agentkit.tools.base import Tool
@ -20,9 +19,9 @@ class MCPTool(Tool):
self, self,
name: str, name: str,
description: str, description: str,
client: Any, client: object,
input_schema: dict[str, Any] | None = None, input_schema: dict[str, object] | None = None,
output_schema: dict[str, Any] | None = None, output_schema: dict[str, object] | None = None,
version: str = "1.0.0", version: str = "1.0.0",
tags: list[str] | None = None, tags: list[str] | None = None,
): ):

View File

@ -14,7 +14,6 @@ from __future__ import annotations
import re import re
from datetime import datetime, timezone from datetime import datetime, timezone
from typing import Any
from agentkit.memory.profile import MemoryFile, MemoryStore from agentkit.memory.profile import MemoryFile, MemoryStore
from agentkit.tools.base import Tool from agentkit.tools.base import Tool
@ -75,7 +74,7 @@ class MemoryTool(Tool):
) )
self._store = memory_store self._store = memory_store
async def execute(self, **kwargs) -> dict[str, Any]: async def execute(self, **kwargs) -> dict[str, object]:
action = kwargs.get("action", "") action = kwargs.get("action", "")
file_key = kwargs.get("file", "") file_key = kwargs.get("file", "")
@ -150,7 +149,7 @@ class MemoryTool(Tool):
async def _update_soul( async def _update_soul(
self, mf: MemoryFile, section: str, content: str, reason: str self, mf: MemoryFile, section: str, content: str, reason: str
) -> dict[str, Any]: ) -> dict[str, object]:
"""执行 SOUL 动态更新,带版本追踪和更新历史. """执行 SOUL 动态更新,带版本追踪和更新历史.
采用原子写入策略先在内存中构建完整内容再一次性写入文件 采用原子写入策略先在内存中构建完整内容再一次性写入文件

View File

@ -8,7 +8,6 @@ from __future__ import annotations
import re import re
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import Enum from enum import Enum
from typing import Any
class ErrorType(Enum): class ErrorType(Enum):
@ -49,7 +48,7 @@ class ParsedOutput:
raw_output: str = "" raw_output: str = ""
suggestions: list[str] = field(default_factory=list) suggestions: list[str] = field(default_factory=list)
def to_dict(self) -> dict[str, Any]: def to_dict(self) -> dict[str, object]:
return { return {
"exit_code": self.exit_code, "exit_code": self.exit_code,
"is_error": self.is_error, "is_error": self.is_error,

View File

@ -1,7 +1,6 @@
"""ToolRegistry - 工具注册中心""" """ToolRegistry - 工具注册中心"""
import logging import logging
from typing import Any
from agentkit.core.exceptions import ToolNotFoundError from agentkit.core.exceptions import ToolNotFoundError
from agentkit.tools.base import Tool from agentkit.tools.base import Tool

View File

@ -6,7 +6,6 @@ SchemaGenerateTool: 生成 Schema.org JSON-LD 标记
import json import json
import logging import logging
from typing import Any
import httpx import httpx
@ -47,8 +46,8 @@ class SchemaExtractTool(Tool):
self, self,
name: str = "schema_extract", name: str = "schema_extract",
description: str = "从网页 HTML 中提取结构化数据JSON-LD、Microdata、RDFa 等)", description: str = "从网页 HTML 中提取结构化数据JSON-LD、Microdata、RDFa 等)",
input_schema: dict[str, Any] | None = None, input_schema: dict[str, object] | None = None,
output_schema: dict[str, Any] | None = None, output_schema: dict[str, object] | None = None,
version: str = "1.0.0", version: str = "1.0.0",
tags: list[str] | None = None, tags: list[str] | None = None,
): ):
@ -62,7 +61,7 @@ class SchemaExtractTool(Tool):
) )
@staticmethod @staticmethod
def _default_input_schema() -> dict[str, Any]: def _default_input_schema() -> dict[str, object]:
return { return {
"type": "object", "type": "object",
"properties": { "properties": {
@ -81,7 +80,7 @@ class SchemaExtractTool(Tool):
} }
@staticmethod @staticmethod
def _default_output_schema() -> dict[str, Any]: def _default_output_schema() -> dict[str, object]:
return { return {
"type": "object", "type": "object",
"properties": { "properties": {
@ -164,7 +163,7 @@ class SchemaExtractTool(Tool):
) )
# 整理结果 # 整理结果
schemas: list[dict[str, Any]] = [] schemas: list[dict[str, object]] = []
for fmt in formats: for fmt in formats:
items = data.get(fmt, []) items = data.get(fmt, [])
if items: if items:
@ -206,8 +205,8 @@ class SchemaGenerateTool(Tool):
self, self,
name: str = "schema_generate", name: str = "schema_generate",
description: str = "生成 Schema.org JSON-LD 结构化数据标记", description: str = "生成 Schema.org JSON-LD 结构化数据标记",
input_schema: dict[str, Any] | None = None, input_schema: dict[str, object] | None = None,
output_schema: dict[str, Any] | None = None, output_schema: dict[str, object] | None = None,
version: str = "1.0.0", version: str = "1.0.0",
tags: list[str] | None = None, tags: list[str] | None = None,
): ):
@ -221,7 +220,7 @@ class SchemaGenerateTool(Tool):
) )
@staticmethod @staticmethod
def _default_input_schema() -> dict[str, Any]: def _default_input_schema() -> dict[str, object]:
return { return {
"type": "object", "type": "object",
"properties": { "properties": {
@ -238,7 +237,7 @@ class SchemaGenerateTool(Tool):
} }
@staticmethod @staticmethod
def _default_output_schema() -> dict[str, Any]: def _default_output_schema() -> dict[str, object]:
return { return {
"type": "object", "type": "object",
"properties": { "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无需外部依赖""" """手动构建 JSON-LD无需外部依赖"""
jsonld_obj: dict[str, Any] = { jsonld_obj: dict[str, object] = {
"@context": "https://schema.org", "@context": "https://schema.org",
"@type": schema_type, "@type": schema_type,
} }
jsonld_obj.update(properties) jsonld_obj.update(properties)
return json.dumps(jsonld_obj, ensure_ascii=False, indent=2) 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带验证""" """使用 pydantic-schemaorg 生成 JSON-LD带验证"""
if not _PYDANTIC_SCHEMAORG_AVAILABLE: if not _PYDANTIC_SCHEMAORG_AVAILABLE:
return None return None
@ -278,7 +277,7 @@ class SchemaGenerateTool(Tool):
else: else:
return None return None
jsonld_obj: dict[str, Any] = { jsonld_obj: dict[str, object] = {
"@context": "https://schema.org", "@context": "https://schema.org",
"@type": schema_type, "@type": schema_type,
} }

View File

@ -12,7 +12,6 @@ from __future__ import annotations
import math import math
import re import re
from collections import Counter from collections import Counter
from typing import Any
from agentkit.tools.base import Tool from agentkit.tools.base import Tool
@ -92,7 +91,7 @@ class ToolSearchIndex:
"""Convert a tool's searchable metadata into a single text document.""" """Convert a tool's searchable metadata into a single text document."""
parts: list[str] = [str(tool.name), str(tool.description)] 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: if schema:
props = schema.get("properties", {}) props = schema.get("properties", {})
for pname, pinfo in props.items(): for pname, pinfo in props.items():

View File

@ -14,7 +14,7 @@ import shlex
import time import time
import uuid import uuid
from collections import deque from collections import deque
from typing import Any, Callable, Awaitable from typing import Callable, Awaitable
from agentkit.tools.base import Tool from agentkit.tools.base import Tool
from agentkit.tools.output_parser import OutputParser, ParsedOutput from agentkit.tools.output_parser import OutputParser, ParsedOutput
@ -232,8 +232,8 @@ class ShellTool(Tool):
self, self,
name: str = "shell", name: str = "shell",
description: str = "执行 Shell 命令,支持会话模式保持跨命令状态", description: str = "执行 Shell 命令,支持会话模式保持跨命令状态",
input_schema: dict[str, Any] | None = None, input_schema: dict[str, object] | None = None,
output_schema: dict[str, Any] | None = None, output_schema: dict[str, object] | None = None,
version: str = "1.0.0", version: str = "1.0.0",
tags: list[str] | None = None, tags: list[str] | None = None,
confirm_callback: Callable[[str], Awaitable[bool]] | None = None, confirm_callback: Callable[[str], Awaitable[bool]] | None = None,
@ -253,10 +253,10 @@ class ShellTool(Tool):
self._confirm_callback = confirm_callback self._confirm_callback = confirm_callback
self._default_timeout = default_timeout self._default_timeout = default_timeout
self._max_output_length = max_output_length 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 @staticmethod
def _default_input_schema() -> dict[str, Any]: def _default_input_schema() -> dict[str, object]:
return { return {
"type": "object", "type": "object",
"properties": { "properties": {
@ -287,7 +287,7 @@ class ShellTool(Tool):
} }
@staticmethod @staticmethod
def _default_output_schema() -> dict[str, Any]: def _default_output_schema() -> dict[str, object]:
return { return {
"type": "object", "type": "object",
"properties": { "properties": {
@ -613,6 +613,6 @@ class ShellTool(Tool):
return self._session_manager return self._session_manager
@property @property
def audit_log(self) -> list[dict[str, Any]]: def audit_log(self) -> list[dict[str, object]]:
"""获取审计日志(副本)""" """获取审计日志(副本)"""
return list(self._audit_log) return list(self._audit_log)

View File

@ -4,7 +4,7 @@ import asyncio
import logging import logging
import os import os
import re import re
from typing import Any, Callable, Awaitable from typing import Callable, Awaitable
from agentkit.tools.base import Tool from agentkit.tools.base import Tool
@ -34,8 +34,8 @@ class SkillInstallTool(Tool):
"重要:安装前应先用 skill_search 工具搜索确认技能名称和来源(source)。" "重要:安装前应先用 skill_search 工具搜索确认技能名称和来源(source)。"
"如果用户只提供了模糊名称,先用 skill_search 搜索,再根据搜索结果安装。" "如果用户只提供了模糊名称,先用 skill_search 搜索,再根据搜索结果安装。"
), ),
input_schema: dict[str, Any] | None = None, input_schema: dict[str, object] | None = None,
output_schema: dict[str, Any] | None = None, output_schema: dict[str, object] | None = None,
version: str = "1.0.0", version: str = "1.0.0",
tags: list[str] | None = None, tags: list[str] | None = None,
confirm_callback: Callable[[str], Awaitable[bool]] | None = None, confirm_callback: Callable[[str], Awaitable[bool]] | None = None,

View File

@ -2,7 +2,6 @@
import asyncio import asyncio
import logging import logging
from typing import Any
from agentkit.tools.base import Tool from agentkit.tools.base import Tool
@ -27,8 +26,8 @@ class SkillSearchTool(Tool):
"搜索可用的 Agent 技能包。在安装技能之前,应先使用此工具搜索确认技能名称和来源。" "搜索可用的 Agent 技能包。在安装技能之前,应先使用此工具搜索确认技能名称和来源。"
"返回匹配的技能列表,包含名称、描述和安装来源。" "返回匹配的技能列表,包含名称、描述和安装来源。"
), ),
input_schema: dict[str, Any] | None = None, input_schema: dict[str, object] | None = None,
output_schema: dict[str, Any] | None = None, output_schema: dict[str, object] | None = None,
version: str = "1.0.0", version: str = "1.0.0",
tags: list[str] | None = None, tags: list[str] | None = None,
): ):

View File

@ -14,7 +14,6 @@ import shlex
import time import time
from collections import deque from collections import deque
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Any
from agentkit.tools.output_parser import OutputParser, ParsedOutput from agentkit.tools.output_parser import OutputParser, ParsedOutput

View File

@ -1,7 +1,6 @@
"""WebCrawlTool - 基于 Crawl4AI 的网页抓取工具,支持优雅降级""" """WebCrawlTool - 基于 Crawl4AI 的网页抓取工具,支持优雅降级"""
import logging import logging
from typing import Any
from agentkit.tools.base import Tool from agentkit.tools.base import Tool
@ -31,8 +30,8 @@ class WebCrawlTool(Tool):
self, self,
name: str = "web_crawl", name: str = "web_crawl",
description: str = "抓取网页内容,支持 Markdown/HTML 输出和 CSS 选择器提取", description: str = "抓取网页内容,支持 Markdown/HTML 输出和 CSS 选择器提取",
input_schema: dict[str, Any] | None = None, input_schema: dict[str, object] | None = None,
output_schema: dict[str, Any] | None = None, output_schema: dict[str, object] | None = None,
version: str = "1.0.0", version: str = "1.0.0",
tags: list[str] | None = None, tags: list[str] | None = None,
): ):
@ -46,7 +45,7 @@ class WebCrawlTool(Tool):
) )
@staticmethod @staticmethod
def _default_input_schema() -> dict[str, Any]: def _default_input_schema() -> dict[str, object]:
return { return {
"type": "object", "type": "object",
"properties": { "properties": {
@ -74,7 +73,7 @@ class WebCrawlTool(Tool):
} }
@staticmethod @staticmethod
def _default_output_schema() -> dict[str, Any]: def _default_output_schema() -> dict[str, object]:
return { return {
"type": "object", "type": "object",
"properties": { "properties": {
@ -142,7 +141,7 @@ class WebCrawlTool(Tool):
status_code = result.status_code if hasattr(result, "status_code") else 200 status_code = result.status_code if hasattr(result, "status_code") else 200
response: dict[str, Any] = { response: dict[str, object] = {
"content": content, "content": content,
"status_code": status_code, "status_code": status_code,
"links": links, "links": links,

View File

@ -10,7 +10,6 @@ import json
import logging import logging
import re import re
import urllib.parse import urllib.parse
from typing import Any
import httpx import httpx
@ -32,8 +31,8 @@ class WebSearchTool(Tool):
self, self,
name: str = "web_search", name: str = "web_search",
description: str = "搜索互联网信息。返回搜索结果列表,包含标题、链接和摘要。", description: str = "搜索互联网信息。返回搜索结果列表,包含标题、链接和摘要。",
input_schema: dict[str, Any] | None = None, input_schema: dict[str, object] | None = None,
output_schema: dict[str, Any] | None = None, output_schema: dict[str, object] | None = None,
version: str = "1.0.0", version: str = "1.0.0",
tags: list[str] | None = None, tags: list[str] | None = None,
tavily_api_key: str | None = None, tavily_api_key: str | None = None,
@ -53,7 +52,7 @@ class WebSearchTool(Tool):
self._default_max_results = default_max_results self._default_max_results = default_max_results
@staticmethod @staticmethod
def _default_input_schema() -> dict[str, Any]: def _default_input_schema() -> dict[str, object]:
return { return {
"type": "object", "type": "object",
"properties": { "properties": {
@ -71,7 +70,7 @@ class WebSearchTool(Tool):
} }
@staticmethod @staticmethod
def _default_output_schema() -> dict[str, Any]: def _default_output_schema() -> dict[str, object]:
return { return {
"type": "object", "type": "object",
"properties": { "properties": {