Skip to content

feat: add model-specific account pools#200

Open
lubshad wants to merge 4 commits into
ndycode:mainfrom
lubshad:feat/model-account-pools
Open

feat: add model-specific account pools#200
lubshad wants to merge 4 commits into
ndycode:mainfrom
lubshad:feat/model-account-pools

Conversation

@lubshad

@lubshad lubshad commented Jul 17, 2026

Copy link
Copy Markdown

Summary

  • Add modelAccountPools for routing effective model IDs through preferred stable account IDs.
  • Add codex-pool to inspect and manage those pools with 1-based account numbers while persisting stable account IDs.
  • Support status, set, add, remove, clear, dryRun, and redacted JSON output.
  • Preserve configured rotation behavior within the preferred pool and fall back to the healthy general pool when needed.
  • Serialize atomic config writes, preserve unrelated config fields, and refuse malformed pool configuration.
  • Document routing, pool management, fallback behavior, and global-config/per-project-account implications.

Testing

  • npm run typecheck
  • npm run lint
  • npm test (113 files, 2,796 tests)
  • npm run build
  • npm run audit:ci

Compliance Confirmation

  • This change stays within the repository scope and OpenAI Terms of Service expectations.
  • This change uses official authentication flows only and does not add bypass, scraping, or credential-sharing behavior.
  • Tests and documentation were updated for user-facing behavior.

Notes

  • Account indexes are accepted only by codex-pool; persisted modelAccountPools values remain stable account IDs.
  • Restart OpenCode after applying a pool mutation.

Summary by CodeRabbit

  • New Features
    • Added modelAccountPools to route requests to preferred, healthy accounts per normalized model.
    • Introduced the new codex-pool tool for viewing and mutating per-model preferred account pools.
    • Updated sticky, round-robin, and hybrid routing to respect preferred account pools (with health-based fallbacks).
  • Bug Fixes
    • Enhanced routing diagnostics with accountPoolMode (preferred, general, general-fallback) and configuredAccountPoolSize in both text and TUI views.
  • Documentation
    • Documented configuration, mutation/restart behavior, routing rules, and privacy guidance for account-ID mappings.
  • Tests
    • Added coverage for pool management, diagnostics, matching rules, and fallback scenarios.

note: greptile review for oc-chatgpt-multi-auth. cite files like lib/foo.ts:123. confirm regression tests + windows concurrency/token redaction coverage.

Greptile Summary

adds modelAccountPools config to route requests for a given model through a preferred subset of healthy accounts, with automatic fallback to the general pool. introduces the codex-pool tool for 1-based account-number management that persists stable account IDs.

  • rotation layer (lib/accounts/rotation.ts): getPreferredSelectableIndices builds a healthy preferred set at selection time and returns null when empty, transparently falling back to the full general pool across all three strategies (sticky, round-robin, hybrid).
  • config layer (lib/config.ts): mutations are serialized in-process via a promise queue and cross-process via proper-lockfile; atomic writes use the existing renameWithWindowsRetry helper, preserving all unrelated config keys.
  • visibility (index.ts, lib/runtime.ts): accountPoolMode (general | preferred | general-fallback) and configuredAccountPoolSize are added to both the text and TUI routing diagnostics snapshots.

Confidence Score: 5/5

safe to merge — the core routing, serialization, and fallback paths are all correct and well-tested.

the promise-queue + file-lock concurrency model is sound, isSelectable correctly rejects disabled accounts before they enter the preferred set, atomic writes use the established renameWithWindowsRetry path, and all three fallback modes are exercised by the new integration tests. no incorrect behavior was found in the changed paths.

lib/tools/codex-pool.ts — the accounts schema accepts floats; minor fix only.

Important Files Changed

Filename Overview
lib/accounts/rotation.ts adds preferredAccountIds filtering to all three rotation strategies; getPreferredSelectableIndices correctly respects isSelectable (including enabled=false) and falls back to null when no preferred account is healthy
lib/config.ts adds updateModelAccountPool with correct in-process promise-queue serialization + proper-lockfile cross-process guard, atomic temp-file write via renameWithWindowsRetry, and a clean getModelAccountPool reader
lib/tools/codex-pool.ts new tool correctly separates 1-based user-facing indexes from stable persisted IDs; accounts schema uses z.number() without .int(), so a float like 1.5 passes schema validation but fails at runtime in resolveAccountIds
index.ts preferredAccountIds computed once per outer retry-loop iteration (correctly recomputed after model fallback); accountPoolMode ternary correctly maps general/preferred/general-fallback; Array.includes() used on a small array so no real perf concern
lib/schemas.ts adds modelAccountPools schema with correct z.record(z.string().min(1), z.array(z.string().min(1))) shape; consistent with existing field conventions
lib/runtime.ts adds accountPoolMode and configuredAccountPoolSize to SelectionSnapshot (optional) and RoutingVisibilitySnapshot (required with null sentinel); type changes are backward-compatible
test/model-pool-config.test.ts good coverage of set/add/remove/clear mutations, concurrent writes, foreign lock waiting, dry-run, no-op detection, and malformed config rejection
test/tools-codex-pool.test.ts solid coverage for status, sensitive/non-sensitive JSON, account-number resolution, dry-run, clear, invalid numbers, and missing accountId rejection

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[request arrives with model] --> B[getModelAccountPool\nfrom pluginConfig]
    B -->|preferredAccountIds.length === 0| C[general pool]
    B -->|preferredAccountIds populated| D[getPreferredSelectableIndices]
    D -->|≥1 healthy preferred account| E[preferred pool\naccountPoolMode: preferred]
    D -->|all preferred unhealthy / unknown\nreturns null| F[fallback to general pool\naccountPoolMode: general-fallback]
    C --> G[getAccountForStrategy\nsticky / round-robin / hybrid]
    E --> G
    F --> G
    G -->|account selected| H[attempt request]
    H -->|success| I[snapshot: accountPoolMode\nconfiguredAccountPoolSize]
    H -->|retry| G

    style E fill:#d4edda
    style F fill:#fff3cd
    style C fill:#cce5ff
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[request arrives with model] --> B[getModelAccountPool\nfrom pluginConfig]
    B -->|preferredAccountIds.length === 0| C[general pool]
    B -->|preferredAccountIds populated| D[getPreferredSelectableIndices]
    D -->|≥1 healthy preferred account| E[preferred pool\naccountPoolMode: preferred]
    D -->|all preferred unhealthy / unknown\nreturns null| F[fallback to general pool\naccountPoolMode: general-fallback]
    C --> G[getAccountForStrategy\nsticky / round-robin / hybrid]
    E --> G
    F --> G
    G -->|account selected| H[attempt request]
    H -->|success| I[snapshot: accountPoolMode\nconfiguredAccountPoolSize]
    H -->|retry| G

    style E fill:#d4edda
    style F fill:#fff3cd
    style C fill:#cce5ff
Loading

Reviews (4): Last reviewed commit: "fix: serialize model pool config across ..." | Re-trigger Greptile

@lubshad
lubshad requested a review from ndycode as a code owner July 17, 2026 03:33
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Adds modelAccountPools configuration, routes requests through preferred healthy accounts with general-pool fallback, provides the codex-pool tool, and reports pool selection mode and size in diagnostics and interfaces.

Changes

Model account pool routing

Layer / File(s) Summary
Pool configuration and persistence
lib/schemas.ts, lib/config.ts, lib/runtime.ts, docs/..., README.md
Adds validated model-pool configuration, normalized lookup, serialized atomic mutations, and documentation for pool behavior and fallback.
Pool management tool and registry
lib/tools/codex-pool.ts, lib/tools/index.ts, lib/tools/codex-help.ts, lib/tools/AGENTS.md, docs/...
Adds codex-pool status and mutation actions, registers the tool, and updates help and tool-registry documentation.
Preferred account selection
lib/accounts.ts, lib/accounts/rotation.ts, test/rotation-strategy.test.ts
Forwards preferred IDs through sticky, round-robin, and hybrid strategies, restricting selection to healthy preferred accounts before general fallback.
Request routing and diagnostics
index.ts, test/index.test.ts, test/model-pool-config.test.ts, test/tools-codex-pool.test.ts
Integrates model-pool lookup into account traversal and validates persistence, tool behavior, and routing visibility output.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RequestLoop
  participant Config
  participant AccountManager
  participant AccountRotation
  participant RoutingVisibility
  RequestLoop->>Config: Resolve modelAccountPools
  Config-->>RequestLoop: preferredAccountIds
  RequestLoop->>AccountManager: Select account with preferred IDs
  AccountManager->>AccountRotation: Apply strategy and selection pool
  AccountRotation-->>AccountManager: Preferred account or general fallback
  AccountManager-->>RequestLoop: Selected account
  RequestLoop->>RoutingVisibility: Record pool mode and configured size
Loading

Possibly related PRs

Suggested reviewers: ndycode, ndycode

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding model-specific account pools.
Description check ✅ Passed The description follows the template well, with complete Summary, Testing, Compliance Confirmation, and Notes sections.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
index.ts (1)

1948-1949: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Extract the configuration lookup from the retry loop.

getModelAccountPool parses the configuration and constructs a new array every time it is called. Since pluginConfig and model do not change within the inner attempted.size loop (model fallback mutations trigger a break out of this loop and restart the outer while (true) loop entirely), hoisting this call just outside the inner loop will avoid unnecessary configuration re-evaluations for each candidate account attempt.

♻️ Proposed refactor
+			const preferredAccountIds = getModelAccountPool(pluginConfig, model);
 			while (attempted.size < Math.max(1, accountCount)) {
-				const preferredAccountIds = getModelAccountPool(pluginConfig, model);
 				const selectionExplainability = accountManager.getSelectionExplainability(
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@index.ts` around lines 1948 - 1949, Move the
getModelAccountPool(pluginConfig, model) call out of the attempted.size retry
loop and compute preferredAccountIds once before entering it. Reuse that array
for each candidate-account attempt, while preserving the existing outer-loop
restart behavior when model fallback changes the model.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/accounts/rotation.ts`:
- Around line 56-72: Add a falsy-account guard at the start of the filter
callback in getPreferredSelectableIndices before accessing account.accountId,
while preserving the existing preferred-ID and isSelectable checks for valid
accounts.

---

Nitpick comments:
In `@index.ts`:
- Around line 1948-1949: Move the getModelAccountPool(pluginConfig, model) call
out of the attempted.size retry loop and compute preferredAccountIds once before
entering it. Reuse that array for each candidate-account attempt, while
preserving the existing outer-loop restart behavior when model fallback changes
the model.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4d585bcd-3953-4fe0-8516-7d5dc76674c2

📥 Commits

Reviewing files that changed from the base of the PR and between f0ae880 and 56eb42a.

📒 Files selected for processing (11)
  • README.md
  • docs/configuration.md
  • docs/development/CONFIG_FIELDS.md
  • index.ts
  • lib/accounts.ts
  • lib/accounts/rotation.ts
  • lib/config.ts
  • lib/runtime.ts
  • lib/schemas.ts
  • test/index.test.ts
  • test/rotation-strategy.test.ts

Comment thread lib/accounts/rotation.ts
Comment thread test/rotation-strategy.test.ts
@lubshad

lubshad commented Jul 17, 2026

Copy link
Copy Markdown
Author

Addressed automated review feedback in 5e67b53:

  • hoisted model-pool resolution outside the candidate-account retry loop
  • added the defensive account guard
  • added integration assertions for general, preferred, and general-fallback diagnostics

Validation: npm run typecheck, npm run lint, npm run build, and npm test (111 files, 2782 tests).

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/architecture.md`:
- Line 76: Synchronize both architecture inventories with the 24-tool registry:
in docs/architecture.md lines 76-76, change both 21 counts to 24 and add
codex-warm to daily account use; in docs/development/ARCHITECTURE.md lines
187-187, change both 23 counts to 24 and add codex-warm to the daily-account
group. Keep both documents consistent with the current subsystem map.

In `@lib/config.ts`:
- Around line 167-173: Update the model account pool mutation flow around
modelAccountPoolMutationQueue and performModelAccountPoolMutation to coordinate
the complete config read-modify-write across processes sharing CONFIG_PATH. Add
an inter-process lock or equivalent CAS/retry mechanism that protects reading,
applying the mutation, and writing/renaming the config, while retaining the
existing in-process queue serialization.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 074e39f7-eb89-4eec-8433-19f8bbac347d

📥 Commits

Reviewing files that changed from the base of the PR and between 5e67b53 and 986a41e.

📒 Files selected for processing (17)
  • AGENTS.md
  • README.md
  • docs/architecture.md
  • docs/configuration.md
  • docs/development/ARCHITECTURE.md
  • docs/development/CONFIG_FIELDS.md
  • lib/AGENTS.md
  • lib/config.ts
  • lib/tools/AGENTS.md
  • lib/tools/codex-help.ts
  • lib/tools/codex-pool.ts
  • lib/tools/index.ts
  • test/doc-parity.test.ts
  • test/index-retry.test.ts
  • test/index.test.ts
  • test/model-pool-config.test.ts
  • test/tools-codex-pool.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • README.md
  • test/index.test.ts

Comment thread docs/architecture.md
Comment thread lib/config.ts Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant