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

View File

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

View File

@ -2,7 +2,7 @@
from __future__ import annotations
from typing import Any, Callable, Awaitable, Protocol as TypingProtocol, runtime_checkable
from typing import Callable, Awaitable, Protocol as TypingProtocol, runtime_checkable
from agentkit.bus.message import AgentMessage

View File

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

View File

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

View File

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

View File

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

View File

@ -19,7 +19,6 @@ import uuid
from collections.abc import Awaitable, Callable
from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import Any
from urllib.parse import parse_qs, quote, urlparse
import httpx
@ -37,7 +36,7 @@ from agentkit.calendar.sync.base import AbstractSyncProvider
logger = logging.getLogger(__name__)
# Async callback signature: (event_type: str, payload: dict) -> None
NotifyCallback = Callable[[str, dict[str, Any]], Awaitable[None]]
NotifyCallback = Callable[[str, dict[str, object]], Awaitable[None]]
GRAPH_BASE = "https://graph.microsoft.com/v1.0"
TOKEN_URL = "https://login.microsoftonline.com/common/oauth2/v2.0/token"
@ -70,7 +69,7 @@ def _parse_iso(dt_str: str) -> datetime:
return dt.astimezone(timezone.utc)
def _outlook_dt_to_iso(dt_obj: dict[str, Any]) -> str:
def _outlook_dt_to_iso(dt_obj: dict[str, object]) -> str:
"""Convert Outlook dateTimeTimeZone to ISO 8601 UTC.
ponytail: assumes Graph returns UTC (no ``Prefer: outlook.timezone`` header
@ -102,7 +101,7 @@ def _iso_to_outlook_dt(iso_str: str, is_all_day: bool) -> dict[str, str]:
return {"dateTime": dt.strftime("%Y-%m-%dT%H:%M:%S"), "timeZone": "UTC"}
def _outlook_recurrence_to_rrule(recurrence: dict[str, Any] | None) -> str | None:
def _outlook_recurrence_to_rrule(recurrence: dict[str, object] | None) -> str | None:
"""Convert Outlook recurrence pattern to RRULE string."""
if not recurrence:
return None
@ -134,7 +133,7 @@ def _outlook_recurrence_to_rrule(recurrence: dict[str, Any] | None) -> str | Non
return ";".join(parts) if parts else None
def _rrule_to_outlook_recurrence(rrule: str, start_date: str) -> dict[str, Any] | None:
def _rrule_to_outlook_recurrence(rrule: str, start_date: str) -> dict[str, object] | None:
"""Convert RRULE string to Outlook recurrence pattern.
``start_date`` is the event's start date in ``YYYY-MM-DD`` format (required
@ -151,7 +150,7 @@ def _rrule_to_outlook_recurrence(rrule: str, start_date: str) -> dict[str, Any]
if not pattern_type:
return None
pattern: dict[str, Any] = {"type": pattern_type}
pattern: dict[str, object] = {"type": pattern_type}
interval = parts.get("INTERVAL")
pattern["interval"] = int(interval) if interval else 1
@ -167,7 +166,7 @@ def _rrule_to_outlook_recurrence(rrule: str, start_date: str) -> dict[str, Any]
if count:
pattern["numberOfOccurrences"] = int(count)
range_obj: dict[str, Any] = {
range_obj: dict[str, object] = {
"type": "numbered",
"startDate": start_date,
"numberOfOccurrences": int(count),
@ -202,7 +201,7 @@ class OutlookSyncProvider(AbstractSyncProvider):
def __init__(
self,
db_path: str | Path | None = None,
client_factory: Callable[[], Any] | None = None,
client_factory: Callable[[], object] | None = None,
notify_callback: NotifyCallback | None = None,
) -> None:
self.db_path = Path(db_path) if db_path is not None else DEFAULT_CALENDAR_DB_PATH
@ -216,19 +215,19 @@ class OutlookSyncProvider(AbstractSyncProvider):
# Client / auth
# ------------------------------------------------------------------
def _get_client(self) -> Any:
def _get_client(self) -> object:
"""Return an httpx.AsyncClient (real or mock from factory)."""
if self._client_factory is not None:
return self._client_factory()
return httpx.AsyncClient(timeout=30.0)
def _load_creds(self, config: ExternalCalendarConfig) -> dict[str, Any]:
def _load_creds(self, config: ExternalCalendarConfig) -> dict[str, object]:
return json.loads(config.credentials) if config.credentials else {}
def _save_creds(self, config: ExternalCalendarConfig, creds: dict[str, Any]) -> None:
def _save_creds(self, config: ExternalCalendarConfig, creds: dict[str, object]) -> None:
config.credentials = json.dumps(creds)
async def _refresh_token(self, client: Any, config: ExternalCalendarConfig) -> dict[str, Any]:
async def _refresh_token(self, client: object, config: ExternalCalendarConfig) -> dict[str, object]:
"""Refresh the access token using the refresh_token grant.
Posts to the Azure AD token endpoint, updates ``config.credentials``
@ -271,13 +270,13 @@ class OutlookSyncProvider(AbstractSyncProvider):
async def _request(
self,
client: Any,
client: object,
config: ExternalCalendarConfig,
method: str,
url: str,
*,
json_body: dict[str, Any] | None = None,
) -> dict[str, Any]:
json_body: dict[str, object] | None = None,
) -> dict[str, object]:
"""Make an authenticated Graph API request with 401 auto-refresh + retry."""
creds = self._load_creds(config)
headers = {"Authorization": f"Bearer {creds.get('access_token', '')}"}
@ -330,7 +329,7 @@ class OutlookSyncProvider(AbstractSyncProvider):
return result
async def _pull_delta(
self, client: Any, config: ExternalCalendarConfig
self, client: object, config: ExternalCalendarConfig
) -> tuple[list[CalendarEvent], str | None]:
"""Call /me/calendarView/delta. Returns (events, delta_token)."""
url = self._build_delta_url(config)
@ -356,7 +355,7 @@ class OutlookSyncProvider(AbstractSyncProvider):
end = (datetime.now(timezone.utc) + timedelta(days=90)).strftime("%Y-%m-%dT%H:%M:%SZ")
return f"{GRAPH_BASE}/me/calendarView/delta?startDateTime={start}&endDateTime={end}"
def _parse_outlook_event(self, raw: dict[str, Any], user_id: str) -> CalendarEvent | None:
def _parse_outlook_event(self, raw: dict[str, object], user_id: str) -> CalendarEvent | None:
"""Convert a Graph event JSON to a CalendarEvent dataclass."""
eid = raw.get("id")
if not eid:
@ -459,7 +458,7 @@ class OutlookSyncProvider(AbstractSyncProvider):
return result
async def _push_single(
self, client: Any, config: ExternalCalendarConfig, event: CalendarEvent
self, client: object, config: ExternalCalendarConfig, event: CalendarEvent
) -> CalendarEvent:
"""Push a single event to Outlook, return event with external_id set."""
body = self._event_to_outlook(event)
@ -485,9 +484,9 @@ class OutlookSyncProvider(AbstractSyncProvider):
event.external_provider = "outlook"
return event
def _event_to_outlook(self, event: CalendarEvent) -> dict[str, Any]:
def _event_to_outlook(self, event: CalendarEvent) -> dict[str, object]:
"""Convert CalendarEvent to Outlook Graph event JSON."""
body: dict[str, Any] = {
body: dict[str, object] = {
"subject": event.title,
"start": _iso_to_outlook_dt(event.start_time, event.is_all_day),
"end": _iso_to_outlook_dt(event.end_time, event.is_all_day),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,7 +10,6 @@ from __future__ import annotations
import logging
from pathlib import Path
from typing import Any
from docxtpl import DocxTemplate
from jinja2.sandbox import SandboxedEnvironment
@ -40,7 +39,7 @@ class TemplateRenderer:
)
def render_template(
self, template_path: str | Path, data: dict[str, Any], output_path: Path
self, template_path: str | Path, data: dict[str, object], output_path: Path
) -> Path:
"""Fill a .docx template with data using Jinja2 sandbox.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,7 +7,7 @@ U3 将扩展为完整 IngestionPipeline含解析、预览、净化
from __future__ import annotations
import logging
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING
from agentkit.rag_platform.indexing import KB_CHUNKS_TABLE
from agentkit.rag_platform.models import QueryResult
@ -64,7 +64,7 @@ class RAGPipeline:
KB_CHUNKS_TABLE,
)
async def ingest(self, text: str, metadata: dict[str, Any] | None = None) -> list["TextNode"]:
async def ingest(self, text: str, metadata: dict[str, object] | None = None) -> list["TextNode"]:
"""将文本摄入向量存储。
Args:

View File

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

View File

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

View File

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

View File

@ -12,7 +12,7 @@ from __future__ import annotations
import asyncio
import logging
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING
from sqlalchemy import text
@ -184,7 +184,7 @@ class RetrievalEngine:
out: list[QueryResult] = []
for row in rows:
chunk_id, content, metadata, document_id, score = row
meta: dict[str, Any] = metadata if isinstance(metadata, dict) else {}
meta: dict[str, object] = metadata if isinstance(metadata, dict) else {}
kb_id = meta.get("kb_id", "")
out.append(
QueryResult(

View File

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

View File

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

View File

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

View File

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

View File

@ -20,7 +20,6 @@ from __future__ import annotations
import hmac
import logging
from typing import Any
import jwt
from starlette.middleware.base import BaseHTTPMiddleware
@ -93,7 +92,7 @@ class AuthMiddleware(BaseHTTPMiddleware):
"""Dev mode = no JWT secret, no global API key, no client keys."""
return not self._jwt_secret and self._api_key is None and not self._client_keys
def _verify_jwt(self, token: str) -> dict[str, Any] | None:
def _verify_jwt(self, token: str) -> dict[str, object] | None:
"""Verify a JWT bearer token. Returns payload or None.
V2 tokens carry a ``sid`` claim. The middleware does NOT

View File

@ -27,7 +27,6 @@ import os
from collections.abc import Mapping
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
import aiosqlite
from sqlalchemy import String
@ -774,7 +773,7 @@ async def init_auth_db(db_path: str | Path | None = None) -> Path:
# ---------------------------------------------------------------------------
def user_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> dict[str, Any]:
def user_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> dict[str, object]:
"""Convert a ``users`` row into a JSON-safe dict."""
return {
"id": row["id"],
@ -791,7 +790,7 @@ def user_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> dict[str, Any
}
def auth_session_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> dict[str, Any]:
def auth_session_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> dict[str, object]:
"""Convert an ``auth_sessions`` row into a JSON-safe dict.
The ``revoked`` field is normalized to a Python ``bool`` (the DB stores
@ -816,7 +815,7 @@ def auth_session_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> dict[
}
def department_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> dict[str, Any]:
def department_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> dict[str, object]:
"""Convert a ``departments`` row into a JSON-safe dict.
The ``is_active`` field is normalized to a Python ``bool`` (the DB
@ -831,7 +830,7 @@ def department_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> dict[st
}
def user_department_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> dict[str, Any]:
def user_department_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> dict[str, object]:
"""Convert a ``user_departments`` row into a JSON-safe dict."""
return {
"user_id": row["user_id"],
@ -840,7 +839,7 @@ def user_department_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> di
}
def skill_state_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> dict[str, Any]:
def skill_state_row_to_dict(row: aiosqlite.Row | Mapping[str, object]) -> dict[str, object]:
"""Convert a ``skill_states`` row into a JSON-safe dict.
The ``is_disabled`` field is normalized to a Python ``bool`` (the DB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,7 +7,7 @@ The LLM calls this tool to signal "I'm done planning, move to building".
from __future__ import annotations
import logging
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING
from agentkit.tools.base import Tool
@ -55,7 +55,7 @@ class AdvancePhaseTool(Tool):
)
self._engine = engine
async def execute(self, **kwargs) -> dict[str, Any]:
async def execute(self, **kwargs) -> dict[str, object]:
# Capture previous phase before transition (engine is single-threaded per request).
previous = self._engine.current_phase
new_phase = self._engine.advance_phase()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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