Skip to content

🤖 feat: custom model → known model mapping for metadata resolution#2500

Open
ibetitsmike wants to merge 16 commits intomainfrom
mike/custom-model-mapping
Open

🤖 feat: custom model → known model mapping for metadata resolution#2500
ibetitsmike wants to merge 16 commits intomainfrom
mike/custom-model-mapping

Conversation

@ibetitsmike
Copy link
Contributor

@ibetitsmike ibetitsmike commented Feb 19, 2026

Summary

Adds a "Treat as" model mapping feature that lets users map custom models (Ollama, OpenRouter, private gateways, etc.) to a known model so the system inherits that model's context window, pricing, capabilities, and tokenizer — without changing which model ID is sent to the provider.

Background

Users running custom or self-hosted models currently get $0.00 pricing, no context window limits, and a generic fallback tokenizer. This makes cost tracking, compaction warnings, and token breakdowns useless for these models. By mapping to a known model, all metadata-dependent features "just work."

Implementation

Data model: Added mappedToModel?: string to ProviderModelEntry schema, persisted in providers.jsonc.

Resolution layer: A single resolveModelForMetadata(fullModelId, providersConfig) function in modelEntries.ts handles the mapping lookup. All metadata consumers (stats, capabilities, context limit, cost display, tokenizer) resolve through this function before looking up metadata. The actual model ID sent to the provider is never changed.

Metadata wrappers: getModelStatsResolved and getModelCapabilitiesResolved wrap existing functions with mapping resolution. createDisplayUsage and getTokenizerForModel gained optional metadataModelOverride params.

UI: Custom models show a "Treat as" searchable dropdown in edit mode (using the existing SearchableModelSelect). View mode shows a muted "→ ModelName" badge when mapped.

Call site wiring: WorkspaceStore, CostsTab, StreamManager, AIService, StreamContextBuilder, StreamingTokenTracker, ChatInput, and useCreationWorkspace all resolve mappings at the appropriate points.

Robustness (from Codex review):

  • WorkspaceStore subscribes to onConfigChanged for live config refresh (not just on init)
  • Consumer token stats cache (both in-memory and persisted tokenStatsCache) is invalidated on config changes
  • Provider config refresh uses versioned serialization (latest-wins) to prevent stale overwrites
  • Config is preserved during client reconnects instead of being cleared to null
  • PDF/media capability checks use resolved capabilities for mapped models

Files changed (22+)

Layer Files
Schema schemas/api.ts
Config/resolver modelEntries.ts
Metadata wrappers modelStats.ts, modelCapabilities.ts
Context/cost/tokenizer contextLimit.ts, displayUsage.ts, tokenizer.ts
UI ModelRow.tsx, ModelsSection.tsx
Call site wiring WorkspaceStore.ts, WorkspaceConsumerManager.ts, CostsTab.tsx, streamManager.ts, aiService.ts, streamContextBuilder.ts, StreamingTokenTracker.ts, tokenStatsCalculator.ts, tokenizerService.ts, router.ts, ChatInput/index.tsx, useCreationWorkspace.ts, agentSession.ts
Tests modelEntries.test.ts, modelStats.test.ts, contextLimit.test.ts

Validation

  • make static-check passes (typecheck + lint + fmt)
  • 1610 tests pass, 0 failures across 125 test files
  • New tests cover: resolver with/without mapping, normalization roundtrip, stats resolution through mapping, context limit integration with mapping

Risks

  • Low: agentSession.ts uses getModelCapabilitiesResolved(model, null) since no provider service is injected — falls back to current behavior.

Generated with mux • Model: anthropic:claude-opus-4-6 • Thinking: xhigh • Cost: $5.39

@ibetitsmike
Copy link
Contributor Author

@codex review

Copy link

@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: 73587395f4

ℹ️ 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".

@ibetitsmike
Copy link
Contributor Author

@codex review

Both P1 and P2 feedback addressed:

  1. Stale providersConfig — WorkspaceStore now subscribes to client.providers.onConfigChanged() to keep providersConfig fresh after Settings edits.
  2. tokenStatsCalculator — Now accepts optional providersConfig param, wired through tokenizerService and the oRPC router to pass real provider config from providerService.getConfig().

Copy link

@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: 493ff36fc6

ℹ️ 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".

@ibetitsmike
Copy link
Contributor Author

@codex review

Addressed the P2 consumer cache invalidation:

  • Added invalidateAll() to WorkspaceConsumerManager that clears the in-memory token stats cache.
  • Called from refreshProvidersConfig after updating providersConfig, so when a user changes model mapping in Settings, the next consumers panel access triggers fresh recalculation with the updated metadata/tokenizer.

Copy link

@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: a2108d4406

ℹ️ 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".

Address Codex P1 (persisted cache rehydration bypass) and P2
(concurrent refresh race) from round 3 review.
@ibetitsmike
Copy link
Contributor Author

@codex review

Addressed both round 3 issues:

  1. P1 — Persisted cache bypass: refreshProvidersConfig now also clears tokenStatsCache from all sessionUsage entries, preventing tryHydrateConsumersFromSessionUsageCache from serving stale data after mapping changes.
  2. P2 — Concurrent refresh race: Added a monotonic providersConfigVersion counter. Each refreshProvidersConfig call bumps the version before the async fetch; on completion, it bails if a newer version has been issued, ensuring latest-wins ordering.

Copy link

@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: 72bb5f022a

ℹ️ 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".

Use separate version counters for request initiation and successful
application so a failed newer request doesn't discard a valid older
config snapshot.
@ibetitsmike
Copy link
Contributor Author

@codex review

Fixed P2 from round 4:

  • Split version tracking into providersConfigVersion (request counter) and providersConfigAppliedVersion (last successful write).
  • A response is only discarded if a newer successful response already applied (version < appliedVersion).
  • On fetch failure, the catch block preserves the existing providersConfig — no data is ever lost.

Copy link

@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: e53c0cfcd5

ℹ️ 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".

@ibetitsmike
Copy link
Contributor Author

@codex review

Addressed both round 5 issues:

  1. P2 — Config preserved on reconnect: Removed this.providersConfig = null from setClient(). The old config now stays valid during reconnect until refreshProvidersConfig replaces it with fresh data.
  2. P2 — Wired resolved capabilities: getModelCapabilitiesResolved is now used in ChatInput (index.tsx with existing providersConfig), useCreationWorkspace (added useProvidersConfig + fixed deps array), and agentSession (uses null fallback since no provider service is injected).

@chatgpt-codex-connector
Copy link

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.

@ibetitsmike
Copy link
Contributor Author

@codex review

Copy link

@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: 0355f316aa

ℹ️ 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".

@ibetitsmike
Copy link
Contributor Author

Addressed both round 6 P2 issues:

  1. Tool-definition token countingfetchAllToolDefinitions now receives metadataModel instead of the original model, ensuring consistent tokenizer family for tool overhead in mapped custom models.
  2. Stale on-disk cache for unopened workspaces — Added providersConfigVersion to the token stats cache schema. Cache writes stamp the current version; tryHydrateConsumersFromSessionUsageCache rejects entries with mismatched versions, preventing stale data hydration after mapping changes.

@codex review

Copy link

@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: 604108b62d

ℹ️ 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".

@ibetitsmike ibetitsmike added this pull request to the merge queue Feb 19, 2026
@ibetitsmike ibetitsmike removed this pull request from the merge queue due to a manual request Feb 19, 2026
@ibetitsmike
Copy link
Contributor Author

Addressed the latest Codex feedback:

  1. CostsTab now resolves mapped metadata model before supports1MContext, so elevated 1M pricing applies correctly for mapped models.
  2. WorkspaceConsumerManager now drops stale in-flight results when provider-config version changes and schedules a recalculation.
  3. Tool-definition counting now checks tool availability against the runtime model while still tokenizing definitions with the mapped metadata tokenizer.

@codex review

Copy link

@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: a12b376a13

ℹ️ 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 false;
}

if (tokenStatsCache.providersConfigVersion !== this.providersConfigAppliedVersion) {

Choose a reason for hiding this comment

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

P2 Badge Use stable config fingerprint for token-stats cache

This cache gate compares persisted tokenStatsCache.providersConfigVersion to providersConfigAppliedVersion, but that version is an in-memory counter that resets when WorkspaceStore is recreated. Because providersConfigVersion is persisted by TokenizerService, caches written in a prior app session (or after additional refresh increments) can be rejected even when provider mappings are unchanged, forcing unnecessary full token re-calculation on startup/reconnect instead of reusing the persisted cache.

Useful? React with 👍 / 👎.

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

Comments