Skip to content

Remove the direct router and API-key-selection import cycle #3894

Description

@parkjs101

Area

Proxy and routing

What are you trying to accomplish?

Maintain provider routing and API-key failover without coupling the router's selection-snapshot helper to the module that performs persisted selection changes and route resolution. The scope is a small dependency cleanup with unchanged routing and key-selection behavior.

What prevents this today?

At dev commit 76436a3ee221d577cc94d6c10544c03bb1f9d9db, the following direct runtime cycle exists:

src/router.ts
  -> src/providers/api-key-selection.ts
  -> src/router.ts

The helper only derives entryId, reference, and revision from its provider argument. It does not need route resolution or persisted mutation. Keeping it in the larger module creates an unnecessary direct cycle.

This is a maintainability issue. No current startup crash or guaranteed module-order failure is claimed.

What should OpenCodex do?

Remove this specific direct cycle while preserving current routing, selection snapshots, and failover behavior:

  • Put the snapshot helper in a small module whose dependencies are type-only, or use an equivalent existing dependency-neutral location.
  • Have the router and API-key-selection module consume that helper.
  • Preserve the existing helper export if it is moved, so existing imports remain compatible.
  • Keep routedProviderConfig imports in the selection and failover modules within this scope.
  • Add focused coverage for the helper's behavior and its runtime dependency boundary, including that the router no longer imports the stateful API-key-selection module directly.

This issue does not require making the entire router dependency graph acyclic or banning every provider-to-router dependency.

Example usage or interface

Before:

// src/router.ts
import { captureProviderApiKeySelection } from "./providers/api-key-selection";

Possible after:

// src/router.ts
import { captureProviderApiKeySelection } from "./providers/api-key-selection-capture";

// src/providers/api-key-selection.ts
import { captureProviderApiKeySelection } from "./api-key-selection-capture";
export { captureProviderApiKeySelection } from "./api-key-selection-capture";
import { routedProviderConfig } from "../router";

For a provider with a selected pool key and a selection revision, the captured entry ID, key reference, and revision must remain identical. Missing pool entries and optional fields must retain their existing behavior.

Alternatives or workarounds

Extracting routedProviderConfig itself would move a broader set of routing dependencies and is not necessary to remove this direct edge.

A global router cycle test would exceed this issue's scope. An in-memory graph check applying only the proposed router import change still found this other static cycle:

router -> routing/evaluator -> routing/quota -> providers/quota
       -> codex/auth-api -> lib/state-store-registrations -> router

The return edge is state-store-registrations.ts:42. That broader dependency work should be evaluated separately.

Additional context

The current direct cycle was verified from source and a static runtime-import graph using Bun's TypeScript transpiler. The hypothetical extraction was evaluated in memory; no source change is included.

On this checkout, all 17 tests in tests/lab/core-lab-boundary.test.ts passed. The focused key-failover and provider-key-store test files could not load because zod/v4 was missing, so their success is not claimed. They should be run alongside the new focused coverage when implementing this change:

bun test tests/adapters/key-failover.test.ts tests/providers/provider-key-store.test.ts tests/lab/core-lab-boundary.test.ts

Checks

  • I searched existing issues and documentation.
  • This request describes a concrete OpenCodex workflow rather than merely naming a desired technology.
  • I removed secrets and personal data.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestlanded-via-maintainerOriginal PR closed after landing via a maintainer merge trainproxyHTTP proxy, routing, reverse-proxy / management auth

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions