Skip to content

Add model context length and manual model deletion#520

Open
roiding wants to merge 7 commits into
cita-777:mainfrom
roiding:pr/upstream-main
Open

Add model context length and manual model deletion#520
roiding wants to merge 7 commits into
cita-777:mainfrom
roiding:pr/upstream-main

Conversation

@roiding
Copy link
Copy Markdown

@roiding roiding commented Apr 29, 2026

Title
Add model context length and manual model deletion

Body

Summary

  • add context_length to /v1/models responses
  • add support for deleting manually added models from the accounts UI
  • include tests for the model context length cache

Test plan

  • verify /v1/models includes context_length
  • verify manually added models can be deleted from Accounts
  • run relevant tests/type checks locally

Summary by CodeRabbit

  • New Features

    • Delete manually added models from an account via the account modal (delete button + success/error feedback).
    • Model listings now show per-model context length, scoped by account/endpoint and chosen conservatively when multiple sources exist.
  • Tests

    • Added coverage for context-length caching, extraction, scoping, persistence, concurrency, and manual-model deletion isolation.

Hermes Agent added 2 commits April 29, 2026 17:04
- New modelContextLengthCache service for in-memory model→context_length mapping
- Platform adapters (newApi, standardApiProvider) extract context_length from upstream /v1/models
- modelsSurface injects context_length into both OpenAI and Claude response formats
- Default 1,000,000 when upstream does not provide context_length
- Supports field names: context_length, contextLength, max_context_length, contextWindow, etc.
- Backend: DELETE /api/accounts/:id/models/manual endpoint
- Only deletes models where isManual=true (safe against auto-discovered models)
- Frontend: AccountModelsModal shows '✕ 删除' button next to each manual model
- Frontend: api.removeAccountManualModels() function
- Accounts.tsx: wires up delete handler with toast feedback
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 29, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 96f42a82-a03b-4ab1-994b-307a9e38469a

📥 Commits

Reviewing files that changed from the base of the PR and between eaa3279 and edcbce7.

📒 Files selected for processing (2)
  • src/server/proxy-core/surfaces/modelsSurface.test.ts
  • src/server/proxy-core/surfaces/modelsSurface.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/server/proxy-core/surfaces/modelsSurface.ts

📝 Walkthrough

Walkthrough

Adds a scoped in-memory model context-length cache and threads extraction/caching through platform adapters and model discovery (per-refresh and per-scan scopes). Exposes cache APIs and payload parsing, surfaces per-model context_length in model-list responses, and adds backend + frontend flows to remove manual account models with tests.

Changes

Model Context Length Cache & Tests

Layer / File(s) Summary
Cache implementation and scope builders
src/server/services/modelContextLengthCache.ts
New in-memory per-scope cache with normalization/validation, URL canonicalization, scope builders buildAccountModelContextLengthScope and buildEndpointModelContextLengthScope, and APIs: set/get/has/getAll/clear plus extractContextLengthsFromPayload.
Cache tests
src/server/services/modelContextLengthCache.test.ts
Vitest coverage for single/bulk set/get, defaults, normalization, invalid inputs, scope isolation/clearing, payload extraction variants, endpoint-scope canonicalization, and getAllModelContextLengths.

Context-Length Extraction & Platform Integration

Layer / File(s) Summary
Standard provider extraction and caching
src/server/services/platforms/standardApiProvider.ts
After fetching standard models payload, call extractContextLengthsFromPayload(payload) and setModelContextLengths(...) using options.contextSourceScope or a default endpoint scope before parsing rows.
Platform adapters: thread context scope & persist payload lengths
src/server/services/platforms/*.ts (newApi.ts, openai.ts, claude.ts, cliproxyapi.ts, gemini.ts, oneApi.ts, oneHub.ts, veloera.ts)
Extended getModels(...) signatures with optional contextSourceScope; adapters extract context-lengths from upstream payloads and persist them via setModelContextLengths(...). newApi.ts additionally threads sourceScope into OpenAI extraction path.
Platform adapter base API
src/server/services/platforms/base.ts
Abstract and concrete getModels signatures updated to accept optional contextSourceScope.
Model discovery callers: per-account & scan scopes
src/server/services/modelService.ts
Compute per-account scope via buildAccountModelContextLengthScope(account.id); create per-refresh UUID namespace and per-scan sub-scopes, pass scan scope into adapter.getModels(...), aggregate discovered scan-scoped lengths, clear scan caches, and persist aggregated per-account lengths.
Models surface: include context_length in responses
src/server/proxy-core/surfaces/modelsSurface.ts, src/server/proxy-core/surfaces/modelsSurface.test.ts
readVisibleModels now returns model metadata including selectedAccountId and routing candidates; added resolveModelContextLength that prefers eligible candidate account-scoped values (minimum across eligible accounts), falls back to selectedAccountId, otherwise default; attaches context_length to Claude- and OpenAI-shaped model list entries. Tests updated accordingly.

Manual Model Removal: Backend Service, Route, Tests

Layer / File(s) Summary
Service to delete manual models
src/server/services/accountManualModelService.ts
New removeManualModelsFromAccount(accountId, modelNames) normalizes/filters names, deletes schema.modelAvailability rows where accountId and isManual=true and modelName IN (...) inside a transaction, triggers rebuildRoutesBestEffort(), and returns { deletedCount }.
Route handler
src/server/routes/api/accounts.ts, src/server/routes/api/accounts.manual-models.test.ts
Adds DELETE /api/accounts/:id/models/manual endpoint: payload/body validation, name normalization/de-dupe, account existence check, calls service, returns 400/404/500 as appropriate and { success: true } on success. Tests add deletion-only-for-target-account case and extend hook timeouts.
Frontend API & UI wiring
src/web/api.ts, src/web/pages/Accounts.tsx, src/web/pages/accounts/AccountModelsModal.tsx
api.removeAccountManualModels added; Accounts wires onRemoveManualModel to call API, show toasts, and refresh modal guarded by request-sequence checks; modal shows delete button for manual models and invokes handler.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant Server as Server\n(Model Listing)
    participant Adapter as Platform\nAdapter
    participant Cache as Context Length\nCache
    participant DB as Database

    Client->>Server: GET /models
    activate Server
    Server->>Adapter: adapter.getModels(..., contextSourceScope?)
    activate Adapter
    Adapter->>Adapter: fetch upstream models payload
    Adapter->>Cache: extractContextLengthsFromPayload(payload) -> setModelContextLengths(scope)
    Cache-->>Adapter: ack
    Adapter-->>Server: model id list
    Server->>Cache: getModelContextLength(modelId, accountScope?)
    Cache-->>Server: contextLength (or default)
    Server-->>Client: models + context_length
    deactivate Server
Loading
sequenceDiagram
    participant Client as Client
    participant UI as Accounts UI
    participant API as Server API
    participant Service as AccountManualModelService
    participant DB as Database
    participant Router as RouteRebuilder

    Client->>UI: click delete manual model
    UI->>API: DELETE /api/accounts/:id/models/manual {models:[name]}
    activate API
    API->>Service: removeManualModelsFromAccount(accountId, [name])
    activate Service
    Service->>DB: DELETE FROM modelAvailability WHERE accountId & isManual & name IN (...)
    DB-->>Service: changes count
    Service->>Router: rebuildRoutesBestEffort()
    Router-->>Service: done
    Service-->>API: { deletedCount }
    API-->>UI: { success: true }
    UI-->>Client: show toast, refresh modal list
    deactivate API
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • cita-777/metapi#270 — touches model discovery/refresh flow similar to modelService changes.
  • cita-777/metapi#50 — modifies discovery/refresh logic that overlaps with per-scan scoping and discovery behavior.
  • cita-777/metapi#80 — related to manual-model management and account routes/features.

Suggested labels

size: L

Poem

🐰 I scoped each model's length with a hop and a twitch,

Cached bits per-account in a neat little niche.
A delete for manual names, routes rebuilt with care,
Models now report lengths so callers know what's fair.
— CodeRabbit, nibbling bytes in the air.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.92% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add model context length and manual model deletion' accurately summarizes the two main features introduced in this changeset: context length support and manual model deletion functionality.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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 and usage tips.

@github-actions github-actions Bot added area: server Server-side API and backend changes area: web Web UI changes size: M 200 to 499 lines changed labels Apr 29, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: af9aac3ad7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".


const DEFAULT_CONTEXT_LENGTH = 1_000_000;

const cache = new Map<string, number>();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Scope context-length cache by account or endpoint

modelContextLengthCache stores values in a single process-wide map keyed only by model name, but the values are populated from per-account upstream /v1/models payloads (via setModelContextLengths in platform adapters). In a multi-account deployment where the same model ID has different limits (or one provider omits the field), the most recently refreshed account overwrites the value for all others, so /v1/models can return incorrect context_length metadata and cause clients to size prompts incorrectly for the actual selected channel.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
src/server/services/modelContextLengthCache.test.ts (1)

33-45: Add a whitespace-only model-name test case.

Current invalid-name assertions cover empty string but not whitespace-only input (e.g. ' '). Adding that case would lock in the expected behavior and catch empty-key regressions.

Also applies to: 67-78

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server/services/modelContextLengthCache.test.ts` around lines 33 - 45,
Add tests that assert whitespace-only model names are treated as invalid: update
the 'ignores invalid values' case to call setModelContextLength('   ', 128000)
and expect hasModelContextLength('   ') toBe(false), and likewise add the same
whitespace-only check in the other invalid-name test block (the one around lines
67-78). This ensures setModelContextLength and hasModelContextLength both
trim/validate names (or reject whitespace-only keys) consistent with the
empty-string behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/server/routes/api/accounts.ts`:
- Around line 1969-1983: Extract the deletion loop into a new service function
(e.g., removeManualModelsFromAccount in
src/server/services/accountManualModelsService.ts) that accepts { accountId,
modelNames } and runs the deletes inside a single db.transaction using the
transaction handle (tx) for the .delete(schema.modelAvailability) calls and the
same where(...) predicate (use input.accountId and each modelName); then replace
the loop in the route with a call to this service and only call
rebuildRoutesBestEffort() after the service resolves successfully; ensure errors
are propagated so the route can return the appropriate error response.

In `@src/server/services/modelContextLengthCache.ts`:
- Around line 13-43: The cache is currently process-global and keyed only by
model name, causing cross-source collisions; change the cache scheme to be
scoped by source by updating the cache variable and normalizeKey to incorporate
a source identifier (e.g., normalizeKey(source, modelName) or use a compound key
`${source}:${modelName}`), update setModelContextLength(source, modelName,
contextLength) and getModelContextLength(source, modelName) signatures to use
the scoped key, and modify setModelContextLengths to accept a source and
replace/refresh only that source's entries (or clear existing entries for that
source before bulk-setting) so stale or missing upstream context_length values
from one source don't affect others.
- Around line 22-34: The validation currently checks raw names but not the
normalized result, allowing whitespace-only names to become an empty key; update
setModelContextLength to compute const key = normalizeKey(modelName) and only
call cache.set(key, ...) if key is non-empty (truthy) and contextLength is
valid, and similarly update setModelContextLengths to compute const key =
normalizeKey(name) inside the loop and only write cache.set(key, ...) when key
is non-empty and length is a finite positive number.

In `@src/web/pages/Accounts.tsx`:
- Around line 3480-3486: The async handler onRemoveManualModel can reopen the
modal after the user has closed or changed it; to fix, capture the current modal
state/account (e.g., const currentAccountId = modelModal?.account?.id and const
wasOpen = modelModal?.open) before awaiting api.removeAccountManualModels, then
after the await verify that modelModal is still open and modelModal.account.id
=== currentAccountId (or wasOpen is true) before calling
loadModelModalModels(modelModal.account, {}); return early if the modal was
closed or the account changed to avoid re-opening stale UI.

---

Nitpick comments:
In `@src/server/services/modelContextLengthCache.test.ts`:
- Around line 33-45: Add tests that assert whitespace-only model names are
treated as invalid: update the 'ignores invalid values' case to call
setModelContextLength('   ', 128000) and expect hasModelContextLength('   ')
toBe(false), and likewise add the same whitespace-only check in the other
invalid-name test block (the one around lines 67-78). This ensures
setModelContextLength and hasModelContextLength both trim/validate names (or
reject whitespace-only keys) consistent with the empty-string behavior.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2db3f102-e339-4deb-ac30-214a3b75022e

📥 Commits

Reviewing files that changed from the base of the PR and between 262651a and af9aac3.

📒 Files selected for processing (9)
  • src/server/proxy-core/surfaces/modelsSurface.ts
  • src/server/routes/api/accounts.ts
  • src/server/services/modelContextLengthCache.test.ts
  • src/server/services/modelContextLengthCache.ts
  • src/server/services/platforms/newApi.ts
  • src/server/services/platforms/standardApiProvider.ts
  • src/web/api.ts
  • src/web/pages/Accounts.tsx
  • src/web/pages/accounts/AccountModelsModal.tsx

Comment on lines +1969 to +1983
try {
for (const modelName of normalizedModels) {
await db
.delete(schema.modelAvailability)
.where(
and(
eq(schema.modelAvailability.accountId, accountId),
eq(schema.modelAvailability.modelName, modelName),
eq(schema.modelAvailability.isManual, true),
),
)
.run();
}
await rebuildRoutesBestEffort();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Extract deletion workflow to a service and execute it atomically.

Line 1970-Line 1981 performs DB mutation orchestration directly in the route, and a mid-loop failure can leave partial deletions while returning an error.

Proposed refactor direction
-      try {
-        for (const modelName of normalizedModels) {
-          await db
-            .delete(schema.modelAvailability)
-            .where(
-              and(
-                eq(schema.modelAvailability.accountId, accountId),
-                eq(schema.modelAvailability.modelName, modelName),
-                eq(schema.modelAvailability.isManual, true),
-              ),
-            )
-            .run();
-        }
+      try {
+        await removeManualModelsFromAccount({
+          accountId,
+          modelNames: normalizedModels,
+        });
         await rebuildRoutesBestEffort();

         return { success: true };
       } catch (err: any) {
// src/server/services/accountManualModelsService.ts
export async function removeManualModelsFromAccount(input: {
  accountId: number;
  modelNames: string[];
}) {
  await db.transaction(async (tx) => {
    for (const modelName of input.modelNames) {
      await tx
        .delete(schema.modelAvailability)
        .where(
          and(
            eq(schema.modelAvailability.accountId, input.accountId),
            eq(schema.modelAvailability.modelName, modelName),
            eq(schema.modelAvailability.isManual, true),
          ),
        )
        .run();
    }
  });
}

As per coding guidelines: "Route files in src/server/routes/** are adapters, not owners... must not own ... persistence."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server/routes/api/accounts.ts` around lines 1969 - 1983, Extract the
deletion loop into a new service function (e.g., removeManualModelsFromAccount
in src/server/services/accountManualModelsService.ts) that accepts { accountId,
modelNames } and runs the deletes inside a single db.transaction using the
transaction handle (tx) for the .delete(schema.modelAvailability) calls and the
same where(...) predicate (use input.accountId and each modelName); then replace
the loop in the route with a call to this service and only call
rebuildRoutesBestEffort() after the service resolves successfully; ensure errors
are propagated so the route can return the appropriate error response.

Comment thread src/server/services/modelContextLengthCache.ts Outdated
Comment thread src/server/services/modelContextLengthCache.ts Outdated
Comment thread src/web/pages/Accounts.tsx
@cita-777
Copy link
Copy Markdown
Owner

cita-777 commented May 8, 2026

请处理coderabbit ai给出的comments和CI错误

@roiding roiding closed this May 12, 2026
@roiding roiding reopened this May 12, 2026
@github-actions github-actions Bot added size: L 500 to 999 lines changed and removed size: M 200 to 499 lines changed labels May 12, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0b2201e8c2

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

return;
}

cache.set(scopeKey, nextScopeCache);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve context-length entries across credential scans

setModelContextLengths overwrites the whole scope cache each time it is called, but refreshModelsForAccount now passes the same account scope into multiple adapter.getModels(...) calls (account credential plus each managed token). If a later call returns only a subset of models or omits context_length, this replacement drops entries discovered earlier in the same refresh, so /v1/models reports fallback 1_000_000 for still-routable models from earlier credentials. This is reproducible on accounts with multiple tokens that expose different model metadata.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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)
src/server/routes/api/accounts.ts (1)

1951-1958: 💤 Low value

Consider removing duplicate normalization logic.

The normalization at lines 1951-1955 (trim, dedupe, filter empty) is duplicated in removeManualModelsFromAccount at lines 9-11 of src/server/services/accountManualModelService.ts. Since the service already handles this defensively, the route could pass the raw models array directly and let the service normalize it, reducing duplication.

♻️ Simplified route validation
-      const normalizedModels = Array.from(
-        new Set(
-          models.map((m) => String(m).trim()).filter((m) => m.length > 0),
-        ),
-      );
-      if (normalizedModels.length === 0) {
-        return reply.code(400).send({ message: "模型列表不能为空" });
-      }
-
       const account = await db
         .select()
         .from(schema.accounts)
         .where(eq(schema.accounts.id, accountId))
         .get();

       if (!account) {
         return reply.code(404).send({ message: "账号不存在" });
       }

       try {
-        await removeManualModelsFromAccount(accountId, normalizedModels);
+        const result = await removeManualModelsFromAccount(accountId, models);
+        if (result.deletedCount === 0) {
+          return reply.code(400).send({ message: "没有匹配的手动模型可删除" });
+        }

         return { success: true };
🤖 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 `@src/server/routes/api/accounts.ts` around lines 1951 - 1958, Remove the
duplicated normalization block that builds normalizedModels and instead pass the
raw models array to the service; specifically delete the Array.from(new
Set(...trim...)) logic and any use of normalizedModels, replace it with a simple
sanity check that models is an array (e.g., if (!Array.isArray(models) ||
models.length === 0) return reply.code(400)...), and call
removeManualModelsFromAccount(accountId, models) so the service
(removeManualModelsFromAccount) performs the trimming/deduping/filtering.
🤖 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 `@src/server/services/modelContextLengthCache.ts`:
- Around line 52-54: The function buildEndpointModelContextLengthScope currently
uses the raw baseUrl via normalizeKey, causing semantically-equivalent URLs
(trailing slashes, default ports, case differences) to map to different cache
keys; update it to first canonicalize the endpoint (e.g., parse with the URL
constructor, lowercase scheme/host, remove default ports like :80/:443, strip
trailing slash, and include pathname consistently) and then call normalizeKey on
that canonicalized string (you can add a helper like canonicalizeEndpointUrl or
extend normalizeKey usage) so equivalent endpoints produce the same scope key.

---

Nitpick comments:
In `@src/server/routes/api/accounts.ts`:
- Around line 1951-1958: Remove the duplicated normalization block that builds
normalizedModels and instead pass the raw models array to the service;
specifically delete the Array.from(new Set(...trim...)) logic and any use of
normalizedModels, replace it with a simple sanity check that models is an array
(e.g., if (!Array.isArray(models) || models.length === 0) return
reply.code(400)...), and call removeManualModelsFromAccount(accountId, models)
so the service (removeManualModelsFromAccount) performs the
trimming/deduping/filtering.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: df3cd867-ba5c-4f0a-adfe-542b6bdf92d9

📥 Commits

Reviewing files that changed from the base of the PR and between af9aac3 and 0b2201e.

📒 Files selected for processing (19)
  • src/server/proxy-core/surfaces/modelsSurface.test.ts
  • src/server/proxy-core/surfaces/modelsSurface.ts
  • src/server/routes/api/accounts.manual-models.test.ts
  • src/server/routes/api/accounts.ts
  • src/server/services/accountManualModelService.ts
  • src/server/services/modelContextLengthCache.test.ts
  • src/server/services/modelContextLengthCache.ts
  • src/server/services/modelService.ts
  • src/server/services/platforms/base.ts
  • src/server/services/platforms/claude.ts
  • src/server/services/platforms/cliproxyapi.ts
  • src/server/services/platforms/gemini.ts
  • src/server/services/platforms/newApi.ts
  • src/server/services/platforms/oneApi.ts
  • src/server/services/platforms/oneHub.ts
  • src/server/services/platforms/openai.ts
  • src/server/services/platforms/standardApiProvider.ts
  • src/server/services/platforms/veloera.ts
  • src/web/pages/Accounts.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/server/proxy-core/surfaces/modelsSurface.ts
  • src/server/services/platforms/standardApiProvider.ts
  • src/web/pages/Accounts.tsx
  • src/server/services/modelContextLengthCache.test.ts

Comment thread src/server/services/modelContextLengthCache.ts
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 039d0d92dc

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/server/services/modelService.ts Outdated
}
};

const beginModelContextScanScope = () => `${modelContextScope}:scan:${modelContextScanCounter += 1}`;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Make per-scan context cache keys globally unique

The temporary scope key generated for each credential scan is only account:<id>:scan:<counter> where counter is local to a single refreshModelsForAccount invocation, so two concurrent refreshes for the same account will reuse identical scope names (e.g. both start at scan:1). Because each scan unconditionally calls clearModelContextLengthCache(sourceScope) in finally, one refresh can clear the other refresh's in-flight context-length data before it is collected, producing missing or mixed context_length values in the account cache and incorrect /v1/models metadata under concurrent refresh triggers.

Useful? React with 👍 / 👎.

@github-actions github-actions Bot added size: XL 1000 to 1999 lines changed and removed size: L 500 to 999 lines changed labels May 13, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/server/services/modelService.discovery.test.ts (1)

147-210: ⚡ Quick win

Add a second-refresh regression for account-scope replacement.

This only proves scan-scope merging within a single refresh. A follow-up successful refresh that returns fewer models is the case that will catch stale account-scope entries if the final write merges instead of replaces.

🤖 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 `@src/server/services/modelService.discovery.test.ts` around lines 147 - 210,
The test only exercises merging within a single refresh; to catch the regression
where a later successful refresh that returns fewer models might replace (not
preserve) account-scoped context-length entries, update this test to perform a
second refresh: after the first refresh (which returns model-a and model-b)
change the mocked getModels behavior (or rely on the existing branch for
'managed-token-subset') to simulate a subsequent refresh that returns only
model-a, call refreshModelsForAccount(account.id) again, and then assert that
getModelContextLength('model-b',
buildAccountModelContextLengthScope(account.id)) still returns 256000; use the
existing helpers refreshModelsForAccount, buildAccountModelContextLengthScope,
getModelContextLength and setModelContextLengths to implement the two-step
scenario.
🤖 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.

Nitpick comments:
In `@src/server/services/modelService.discovery.test.ts`:
- Around line 147-210: The test only exercises merging within a single refresh;
to catch the regression where a later successful refresh that returns fewer
models might replace (not preserve) account-scoped context-length entries,
update this test to perform a second refresh: after the first refresh (which
returns model-a and model-b) change the mocked getModels behavior (or rely on
the existing branch for 'managed-token-subset') to simulate a subsequent refresh
that returns only model-a, call refreshModelsForAccount(account.id) again, and
then assert that getModelContextLength('model-b',
buildAccountModelContextLengthScope(account.id)) still returns 256000; use the
existing helpers refreshModelsForAccount, buildAccountModelContextLengthScope,
getModelContextLength and setModelContextLengths to implement the two-step
scenario.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fd20c32d-329c-4a55-ab17-a0b2a464c013

📥 Commits

Reviewing files that changed from the base of the PR and between 039d0d9 and eaa3279.

📒 Files selected for processing (2)
  • src/server/services/modelService.discovery.test.ts
  • src/server/services/modelService.ts

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eaa3279833

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines 48 to +52
const decision = await input.tokenRouter.explainSelection(modelName, [], input.downstreamPolicy);
if (typeof decision.selectedChannelId === 'number') {
allowed.push(modelName);
allowed.push({
id: modelName,
selectedAccountId: decision.selectedAccountId,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Derive context_length from all eligible channels

/v1/models now picks context_length from a single explainSelection result, but route selection is strategy-dependent (round-robin/weighted/stable-first) and can choose different channels/accounts for the same model across requests. In multi-channel routes where the same model has different limits (or one channel has no cached metadata and falls back to 1_000_000), this can overstate the safe window and cause intermittent upstream failures when a lower-limit channel is chosen at dispatch time. The value should be computed from all eligible routing candidates (e.g., conservative minimum) rather than one sampled account.

Useful? React with 👍 / 👎.

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

@roiding
Copy link
Copy Markdown
Author

roiding commented May 13, 2026

就一个传个context length下去,改这么多回,老有所谓的设计问题,hermes默认就是256k,难道我去搞hermes?

@roiding
Copy link
Copy Markdown
Author

roiding commented May 13, 2026

虽然从设计上是有问题,中转站,多个相同模型的context length理论上可能不同,但实际上同一个模型的context length又怎么可能不同?或者说,我给了默认值1M,实际比这个小,调用就会报错,用户再手动压缩就行了啊,给一个理论最大值没毛病啊。

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

Labels

area: server Server-side API and backend changes area: web Web UI changes size: XL 1000 to 1999 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants