mirror of
https://github.com/hanzoai/insights.git
synced 2026-10-30 16:15:31 +00:00
The tree was a debranded snapshot of upstream taken around 2026-02-20, and upstream has moved about five months since. This replaces it with upstream master, mechanically re-derived: bin/debrand does the rename, and the pipeline and squashed migrations are restored from the shipped tree. Not a merge. The previous import replaced the tree wholesale in a single commit, so there is no shared content history to merge against. Reproducing the rename on the new tree is what actually converges. What survives: .hanzo/workflows, the Dockerfiles and bin/docker* entrypoints, compose files, LLM.md, and insights/migrations. The squashed initial migration is load-bearing — the live database records it by name, so upstream's ~918 individual migrations cannot come back. Nine products are dropped as unreferenced. Twenty-one more that we do not run are kept anyway because the core app still imports them; unpicking those is a refactor and does not belong in a rebase. This base does not build yet, and the reasons are not in the rename: upstream now wants Python 3.13.13 against our 3.12.12 and Django 5.2 against our 4.2, and the dependency manifests still name upstream packages that we republish under our own names. Those are decisions, not renames.
165 lines
7.0 KiB
Python
165 lines
7.0 KiB
Python
"""Per-team, per-step agent runtime/model overrides for the signals agentic steps, resolved from
|
|
the `signals-pipeline-models` flag payload.
|
|
|
|
Each agentic step (report research, repo selection, scout, custom-agent loops) can be pinned to a
|
|
different runtime/model/effort per team, with no deploy — e.g. trial Codex + gpt-5.5 on `research`
|
|
for one team while everyone else stays on the default Claude runtime. Mirrors the `signals-scout`
|
|
payload pattern in `scout_harness/team_limits.py`: one 100%-on payload flag, read for a synthetic
|
|
discovery distinct_id, parsed defensively so a malformed payload falls back to the agent-server
|
|
default rather than breaking a run.
|
|
|
|
Payload shape (`team_configs` maps team id — or the `*` wildcard — to per-step overrides):
|
|
|
|
{
|
|
"team_configs": {
|
|
"2": {
|
|
"steps": {
|
|
"research": {"runtime_adapter": "codex", "model": "gpt-5.5", "reasoning_effort": "xhigh"},
|
|
"repo_selection": {"runtime_adapter": "codex", "model": "gpt-5.5"},
|
|
"*": {"model": "claude-sonnet-4-6"}
|
|
}
|
|
},
|
|
"*": {"steps": {"research": {"runtime_adapter": "codex", "model": "gpt-5.5", "reasoning_effort": "xhigh"}}}
|
|
}
|
|
}
|
|
|
|
Resolution for `(team_id, step)` is most-specific-first, first match wins (atomic — a step block's
|
|
fields are taken as a set so a Codex runtime never pairs with a Claude model):
|
|
|
|
team_configs[team_id].steps[step]
|
|
→ team_configs[team_id].steps["*"]
|
|
→ team_configs["*"].steps[step]
|
|
→ team_configs["*"].steps["*"]
|
|
→ default (agent-server Claude runtime)
|
|
|
|
A resolved override threads through `CustomPromptSandboxContext`
|
|
(runtime_adapter/model/reasoning_effort) → `Task.create_and_run` → the run state → the agent server
|
|
(see `products/tasks` RuntimeAdapter). A step config may set only `model` (swap the model within the
|
|
default Claude runtime) or `runtime_adapter` + `model` together (switch the whole harness, e.g.
|
|
Codex). Missing fields stay `None` (agent-server default).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from dataclasses import dataclass
|
|
|
|
import hanzo_insights
|
|
|
|
from insights.exceptions_capture import capture_exception
|
|
|
|
# Must stay 100%-on so the payload is served for the synthetic discovery distinct_id.
|
|
SIGNALS_PIPELINE_MODELS_FLAG = "signals-pipeline-models"
|
|
|
|
# Config is team-keyed in the payload, not per-user, so the read uses a fixed distinct_id.
|
|
PIPELINE_MODELS_DISCOVERY_DISTINCT_ID = "internal_signals_pipeline_models_discovery"
|
|
|
|
WILDCARD = "*"
|
|
|
|
# Step names match the `ai_stage` tags on $ai_generation so payload authors target the
|
|
# vocabulary they see in LLM analytics. Scout generations tag `scout:<skill>` for per-scout
|
|
# cost attribution; the step key here stays the bare stage.
|
|
STEP_SCOUT = "scout"
|
|
STEP_RESEARCH = "research"
|
|
STEP_REPO_SELECTION = "repo_selection"
|
|
STEP_IMPLEMENTATION = "implementation"
|
|
STEP_CUSTOM_AGENT = "custom_agent"
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class AgentRuntime:
|
|
"""The runtime/model/effort override for one signals agentic sandbox run.
|
|
|
|
All-`None` keeps the agent server's built-in default (the Claude runtime). The three move as a
|
|
set — a Codex runtime with a Claude model id would mis-route — so a payload step block resolves
|
|
them together, atomically.
|
|
"""
|
|
|
|
runtime_adapter: str | None = None
|
|
model: str | None = None
|
|
reasoning_effort: str | None = None
|
|
|
|
|
|
DEFAULT_RUNTIME = AgentRuntime()
|
|
|
|
# The Codex trial config (used by the local `analyze_report --codex` override).
|
|
CODEX_RUNTIME = AgentRuntime(runtime_adapter="codex", model="gpt-5.5", reasoning_effort="xhigh")
|
|
|
|
|
|
def _read_flag_payload() -> dict | None:
|
|
"""Read + parse the `signals-pipeline-models` flag payload once.
|
|
|
|
The flag must stay 100%-on so the payload is served for the synthetic discovery distinct_id;
|
|
`match_value=True` forces the true-variant payload under local evaluation. Returns the parsed
|
|
dict, or `None` when the payload is absent / not an object / unreadable — a read error never
|
|
breaks a run, callers fall back to the default. Mirrors `scout_harness/team_limits._read_flag_payload`.
|
|
"""
|
|
try:
|
|
payload = hanzo_insights.get_feature_flag_payload(
|
|
SIGNALS_PIPELINE_MODELS_FLAG, PIPELINE_MODELS_DISCOVERY_DISTINCT_ID, match_value=True
|
|
)
|
|
if isinstance(payload, str):
|
|
payload = json.loads(payload)
|
|
return payload if isinstance(payload, dict) else None
|
|
except Exception as error:
|
|
capture_exception(error)
|
|
return None
|
|
|
|
|
|
def _parse_runtime(step_block: object) -> AgentRuntime:
|
|
"""Parse one step's config blob into an `AgentRuntime`; anything malformed → default.
|
|
|
|
Fields are optional and independently typed-checked: a non-string (or empty) value for a field
|
|
is dropped to `None` rather than failing the whole block, so a typo in `reasoning_effort` can't
|
|
silently un-set the `model`. A non-dict block (or empty) yields the default — the "fail to
|
|
parse → fall back to defaults" contract."""
|
|
if not isinstance(step_block, dict):
|
|
return DEFAULT_RUNTIME
|
|
|
|
def _str_or_none(key: str) -> str | None:
|
|
value = step_block.get(key)
|
|
return value if isinstance(value, str) and value else None
|
|
|
|
return AgentRuntime(
|
|
runtime_adapter=_str_or_none("runtime_adapter"),
|
|
model=_str_or_none("model"),
|
|
reasoning_effort=_str_or_none("reasoning_effort"),
|
|
)
|
|
|
|
|
|
def _resolve_from_payload(payload: dict | None, team_id: int, step: str) -> AgentRuntime:
|
|
"""Resolve `(team_id, step)` against an already-read payload, most-specific-first.
|
|
|
|
Walks team layers (`team_id` then `*`) and, within each, step layers (`step` then `*`); the
|
|
first present step block wins and is parsed atomically. A present-but-malformed block parses to
|
|
the default (and stops the walk) — a deliberate config of that exact slot. Absent everywhere →
|
|
default."""
|
|
if not isinstance(payload, dict):
|
|
return DEFAULT_RUNTIME
|
|
team_configs = payload.get("team_configs")
|
|
if not isinstance(team_configs, dict):
|
|
return DEFAULT_RUNTIME
|
|
|
|
for team_key in (str(team_id), WILDCARD):
|
|
team_block = team_configs.get(team_key)
|
|
if not isinstance(team_block, dict):
|
|
continue
|
|
steps = team_block.get("steps")
|
|
if not isinstance(steps, dict):
|
|
continue
|
|
for step_key in (step, WILDCARD):
|
|
step_block = steps.get(step_key)
|
|
if step_block is not None:
|
|
return _parse_runtime(step_block)
|
|
return DEFAULT_RUNTIME
|
|
|
|
|
|
def resolve_agent_runtime(team_id: int, step: str) -> AgentRuntime:
|
|
"""The runtime/model/effort override for `(team_id, step)`, or the default when unconfigured.
|
|
|
|
Reads the `signals-pipeline-models` payload once and resolves most-specific-first (see
|
|
`_resolve_from_payload`). Any failure — unreadable/malformed payload — falls back to the
|
|
default; gating the runtime must never be able to fail an agentic run. Blocking network I/O
|
|
(the payload read), so async callers wrap this in `database_sync_to_async`."""
|
|
return _resolve_from_payload(_read_flag_payload(), team_id, step)
|