Skip to content

[Feature]: make routed account identity obvious in request logs (which Codex/OpenRouter account served each request) #4057

Closed

Description

@nordz0r

Area

Multiple areas

What are you trying to accomplish?

I run a provider setup with multiple accounts/credentials behind one provider name: a Codex main+pool, several OpenRouter API keys (free-tier rotation), and chained clients. When debugging quota exhaustion, cache anomalies, or routing behavior, I need to see which specific account served each request directly in the logs — dashboard Logs panel, CLI ocx logs, and usage.jsonl.

What prevents this today?

  • usage.jsonl has accountLogLabel, but it is Codex-only ("main" | p{hex}). Other OAuth providers have no per-account labeling (see closed [Feature]: persist per-account usage for OAuth providers (xai/cursor), not only Codex main/pN labels #2699).
  • For OpenRouter, multiple API keys under one provider are indistinguishable: the provider field shows only the provider name (e.g. openrouter), never which key made the upstream call.
  • The dashboard Logs panel shows model, latency, tokens — but account/credential identity is absent or requires manually cross-referencing usage.jsonl rows.

What should OpenCodex do?

When a request is routed through a provider that has multiple configured accounts or keys, each log row (dashboard Logs table, ocx logs output, and usage.jsonl) should display a stable per-account label identifying the credential that served the request. A reader should be able to answer "which account handled request X" without reading raw JSONL or the source code.

Example usage or interface

Dashboard Logs table gains an Account column:

Time Provider Model Account Tokens Latency
12:01 openai gpt-5.6 p3 12k 2.1s
12:02 openrouter free/x key-2 8k 1.4s

CLI:

ocx logs --account p3          # filter rows by account label
ocx logs --last 50 --account key-2

usage.jsonl row gains a populated accountLogLabel for every multi-account provider (e.g. "accountLogLabel": "key-2" on OpenRouter rows), not just Codex.

Alternatives or workarounds

  • Manually grepping usage.jsonl — only works for Codex rows today.
  • One provider entry per API key (openrouter-key1, openrouter-key2) — bloats the provider catalog and breaks free-tier rotation logic.
  • feat(pricing): apply base-provider price overlays to all account log labels #3817 (closed) improved price overlays for account log labels — the labeling machinery exists, it just does not cover non-Codex providers or surface in the UI.

Additional context

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

  1. github-actions commented on Sep 8, 2026

    @github-actions
    Contributor

    Issue reopened

    The report now contains the information required by the automated check. Thanks for updating it.

  2. lidge-jun commented on Sep 8, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 52 / 80

    이 이슈는 한 provider 이름 뒤에 여러 자격 증명이 있을 때, 요청 로그에서 “이번 요청은 어느 계정/키가 처리했는가”를 바로 보고 싶다는 관측성 요청입니다. 작성자는 Codex main+pool, OpenRouter API 키 여러 개(무료 티어 로테이션), 체인 클라이언트를 같이 돌립니다. 쿼터 고갈·캐시 이상·라우팅을 디버깅할 때 dashboard Logs, ocx logs, usage.jsonl에서 계정 정체를 한눈에 보고 싶어 합니다. 제안 UI는 Logs 표에 Account 열, CLI --account 필터, 그리고 multi-account provider마다 accountLogLabel을 채우는 것입니다.

    지금 dev HEAD(164bd2b36, package 2.49.0, tip #4053)를 기준으로 보면, 이슈 본문의 “OAuth는 라벨이 없다 / Codex만 있다” 문장은 이미 절반 이상 낡았습니다. #2699가 들어와 UsageAccountLogLabel은 main | p<hex6> | o<hex6> 세 가족을 받습니다. isCodexUsageAccountLogLabel 이름만 Codex처럼 남아 있고, 실제 persistence 게이트는 o 라벨도 통과시킵니다. non-Codex OAuth는 stampOAuthAccountLabel(src/providers/label.ts)이 authMode === "oauth"이고 openai/anthropic이 아닐 때 o{sha256(provider\\0accountId)[:6]}를 찍고, core.ts admission/resolve 경로에서 호출됩니다. Codex는 여전히 codexAuthContextLogLabel로 main/p…를 찍습니다. #3817(닫힘)은 가격 오버레이가 이 라벨 가족을 따라가도록 맞춘 후속입니다.

    진짜 남은 구멍은 API-key pool(특히 OpenRouter) 쪽입니다. apiKeyPool 항목은 id + optional label을 갖고(src/types/provider.ts, src/providers/api-keys.ts), key-failover.ts는 429/401 때 failedId/candidateId로 키를 돌립니다. 그런데 stampOAuthAccountLabel은 oauth만 스탬프하고, key-auth 경로에는 “이번 요청에 쓴 pool entry id/label → accountLogLabel” 연결이 없습니다. usage.jsonl / /api/logs의 apiKeyId 필드는 주석상 데이터플레인 입장(admission)용 configured key id이지 OpenRouter upstream 키 id가 아닙니다. 그래서 provider 칸에 openrouter만 보이고 “key-2가 서빙했다”는 답을 JSONL에서도 못 얻는 체감은 맞습니다.

    UI/CLI 면도 이슈 주장과 맞습니다. gui/src/pages/Logs.tsx의 LogEntry/LogAttempt 타입에 accountLogLabel이 없고, logs-filter-bar.tsx 필터도 provider/model/time/status/conversation만 있습니다. 서버가 행에 accountLogLabel을 실어 줘도(요청 로그 persistence는 이미 조건부로 넣음) 대시보드는 Account 열을 그리지 않습니다. ocx logs(src/cli/observe.ts)도 --account 필터가 없습니다. 다만 사람이 읽기 쉬운 key-2 같은 문자열을 그대로 로그에 박자는 제안은, 현재 라벨이 이메일/원시 id를 쓰지 않는 digest인 프라이버시 계약(src/codex/account-label.ts 주석)과 충돌합니다. 운영자 label을 그대로 노출할지, k<hex6> 같은 새 가족을 둘지는 설계 선택입니다.

    관련 이슈: #3433(캐시 이상 디버깅), #3994(쿼터 소진 진단), #4024(OpenRouter free 쿼터 park)는 같은 운영 스토리의 이웃이지 중복이 아닙니다. #4024는 “죽은 키를 얼마나 오래 쉴지”이고, 이 이슈는 “어느 키가 실제로 나갔는지 로그에 남기기”입니다. conversation affinity / pool / quota 작업이 HEAD에 이미 무거운 상태에서, 2.49.x 마감용으로는 관측성 보강이지 블로커가 아닙니다. naive PR이 config.ts/types.ts에 큰 스키마를 끼워 넣으면 types/config 분할 캠페인에 무효화될 수 있으니, 라벨 가족·필터만 건드리는 얇은 PR이 안전합니다.

    경로 src/usage/log.ts UsageAccountLogLabel / isCodexUsageAccountLogLabel - #2699 이후 o<hex6>도 persistence 통과. “Codex-only” 서술은 구버전 진단
    경로 src/providers/label.ts stampOAuthAccountLabel - oauth만 스탬프. OpenRouter 등 key-auth apiKeyPool은 대상 밖
    경로 src/providers/key-failover.ts / src/providers/api-keys.ts - 키 id·label·회전은 있으나 request log에 account 정체로 안 실림
    경로 src/server/request-log.ts apiKeyId 주석 - admission configured key용. upstream pool key와 이름만 비슷하고 의미가 다름
    경로 gui/src/pages/Logs.tsx LogEntry - accountLogLabel 필드·Account 열·필터 없음. Codex p…/main도 UI에 안 보임
    경로 src/cli/observe.ts - ocx logs --account 없음
    이슈 예시 key-2 - 사람이 읽기 쉬운 label 직접 기록은 digest-only 프라이버시 계약과 충돌 가능

    메인테이너의 판단이 필요한 지점

    • key-pool 라벨 가족: 기존 o/p에 맞춘 k<hex6>(provider\\0keyId) vs 운영자 apiKeyPool[].label 그대로(마스킹/길이 제한 필요)
    • 스탬프 위치를 oauth와 대칭으로 admission/resolve에 둘지, key-failover 회전 직후에만 갱신할지
    • Logs UI는 Account 열만 먼저(이미 있는 Codex/OAuth 라벨 노출) vs key-pool 스탬프와 한 PR에 묶을지
    • CLI --account가 usage summary attribution과 같은 필터 축을 쓸지, request-log ring만 볼지
    • [Feature]: OpenRouter — automatic key rotation & model failover when the free-tier quota is exhausted #4024(쿼터 park)와 일정을 묶을지, 관측만 먼저 얇게 넣을지

    너의 추천
    enhancement로 유지하고 design-first. 닫지 마세요(중복 아님). 1단계: 이미 찍히는 main/p…/o…를 Logs 표·상세·ocx logs에 보이게 하고(필터 포함). 2단계: key-auth apiKeyPool에 digest 라벨 스탬프를 oauth와 같은 persistence 게이트로 넣기. 사람이 붙인 key-2 문자열을 JSONL에 그대로 쓰지 말고, UI에서 pool label로 조인 표시하는 편이 프라이버시·안정성에 맞습니다. dashboard.* / 큰 config 스키마 PR은 types/config 분할과 겹치면 close-don't-rebase. 2.49.x 블로커로 끌어올리지 말고, #4024 재현 패킷이 오면 같은 OpenRouter 운영 묶음으로 리뷰하면 됩니다.

    이 댓글은 grok-bot이 작성했습니다

  3. added 2 commits that reference this issue on Sep 9, 2026
    7dd50ea
    da9aef5
  4. lidge-jun commented on Sep 9, 2026

    @lidge-jun
    Owner

    Landed via #4134 at 7dd50ea

  5. added 2 commits that reference this issue on Sep 17, 2026
    a5f5fc7
    8c3b717
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

    account-poolOAuth, credentials, Codex pool, quota, failover, plansenhancementNew feature or requestlanded-via-maintainerOriginal PR closed after landing via a maintainer merge train

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions