Skip to content

Fix ClaudeOR: direct OpenRouter API call#14

Open
kaypeter87 wants to merge 3 commits into
agentic-box:mainfrom
kaypeter87:claudeor-direct-api
Open

Fix ClaudeOR: direct OpenRouter API call#14
kaypeter87 wants to merge 3 commits into
agentic-box:mainfrom
kaypeter87:claudeor-direct-api

Conversation

@kaypeter87
Copy link
Copy Markdown

@kaypeter87 kaypeter87 commented Mar 30, 2026

Summary

  • claude --print returns empty responses through OpenRouter's Anthropic-compatible endpoint, making ClaudeOR unusable in council deliberation
  • Replaces the Claude CLI subprocess with a direct HTTP call to OpenRouter's /v1/chat/completions API via a standalone Python script (claudeor_api.py, stdlib only — no new dependencies)
  • Also unlocks non-Anthropic models for ClaudeOR since it's no longer constrained to the Anthropic Messages API format

Changes

  • New: owlex/agents/claudeor_api.py — standalone script that reads prompt from stdin, calls OpenRouter, prints response to stdout
  • Modified: owlex/agents/claudeor.py — runner now spawns python3 claudeor_api.py instead of claude --print. Stateless (no session resume), so council R2 falls back to exec mode with full context automatically

Test plan

  • All 152 existing tests pass
  • claudeor_api.py tested standalone — returns correct response from OpenRouter
  • Full council_ask with ClaudeOR participating (requires MCP server restart)

🤖 Generated with Claude Code

kaypeter87 and others added 3 commits March 30, 2026 09:33
claude --print returns empty responses when routed through OpenRouter's
Anthropic-compatible endpoint. This replaces the CLI subprocess with a
direct HTTP call to OpenRouter's /v1/chat/completions API using Python
stdlib (urllib), which also unlocks non-Anthropic models.

The approach is stateless (no session resume), so council R2 naturally
falls back to exec mode with full context.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Background tasks (e.g. council_ask) return a task_id immediately while
work continues in an asyncio task. That background task was calling
ctx.info() per line of subprocess output, which tags every notification
with the originating request_id via FastMCP's send_log_message. By then
the tool call had already been responded to, so every notification
referenced a stale request_id — combined with the flood from 3-5
parallel streaming agents, this caused clients to drop the stdio
transport mid-deliberation.

Route background notifications through a new send_mcp_log helper that
calls session.send_log_message directly (no related_request_id), and
drop the per-line notification in _read_stream_lines (output is still
captured in task.output_lines). Refactor Council.notify to use the
same helper, which also fixes its silent exception swallowing and
missing asyncio.shield.
Claude Code's MCP client (and any spec-conformant client) drops the
stdio transport on receipt of a progress notification carrying a
progressToken it didn't opt into via the request's _meta.progressToken.
Council.notify was hardcoding progress_token="owlex-council" and
emitting a notifications/progress message on every milestone, so every
council_ask call disconnected the server within milliseconds of the
tool returning.

Remove the send_progress_notification call and the progress parameter
entirely; log messages (which don't require a token) already carry the
same milestone info through send_mcp_log. If progress reporting is
re-added later it must read the progressToken from the originating
request's _meta instead of fabricating one.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant