Skip to content

feat(model_group): Thompson-sample intra-group live routing - #1034

Draft
seonghobae wants to merge 7 commits into
codex/nim-evidence-successorfrom
feat/thompson-sampling-model-group-selection
Draft

feat(model_group): Thompson-sample intra-group live routing#1034
seonghobae wants to merge 7 commits into
codex/nim-evidence-successorfrom
feat/thompson-sampling-model-group-selection

Conversation

@seonghobae

@seonghobae seonghobae commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Scope

Live model-group serving now uses Thompson-sampled per-member stability evidence while admin/report reads remain deterministic. The live score keeps this repository's existing physical quantity: sampled stability divided by measured EWMA latency. Members with no completed outcomes retain the existing neutral/static-order cold-start contract.

Exact identity

TDD / review repair

The original implementation was ad00e1b7695ec153dfac015cba40b87084befa1b.

A valid review finding showed that fractional prior replacement can create binary-float drift such as 0.9999999999999999; the prior implementation then truncated one completed Bernoulli outcome to zero and disabled sampled routing for that member.

  • RED: c8223a3e4f10c2c48396359abdbad7520b84e920 reproduces the invariant violation after a 1.0 -> 0.4 prior refresh and asserts both report and routing observation counts remain exactly one.
  • GREEN: 40a5cc41e4e10525e6fdb73f4a7b19682a083e24 centralizes integer completed-outcome recovery in _outcome_count and uses it in both member_observation_count and member_report.
  • Research traceability: dabbe13ce5e90fa0e3f9650c42e25884e595b4c4 adds docs/papers/thompson-sampling-model-group-routing.md with APA-style primary publication references and implementation/acceptance mapping.

The floating-count and research-artifact review threads are resolved on the current head. The research-artifact thread is resolved because the missing artifact itself is now present.

Research boundary

Grounding is Thompson (1933), Chapelle & Li (2011), and Agrawal & Goyal (2012). The COLT result is not claimed as a regret/optimality proof for this repository's composite Beta stability sample / EWMA latency score; its theorem applies to the analyzed stochastic Bernoulli bandit setting. The repository-specific cold-start rule for unobserved members is also documented as a product policy rather than a consequence of Thompson-sampling theory. A buyer-facing optimality claim requires right-cleared traffic/replay evidence for the composite objective.

Current gate state

No merge is authorized yet. On exact head dabbe13c...:

  • CodeQL PR run 33698259878: startup_failure before analysis evidence
  • Tests 33698258557: queued
  • Security Scan 33698258578: queued
  • Security 33698258735: queued
  • SAST Semgrep 33698258659: queued
  • Fuzz 33698258508: queued
  • OSV-Scanner PR 33698258920: queued
  • Scorecard PR 33698258504: non-terminal

The organization runner/control-plane evidence has been handed to canonical owner issue ContextualWisdomLab/.github#712. This PR does not use no-op retriggers, force-push, self-approval, or required-check weakening. Ordinary merge waits for final exact-head terminal GREEN evidence and valid review resolution.

Current exact-head gate

  • exact head: b03f3b10211e188e81313cba5e1cf641991a6f71
  • trusted prerequisite base: fix(nim): preserve current hosted-access evidence on trusted branch #1068 at fa5446294ae7ae69f1c2958aa1ab6c071fa760bc
  • effective diff is the six Thompson-routing, research, and regression files
  • focused model-group, multimodal, uptime, paper, and workflow contracts: 93 passed
  • exact stacked full suite: 3399 passed, 2 skipped
  • git diff --check: pass

All review threads are resolved. Fresh hosted Checks and independent current-head review remain required; no predecessor gate evidence transfers.

Live serving selection (ModelGroupRouter.sampled_ranked_member_ids,
wired in via _measured_member_order(sample=True) -> _refine_partition,
which every _ranked_agents caller funnels through) now draws one
Thompson sample per member from its own Beta(alpha, beta) posterior
instead of comparing the posterior mean, for any member with at least
one real observation. Admin/report reads (get_model_group,
list_model_groups) keep calling _measured_member_order with the
default sample=False, so they stay exactly deterministic.

Traffic consequence: previously, once one group member edged ahead on
the posterior-mean score, _all_ subsequent traffic to that group went
to it -- a transient failure or a lucky first success could silently
starve every sibling member of further observations forever. Now
traffic spreads across close competitors in proportion to genuine
posterior uncertainty: a member is picked with probability roughly
equal to its probability of actually being the best (the classic
Thompson Sampling exploration/exploitation balance), so a stronger
member still wins most requests while a plausible sibling keeps
getting enough real traffic to either confirm or disprove its early
read. Members with zero observations are unaffected -- they keep
sharing the exact neutral UNOBSERVED_MEMBER_SCORE, unsampled, so a
group with no evidence yet still falls back to the caller's static
order untouched.

The Beta(0, x)/Beta(x, 0) degenerate case (a legal prior_alpha/beta of
exactly 0.0 via update_prior, or a custom prior_resolver) falls back to
the same closed-form posterior mean _score_locked already uses, instead
of raising out of random.betavariate (`alpha and beta must be > 0`).

Grounding: Thompson (1933), Biometrika 25(3-4); the empirical case for
plain (untuned) Beta-posterior TS over posterior-reshaping in Chapelle
& Li (2011), NeurIPS 24; the finite-time near-optimal regret bound for
Beta-Bernoulli TS in Agrawal & Goyal (2012), COLT'12 -- confirming the
large single-observation variance this ships with (win probability
5/6 for the true winner, dropping to ~1-in-600 in this repo's own
success/failure + EWMA-latency score once one member has any timed
success) is the intended exploration mechanism, not a defect to damp.

Test reconciliation: 5 existing tests that observe exactly one success
and one failure across a live-serving group -- ordering that is now
sampled instead of deterministic -- seed orchestrator._group_router._rng
with a fixed random.Random(0) (verified locally: the success member
wins at that seed in every affected fixture) right before their
observe_* calls, matching this file's existing convention of reaching
into router internals directly. Every other group-bearing test in the
repo was traced by hand and needs no change: either it asserts through
ranked_member_ids/member_report/member_score directly (untouched
methods), calls _measured_member_order positionally so sample defaults
to False, has zero real observations on more than one group member
(both stay at the shared UNOBSERVED_MEMBER_SCORE), resolves through a
downstream price-ordered or single-member path that never reaches
sampled_ranked_member_ids, or pins an exact provider model string
outside group-alias resolution entirely. Three new unit tests exercise
sampled_ranked_member_ids directly: the unobserved/neutral-score
contract, the degenerate zero-alpha/beta fallback, and the general
1-in-6 exploration case (a seeded 200-trial win-rate check) that the
higher-level integration tests don't have to carry.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 2, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 2108f3e6-8d5d-458a-9ecc-9dbd83089069

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

devin-ai-integration[bot]

This comment was marked as resolved.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

Devin Review

Comment thread contextual_orchestrator/model_group.py

Copy link
Copy Markdown
Contributor Author

Autonomous loop note: exact head dabbe13ce5e90fa0e3f9650c42e25884e595b4c4 (base main@212ff437, pre-dates the json import fix) failed three checks, matching the same pattern seen on #1012/#1020 today:

  • Full unit and contract suite (run 33698258557): known tests/test_admin_contract.py::test_model_group_mutations_refresh_audit_events - NameError: name 'json' is not defined — already fixed on main (212ff43739a4348e); unrelated to this PR's Thompson-sampling diff.
  • CodeQL analysis (run 33698258735): "CodeQL job status was configuration error" — known org-wide advanced-configuration-vs-default-setup conflict tracked centrally in .github.
  • noema-review (run 33698255970): 502 Bad Gateway, phase=connecting.

Called update_pull_request_branch to merge current main in, clearing the stale-fixture failure. This PR's own "no merge is authorized yet" gate (queued/non-terminal exact-head evidence) stands untouched — this is CI hygiene only.


Generated by Claude Code

@seonghobae
seonghobae changed the base branch from main to codex/nim-evidence-successor September 4, 2026 19:39

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

OpenCode reviewed the current-head product diff. Coverage is a separate gate.

Changed files

  • contextual_orchestrator/model_group.py — Python module behavior
  • contextual_orchestrator/orchestrator.py — Python module behavior
  • docs/papers/thompson-sampling-model-group-routing.md — operator or user guidance
  • tests/test_model_group.py — regression suite
  • tests/test_multimodal_model_group_http.py — regression suite
  • tests/test_openrouter_uptime.py — regression suite

Changed behavior

flowchart LR
  PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
  Evidence --> S1["Python: model_group.py (2 files)"]
  S1 --> I1["Python module behavior"]
  I1 --> R1["Review risk: Python: model_group.py (2 files)"]
  R1 --> V1["pytest plus coverage"]
  Evidence --> S2["Docs: thompson-sampling-model-group-routing.md"]
  S2 --> I2["operator or user guidance"]
  I2 --> R2["Review risk: Docs: thompson-sampling-model-group-routing.md"]
  R2 --> V2["docs review"]
  Evidence --> S3["Test: test_model_group.py (3 files)"]
  S3 --> I3["regression suite"]
  I3 --> R3["Review risk: Test: test_model_group.py (3 files)"]
  R3 --> V3["targeted test run"]
Loading

Findings

No source-backed product finding is synthesized from the coverage gate. A coverage miss belongs in the status comment.

  • Head SHA: b03f3b10211e188e81313cba5e1cf641991a6f71
  • Workflow run: 33926041351
  • Workflow attempt: 1
  • Coverage gate: failure

Review outcome

Coverage is a gate, not the review. This body reviews the changed product files.

Changed-File Evidence Map

flowchart LR
  PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
  Evidence --> S1["Python: model_group.py (2 files)"]
  S1 --> I1["Python module behavior"]
  I1 --> R1["Review risk: Python: model_group.py (2 files)"]
  R1 --> V1["pytest plus coverage"]
  Evidence --> S2["Docs: thompson-sampling-model-group-routing.md"]
  S2 --> I2["operator or user guidance"]
  I2 --> R2["Review risk: Docs: thompson-sampling-model-group-routing.md"]
  R2 --> V2["docs review"]
  Evidence --> S3["Test: test_model_group.py (3 files)"]
  S3 --> I3["regression suite"]
  I3 --> R3["Review risk: Test: test_model_group.py (3 files)"]
  R3 --> V3["targeted test run"]
Loading

@opencode-agent

opencode-agent Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

OpenCode Review Overview

Coverage evidence did not pass, so approval is blocked. The formal pull-request review is the source-backed diff review, not this status comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request priority: medium Normal-priority or P2 work type: feature New or expanded product capability

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant