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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,7 @@ Two examples already run at this load. The [deep-research engine](https://agentf
| System prompt override | `system_prompt="..."` |
| OpenCode per-run configuration | Preserves caller `OPENCODE_CONFIG_CONTENT` while applying the harness overlay |
| OpenCode prompt compatibility | `AGENTFIELD_OPENCODE_INLINE_SYSTEM_PROMPT=1` enables the opt-in inline rollback |
| Provider-agnostic reasoning variants | `variant="high"` or a `#high` model suffix |
| Multi-layer output recovery | Cosmetic repair → retry → full retry |

#### Connector API (Fleet Management)
Expand Down
2 changes: 2 additions & 0 deletions docs/design/harness-v2-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ fix = await app.harness(
"Refactor to async/await",
provider="codex", # Override default provider
model="o3", # Override default model
variant="high", # Provider-specific reasoning effort
max_turns=100,
tools=["Read", "Write", "Edit", "Bash"],
max_budget_usd=5.0,
Expand Down Expand Up @@ -358,6 +359,7 @@ class HarnessConfig(BaseModel):
# Provider selection: explicit > AGENTFIELD_HARNESS_PROVIDER > "aforge"
provider: str = "aforge" # | "claude-code" | "codex" | "gemini" | "opencode" | "pi" | "omp"
model: Optional[str] = None # None → the provider's own default
variant: Optional[str] = None # Explicit reasoning-effort variant

# Execution limits
max_turns: int = 30
Expand Down
5 changes: 4 additions & 1 deletion docs/harness-providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,10 @@ result = await app.harness(
)
```

An explicit `variant="high"` keyword wins over the suffix. Per provider:
An explicit `variant="high"` keyword wins over the suffix. In Python,
`variant` is also available on `HarnessConfig` and `HarnessRunner.run`; a
per-call `Agent.harness(..., variant=...)` value overrides the configured
default. Per provider:

Pi and OMP accept the same OpenRouter model strings in every SDK, for example
`openrouter/minimax/minimax-m2.7` or
Expand Down
4 changes: 4 additions & 0 deletions sdk/python/agentfield/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -3861,6 +3861,7 @@ async def harness(
schema: Any = None,
provider: Optional[str] = None,
model: Optional[str] = None,
variant: Optional[str] = None,
max_turns: Optional[int] = None,
max_budget_usd: Optional[float] = None,
tools: Optional[List[str]] = None,
Expand All @@ -3885,6 +3886,8 @@ async def harness(
"opencode", "grok", "pi", "omp"). Omit to use
``AGENTFIELD_HARNESS_PROVIDER`` when set, otherwise ``aforge``.
model: Override model identifier. Empty uses the provider's own default.
variant: Provider-specific reasoning-effort variant. Wins over a
``#variant`` suffix on the model when supported by the provider.
max_turns: Maximum agent iterations.
max_budget_usd: Cost cap in USD.
tools: Allowed tools list.
Expand Down Expand Up @@ -3917,6 +3920,7 @@ async def harness(
schema=schema,
provider=provider,
model=model,
variant=variant,
max_turns=max_turns,
max_budget_usd=max_budget_usd,
tools=tools,
Expand Down
3 changes: 3 additions & 0 deletions sdk/python/agentfield/harness/_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ def _resolve_options(
for field_name in [
"provider",
"model",
"variant",
"max_turns",
"max_budget_usd",
"max_retries",
Expand Down Expand Up @@ -253,6 +254,7 @@ async def run(
schema: Any = None,
provider: Optional[str] = None,
model: Optional[str] = None,
variant: Optional[str] = None,
max_turns: Optional[int] = None,
max_budget_usd: Optional[float] = None,
tools: Optional[list[str]] = None,
Expand All @@ -266,6 +268,7 @@ async def run(
overrides = {
"provider": provider,
"model": model,
"variant": variant,
"max_turns": max_turns,
"max_budget_usd": max_budget_usd,
"tools": tools,
Expand Down
7 changes: 7 additions & 0 deletions sdk/python/agentfield/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,13 @@ class HarnessConfig(BaseModel):
"(aforge picks its own; claude-code uses sonnet)."
),
)
variant: Optional[str] = Field(
default=None,
description=(
"Provider-specific reasoning-effort variant (for example, "
'"high" or "minimal"). Wins over a #variant model suffix.'
),
)
max_turns: int = Field(default=30, description="Maximum agent iterations.")
max_budget_usd: Optional[float] = Field(
default=None, description="Cost cap in USD."
Expand Down
2 changes: 2 additions & 0 deletions sdk/python/tests/test_harness_agent_wiring.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ async def test_harness_passes_all_options(self):
"task",
provider="codex",
model="o3",
variant="high",
max_turns=10,
max_budget_usd=5.0,
tools=["Read"],
Expand All @@ -115,6 +116,7 @@ async def test_harness_passes_all_options(self):
_, kwargs = mock_run.call_args
assert kwargs["provider"] == "codex"
assert kwargs["model"] == "o3"
assert kwargs["variant"] == "high"
assert kwargs["max_turns"] == 10
assert kwargs["max_budget_usd"] == 5.0
assert kwargs["tools"] == ["Read"]
Expand Down
3 changes: 3 additions & 0 deletions sdk/python/tests/test_harness_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ async def test_run_resolves_harness_config_defaults_with_per_call_overrides(tmp_
config = SimpleNamespace(
provider="codex",
model="default-model",
variant="low",
max_turns=30,
max_budget_usd=1.5,
tools=["Read", "Write"],
Expand All @@ -422,6 +423,7 @@ async def test_run_resolves_harness_config_defaults_with_per_call_overrides(tmp_
await runner.run(
"hello",
model="override-model",
variant="max",
max_turns=5,
env={"OVERRIDE": "1"},
permission_mode="auto",
Expand All @@ -430,6 +432,7 @@ async def test_run_resolves_harness_config_defaults_with_per_call_overrides(tmp_
assert provider.last_options is not None
assert provider.last_options["provider"] == "codex"
assert provider.last_options["model"] == "override-model"
assert provider.last_options["variant"] == "max"
assert provider.last_options["max_turns"] == 5
assert provider.last_options["max_budget_usd"] == 1.5
assert provider.last_options["permission_mode"] == "auto"
Expand Down
7 changes: 7 additions & 0 deletions sdk/python/tests/test_harness_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def test_harness_config_defaults():

assert cfg.provider == "codex"
assert cfg.model is None
assert cfg.variant is None
assert cfg.max_turns == 30
assert cfg.max_budget_usd is None
assert cfg.max_retries == 3
Expand All @@ -35,6 +36,12 @@ def test_harness_config_defaults():
assert cfg.omp_bin == "omp"


def test_harness_config_accepts_explicit_variant():
cfg = HarnessConfig(provider="opencode", variant="high")

assert cfg.variant == "high"


def test_build_provider_raises_for_unknown_provider():
cfg = HarnessConfig(provider="unknown-provider")

Expand Down
Loading