[eric] conventions: one-line-comment + no-gratuitous-blank rules in CLAUDE.md; apply to configure_provider_env (271->223)

This commit is contained in:
ciregenz
2026-06-25 02:11:05 -07:00
parent 87db6a8a2b
commit 3960c2bd46
4 changed files with 36 additions and 64 deletions
+12
View File
@@ -42,6 +42,18 @@ Personal / per-machine notes go in `CLAUDE.local.md` (gitignored), **not** here.
are dynamic-key maps (a registry keyed by a runtime id) and external protocol shapes (the Claude
Agent SDK hook returns, `model_dump` output).
## Comments
- **Every comment is ONE physical line. No exceptions.** Never wrap a comment across multiple `#`
lines — collapse it into a single line (long is fine; multi-line is not).
- **Delete comments that aren't pulling weight.** Keep only the WHY (a non-obvious reason, gotcha, or
ambiguity); delete anything that restates the code, and delete dead/commented-out code outright.
- A module/function docstring is exempt (it's a docstring, not a `#` comment).
## Whitespace
- **No gratuitous blank lines.** One blank line separates logical units; never stack 2+ blank lines.
- **Tight imports** — no blank lines inside an import block beyond the single separator between
stdlib / third-party / local groups. Delete any blank line that isn't doing real readability work.
When you add or change code, the files you touch must be clean under these rules. Pre-existing debt
in files you are not otherwise editing is grandfathered via the linter's exception lists — do not
mass-migrate untouched files.
+4
View File
@@ -13,6 +13,10 @@ All conventions in the root `CLAUDE.md` apply here. Python-specific emphasis:
structured data — model it. Legitimate dicts: dynamic-key registries and external protocol shapes
(SDK hook returns, `model_dump` output).
- **Single-purpose file naming** — a one-export file is named after its export.
- **Comments are ONE line each, no exceptions** — never wrap across multiple `#` lines; keep only
WHY/gotcha comments, delete restating or dead-code comments. Docstrings are exempt.
- **No gratuitous blank lines** — never stack 2+ blanks; keep imports tight (only the single
stdlib/third-party/local group separators).
Touch a file → it must be clean under these rules. Pre-existing debt is grandfathered in
`linter/config/config.json`; don't mass-migrate untouched files.
@@ -1,8 +1,7 @@
"""Configure the SDK environment for the run's provider route: set ANTHROPIC/OPENAI/GOOGLE
auth env vars (direct key, OpenSwarm Pro proxy, OpenRouter, or 9Router) and pin subagent models,
ensuring 9Router is up where the route needs it. Lifted out of the agent loop; mutates
options_kwargs[\"env\"] in place exactly as inline. sub_conns is the active-connection list used
for subagent-model fallback (empty today)."""
ensuring 9Router is up where the route needs it. sub_conns is the active-connection list for
subagent-model fallback (empty today)."""
import os
from typing import Dict, List, Optional
@@ -41,25 +40,14 @@ async def configure_provider_env(
options_kwargs["env"] = {
"ANTHROPIC_API_KEY": global_settings.anthropic_api_key,
"ANTHROPIC_BASE_URL": "https://api.anthropic.com",
# Pin subagent envs so they don't drift back to the proxy.
# Pin subagents so they don't drift back to the proxy.
"CLAUDE_CODE_SUBAGENT_MODEL": "claude-sonnet-4-6",
"ANTHROPIC_SMALL_FAST_MODEL": "claude-haiku-4-5",
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-haiku-4-5",
}
logger.info(f"[MCP-DEBUG] Using direct Anthropic API key (route=api) for {session.model}")
elif is_pinned_api_route and api_route_provider == "openai" and getattr(global_settings, "openai_api_key", None):
# Goes through 9Router's Anthropic→OpenAI translator like
# other own-key routes, but we point OPENAI_BASE_URL at a
# tiny local pass-through (/api/openai-passthrough/v1) that
# renames max_tokens → max_completion_tokens before relaying
# to api.openai.com. OpenAI's GPT-5 family rejects max_tokens
# with HTTP 400, and 9Router 0.3.60 doesn't know about
# max_completion_tokens yet (its CLI<->OpenAI translator
# emits the legacy field). The pin on 0.3.60 is intentional
# (newer 9Router versions regress WebSearch, see
# nine_router.py comment) so we patch the boundary instead
# of bumping. Pre-fix: every gpt-5.* / gpt-5.* own-key
# session 400'd silently.
# openai-passthrough renames max_tokens->max_completion_tokens before relaying (GPT-5 400s on max_tokens; 9Router 0.3.60, pinned for WebSearch, emits the legacy field).
passthrough_url = f"http://127.0.0.1:{os.environ.get('OPENSWARM_PORT', '8324')}/api/openai-passthrough/v1"
options_kwargs["env"] = {
"OPENAI_API_KEY": global_settings.openai_api_key,
@@ -69,9 +57,7 @@ async def configure_provider_env(
}
logger.info(f"[MCP-DEBUG] Using direct OpenAI API key (route=api) for {session.model} via openai-passthrough")
elif is_pinned_api_route and api_route_provider == "custom":
# User-configured OpenAI-compatible endpoint (Ollama Cloud,
# Together, local Ollama, etc.). Routes through 9Router's
# openai-compatible provider node we synced from settings.
# User OpenAI-compatible endpoint (Ollama/Together/LM Studio) via 9Router's synced provider node.
from backend.apps.nine_router import ensure_running as p_9r_ensure_c
if not nine_router_running():
logger.info(f"[MCP-DEBUG] custom provider selected but 9Router not running; waiting for startup")
@@ -90,34 +76,24 @@ async def configure_provider_env(
"ENABLE_TOOL_SEARCH": "auto",
}
if cp:
# Local OpenAI-compatible servers (LM Studio, Ollama, ...)
# often run with auth disabled, the user leaves api_key
# blank in Settings. The OpenAI-style SDK insists on a
# non-empty key; substitute a harmless placeholder so the
# CLI can issue requests. Servers that DO check auth always
# have a real key configured.
# Local servers often run auth-disabled; placeholder key since the OpenAI SDK requires non-empty.
env["OPENAI_API_KEY"] = (cp.api_key or "").strip() or "no-auth-required"
from backend.apps.nine_router import normalize_openai_compat_base_url as norm_cp_url
env["OPENAI_BASE_URL"] = norm_cp_url(cp.base_url or "")
# Pin subagent ids, without these, CLI's default Haiku 4.5
# gets sent to the custom provider and 404s.
# Pin subagents or CLI's default Haiku 4.5 404s on the custom provider.
if global_settings.anthropic_api_key:
env["CLAUDE_CODE_SUBAGENT_MODEL"] = "claude-sonnet-4-6"
env["ANTHROPIC_SMALL_FAST_MODEL"] = "claude-haiku-4-5-20251001"
env["ANTHROPIC_DEFAULT_HAIKU_MODEL"] = "claude-haiku-4-5-20251001"
else:
# Pin to the same custom-provider model so subagents stay
# within the user's configured endpoint instead of hitting
# an unconfigured Anthropic lane.
# No Anthropic key: pin subagents to the custom model so they stay on the user's endpoint.
env["CLAUDE_CODE_SUBAGENT_MODEL"] = resolved_model
env["ANTHROPIC_SMALL_FAST_MODEL"] = resolved_model
env["ANTHROPIC_DEFAULT_HAIKU_MODEL"] = resolved_model
options_kwargs["env"] = env
logger.info(f"[MCP-DEBUG] Using custom provider for {session.model}{resolved_model}")
elif is_pinned_api_route and api_route_provider == "gemini" and getattr(global_settings, "google_api_key", None):
# Routed through the local anthropic-proxy so it can scrub the
# JSON-Schema fields Gemini's API rejects ($schema, additionalProperties,
# propertyNames, exclusiveMinimum, nested const) that 9Router 0.3.60 misses.
# Local anthropic-proxy scrubs JSON-Schema fields Gemini rejects ($schema, additionalProperties, propertyNames, exclusiveMinimum, nested const) that 9Router 0.3.60 misses.
proxy_url = f"http://127.0.0.1:{os.environ.get('OPENSWARM_PORT', '8324')}/api/anthropic-proxy"
options_kwargs["env"] = {
"GEMINI_API_KEY": global_settings.google_api_key,
@@ -127,12 +103,7 @@ async def configure_provider_env(
}
logger.info(f"[MCP-DEBUG] Using direct Google API key (route=api) for {session.model} via local proxy")
elif api_type == "openrouter" and getattr(global_settings, "openrouter_api_key", None):
# OpenRouter primary. The route="openrouter" entry's
# router_model_id is `openrouter/<vendor>/<model>` so
# 9Router routes via the apikey connection synced from
# CLI's WebSearch delegation needs an Anthropic-shaped lane;
# if the user has no Anthropic key/sub/Pro, fall back to OR's
# resold Claude so subagents stay on the same OR billing.
# OpenRouter via 9Router; with no Anthropic key/sub, fall back to OR's resold Claude for subagents (incl. WebSearch delegation) so they stay on the same OR billing.
if not nine_router_running():
from backend.apps.nine_router import ensure_running as nine_router_ensure
logger.info(f"[MCP-DEBUG] OpenRouter selected but 9Router not running; waiting for startup")
@@ -166,33 +137,24 @@ async def configure_provider_env(
options_kwargs["env"] = {
"ANTHROPIC_AUTH_TOKEN": bearer,
"ANTHROPIC_BASE_URL": proxy_url,
# Pin subagent ids; CLI default 'claude-haiku-4-5-20251001'
# gets rejected by Pro's surface as "No credentials for provider: anthropic".
# (Free-trial clamps to its allowed Claude set + weights credits server-side.)
# Pin subagents; Pro rejects CLI's default haiku as "No credentials for provider: anthropic".
"CLAUDE_CODE_SUBAGENT_MODEL": "claude-sonnet-4-6",
"ANTHROPIC_SMALL_FAST_MODEL": "claude-haiku-4-5-20251001",
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-haiku-4-5-20251001",
# auto, never the bare default: tengu_defer_all_bn4 marks every tool
# defer_loading=true, which collides with our cache_control and 400s the
# first tool-laden request (the other anthropic branches all set this).
# auto: tengu_defer_all_bn4 defers every tool, which collides with cache_control and 400s the first tool-laden request.
"ENABLE_TOOL_SEARCH": "auto",
}
# Free lane meters one run per agent task: tag every call of this task (and its
# subagents, which inherit the env) AND its aux calls (title-gen, see generate_title)
# with the session id, so a query plus its title generation is ONE run, not two.
# The base goes straight to the cloud (no 9Router), so the header rides through.
# Tag every call (subagents inherit env) + aux calls with the task id so a query plus its title-gen meter as ONE free run; base goes straight to cloud (no 9Router) so the header rides.
if getattr(global_settings, "connection_mode", "own_key") == "free-trial":
options_kwargs["env"]["ANTHROPIC_CUSTOM_HEADERS"] = f"X-Openswarm-Task-Id: {session.id}"
# The cloud serves every free run as Haiku, so keep the subagent on Haiku too:
# a sonnet subagent makes the CLI attach `effort`, which Haiku 400s on.
# Cloud serves every free run as Haiku; keep the subagent on Haiku too (sonnet adds `effort`, Haiku 400s).
options_kwargs["env"]["CLAUDE_CODE_SUBAGENT_MODEL"] = "claude-haiku-4-5-20251001"
logger.info(f"[MCP-DEBUG] Using OpenSwarm cloud proxy at {proxy_url}")
elif api_type == "anthropic" and not resolved_is_9router and global_settings.anthropic_api_key:
options_kwargs["env"] = {"ANTHROPIC_API_KEY": global_settings.anthropic_api_key}
logger.info("[MCP-DEBUG] Using direct Anthropic API key")
elif nine_router_running():
# Gemini-bound ids go through the local proxy for schema scrubbing;
# everything else hits 9Router directly.
# Gemini-bound ids go through the local proxy for schema scrubbing; everything else hits 9Router directly.
is_gemini_bound = (
isinstance(resolved_model, str)
and resolved_model.startswith(("gemini/", "gc/", "ag/"))
@@ -208,12 +170,7 @@ async def configure_provider_env(
"ANTHROPIC_API_KEY": "9router",
"ANTHROPIC_BASE_URL": "http://localhost:20128",
}
# Pin subagent ids to whichever lane the user has, else CLI's
# default Haiku 4.5 hits 9Router with no Claude route and 401s.
# NOTE: sub_conns is the connection list passed in. Callers currently pass []
# (see RunOptions), so `active` is empty and this pinning is inert until the
# real connection list is wired through — a latent regression from the run/ split,
# surfaced by pyright (the old inline `_conns` reference was left dangling here).
# Pin subagents to whichever lane the user has, else CLI's default Haiku 4.5 hits 9Router with no Claude route and 401s. NOTE: callers pass sub_conns=[] today so this is inert (latent regression from the run/ split; pyright caught the dangling _conns ref).
active = {c.get("provider") for c in sub_conns
if isinstance(c, dict) and c.get("isActive")}
sub_model = None
@@ -241,11 +198,7 @@ async def configure_provider_env(
logger.info(
f"[MCP-DEBUG] 9Router direct, subagent_model={sub_model}, small_fast={small_model}"
)
# ENABLE_TOOL_SEARCH=auto: without it, CLI's tengu_defer_all_bn4
# Statsig flag defers 16 tools with no way to load them on non-
# Anthropic networks. "auto" eagerly loads tools when schema
# budget fits in ~10% of context. Don't pass --bare, sets
# CLAUDE_CODE_SIMPLE=1 which strips the system prompt scaffolding.
# auto eagerly loads tools when the schema budget fits; without it tengu_defer_all_bn4 defers 16 tools unloadable off Anthropic networks. Don't use --bare (strips system prompt).
env["ENABLE_TOOL_SEARCH"] = "auto"
options_kwargs["env"] = env
logger.info(f"[MCP-DEBUG] Using 9Router (api_type={api_type})")
+3
View File
@@ -7,6 +7,9 @@ Python-only rules do not. Specifically:
- **Single-purpose file naming** — a one-export file is named after its export.
- **Type everything** under strict `tsconfig`; model data with typed interfaces, not untyped
objects (the TS equivalent of "no bare dicts").
- **Comments are ONE line each, no exceptions** (`//` — never stack into a multi-line block); keep
only WHY/gotcha, delete restating or dead-code comments. JSDoc blocks are exempt.
- **No gratuitous blank lines** — never stack 2+ blanks; keep imports tight.
- **Does not apply (Python-only):** `@typechecked`, pydantic `BaseModel`, the no-relative-imports
rule (the frontend uses the `@/` path alias and local relative imports), and `p-private`.