Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions config/repo_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,56 @@ def should_disable_auto_fix(repo: str) -> bool:
return cast(bool, get_repo_setting(repo, "disable_auto_fix", False))


def get_default_agent_model(repo: str) -> str | None:
"""Return the repository-level default agent model, or ``None`` when unset.

This is the second tier of the per-agent model resolution precedence
(see ``orchestrator/agent_model_resolution.py``):

1. ``PipelineConfig.agent_models[role]`` (per-pipeline override)
2. ``repositories.yaml`` ``default_agent_model`` (this helper)
3. Built-in ``"opus"`` default

The value follows the same classifier as ``agent_models``: a recognised
Claude alias (``opus``, ``opus[1m]``, ``sonnet``, ``sonnet[1m]``,
``haiku``, ``claude-*``) routes through the Anthropic upstream, anything
else routes through the in-cluster LiteLLM proxy with the alias
``"opus"`` presented to Claude Code (cq-5 mitigation).

Args:
repo: Repository in "owner/repo" format

Returns:
The configured model string, or ``None`` when the repo has no
per-repo entry, the entry omits ``default_agent_model``, or the
``repositories.yaml`` file is absent (a missing config file is the
same observable as a missing entry — preserves the
no-op-by-default invariant for callers like ``resolve_agent_model``
that run inside spawn paths where the config file may not be
present, e.g. unit tests and ephemeral CI environments).

Raises:
ValueError: When ``default_agent_model`` is set to a non-string
YAML value (e.g. ``default_agent_model: 4``). Surfacing the
misconfiguration loudly here keeps it out of ``classify_model``,
where a non-string would otherwise raise an opaque ``TypeError``
from the regex internals.
"""
try:
value = get_repo_setting(repo, "default_agent_model", None)
except FileNotFoundError:
return None
if value is None:
return None
if not isinstance(value, str):
raise ValueError(
f"default_agent_model for {repo!r} must be a string, got "
f"{type(value).__name__}: {value!r}. Set it to a recognised "
f"Claude alias (opus, sonnet, …) or a LiteLLM model name."
)
return value


try:
from egg_config.validators import validate_checks
except ImportError:
Expand Down
18 changes: 18 additions & 0 deletions config/repositories.yaml.example
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,17 @@ readable_repos:
# **/*_test.go in tests_globs). Security-relevant blocklists
# (.egg-state/contracts/, .github/) are hard-coded and cannot be
# relaxed. See docs/guides/sdlc-pipeline.md#per-repository-role-patterns.
# - default_agent_model: Repository-level default for the per-agent
# model knob added in #2769. Used by every agent role unless the
# pipeline submission overrides it via ``agent_models``. A recognised
# Claude alias (opus, opus[1m], sonnet, sonnet[1m], haiku, claude-*)
# routes through the Anthropic upstream; anything else routes through the
# in-cluster LiteLLM proxy with the recognised alias "opus" presented
# to Claude Code (cq-5 mitigation). Precedence:
# PipelineConfig.agent_models[role]
# > this default_agent_model
# > built-in "opus" default
# Default: not set (every role runs on built-in "opus").
repo_settings:
# Example:
# YOUR_USERNAME/egg:
Expand Down Expand Up @@ -190,6 +201,13 @@ repo_settings:
# tests_globs: ["**/__tests__/**", "**/*.test.ts", "**/*.test.tsx", "**/*.spec.ts"]
# code_globs: ["**/*.ts", "**/*.tsx", "**/*.js"]
# docs_globs: ["**/*.md", "docs/"]
#
# # Per-agent model example (#2769): route every role on this repo through
# # the LiteLLM proxy by default, picking up the hosted-Qwen model entry
# # populated in the LiteLLM ConfigMap. The pipeline-level ``agent_models``
# # field still wins when set.
# YOUR_USERNAME/qwen-pilot:
# default_agent_model: qwen3-coder-30b

# User mode configuration (optional)
# When auth_mode is set to "user" for a repo, operations will be
Expand Down
22 changes: 15 additions & 7 deletions docs/architecture/upstream-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,15 @@ when LiteLLM is not configured.

Status: this seam lands in two stacked changes for [#2769](https://github.com/jwbron/egg/issues/2769). Slice 1 — described here — is the
**gateway router + LiteLLM Deployment**, no-op by default. Slice 2
(per-agent model config) adds the orchestrator-side resolution and the
body-rewrite that connects an agent role to a concrete upstream model;
it ships the operator guide at `docs/guides/per-agent-models.md` (that
file does not exist until slice 2 lands).
adds the orchestrator-side resolution (`PipelineConfig.agent_models`,
`default_agent_model`, `resolve_agent_model`) and the gateway-side
body rewrite (`_rewrite_upstream_model`) that together connect an
agent role to a concrete upstream model. Operators looking to actually
flip a role to a non-Claude backend should start at the
[Per-Agent Models guide](../guides/per-agent-models.md) — it walks
through the two configuration knobs, the precedence chain, the cq-5
recognized-alias mitigation, and the cq-4 operator smoke test
end-to-end.

## Why a router, not a hard-wired second client

Expand Down Expand Up @@ -414,9 +419,12 @@ The full set is at [`.egg-state/contracts/issue-2769.json`](../../.egg-state/con
- [Orchestrator Architecture](orchestrator.md) — Spawner and
session-creation context for slice 2's per-agent model
resolution
- Per-Agent Models Guide — `docs/guides/per-agent-models.md`, added in
slice 2 (not present yet) — how an operator actually flips an agent
role to a non-Claude model end-to-end
- [Per-Agent Models Guide](../guides/per-agent-models.md) — Operator
walkthrough for the slice-2 configuration plumbing
(`PipelineConfig.agent_models`, repo-level `default_agent_model`,
the `resolve_agent_model` precedence + classifier, the gateway-side
`_rewrite_upstream_model` helper, the cq-5 recognized-alias
mitigation, and the cq-4 hosted-Qwen smoke test)
- [Network Isolation](network-isolation.md) — Cluster network
posture; LiteLLM is gateway-only and NetworkPolicy is unchanged
- Issue [#2769](https://github.com/jwbron/egg/issues/2769) — Original
Expand Down
Loading
Loading