feat: Add Loki MCP server scaffold, fix adapter blocking, upgrade model

- Scaffold mcp_servers/loki/ with config and async HTTP client
- Fix Slack/Telegram adapters to use non-blocking connections
- Upgrade default model to claude-sonnet-4-6
- Improve Agent SDK message collection for empty ResultMessage cases
- Add Message-ID to email summaries, increase body truncation limit
- Fix .gitignore inline comments that broke sensitive file exclusions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-22 21:19:28 -07:00
parent fe7c146dc6
commit a9efdc0a01
9 changed files with 135 additions and 23 deletions

View File

@@ -3,13 +3,13 @@
Supports two modes for Claude:
1. Agent SDK (v0.1.36+) - DEFAULT - Uses query() API with Max subscription
- Set USE_AGENT_SDK=true (default)
- Model: claude-sonnet-4-5-20250929 (default for all operations)
- Model: claude-sonnet-4-6 (default for all operations)
- All tools are MCP-based (no API key needed)
- Tools registered via mcp_tools.py MCP server
- Flat-rate subscription cost
2. Direct API (pay-per-token) - Set USE_DIRECT_API=true
- Model: claude-sonnet-4-5-20250929
- Model: claude-sonnet-4-6
- Requires ANTHROPIC_API_KEY in .env
- Uses traditional tool definitions from tools.py
"""
@@ -60,8 +60,8 @@ _USE_AGENT_SDK = os.getenv("USE_AGENT_SDK", "true").lower() == "true"
# Default models by provider
_DEFAULT_MODELS = {
"claude": "claude-sonnet-4-5-20250929",
"claude_agent_sdk": "claude-sonnet-4-5-20250929",
"claude": "claude-sonnet-4-6",
"claude_agent_sdk": "claude-sonnet-4-6",
"glm": "glm-4-plus",
}
@@ -147,9 +147,9 @@ class LLMInterface:
# Set model based on mode
if provider == "claude":
if self.mode == "agent_sdk":
self.model = _DEFAULT_MODELS.get("claude_agent_sdk", "claude-sonnet-4-5-20250929")
self.model = _DEFAULT_MODELS.get("claude_agent_sdk", "claude-sonnet-4-6")
else:
self.model = _DEFAULT_MODELS.get(provider, "claude-sonnet-4-5-20250929")
self.model = _DEFAULT_MODELS.get(provider, "claude-sonnet-4-6")
else:
self.model = _DEFAULT_MODELS.get(provider, "")
@@ -505,6 +505,7 @@ class LLMInterface:
# --- 4. Consume messages until we get a ResultMessage. ---
result_text = ""
assistant_messages = [] # Collect assistant responses
message_count = 0
async for data in query_obj.receive_messages():
message = parse_message(data)
@@ -514,14 +515,29 @@ class LLMInterface:
message_type = type(message).__name__
logger.debug(f"[LLM] Received message #{message_count}: {message_type}")
# Collect text from AssistantMessage objects
if isinstance(message, AssistantMessage):
if hasattr(message, 'content') and message.content:
# Extract text from content blocks
if isinstance(message.content, str):
assistant_messages.append(message.content)
elif isinstance(message.content, list):
for block in message.content:
if hasattr(block, 'type') and block.type == 'text':
if hasattr(block, 'text'):
assistant_messages.append(block.text)
if isinstance(message, ResultMessage):
result_text = message.result or ""
# Use ResultMessage.result if available, otherwise use collected assistant messages
result_text = message.result or "\n".join(assistant_messages)
logger.info(
"[LLM] Agent SDK result received after %d messages: cost=$%.4f, turns=%s",
message_count,
getattr(message, "total_cost_usd", 0),
getattr(message, "num_turns", "?"),
)
if not message.result and assistant_messages:
logger.debug(f"[LLM] ResultMessage.result was empty, using {len(assistant_messages)} collected assistant messages")
break
# Log non-result messages to detect loops