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:
parent
838a05772e
commit
38b9602964
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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}"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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}")
|
||||||
|
|
||||||
# 筛选候选
|
# 筛选候选
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"),
|
||||||
|
|
|
||||||
|
|
@ -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 []
|
||||||
|
|
|
||||||
|
|
@ -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 []
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 []
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 ""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,8 @@ 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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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": []}
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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}")
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue