refactor: classify except Exception in Wave 1 files (144 sites)

- 58 sites in server/app + cli/chat + bus (27 narrowed, 31 kept+guard)
- 86 sites in evolution + memory + skills + quality + session + chat
- health_check/list_sources/parsers keep except Exception (design intent)
- All kept except Exception have asyncio.CancelledError guard

Tests: 1139 passed, 0 regressions
ruff: 20 errors (all pre-existing, 0 new)
This commit is contained in:
chiguyong 2026-07-01 03:39:43 +08:00
parent 838a05772e
commit 38b9602964
30 changed files with 251 additions and 145 deletions

View File

@ -70,7 +70,9 @@ class InMemoryMessageBus:
for handler in self._subscribers[recipient]: for handler in self._subscribers[recipient]:
try: try:
await handler(message) await handler(message)
except Exception as e: except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — user-defined async handlers can throw arbitrary exceptions
logger.warning(f"Handler error for {recipient}: {e}") logger.warning(f"Handler error for {recipient}: {e}")
# Check if this is a response to a pending request # Check if this is a response to a pending request

View File

@ -62,7 +62,7 @@ class RedisMessageBus:
try: try:
await redis.xadd(stream_key, {"data": json.dumps(data)}) await redis.xadd(stream_key, {"data": json.dumps(data)})
except Exception as e: except (ConnectionError, OSError, asyncio.TimeoutError, RuntimeError) as e:
logger.error(f"Failed to publish message to {stream_key}: {e}") logger.error(f"Failed to publish message to {stream_key}: {e}")
raise raise
@ -103,7 +103,9 @@ class RedisMessageBus:
await redis.xgroup_create( await redis.xgroup_create(
stream_key, self._consumer_group, id="0", mkstream=True, stream_key, self._consumer_group, id="0", mkstream=True,
) )
except Exception: except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — xgroup_create raises ResponseError on BUSYGROUP; redis is optional dep
pass # Group already exists pass # Group already exists
while True: while True:
@ -126,12 +128,16 @@ class RedisMessageBus:
for handler in self._subscribers.get(agent_name, []): for handler in self._subscribers.get(agent_name, []):
try: try:
await handler(message) await handler(message)
except Exception as e: except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — user-defined async handlers can throw arbitrary exceptions
logger.warning(f"Handler error for {agent_name}: {e}") logger.warning(f"Handler error for {agent_name}: {e}")
# Acknowledge message # Acknowledge message
await redis.xack(stream_key, self._consumer_group, msg_id) await redis.xack(stream_key, self._consumer_group, msg_id)
except Exception as e: except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — wraps json parse + handler + xack; multi-op block with diverse failure modes
logger.warning(f"Failed to process message {msg_id}: {e}") logger.warning(f"Failed to process message {msg_id}: {e}")
# Move to dead letter after max retries # Move to dead letter after max retries
await self._handle_failed_message( await self._handle_failed_message(
@ -139,7 +145,7 @@ class RedisMessageBus:
) )
except asyncio.CancelledError: except asyncio.CancelledError:
break break
except Exception as e: except Exception as e: # noqa: BLE001 — consumer loop top-level fallback; must keep running on transient errors
logger.error(f"Consumer error for {agent_name}: {e}") logger.error(f"Consumer error for {agent_name}: {e}")
await asyncio.sleep(1) await asyncio.sleep(1)
@ -157,7 +163,7 @@ class RedisMessageBus:
await redis.xadd(dead_key, fields) await redis.xadd(dead_key, fields)
await redis.xack(stream_key, self._consumer_group, msg_id) await redis.xack(stream_key, self._consumer_group, msg_id)
logger.warning(f"Message {msg_id} moved to dead letter queue") logger.warning(f"Message {msg_id} moved to dead letter queue")
except Exception as e: except (ConnectionError, OSError, asyncio.TimeoutError, RuntimeError) as e:
logger.error(f"Failed to move message to dead letter: {e}") logger.error(f"Failed to move message to dead letter: {e}")
async def unsubscribe(self, agent_name: str) -> None: async def unsubscribe(self, agent_name: str) -> None:
@ -205,7 +211,7 @@ class RedisMessageBus:
stream_key = self._stream_key(agent_name) stream_key = self._stream_key(agent_name)
try: try:
await redis.xadd(stream_key, {"data": json.dumps(data)}) await redis.xadd(stream_key, {"data": json.dumps(data)})
except Exception as e: except (ConnectionError, OSError, asyncio.TimeoutError, RuntimeError) as e:
logger.error(f"Failed to broadcast to {agent_name}: {e}") logger.error(f"Failed to broadcast to {agent_name}: {e}")
# Check pending requests (only for replies) # Check pending requests (only for replies)
@ -222,7 +228,7 @@ class RedisMessageBus:
try: try:
redis = await self._get_redis() redis = await self._get_redis()
return await redis.ping() return await redis.ping()
except Exception: except (ConnectionError, OSError, asyncio.TimeoutError, RuntimeError):
return False return False
@property @property
@ -257,7 +263,9 @@ def create_message_bus(
) )
logger.info(f"MessageBus backend: redis_streams ({redis_url})") logger.info(f"MessageBus backend: redis_streams ({redis_url})")
return bus return bus
except Exception as exc: except asyncio.CancelledError:
raise
except Exception as exc: # noqa: BLE001 — factory fallback to InMemoryMessageBus; must catch import/init errors broadly
logger.warning( logger.warning(
f"Failed to initialise RedisMessageBus ({exc}), " f"Failed to initialise RedisMessageBus ({exc}), "
f"falling back to InMemoryMessageBus" f"falling back to InMemoryMessageBus"

View File

@ -194,7 +194,7 @@ class RequestPreprocessor:
"""Resolve an explicitly specified skill via @skill:xxx prefix.""" """Resolve an explicitly specified skill via @skill:xxx prefix."""
try: try:
skill = registry.get(skill_name) skill = registry.get(skill_name)
except Exception: except (ValueError, KeyError, TypeError, RuntimeError):
logger.warning(f"Skill '{skill_name}' not found, falling back to REACT") logger.warning(f"Skill '{skill_name}' not found, falling back to REACT")
return SkillRoutingResult( return SkillRoutingResult(
clean_content=clean_content, clean_content=clean_content,

View File

@ -200,7 +200,7 @@ async def resolve_skill_routing(
result.match_method = "explicit" result.match_method = "explicit"
result.match_confidence = 1.0 result.match_confidence = 1.0
logger.info(f"Session {session_id}: using explicit skill '{explicit_skill}'") logger.info(f"Session {session_id}: using explicit skill '{explicit_skill}'")
except Exception as e: except (ValueError, KeyError, TypeError, RuntimeError) as e:
logger.warning( logger.warning(
f"Session {session_id}: explicit skill '{explicit_skill}' not found: {e}" f"Session {session_id}: explicit skill '{explicit_skill}' not found: {e}"
) )

View File

@ -291,7 +291,7 @@ class SqliteConversationStore:
title_row = await title_cursor.fetchone() title_row = await title_cursor.fetchone()
if title_row: if title_row:
conv.messages.append(ChatMessage(role="user", content=title_row["content"])) conv.messages.append(ChatMessage(role="user", content=title_row["content"]))
except Exception: except (aiosqlite.Error, ValueError, KeyError, TypeError):
pass pass
result.append(conv) result.append(conv)
return result return result
@ -321,7 +321,7 @@ class SqliteConversationStore:
) )
await db.execute("DELETE FROM conversations WHERE id = ?", (conversation_id,)) await db.execute("DELETE FROM conversations WHERE id = ?", (conversation_id,))
await db.commit() await db.commit()
except Exception: except (aiosqlite.Error, ValueError, KeyError, RuntimeError):
await db.rollback() await db.rollback()
logger.exception("Failed to delete conversation %s; rolled back", conversation_id) logger.exception("Failed to delete conversation %s; rolled back", conversation_id)
raise raise
@ -365,6 +365,6 @@ class SqliteConversationStore:
timestamp=self._str_to_dt(row["timestamp"]), timestamp=self._str_to_dt(row["timestamp"]),
metadata=meta, metadata=meta,
) )
except Exception as e: except (aiosqlite.Error, ValueError, KeyError, TypeError, RuntimeError) as e:
logger.warning(f"get_first_user_message failed for {conversation_id}: {e}") logger.warning(f"get_first_user_message failed for {conversation_id}: {e}")
return None return None

View File

@ -134,7 +134,7 @@ async def _chat_async(
data=data or {}, data=data or {},
) )
) )
except Exception: except (asyncio.QueueFull, RuntimeError, ConnectionError):
pass # EQ is best-effort; never break CLI flow pass # EQ is best-effort; never break CLI flow
# Emit session.started event # Emit session.started event
@ -177,7 +177,7 @@ async def _chat_async(
elif p.is_file() and p.suffix in (".yaml", ".yml"): elif p.is_file() and p.suffix in (".yaml", ".yml"):
try: try:
loader.load_from_file(str(p)) loader.load_from_file(str(p))
except Exception: except (ValueError, TypeError, KeyError, OSError, RuntimeError):
pass pass
# Build system prompt — inject memory into system prompt # Build system prompt — inject memory into system prompt
@ -428,7 +428,9 @@ async def _chat_async(
data={"output": full_content}, data={"output": full_content},
) )
except Exception as e: except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — CLI main loop top-level; must not crash on user-facing errors
rprint(f"\n[red]Error: {e}[/red]") rprint(f"\n[red]Error: {e}[/red]")
# Emit task.failed to EQ (if enabled) # Emit task.failed to EQ (if enabled)
if _emit is not None: if _emit is not None:
@ -461,7 +463,7 @@ async def _chat_async(
# Archive old daily logs # Archive old daily logs
memory_store.archive_old_dailies(keep_days=2) memory_store.archive_old_dailies(keep_days=2)
except Exception: except (ConnectionError, OSError, asyncio.TimeoutError, ValueError, KeyError, RuntimeError):
pass # Daily log generation is best-effort pass # Daily log generation is best-effort
# Close EventQueue if it was enabled (emit session.ended and close) # Close EventQueue if it was enabled (emit session.ended and close)
@ -473,7 +475,7 @@ async def _chat_async(
session_id=session.session_id, session_id=session.session_id,
data={}, data={},
) )
except Exception: except (asyncio.QueueFull, RuntimeError, ConnectionError):
pass pass
eq.close() eq.close()
@ -502,7 +504,7 @@ def _build_gateway(server_config: "ServerConfig") -> "LLMGateway":
try: try:
provider = _create_provider(name, pconf) provider = _create_provider(name, pconf)
gateway.register_provider(name, provider) gateway.register_provider(name, provider)
except Exception as e: except (ValueError, TypeError, KeyError, RuntimeError, ConnectionError, OSError) as e:
import logging import logging
logging.getLogger(__name__).warning(f"Failed to register LLM provider '{name}': {e}") logging.getLogger(__name__).warning(f"Failed to register LLM provider '{name}': {e}")
@ -603,7 +605,9 @@ def _render_pm_collaboration_event(message: dict) -> bool:
) )
) )
return True return True
except Exception as e: except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — best-effort rendering; must not break orchestration
# ponytail: best-effort 渲染不中断编排,但记录日志便于调试 # ponytail: best-effort 渲染不中断编排,但记录日志便于调试
import logging import logging
@ -782,7 +786,9 @@ async def _execute_team_cli(
elif etype == "user_intervention": elif etype == "user_intervention":
pass # User typed it themselves pass # User typed it themselves
# Other events (expert_step, expert_result, expert_joined, etc.) are not rendered # Other events (expert_step, expert_result, expert_joined, etc.) are not rendered
except Exception: except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — best-effort rendering; must not break orchestration
pass # Rendering is best-effort; never break orchestration pass # Rendering is best-effort; never break orchestration
team.handoff_transport.register_handler(team.team_channel, _event_handler) team.handoff_transport.register_handler(team.team_channel, _event_handler)
@ -816,7 +822,7 @@ async def _execute_team_cli(
if readable: if readable:
try: try:
line = sys.stdin.readline() line = sys.stdin.readline()
except Exception: except (OSError, ValueError, RuntimeError):
line = "" line = ""
if not line: if not line:
break # EOF break # EOF
@ -846,12 +852,14 @@ async def _execute_team_cli(
) )
) )
except Exception as e: except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — team execution top-level; must report errors to user without crash
rprint(f"[red]团队执行错误: {e}[/red]") rprint(f"[red]团队执行错误: {e}[/red]")
finally: finally:
try: try:
await team.dissolve() await team.dissolve()
except Exception: except (RuntimeError, asyncio.TimeoutError, ConnectionError):
pass pass
return True return True

View File

@ -8,6 +8,8 @@ import math
from dataclasses import dataclass from dataclasses import dataclass
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from sqlalchemy.exc import DBAPIError
if TYPE_CHECKING: if TYPE_CHECKING:
from agentkit.evolution.evolution_store import InMemoryEvolutionStore from agentkit.evolution.evolution_store import InMemoryEvolutionStore
@ -129,7 +131,7 @@ class ABTester:
sample_count=len(experiment_metrics), sample_count=len(experiment_metrics),
) )
logger.info(f"A/B test results persisted for test '{test_id}'") logger.info(f"A/B test results persisted for test '{test_id}'")
except Exception as e: except (DBAPIError, RuntimeError, ValueError, KeyError) as e:
logger.error(f"Failed to persist A/B test results: {e}") logger.error(f"Failed to persist A/B test results: {e}")
async def evaluate(self, test_id: str) -> ABTestResult | None: async def evaluate(self, test_id: str) -> ABTestResult | None:

View File

@ -10,6 +10,7 @@
from __future__ import annotations from __future__ import annotations
import asyncio
import logging import logging
import math import math
import re import re
@ -18,6 +19,7 @@ from datetime import datetime, timedelta, timezone
from typing import Any from typing import Any
from sqlalchemy import text from sqlalchemy import text
from sqlalchemy.exc import DBAPIError
_SAFE_TABLE_NAME_PATTERN = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_]*$') _SAFE_TABLE_NAME_PATTERN = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_]*$')
@ -96,7 +98,7 @@ class ExperienceStore:
text = experience.text_for_embedding() text = experience.text_for_embedding()
try: try:
experience.embedding = await self._embedder.embed(text) experience.embedding = await self._embedder.embed(text)
except Exception as e: except (ConnectionError, RuntimeError, asyncio.TimeoutError, ValueError) as e:
logger.warning(f"Failed to generate embedding for experience {experience.experience_id}: {e}") logger.warning(f"Failed to generate embedding for experience {experience.experience_id}: {e}")
async with self._session_factory() as db: async with self._session_factory() as db:
@ -122,7 +124,7 @@ class ExperienceStore:
f"task_type={experience.task_type} outcome={experience.outcome}" f"task_type={experience.task_type} outcome={experience.outcome}"
) )
return experience.experience_id return experience.experience_id
except Exception as e: except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
await db.rollback() await db.rollback()
logger.error(f"Failed to record experience: {e}") logger.error(f"Failed to record experience: {e}")
raise raise
@ -149,7 +151,7 @@ class ExperienceStore:
if self._pgvector_enabled and self._embedder: if self._pgvector_enabled and self._embedder:
return await self._search_pgvector(db, query, top_k, task_type, search_multiplier) return await self._search_pgvector(db, query, top_k, task_type, search_multiplier)
return await self._search_client_side(db, query, top_k, task_type, search_multiplier) return await self._search_client_side(db, query, top_k, task_type, search_multiplier)
except Exception as e: except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
logger.error(f"Failed to search experiences: {e}") logger.error(f"Failed to search experiences: {e}")
return [] return []
@ -343,7 +345,7 @@ class ExperienceStore:
) )
) )
return metrics_list return metrics_list
except Exception as e: except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
logger.error(f"Failed to get metrics: {e}") logger.error(f"Failed to get metrics: {e}")
return [] return []
@ -375,7 +377,7 @@ class InMemoryExperienceStore:
text = experience.text_for_embedding() text = experience.text_for_embedding()
try: try:
experience.embedding = await self._embedder.embed(text) experience.embedding = await self._embedder.embed(text)
except Exception as e: except (ConnectionError, RuntimeError, asyncio.TimeoutError, ValueError) as e:
logger.warning(f"Failed to generate embedding for experience {experience.experience_id}: {e}") logger.warning(f"Failed to generate embedding for experience {experience.experience_id}: {e}")
# 存储副本,避免外部修改影响内部状态 # 存储副本,避免外部修改影响内部状态
@ -411,7 +413,7 @@ class InMemoryExperienceStore:
if self._embedder: if self._embedder:
try: try:
query_embedding = await self._embedder.embed(query) query_embedding = await self._embedder.embed(query)
except Exception as e: except (ConnectionError, RuntimeError, asyncio.TimeoutError, ValueError) as e:
logger.warning(f"Failed to generate query embedding: {e}") logger.warning(f"Failed to generate query embedding: {e}")
# 筛选候选 # 筛选候选

View File

@ -13,6 +13,7 @@
from __future__ import annotations from __future__ import annotations
import asyncio
import copy import copy
import logging import logging
import random import random
@ -292,7 +293,7 @@ class MutationOperator:
model="default", model="default",
) )
return response.content.strip() or instructions return response.content.strip() or instructions
except Exception as e: except (ConnectionError, RuntimeError, asyncio.TimeoutError, ValueError) as e:
logger.warning(f"LLM instruction mutation failed: {e}") logger.warning(f"LLM instruction mutation failed: {e}")
# Fallback: simple text mutation (shuffle paragraphs) # Fallback: simple text mutation (shuffle paragraphs)

View File

@ -8,6 +8,8 @@ from dataclasses import dataclass, field
from datetime import datetime, timezone from datetime import datetime, timezone
from typing import Any from typing import Any
from sqlalchemy.exc import DBAPIError
from agentkit.core.protocol import EvolutionEvent, TaskMessage, TaskResult from agentkit.core.protocol import EvolutionEvent, TaskMessage, TaskResult
from agentkit.evolution.ab_tester import ABTestConfig, ABTestResult, ABTester from agentkit.evolution.ab_tester import ABTestConfig, ABTestResult, ABTester
from agentkit.evolution.evolution_store import EvolutionStore from agentkit.evolution.evolution_store import EvolutionStore
@ -371,7 +373,7 @@ class EvolutionMixin:
entry.event_id = event_id entry.event_id = event_id
break break
return True return True
except Exception as e: except (DBAPIError, RuntimeError, ValueError, KeyError) as e:
logger.error(f"Failed to apply evolution change: {e}") logger.error(f"Failed to apply evolution change: {e}")
return False return False
@ -382,7 +384,7 @@ class EvolutionMixin:
try: try:
return await self._evolution_store.rollback(log_entry.event_id) return await self._evolution_store.rollback(log_entry.event_id)
except Exception as e: except (DBAPIError, RuntimeError, ValueError, KeyError) as e:
logger.error(f"Failed to rollback evolution change: {e}") logger.error(f"Failed to rollback evolution change: {e}")
return False return False

View File

@ -3,6 +3,7 @@
通过 LLM 分析执行轨迹生成结构化反思 RuleBasedReflector 提供更深入的洞察 通过 LLM 分析执行轨迹生成结构化反思 RuleBasedReflector 提供更深入的洞察
""" """
import asyncio
import json import json
import logging import logging
import re import re
@ -62,7 +63,7 @@ class LLMReflector:
task_type="reflection", task_type="reflection",
) )
return self._parse_reflection_response(response.content, task, result) return self._parse_reflection_response(response.content, task, result)
except Exception as e: except (ConnectionError, RuntimeError, asyncio.TimeoutError, ValueError, KeyError) as e:
logger.warning(f"LLM reflection failed, returning default: {e}") logger.warning(f"LLM reflection failed, returning default: {e}")
return Reflection( return Reflection(
task_id=getattr(task, "task_id", "unknown"), task_id=getattr(task, "task_id", "unknown"),

View File

@ -13,6 +13,7 @@ from typing import Any
from sqlalchemy import select from sqlalchemy import select
from sqlalchemy.dialects.postgresql import JSONB from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy import Column, DateTime, Float, Integer, String, Text, UniqueConstraint from sqlalchemy import Column, DateTime, Float, Integer, String, Text, UniqueConstraint
from sqlalchemy.exc import DBAPIError
from sqlalchemy.orm import declarative_base from sqlalchemy.orm import declarative_base
from agentkit.core.protocol import EvolutionEvent from agentkit.core.protocol import EvolutionEvent
@ -154,7 +155,7 @@ class PostgreSQLEvolutionStore:
event.event_id = event_id event.event_id = event_id
logger.info(f"Evolution event recorded: {event_id} for agent '{event.agent_name}'") logger.info(f"Evolution event recorded: {event_id} for agent '{event.agent_name}'")
return event_id return event_id
except Exception as e: except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
await db.rollback() await db.rollback()
logger.error(f"Failed to record evolution event: {e}") logger.error(f"Failed to record evolution event: {e}")
raise raise
@ -178,7 +179,7 @@ class PostgreSQLEvolutionStore:
await db.commit() await db.commit()
logger.info(f"Evolution event {event_id} rolled back") logger.info(f"Evolution event {event_id} rolled back")
return True return True
except Exception as e: except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
await db.rollback() await db.rollback()
logger.error(f"Failed to rollback evolution event {event_id}: {e}") logger.error(f"Failed to rollback evolution event {event_id}: {e}")
return False return False
@ -218,7 +219,7 @@ class PostgreSQLEvolutionStore:
} }
for e in entries for e in entries
] ]
except Exception as e: except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
logger.error(f"Failed to list evolution events: {e}") logger.error(f"Failed to list evolution events: {e}")
return [] return []
@ -242,7 +243,7 @@ class PostgreSQLEvolutionStore:
db.add(entry) db.add(entry)
await db.commit() await db.commit()
return vid return vid
except Exception as e: except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
await db.rollback() await db.rollback()
logger.error(f"Failed to record skill version: {e}") logger.error(f"Failed to record skill version: {e}")
raise raise
@ -271,7 +272,7 @@ class PostgreSQLEvolutionStore:
} }
for e in entries for e in entries
] ]
except Exception as e: except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
logger.error(f"Failed to list skill versions: {e}") logger.error(f"Failed to list skill versions: {e}")
return [] return []
@ -295,7 +296,7 @@ class PostgreSQLEvolutionStore:
db.add(entry) db.add(entry)
await db.commit() await db.commit()
return rid return rid
except Exception as e: except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
await db.rollback() await db.rollback()
logger.error(f"Failed to record A/B test result: {e}") logger.error(f"Failed to record A/B test result: {e}")
raise raise
@ -324,6 +325,6 @@ class PostgreSQLEvolutionStore:
} }
for e in entries for e in entries
] ]
except Exception as e: except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
logger.error(f"Failed to get A/B test results: {e}") logger.error(f"Failed to get A/B test results: {e}")
return [] return []

View File

@ -151,7 +151,7 @@ class PitfallDetector:
task_type=task_type, task_type=task_type,
) )
return results return results
except Exception as e: except (RuntimeError, ValueError, KeyError) as e:
logger.error(f"Failed to search experiences for pitfall detection: {e}") logger.error(f"Failed to search experiences for pitfall detection: {e}")
return [] return []

View File

@ -10,6 +10,7 @@
- LLMPromptOptimizer: 基于 LLM 分析反思结果生成改进指令 - LLMPromptOptimizer: 基于 LLM 分析反思结果生成改进指令
""" """
import asyncio
import logging import logging
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Any from typing import Any
@ -202,7 +203,7 @@ class LLMPromptOptimizer:
""" """
try: try:
optimized_instruction = await self._llm_optimize_instruction(module, trace, reflection) optimized_instruction = await self._llm_optimize_instruction(module, trace, reflection)
except Exception as e: except (ConnectionError, RuntimeError, asyncio.TimeoutError, ValueError) as e:
logger.warning(f"LLM prompt optimization failed, falling back to bootstrap: {e}") logger.warning(f"LLM prompt optimization failed, falling back to bootstrap: {e}")
return await self._bootstrap.optimize(module) return await self._bootstrap.optimize(module)

View File

@ -8,6 +8,7 @@
from __future__ import annotations from __future__ import annotations
import asyncio
import json import json
import logging import logging
import re import re
@ -79,7 +80,7 @@ class RiskGuardLearner:
top_k=top_k, top_k=top_k,
task_type=skill_name, task_type=skill_name,
) )
except Exception as e: except (ConnectionError, RuntimeError, asyncio.TimeoutError, ValueError, KeyError) as e:
logger.warning(f"RiskGuardLearner: failed to search experiences: {e}") logger.warning(f"RiskGuardLearner: failed to search experiences: {e}")
return [] return []
@ -95,7 +96,7 @@ class RiskGuardLearner:
# 2. 构建 LLM prompt # 2. 构建 LLM prompt
try: try:
prompt = self._build_prompt(failures) prompt = self._build_prompt(failures)
except Exception as e: except (ValueError, KeyError, RuntimeError) as e:
logger.warning(f"RiskGuardLearner: failed to build prompt: {e}") logger.warning(f"RiskGuardLearner: failed to build prompt: {e}")
return [] return []
@ -117,14 +118,14 @@ class RiskGuardLearner:
agent_name="risk_guard_learner", agent_name="risk_guard_learner",
task_type="risk_guard_learning", task_type="risk_guard_learning",
) )
except Exception as e: except (ConnectionError, RuntimeError, asyncio.TimeoutError, ValueError) as e:
logger.warning(f"RiskGuardLearner: LLM call failed: {e}") logger.warning(f"RiskGuardLearner: LLM call failed: {e}")
return [] return []
# 4. 解析响应 # 4. 解析响应
try: try:
return self._parse_response(response.content, source_ids) return self._parse_response(response.content, source_ids)
except Exception as e: except (ValueError, KeyError, TypeError) as e:
logger.warning(f"RiskGuardLearner: failed to parse response: {e}") logger.warning(f"RiskGuardLearner: failed to parse response: {e}")
return [] return []

View File

@ -6,6 +6,7 @@
from __future__ import annotations from __future__ import annotations
import asyncio
import logging import logging
import httpx import httpx
@ -83,7 +84,7 @@ class ConfluenceAdapter(KBAdapter):
resp = await client.get("/rest/api/user/current") resp = await client.get("/rest/api/user/current")
self._authenticated = resp.status_code == 200 self._authenticated = resp.status_code == 200
return self._authenticated return self._authenticated
except Exception as e: except (httpx.HTTPError, ValueError, KeyError, TypeError) as e:
logger.error(f"Confluence auth error: {e}") logger.error(f"Confluence auth error: {e}")
self._authenticated = False self._authenticated = False
return False return False
@ -139,7 +140,7 @@ class ConfluenceAdapter(KBAdapter):
f"Confluence search HTTP error: {e.response.status_code}{e.response.text[:200]}" f"Confluence search HTTP error: {e.response.status_code}{e.response.text[:200]}"
) )
return [] return []
except Exception as e: except (httpx.HTTPError, ValueError, KeyError, TypeError) as e:
logger.error(f"Confluence search error: {e}") logger.error(f"Confluence search error: {e}")
return [] return []
@ -170,7 +171,7 @@ class ConfluenceAdapter(KBAdapter):
"version": page.get("version", {}).get("number", 0), "version": page.get("version", {}).get("number", 0),
}, },
) )
except Exception as e: except (httpx.HTTPError, ValueError, KeyError, TypeError) as e:
logger.error(f"Confluence get_document error: {e}") logger.error(f"Confluence get_document error: {e}")
return None return None
@ -202,7 +203,7 @@ class ConfluenceAdapter(KBAdapter):
) )
] ]
) )
except Exception as e: except (httpx.HTTPError, ValueError, KeyError, TypeError) as e:
logger.error(f"Confluence list_sources error: {e}") logger.error(f"Confluence list_sources error: {e}")
return [ return [
SourceInfo( SourceInfo(
@ -218,5 +219,7 @@ class ConfluenceAdapter(KBAdapter):
try: try:
resp = await client.get("/rest/api/space", params={"limit": 1}) resp = await client.get("/rest/api/space", params={"limit": 1})
return resp.status_code == 200 return resp.status_code == 200
except Exception: except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — health_check 设计意图:任何异常都返回 False
return False return False

View File

@ -6,6 +6,7 @@
from __future__ import annotations from __future__ import annotations
import asyncio
import logging import logging
import time import time
from typing import TypeAlias from typing import TypeAlias
@ -101,7 +102,7 @@ class FeishuKBAdapter(KBAdapter):
else: else:
logger.error(f"Feishu auth failed: code={data.get('code')}, msg={data.get('msg')}") logger.error(f"Feishu auth failed: code={data.get('code')}, msg={data.get('msg')}")
return None return None
except Exception as e: except (httpx.HTTPError, ValueError, KeyError, TypeError) as e:
logger.error(f"Feishu auth error: {e}") logger.error(f"Feishu auth error: {e}")
return None return None
@ -166,7 +167,7 @@ class FeishuKBAdapter(KBAdapter):
f"Feishu search HTTP error: {e.response.status_code}{e.response.text[:200]}" f"Feishu search HTTP error: {e.response.status_code}{e.response.text[:200]}"
) )
return [] return []
except Exception as e: except (httpx.HTTPError, ValueError, KeyError, TypeError) as e:
logger.error(f"Feishu search error: {e}") logger.error(f"Feishu search error: {e}")
return [] return []
@ -199,7 +200,7 @@ class FeishuKBAdapter(KBAdapter):
"obj_type": node.get("obj_type", ""), "obj_type": node.get("obj_type", ""),
}, },
) )
except Exception as e: except (httpx.HTTPError, ValueError, KeyError, TypeError) as e:
logger.error(f"Feishu get_document error: {e}") logger.error(f"Feishu get_document error: {e}")
return None return None
@ -241,7 +242,7 @@ class FeishuKBAdapter(KBAdapter):
) )
] ]
) )
except Exception as e: except (httpx.HTTPError, ValueError, KeyError, TypeError) as e:
logger.error(f"Feishu list_sources error: {e}") logger.error(f"Feishu list_sources error: {e}")
return [ return [
SourceInfo( SourceInfo(
@ -256,5 +257,7 @@ class FeishuKBAdapter(KBAdapter):
try: try:
token = await self._get_access_token() token = await self._get_access_token()
return token is not None return token is not None
except Exception: except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — health_check 设计意图:任何异常都返回 False
return False return False

View File

@ -6,6 +6,7 @@
from __future__ import annotations from __future__ import annotations
import asyncio
import logging import logging
import httpx import httpx
@ -138,7 +139,7 @@ class GenericHTTPAdapter(KBAdapter):
f"GenericHTTP search HTTP error: {e.response.status_code}{e.response.text[:200]}" f"GenericHTTP search HTTP error: {e.response.status_code}{e.response.text[:200]}"
) )
return [] return []
except Exception as e: except (httpx.HTTPError, ValueError, KeyError, TypeError) as e:
logger.error(f"GenericHTTP search error: {e}") logger.error(f"GenericHTTP search error: {e}")
return [] return []
@ -179,7 +180,7 @@ class GenericHTTPAdapter(KBAdapter):
f"GenericHTTP ingest HTTP error: {e.response.status_code}{e.response.text[:200]}" f"GenericHTTP ingest HTTP error: {e.response.status_code}{e.response.text[:200]}"
) )
return [] return []
except Exception as e: except (httpx.HTTPError, ValueError, KeyError, TypeError) as e:
logger.error(f"GenericHTTP ingest error: {e}") logger.error(f"GenericHTTP ingest error: {e}")
return [] return []
@ -194,7 +195,7 @@ class GenericHTTPAdapter(KBAdapter):
if resp.status_code in (200, 204): if resp.status_code in (200, 204):
return True return True
return False return False
except Exception as e: except (httpx.HTTPError, ValueError, KeyError, TypeError) as e:
logger.error(f"GenericHTTP delete_by_id error: {e}") logger.error(f"GenericHTTP delete_by_id error: {e}")
return False return False
@ -216,7 +217,7 @@ class GenericHTTPAdapter(KBAdapter):
source_id=data.get("source_id", self._source_id), source_id=data.get("source_id", self._source_id),
metadata=data.get("metadata", {}), metadata=data.get("metadata", {}),
) )
except Exception as e: except (httpx.HTTPError, ValueError, KeyError, TypeError) as e:
logger.error(f"GenericHTTP get_document error: {e}") logger.error(f"GenericHTTP get_document error: {e}")
return None return None
@ -254,7 +255,9 @@ class GenericHTTPAdapter(KBAdapter):
) )
] ]
) )
except Exception as e: except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — list_sources 设计意图:任何异常回退到默认信息源
logger.debug(f"GenericHTTP list_sources error (endpoint may not exist): {e}") logger.debug(f"GenericHTTP list_sources error (endpoint may not exist): {e}")
return [ return [
@ -272,14 +275,18 @@ class GenericHTTPAdapter(KBAdapter):
resp = await client.get("/health") resp = await client.get("/health")
if resp.status_code == 200: if resp.status_code == 200:
return True return True
except Exception: except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — health_check 设计意图:任何异常继续尝试 fallback
pass pass
# Fallback: try the base endpoint # Fallback: try the base endpoint
try: try:
resp = await client.get("/") resp = await client.get("/")
return resp.status_code in (200, 401, 403) return resp.status_code in (200, 401, 403)
except Exception: except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — health_check 设计意图:任何异常都返回 False
return False return False
async def authenticate(self) -> bool: async def authenticate(self) -> bool:
@ -289,6 +296,8 @@ class GenericHTTPAdapter(KBAdapter):
""" """
try: try:
self._authenticated = await self.health_check() self._authenticated = await self.health_check()
except Exception: except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — authenticate 设计意图:任何异常都返回 False
self._authenticated = False self._authenticated = False
return self._authenticated return self._authenticated

View File

@ -6,6 +6,7 @@
from __future__ import annotations from __future__ import annotations
import asyncio
import hashlib import hashlib
import logging import logging
from dataclasses import dataclass from dataclasses import dataclass
@ -199,7 +200,7 @@ class ContextualChunker:
) )
context = response.content.strip() context = response.content.strip()
return context return context
except Exception as e: except (ConnectionError, RuntimeError, asyncio.TimeoutError, ValueError) as e:
logger.warning(f"Context generation failed for chunk: {e}") logger.warning(f"Context generation failed for chunk: {e}")
return "" return ""

View File

@ -9,6 +9,8 @@ from __future__ import annotations
import io import io
import logging import logging
import uuid import uuid
import zipfile
import asyncio
from dataclasses import dataclass, field from dataclasses import dataclass, field
from datetime import datetime, timezone from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
@ -194,7 +196,9 @@ class DocumentLoader:
return text, meta return text, meta
except ImportError: except ImportError:
pass pass
except Exception as e: except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — 解析器设计意图:任何失败回退到文本
logger.warning(f"PyMuPDF parsing failed for {filename}: {e}") logger.warning(f"PyMuPDF parsing failed for {filename}: {e}")
# 尝试 pdfplumber # 尝试 pdfplumber
@ -217,7 +221,9 @@ class DocumentLoader:
return text, meta return text, meta
except ImportError: except ImportError:
pass pass
except Exception as e: except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — 解析器设计意图:任何失败回退到文本
logger.warning(f"pdfplumber parsing failed for {filename}: {e}") logger.warning(f"pdfplumber parsing failed for {filename}: {e}")
# 回退到纯文本 # 回退到纯文本
@ -264,7 +270,9 @@ class DocumentLoader:
except ImportError: except ImportError:
logger.warning(f"python-docx not available for {filename}, falling back to text") logger.warning(f"python-docx not available for {filename}, falling back to text")
return self._parse_text(content, filename) return self._parse_text(content, filename)
except Exception as e: except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — 解析器设计意图:任何失败回退到文本
logger.warning(f"python-docx parsing failed for {filename}: {e}") logger.warning(f"python-docx parsing failed for {filename}: {e}")
return self._parse_text(content, filename) return self._parse_text(content, filename)
@ -333,7 +341,9 @@ class DocumentLoader:
except ImportError: except ImportError:
logger.warning(f"openpyxl not available for {filename}, falling back to text") logger.warning(f"openpyxl not available for {filename}, falling back to text")
return self._parse_text(content, filename) return self._parse_text(content, filename)
except Exception as e: except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — 解析器设计意图:任何失败回退到文本
logger.warning(f"openpyxl parsing failed for {filename}: {e}") logger.warning(f"openpyxl parsing failed for {filename}: {e}")
return self._parse_text(content, filename) return self._parse_text(content, filename)
@ -407,7 +417,9 @@ class DocumentLoader:
except ImportError: except ImportError:
logger.warning(f"BeautifulSoup not available for {filename}, falling back to text") logger.warning(f"BeautifulSoup not available for {filename}, falling back to text")
return self._parse_text(content, filename) return self._parse_text(content, filename)
except Exception as e: except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — 解析器设计意图:任何失败回退到文本
logger.warning(f"BeautifulSoup parsing failed for {filename}: {e}") logger.warning(f"BeautifulSoup parsing failed for {filename}: {e}")
return self._parse_text(content, filename) return self._parse_text(content, filename)

View File

@ -8,9 +8,7 @@ import os
import time import time
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from collections import OrderedDict from collections import OrderedDict
from typing import TYPE_CHECKING
if TYPE_CHECKING:
import httpx import httpx
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -108,8 +106,6 @@ class OpenAIEmbedder(Embedder):
def _get_client(self) -> httpx.AsyncClient: def _get_client(self) -> httpx.AsyncClient:
"""Lazily create and reuse a single httpx.AsyncClient.""" """Lazily create and reuse a single httpx.AsyncClient."""
if self._client is None: if self._client is None:
import httpx
self._client = httpx.AsyncClient(timeout=30.0) self._client = httpx.AsyncClient(timeout=30.0)
return self._client return self._client
@ -153,7 +149,7 @@ class OpenAIEmbedder(Embedder):
self._cache.put(text, embedding) self._cache.put(text, embedding)
return embedding return embedding
except Exception as e: except (httpx.HTTPError, ValueError, KeyError, TypeError) as e:
logger.error(f"OpenAI embedding failed: {e}") logger.error(f"OpenAI embedding failed: {e}")
raise raise

View File

@ -9,6 +9,7 @@ from datetime import datetime, timezone
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from sqlalchemy import text from sqlalchemy import text
from sqlalchemy.exc import DBAPIError
from agentkit.memory.base import Memory, MemoryItem, MetadataDict from agentkit.memory.base import Memory, MemoryItem, MetadataDict
from agentkit.memory.embedder import Embedder from agentkit.memory.embedder import Embedder
@ -94,7 +95,7 @@ class EpisodicMemory(Memory):
) )
db.add(entry) db.add(entry)
await db.commit() await db.commit()
except Exception as e: except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
await db.rollback() await db.rollback()
logger.error(f"Failed to store episodic memory: {e}") logger.error(f"Failed to store episodic memory: {e}")
raise raise
@ -111,7 +112,7 @@ class EpisodicMemory(Memory):
if self._pgvector_enabled: if self._pgvector_enabled:
return await self._retrieve_pgvector(db, query_embedding) return await self._retrieve_pgvector(db, query_embedding)
return await self._retrieve_client_side(db, query_embedding) return await self._retrieve_client_side(db, query_embedding)
except Exception as e: except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
logger.error(f"Failed to retrieve episodic memory: {e}") logger.error(f"Failed to retrieve episodic memory: {e}")
return None return None
@ -223,7 +224,7 @@ class EpisodicMemory(Memory):
if self._pgvector_enabled and self._embedder: if self._pgvector_enabled and self._embedder:
return await self._search_pgvector(db, query, top_k, filters, search_multiplier) return await self._search_pgvector(db, query, top_k, filters, search_multiplier)
return await self._search_client_side(db, query, top_k, filters, search_multiplier) return await self._search_client_side(db, query, top_k, filters, search_multiplier)
except Exception as e: except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
logger.error(f"Failed to search episodic memory: {e}") logger.error(f"Failed to search episodic memory: {e}")
return [] return []
@ -405,7 +406,7 @@ class EpisodicMemory(Memory):
await db.execute(stmt) await db.execute(stmt)
await db.commit() await db.commit()
return True return True
except Exception as e: except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
await db.rollback() await db.rollback()
logger.error(f"Failed to delete episodic memory: {e}") logger.error(f"Failed to delete episodic memory: {e}")
return False return False

View File

@ -147,7 +147,7 @@ class HttpRAGService:
except httpx.RequestError as e: except httpx.RequestError as e:
logger.error(f"RAG search request error: {e}") logger.error(f"RAG search request error: {e}")
return [] return []
except Exception as e: except (ValueError, KeyError, TypeError, RuntimeError) as e:
logger.error(f"RAG search unexpected error: {e}") logger.error(f"RAG search unexpected error: {e}")
return [] return []
@ -237,7 +237,7 @@ class HttpRAGService:
except httpx.RequestError as e: except httpx.RequestError as e:
logger.error(f"RAG enhanced_search request error for KB {kb_id}: {e}") logger.error(f"RAG enhanced_search request error for KB {kb_id}: {e}")
raise raise
except Exception as e: except (ValueError, KeyError, TypeError, RuntimeError) as e:
logger.error(f"RAG enhanced_search unexpected error for KB {kb_id}: {e}") logger.error(f"RAG enhanced_search unexpected error for KB {kb_id}: {e}")
raise raise
@ -302,7 +302,7 @@ class HttpRAGService:
except httpx.HTTPStatusError as e: except httpx.HTTPStatusError as e:
logger.error(f"RAG ingest HTTP error: {e.response.status_code}") logger.error(f"RAG ingest HTTP error: {e.response.status_code}")
return None return None
except Exception as e: except (ValueError, KeyError, TypeError, RuntimeError) as e:
logger.error(f"RAG ingest error: {e}") logger.error(f"RAG ingest error: {e}")
return None return None
@ -312,7 +312,7 @@ class HttpRAGService:
try: try:
resp = await client.get("/bases") resp = await client.get("/bases")
return resp.status_code in (200, 401) # 401 = 服务在但需认证 return resp.status_code in (200, 401) # 401 = 服务在但需认证
except Exception: except (httpx.HTTPError, ValueError, KeyError, TypeError):
return False return False
async def close(self) -> None: async def close(self) -> None:

View File

@ -14,6 +14,8 @@ import re
from datetime import datetime, timezone from datetime import datetime, timezone
from typing import TYPE_CHECKING, TypeAlias from typing import TYPE_CHECKING, TypeAlias
from sqlalchemy.exc import DBAPIError
from agentkit.memory.chunking import Chunk, StructuralChunker, TextChunker from agentkit.memory.chunking import Chunk, StructuralChunker, TextChunker
from agentkit.memory.document_loader import Document as LoaderDocument from agentkit.memory.document_loader import Document as LoaderDocument
from agentkit.memory.embedder import Embedder from agentkit.memory.embedder import Embedder
@ -108,7 +110,7 @@ class LocalRAGService:
await self._store_chunks(doc, chunks) await self._store_chunks(doc, chunks)
ingested_ids.append(doc.doc_id) ingested_ids.append(doc.doc_id)
logger.info(f"Ingested document '{doc.title}' with {len(chunks)} chunks") logger.info(f"Ingested document '{doc.title}' with {len(chunks)} chunks")
except Exception as e: except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
logger.error(f"Failed to ingest document '{doc.title}': {e}") logger.error(f"Failed to ingest document '{doc.title}': {e}")
return ingested_ids return ingested_ids
@ -130,7 +132,7 @@ class LocalRAGService:
if self._pgvector_enabled: if self._pgvector_enabled:
return await self._query_pgvector(db, query_embedding, top_k) return await self._query_pgvector(db, query_embedding, top_k)
return await self._query_client_side(db, query_embedding, top_k) return await self._query_client_side(db, query_embedding, top_k)
except Exception as e: except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
logger.error(f"Failed to query knowledge base: {e}") logger.error(f"Failed to query knowledge base: {e}")
return [] return []
@ -151,7 +153,7 @@ class LocalRAGService:
await db.execute(sql, {"doc_id": id}) await db.execute(sql, {"doc_id": id})
await db.commit() await db.commit()
return True return True
except Exception as e: except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
await db.rollback() await db.rollback()
logger.error(f"Failed to delete document {id}: {e}") logger.error(f"Failed to delete document {id}: {e}")
return False return False
@ -190,7 +192,7 @@ class LocalRAGService:
) )
) )
return sources return sources
except Exception as e: except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
logger.error(f"Failed to list sources: {e}") logger.error(f"Failed to list sources: {e}")
return [] return []
@ -202,7 +204,7 @@ class LocalRAGService:
await db.execute(sql_text(f"SELECT 1 FROM {self._table_name} LIMIT 1")) await db.execute(sql_text(f"SELECT 1 FROM {self._table_name} LIMIT 1"))
return True return True
except Exception as e: except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
logger.error(f"Health check failed: {e}") logger.error(f"Health check failed: {e}")
return False return False
@ -268,7 +270,7 @@ class LocalRAGService:
await db.execute(sql, params_list) await db.execute(sql, params_list)
await db.commit() await db.commit()
except Exception as e: except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
await db.rollback() await db.rollback()
logger.error(f"Failed to store chunks for document '{doc.title}': {e}") logger.error(f"Failed to store chunks for document '{doc.title}': {e}")
raise raise
@ -455,7 +457,7 @@ class InMemoryLocalRAGService:
} }
ingested_ids.append(doc.doc_id) ingested_ids.append(doc.doc_id)
logger.info(f"Ingested document '{doc.title}' with {len(chunks)} chunks") logger.info(f"Ingested document '{doc.title}' with {len(chunks)} chunks")
except Exception as e: except (DBAPIError, ValueError, KeyError, RuntimeError) as e:
logger.error(f"Failed to ingest document '{doc.title}': {e}") logger.error(f"Failed to ingest document '{doc.title}': {e}")
return ingested_ids return ingested_ids

View File

@ -378,7 +378,7 @@ class MemoryStore:
try: try:
new_prompt = self.refresh_system_prompt() new_prompt = self.refresh_system_prompt()
self._on_change(new_prompt) self._on_change(new_prompt)
except Exception: except (OSError, ValueError, RuntimeError, TypeError):
import logging import logging
logging.getLogger(__name__).warning("Memory notify_change failed", exc_info=True) logging.getLogger(__name__).warning("Memory notify_change failed", exc_info=True)

View File

@ -392,5 +392,5 @@ class RetrieveKnowledgeTool(Tool):
} }
) )
return {"query": query, "results": results, "call_count": self._call_count} return {"query": query, "results": results, "call_count": self._call_count}
except Exception as e: except (ConnectionError, RuntimeError, ValueError, KeyError, TypeError) as e:
return {"error": str(e), "results": []} return {"error": str(e), "results": []}

View File

@ -13,6 +13,12 @@ import logging
import time import time
from typing import Any, Protocol, runtime_checkable from typing import Any, Protocol, runtime_checkable
# redis 可选依赖;未安装时回退为 Exception 以保留原 catch-all 语义(降级到 fallback
try:
from redis.exceptions import RedisError as _RedisError
except ImportError:
_RedisError = Exception
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -157,7 +163,7 @@ class RedisCascadeStateStore:
pipe.expire(key, self._session_ttl) pipe.expire(key, self._session_ttl)
results = pipe.execute() results = pipe.execute()
return results[0] return results[0]
except Exception as e: except (ImportError, OSError, _RedisError, ValueError, KeyError, RuntimeError, TypeError) as e:
logger.warning(f"Redis cascade increment failed: {e}") logger.warning(f"Redis cascade increment failed: {e}")
self._degrade_to_fallback() self._degrade_to_fallback()
if self._fallback is not None: if self._fallback is not None:
@ -171,7 +177,7 @@ class RedisCascadeStateStore:
r = self._get_sync_redis() r = self._get_sync_redis()
val = r.get(f"{self.INTER_PREFIX}{session_id}") val = r.get(f"{self.INTER_PREFIX}{session_id}")
return int(val) if val is not None else 0 return int(val) if val is not None else 0
except Exception as e: except (ImportError, OSError, _RedisError, ValueError, KeyError, RuntimeError, TypeError) as e:
logger.warning(f"Redis cascade get failed: {e}") logger.warning(f"Redis cascade get failed: {e}")
if self._fallback is not None: if self._fallback is not None:
return self._fallback.get_interaction(session_id) return self._fallback.get_interaction(session_id)
@ -188,7 +194,7 @@ class RedisCascadeStateStore:
pipe.set(key, depth) pipe.set(key, depth)
pipe.expire(key, self._session_ttl) pipe.expire(key, self._session_ttl)
pipe.execute() pipe.execute()
except Exception as e: except (ImportError, OSError, _RedisError, ValueError, KeyError, RuntimeError, TypeError) as e:
logger.warning(f"Redis cascade set_depth failed: {e}") logger.warning(f"Redis cascade set_depth failed: {e}")
self._degrade_to_fallback() self._degrade_to_fallback()
if self._fallback is not None: if self._fallback is not None:
@ -201,7 +207,7 @@ class RedisCascadeStateStore:
r = self._get_sync_redis() r = self._get_sync_redis()
val = r.get(f"{self.DEPTH_PREFIX}{session_id}") val = r.get(f"{self.DEPTH_PREFIX}{session_id}")
return int(val) if val is not None else 0 return int(val) if val is not None else 0
except Exception as e: except (ImportError, OSError, _RedisError, ValueError, KeyError, RuntimeError, TypeError) as e:
logger.warning(f"Redis cascade get_depth failed: {e}") logger.warning(f"Redis cascade get_depth failed: {e}")
if self._fallback is not None: if self._fallback is not None:
return self._fallback.get_depth(session_id) return self._fallback.get_depth(session_id)
@ -217,7 +223,7 @@ class RedisCascadeStateStore:
pipe.delete(f"{self.INTER_PREFIX}{session_id}") pipe.delete(f"{self.INTER_PREFIX}{session_id}")
pipe.delete(f"{self.DEPTH_PREFIX}{session_id}") pipe.delete(f"{self.DEPTH_PREFIX}{session_id}")
pipe.execute() pipe.execute()
except Exception as e: except (ImportError, OSError, _RedisError, ValueError, KeyError, RuntimeError, TypeError) as e:
logger.warning(f"Redis cascade reset failed: {e}") logger.warning(f"Redis cascade reset failed: {e}")
self._degrade_to_fallback() self._degrade_to_fallback()
if self._fallback is not None: if self._fallback is not None:

View File

@ -81,7 +81,7 @@ def _build_llm_gateway(config: ServerConfig) -> LLMGateway:
backend=config.usage_store.get("backend", "memory"), backend=config.usage_store.get("backend", "memory"),
redis_url=config.usage_store.get("redis_url", "redis://localhost:6379"), redis_url=config.usage_store.get("redis_url", "redis://localhost:6379"),
) )
except Exception as e: except (ConnectionError, OSError, asyncio.TimeoutError, ValueError, KeyError, RuntimeError) as e:
logger.warning(f"Failed to initialize usage store: {e}, using in-memory") logger.warning(f"Failed to initialize usage store: {e}, using in-memory")
gateway = LLMGateway(config=config.llm_config, usage_store=usage_store) gateway = LLMGateway(config=config.llm_config, usage_store=usage_store)
@ -93,7 +93,7 @@ def _build_llm_gateway(config: ServerConfig) -> LLMGateway:
try: try:
provider = _create_provider(name, pconf) provider = _create_provider(name, pconf)
gateway.register_provider(name, provider) gateway.register_provider(name, provider)
except Exception as e: except (ValueError, TypeError, KeyError, RuntimeError, ConnectionError, OSError) as e:
logger.warning(f"Failed to register LLM provider '{name}': {e}") logger.warning(f"Failed to register LLM provider '{name}': {e}")
return gateway return gateway
@ -222,7 +222,7 @@ async def lifespan(app: FastAPI):
if agent is not None: if agent is not None:
agent._system_prompt = new_prompt agent._system_prompt = new_prompt
updated += 1 updated += 1
except Exception: except (AttributeError, TypeError, ValueError):
logger.warning( logger.warning(
f"Failed to update system prompt for agent '{agent_name}'", exc_info=True f"Failed to update system prompt for agent '{agent_name}'", exc_info=True
) )
@ -283,7 +283,9 @@ async def lifespan(app: FastAPI):
agent._tool_registry.register(DocumentTool(service=doc_service)) agent._tool_registry.register(DocumentTool(service=doc_service))
app.state.document_service = doc_service app.state.document_service = doc_service
logger.info("DocumentTool registered with word/excel/pdf renderers") logger.info("DocumentTool registered with word/excel/pdf renderers")
except Exception: except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — DocumentTool init + registry; diverse failure modes
logger.exception("Failed to register DocumentTool") logger.exception("Failed to register DocumentTool")
# Override system prompt with memory-injected version + available tools # Override system prompt with memory-injected version + available tools
@ -296,7 +298,9 @@ async def lifespan(app: FastAPI):
) )
logger.info("GUI mode: created default chat agent with memory + tools") logger.info("GUI mode: created default chat agent with memory + tools")
except Exception as e: except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — GUI mode agent creation; multi-step init with diverse failures
logger.warning(f"GUI mode: failed to create default agent: {e}") logger.warning(f"GUI mode: failed to create default agent: {e}")
# Load skills from config and register into SkillRegistry # Load skills from config and register into SkillRegistry
@ -310,7 +314,7 @@ async def lifespan(app: FastAPI):
for tool in agent._tool_registry.list_tools(): for tool in agent._tool_registry.list_tools():
try: try:
tool_registry.register(tool) tool_registry.register(tool)
except Exception: except (ValueError, KeyError, RuntimeError):
pass # Already registered pass # Already registered
# Load skills from configured paths # Load skills from configured paths
@ -331,11 +335,13 @@ async def lifespan(app: FastAPI):
try: try:
loader.load_from_file(str(p)) loader.load_from_file(str(p))
logger.info(f"GUI mode: loaded skill from {p}") logger.info(f"GUI mode: loaded skill from {p}")
except Exception as se: except (ValueError, TypeError, KeyError, OSError, RuntimeError) as se:
logger.warning(f"GUI mode: failed to load skill from {p}: {se}") logger.warning(f"GUI mode: failed to load skill from {p}: {se}")
logger.info(f"GUI mode: {len(skill_registry.list_skills())} skills registered") logger.info(f"GUI mode: {len(skill_registry.list_skills())} skills registered")
except Exception as e: except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — GUI mode skills loading top-level; multi-step init
logger.warning(f"GUI mode: failed to load skills: {e}") logger.warning(f"GUI mode: failed to load skills: {e}")
elif os.environ.get("AGENTKIT_GUI_MODE"): elif os.environ.get("AGENTKIT_GUI_MODE"):
# Agent already exists (e.g. from config), still ensure memory store is available # Agent already exists (e.g. from config), still ensure memory store is available
@ -369,7 +375,7 @@ async def lifespan(app: FastAPI):
if agent is not None: if agent is not None:
agent._system_prompt = new_prompt agent._system_prompt = new_prompt
updated += 1 updated += 1
except Exception: except (AttributeError, TypeError, ValueError):
logger.warning( logger.warning(
f"Failed to update system prompt for agent '{agent_name}'", f"Failed to update system prompt for agent '{agent_name}'",
exc_info=True, exc_info=True,
@ -416,7 +422,9 @@ async def lifespan(app: FastAPI):
app.state.expert_template_registry = expert_registry app.state.expert_template_registry = expert_registry
if total_loaded: if total_loaded:
logger.info(f"Total {total_loaded} ExpertTemplates registered for @board mode") logger.info(f"Total {total_loaded} ExpertTemplates registered for @board mode")
except Exception as e: except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — ExpertTemplate registry loading; multi-step init
logger.warning(f"Failed to load ExpertTemplates: {e}") logger.warning(f"Failed to load ExpertTemplates: {e}")
# Ensure app.state.expert_template_registry always exists (empty registry) # Ensure app.state.expert_template_registry always exists (empty registry)
from agentkit.experts.registry import ExpertTemplateRegistry from agentkit.experts.registry import ExpertTemplateRegistry
@ -468,7 +476,7 @@ async def lifespan(app: FastAPI):
_row = await _cur.fetchone() _row = await _cur.fetchone()
if _row is not None: if _row is not None:
default_cal_user_id = str(_row["id"]) default_cal_user_id = str(_row["id"])
except Exception: except (ConnectionError, OSError, asyncio.TimeoutError, ValueError, KeyError, RuntimeError):
logger.debug("Could not resolve default user_id for CalendarTool", exc_info=True) logger.debug("Could not resolve default user_id for CalendarTool", exc_info=True)
calendar_tool = CalendarTool( calendar_tool = CalendarTool(
@ -492,7 +500,7 @@ async def lifespan(app: FastAPI):
if default_agent is not None: if default_agent is not None:
try: try:
default_agent._tool_registry.register(calendar_tool) default_agent._tool_registry.register(calendar_tool)
except Exception: except (ValueError, KeyError, RuntimeError, AttributeError):
# ponytail: log at debug — CalendarTool double-registration # ponytail: log at debug — CalendarTool double-registration
# is expected on reload, but silent pass hides real errors. # is expected on reload, but silent pass hides real errors.
logger.debug("CalendarTool already registered or registration failed", exc_info=True) logger.debug("CalendarTool already registered or registration failed", exc_info=True)
@ -512,12 +520,18 @@ async def lifespan(app: FastAPI):
"CalendarTool attached to default agent (tools=%d)", "CalendarTool attached to default agent (tools=%d)",
len(default_agent._tool_registry.list_tools()), len(default_agent._tool_registry.list_tools()),
) )
except Exception: except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — CalendarTool attach; multi-step with attribute access
logger.exception("Failed to attach CalendarTool to default agent") logger.exception("Failed to attach CalendarTool to default agent")
except Exception: except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — CalendarTool registration; multi-step init
logger.exception("Failed to register CalendarTool") logger.exception("Failed to register CalendarTool")
logger.info("Calendar subsystem initialized (service + reminder scheduler)") logger.info("Calendar subsystem initialized (service + reminder scheduler)")
except Exception: except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — calendar subsystem top-level init; must not block app startup
logger.exception("Failed to initialize calendar subsystem — calendar API unavailable") logger.exception("Failed to initialize calendar subsystem — calendar API unavailable")
# Bitable subsystem: init DB, service, internal token (KTD11). # Bitable subsystem: init DB, service, internal token (KTD11).
@ -529,7 +543,9 @@ async def lifespan(app: FastAPI):
app.state.bitable_service = BitableService(db=bitable_db) app.state.bitable_service = BitableService(db=bitable_db)
app.state.bitable_internal_token = os.environ.get("AGENTKIT_BITABLE_INTERNAL_TOKEN") app.state.bitable_internal_token = os.environ.get("AGENTKIT_BITABLE_INTERNAL_TOKEN")
logger.info("Bitable subsystem initialized") logger.info("Bitable subsystem initialized")
except Exception: except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — bitable subsystem top-level init; includes import + DB setup
logger.exception("Failed to initialize bitable subsystem") logger.exception("Failed to initialize bitable subsystem")
# RAG platform subsystem (P1): wire KBStore, RetrievalEngine, HitProcessor, # RAG platform subsystem (P1): wire KBStore, RetrievalEngine, HitProcessor,
@ -552,7 +568,7 @@ async def lifespan(app: FastAPI):
from agentkit.rag_platform.store import ensure_tables from agentkit.rag_platform.store import ensure_tables
await ensure_tables(rag_database_url) await ensure_tables(rag_database_url)
except Exception: except (ConnectionError, OSError, asyncio.TimeoutError, ValueError, KeyError, RuntimeError):
logger.exception("Failed to ensure rag_platform tables") logger.exception("Failed to ensure rag_platform tables")
# KBStore — KB/Document persistence # KBStore — KB/Document persistence
@ -561,7 +577,9 @@ async def lifespan(app: FastAPI):
app.state.kb_store = KBStore(session_factory=kb_session_factory) app.state.kb_store = KBStore(session_factory=kb_session_factory)
logger.info("KBStore initialized") logger.info("KBStore initialized")
except Exception: except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — KBStore init includes import + DB setup
logger.exception("Failed to initialize KBStore") logger.exception("Failed to initialize KBStore")
# RetrievalEngine — requires llama_index PGVectorStore # RetrievalEngine — requires llama_index PGVectorStore
@ -576,9 +594,13 @@ async def lifespan(app: FastAPI):
session_factory=kb_session_factory, session_factory=kb_session_factory,
) )
logger.info("RetrievalEngine initialized (pgvector ready)") logger.info("RetrievalEngine initialized (pgvector ready)")
except Exception: except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — RetrievalEngine init includes import + pgvector setup
logger.exception("Failed to initialize RetrievalEngine — vector search unavailable") logger.exception("Failed to initialize RetrievalEngine — vector search unavailable")
except Exception: except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — rag_platform top-level init
logger.exception("Failed to initialize rag_platform DB components") logger.exception("Failed to initialize rag_platform DB components")
# HitProcessor — LLM-based answer generation from retrieval results # HitProcessor — LLM-based answer generation from retrieval results
@ -587,7 +609,9 @@ async def lifespan(app: FastAPI):
app.state.hit_processor = HitProcessor(llm_gateway=app.state.llm_gateway) app.state.hit_processor = HitProcessor(llm_gateway=app.state.llm_gateway)
logger.info("HitProcessor initialized") logger.info("HitProcessor initialized")
except Exception: except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — HitProcessor init includes import
logger.exception("Failed to initialize HitProcessor") logger.exception("Failed to initialize HitProcessor")
# TaskManager — async vectorize/batch-index tasks # TaskManager — async vectorize/batch-index tasks
@ -598,7 +622,9 @@ async def lifespan(app: FastAPI):
app.state.task_manager = TaskManager() app.state.task_manager = TaskManager()
logger.info("TaskManager initialized (degraded mode: InMemoryTaskStore)") logger.info("TaskManager initialized (degraded mode: InMemoryTaskStore)")
except Exception: except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — TaskManager init includes import
logger.exception("Failed to initialize TaskManager") logger.exception("Failed to initialize TaskManager")
# KBSettingsStore — per-KB retrieval/hit-processing defaults (process singleton) # KBSettingsStore — per-KB retrieval/hit-processing defaults (process singleton)
@ -607,7 +633,9 @@ async def lifespan(app: FastAPI):
app.state.kb_settings_store = get_settings_store() app.state.kb_settings_store = get_settings_store()
logger.info("KBSettingsStore initialized") logger.info("KBSettingsStore initialized")
except Exception: except asyncio.CancelledError:
raise
except Exception: # noqa: BLE001 — KBSettingsStore init includes import
logger.exception("Failed to initialize KBSettingsStore") logger.exception("Failed to initialize KBSettingsStore")
yield yield
@ -618,7 +646,7 @@ async def lifespan(app: FastAPI):
from agentkit.bitable.db import close_bitable_db from agentkit.bitable.db import close_bitable_db
await close_bitable_db() await close_bitable_db()
except Exception: except (RuntimeError, asyncio.TimeoutError, ConnectionError, OSError):
pass pass
# Stop MCP servers # Stop MCP servers
if mcp_manager is not None: if mcp_manager is not None:
@ -660,7 +688,7 @@ async def lifespan(app: FastAPI):
from agentkit.server.routes.channels import close_all_adapters from agentkit.server.routes.channels import close_all_adapters
await close_all_adapters() await close_all_adapters()
except Exception: except (RuntimeError, asyncio.TimeoutError, ConnectionError, OSError):
logger.debug("close_all_adapters 异常已忽略") logger.debug("close_all_adapters 异常已忽略")
@ -706,7 +734,7 @@ def _on_config_change(app: FastAPI, config: ServerConfig) -> None:
if hasattr(app.state, "agent_pool") and app.state.agent_pool is not None: if hasattr(app.state, "agent_pool") and app.state.agent_pool is not None:
app.state.agent_pool._llm_gateway = new_gateway app.state.agent_pool._llm_gateway = new_gateway
logger.info(f"LLM Gateway reloaded (config v{current_version})") logger.info(f"LLM Gateway reloaded (config v{current_version})")
except Exception as e: except (ValueError, TypeError, KeyError, RuntimeError, ConnectionError, OSError) as e:
logger.error(f"Failed to reload LLM Gateway: {e}") logger.error(f"Failed to reload LLM Gateway: {e}")
# Reload skills if skill paths changed # Reload skills if skill paths changed
@ -730,13 +758,15 @@ def _on_config_change(app: FastAPI, config: ServerConfig) -> None:
elif p.is_file() and p.suffix in (".yaml", ".yml"): elif p.is_file() and p.suffix in (".yaml", ".yml"):
try: try:
loader.load_from_file(str(p)) loader.load_from_file(str(p))
except Exception: except (ValueError, TypeError, KeyError, OSError, RuntimeError):
pass pass
app.state.skill_registry = new_skill_registry app.state.skill_registry = new_skill_registry
if hasattr(app.state, "agent_pool") and app.state.agent_pool is not None: if hasattr(app.state, "agent_pool") and app.state.agent_pool is not None:
app.state.agent_pool._skill_registry = new_skill_registry app.state.agent_pool._skill_registry = new_skill_registry
logger.info(f"Skills reloaded (config v{current_version})") logger.info(f"Skills reloaded (config v{current_version})")
except Exception as e: except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — skills reload top-level; multi-step init
logger.error(f"Failed to reload skills: {e}") logger.error(f"Failed to reload skills: {e}")
# Update config version on all agents # Update config version on all agents
@ -856,7 +886,7 @@ def create_app(
from agentkit.server.middleware import _load_client_keys from agentkit.server.middleware import _load_client_keys
client_keys = _load_client_keys() client_keys = _load_client_keys()
except Exception as e: except (ValueError, TypeError, KeyError, OSError, RuntimeError) as e:
logger.warning(f"Failed to load client keys for AuthMiddleware: {e}") logger.warning(f"Failed to load client keys for AuthMiddleware: {e}")
app.add_middleware( app.add_middleware(
@ -983,7 +1013,7 @@ def create_app(
try: try:
ts_config = {**ts_config, **_json.loads(ts_env)} ts_config = {**ts_config, **_json.loads(ts_env)}
except Exception: except (ValueError, TypeError):
pass pass
task_store = create_task_store( task_store = create_task_store(
backend=ts_config.get("backend", "memory"), backend=ts_config.get("backend", "memory"),
@ -1039,7 +1069,9 @@ def create_app(
db_path=evo_conf.get("db_path", "~/.agentkit/evolution.db"), db_path=evo_conf.get("db_path", "~/.agentkit/evolution.db"),
database_url=evo_conf.get("database_url"), database_url=evo_conf.get("database_url"),
) )
except Exception as e: except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — evolution store init includes import + DB setup
logger.warning(f"Failed to initialize evolution store: {e}") logger.warning(f"Failed to initialize evolution store: {e}")
app.state.evolution_store = None app.state.evolution_store = None
else: else:
@ -1056,7 +1088,9 @@ def create_app(
redis_url=cs_conf.get("redis_url", "redis://localhost:6379"), redis_url=cs_conf.get("redis_url", "redis://localhost:6379"),
session_ttl=cs_conf.get("session_ttl", 86400), session_ttl=cs_conf.get("session_ttl", 86400),
) )
except Exception as e: except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — cascade state store init includes import + setup
logger.warning(f"Failed to initialize cascade state store: {e}") logger.warning(f"Failed to initialize cascade state store: {e}")
app.state.cascade_state_store = None app.state.cascade_state_store = None
else: else:
@ -1138,7 +1172,7 @@ def create_app(
try: try:
epi_session_factory = create_episodic_session_factory(database_url) epi_session_factory = create_episodic_session_factory(database_url)
epi_model = EpisodeModel epi_model = EpisodeModel
except Exception as db_err: except (ConnectionError, OSError, asyncio.TimeoutError, ValueError, KeyError, RuntimeError, ImportError) as db_err:
import logging as _log import logging as _log
_log.getLogger(__name__).warning( _log.getLogger(__name__).warning(
@ -1155,7 +1189,9 @@ def create_app(
pgvector_enabled=epi_conf.get("pgvector_enabled", True), pgvector_enabled=epi_conf.get("pgvector_enabled", True),
table_name=epi_conf.get("table_name", "episodic_memories"), table_name=epi_conf.get("table_name", "episodic_memories"),
) )
except Exception as e: except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — episodic memory init; multi-step with embedder + DB
import logging import logging
logging.getLogger(__name__).warning( logging.getLogger(__name__).warning(
@ -1174,7 +1210,9 @@ def create_app(
retrieve_tool = memory_retriever.create_retrieve_tool() retrieve_tool = memory_retriever.create_retrieve_tool()
if retrieve_tool: if retrieve_tool:
app.state.retrieve_knowledge_tool = retrieve_tool app.state.retrieve_knowledge_tool = retrieve_tool
except Exception as e: except asyncio.CancelledError:
raise
except Exception as e: # noqa: BLE001 — memory components top-level init; multi-step with imports + DB
import logging import logging
logging.getLogger(__name__).warning(f"Failed to initialize memory components: {e}") logging.getLogger(__name__).warning(f"Failed to initialize memory components: {e}")

View File

@ -7,6 +7,12 @@ import logging
import os import os
from typing import Any, Protocol, runtime_checkable from typing import Any, Protocol, runtime_checkable
# redis 可选依赖;未安装时回退为 Exception 以保留原 catch-all 语义
try:
from redis.exceptions import RedisError as _RedisError
except ImportError:
_RedisError = Exception
from agentkit.session.models import Message, Session, SessionStatus from agentkit.session.models import Message, Session, SessionStatus
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -207,7 +213,7 @@ class RedisSessionStore:
try: try:
redis = await self._get_redis() redis = await self._get_redis()
return await redis.ping() return await redis.ping()
except Exception: except (ImportError, OSError, _RedisError, ValueError, KeyError, RuntimeError):
return False return False
@ -283,7 +289,7 @@ class FileSessionStore:
session = Session.from_dict(data["session"]) session = Session.from_dict(data["session"])
if agent_name is None or session.agent_name == agent_name: if agent_name is None or session.agent_name == agent_name:
sessions.append(session) sessions.append(session)
except Exception: except (ValueError, KeyError, TypeError, OSError):
continue continue
sessions.sort(key=lambda s: s.updated_at, reverse=True) sessions.sort(key=lambda s: s.updated_at, reverse=True)
return sessions[:limit] return sessions[:limit]
@ -343,7 +349,7 @@ def create_session_store(
store = RedisSessionStore(redis_url=redis_url, ttl_seconds=ttl_seconds) store = RedisSessionStore(redis_url=redis_url, ttl_seconds=ttl_seconds)
logger.info(f"SessionStore backend: redis") logger.info(f"SessionStore backend: redis")
return store return store
except Exception as exc: except (ImportError, OSError, _RedisError, ValueError, KeyError, RuntimeError) as exc:
logger.warning(f"Failed to initialise RedisSessionStore ({exc}), falling back to InMemorySessionStore") logger.warning(f"Failed to initialise RedisSessionStore ({exc}), falling back to InMemorySessionStore")
store = InMemorySessionStore() store = InMemorySessionStore()

View File

@ -56,7 +56,7 @@ class SkillLoader:
try: try:
skill = self._load_skill_from_file(yaml_path) skill = self._load_skill_from_file(yaml_path)
skills.append(skill) skills.append(skill)
except Exception as e: except (ValueError, KeyError, TypeError, OSError, RuntimeError) as e:
logger.warning(f"Skipping invalid YAML file '{yaml_path}': {e}") logger.warning(f"Skipping invalid YAML file '{yaml_path}': {e}")
# 加载 SKILL.md 文件 # 加载 SKILL.md 文件
@ -66,7 +66,7 @@ class SkillLoader:
try: try:
skill = self.load_from_skill_md(md_path) skill = self.load_from_skill_md(md_path)
skills.append(skill) skills.append(skill)
except Exception as e: except (ValueError, KeyError, TypeError, OSError, RuntimeError) as e:
logger.warning(f"Skipping invalid SKILL.md file '{md_path}': {e}") logger.warning(f"Skipping invalid SKILL.md file '{md_path}': {e}")
return skills return skills
@ -144,7 +144,7 @@ class SkillLoader:
from importlib.metadata import entry_points as _entry_points from importlib.metadata import entry_points as _entry_points
eps = _entry_points().get(group_name, []) eps = _entry_points().get(group_name, [])
except Exception as e: except (ValueError, KeyError, TypeError, OSError, RuntimeError) as e:
logger.warning(f"Failed to discover entry_points for group '{group_name}': {e}") logger.warning(f"Failed to discover entry_points for group '{group_name}': {e}")
return skills return skills
@ -192,7 +192,7 @@ class SkillLoader:
logger.info( logger.info(
f"Loaded skill '{skill.name}' v{skill.version} from entry_point '{ep.name}'" f"Loaded skill '{skill.name}' v{skill.version} from entry_point '{ep.name}'"
) )
except Exception as e: except (ValueError, KeyError, TypeError, OSError, RuntimeError) as e:
logger.warning(f"Failed to load skill from entry_point '{ep.name}': {e}") logger.warning(f"Failed to load skill from entry_point '{ep.name}': {e}")
return skills return skills
@ -208,6 +208,6 @@ class SkillLoader:
tool = self._tool_registry.get(tool_name) tool = self._tool_registry.get(tool_name)
tools.append(tool) tools.append(tool)
logger.info(f"Bound tool '{tool_name}' to skill '{config.name}'") logger.info(f"Bound tool '{tool_name}' to skill '{config.name}'")
except Exception as e: except (ValueError, KeyError, TypeError, OSError, RuntimeError) as e:
logger.warning(f"Failed to bind tool '{tool_name}' to skill '{config.name}': {e}") logger.warning(f"Failed to bind tool '{tool_name}' to skill '{config.name}': {e}")
return tools return tools