From a7efe3d4c1aa9a88a3876ecbad485bafd10bc2ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kao=20Fe=CC=81lix?= Date: Thu, 25 Dec 2025 22:10:08 +0100 Subject: [PATCH] fix: use consistent model comparison including provider --- packages/ai/src/models.ts | 12 ++++++++++++ packages/coding-agent/src/core/agent-session.ts | 10 +++------- packages/coding-agent/src/core/model-resolver.ts | 4 ++-- .../modes/interactive/components/model-selector.ts | 8 ++++---- packages/web-ui/src/dialogs/ModelSelector.ts | 8 ++++---- 5 files changed, 25 insertions(+), 17 deletions(-) diff --git a/packages/ai/src/models.ts b/packages/ai/src/models.ts index af2e0798..d2ab593d 100644 --- a/packages/ai/src/models.ts +++ b/packages/ai/src/models.ts @@ -54,3 +54,15 @@ const XHIGH_MODELS = new Set(["gpt-5.1-codex-max", "gpt-5.2", "gpt-5.2-codex"]); export function supportsXhigh(model: Model): boolean { return XHIGH_MODELS.has(model.id); } + +/** + * Check if two models are equal by comparing both their id and provider. + * Returns false if either model is null or undefined. + */ +export function modelsAreEqual( + a: Model | null | undefined, + b: Model | null | undefined, +): boolean { + if (!a || !b) return false; + return a.id === b.id && a.provider === b.provider; +} diff --git a/packages/coding-agent/src/core/agent-session.ts b/packages/coding-agent/src/core/agent-session.ts index a8ef1aac..3087ca4f 100644 --- a/packages/coding-agent/src/core/agent-session.ts +++ b/packages/coding-agent/src/core/agent-session.ts @@ -15,7 +15,7 @@ import type { Agent, AgentEvent, AgentState, AppMessage, Attachment, ThinkingLevel } from "@mariozechner/pi-agent-core"; import type { AssistantMessage, Message, Model, TextContent } from "@mariozechner/pi-ai"; -import { isContextOverflow, supportsXhigh } from "@mariozechner/pi-ai"; +import { isContextOverflow, modelsAreEqual, supportsXhigh } from "@mariozechner/pi-ai"; import { getModelsPath } from "../config.js"; import { type BashResult, executeBash as executeBashCommand } from "./bash-executor.js"; import { calculateContextTokens, compact, prepareCompaction, shouldCompact } from "./compaction.js"; @@ -596,9 +596,7 @@ export class AgentSession { if (this._scopedModels.length <= 1) return null; const currentModel = this.model; - let currentIndex = this._scopedModels.findIndex( - (sm) => sm.model.id === currentModel?.id && sm.model.provider === currentModel?.provider, - ); + let currentIndex = this._scopedModels.findIndex((sm) => modelsAreEqual(sm.model, currentModel)); if (currentIndex === -1) currentIndex = 0; const len = this._scopedModels.length; @@ -627,9 +625,7 @@ export class AgentSession { if (availableModels.length <= 1) return null; const currentModel = this.model; - let currentIndex = availableModels.findIndex( - (m) => m.id === currentModel?.id && m.provider === currentModel?.provider, - ); + let currentIndex = availableModels.findIndex((m) => modelsAreEqual(m, currentModel)); if (currentIndex === -1) currentIndex = 0; const len = availableModels.length; diff --git a/packages/coding-agent/src/core/model-resolver.ts b/packages/coding-agent/src/core/model-resolver.ts index 48d9c78c..1734c015 100644 --- a/packages/coding-agent/src/core/model-resolver.ts +++ b/packages/coding-agent/src/core/model-resolver.ts @@ -3,7 +3,7 @@ */ import type { ThinkingLevel } from "@mariozechner/pi-agent-core"; -import type { Api, KnownProvider, Model } from "@mariozechner/pi-ai"; +import { type Api, type KnownProvider, type Model, modelsAreEqual } from "@mariozechner/pi-ai"; import chalk from "chalk"; import { isValidThinkingLevel } from "../cli/args.js"; import type { ModelRegistry } from "./model-registry.js"; @@ -184,7 +184,7 @@ export async function resolveModelScope(patterns: string[], modelRegistry: Model } // Avoid duplicates - if (!scopedModels.find((sm) => sm.model.id === model.id && sm.model.provider === model.provider)) { + if (!scopedModels.find((sm) => modelsAreEqual(sm.model, model))) { scopedModels.push({ model, thinkingLevel }); } } diff --git a/packages/coding-agent/src/modes/interactive/components/model-selector.ts b/packages/coding-agent/src/modes/interactive/components/model-selector.ts index 84178d71..40236899 100644 --- a/packages/coding-agent/src/modes/interactive/components/model-selector.ts +++ b/packages/coding-agent/src/modes/interactive/components/model-selector.ts @@ -1,4 +1,4 @@ -import type { Model } from "@mariozechner/pi-ai"; +import { type Model, modelsAreEqual } from "@mariozechner/pi-ai"; import { Container, Input, @@ -143,8 +143,8 @@ export class ModelSelectorComponent extends Container { // Sort: current model first, then by provider models.sort((a, b) => { - const aIsCurrent = this.currentModel?.id === a.model.id && this.currentModel?.provider === a.provider; - const bIsCurrent = this.currentModel?.id === b.model.id && this.currentModel?.provider === b.provider; + const aIsCurrent = modelsAreEqual(this.currentModel, a.model); + const bIsCurrent = modelsAreEqual(this.currentModel, b.model); if (aIsCurrent && !bIsCurrent) return -1; if (!aIsCurrent && bIsCurrent) return 1; return a.provider.localeCompare(b.provider); @@ -176,7 +176,7 @@ export class ModelSelectorComponent extends Container { if (!item) continue; const isSelected = i === this.selectedIndex; - const isCurrent = this.currentModel?.id === item.model.id; + const isCurrent = modelsAreEqual(this.currentModel, item.model); let line = ""; if (isSelected) { diff --git a/packages/web-ui/src/dialogs/ModelSelector.ts b/packages/web-ui/src/dialogs/ModelSelector.ts index ad228887..08213f50 100644 --- a/packages/web-ui/src/dialogs/ModelSelector.ts +++ b/packages/web-ui/src/dialogs/ModelSelector.ts @@ -3,7 +3,7 @@ import { Badge } from "@mariozechner/mini-lit/dist/Badge.js"; import { Button } from "@mariozechner/mini-lit/dist/Button.js"; import { DialogHeader } from "@mariozechner/mini-lit/dist/Dialog.js"; import { DialogBase } from "@mariozechner/mini-lit/dist/DialogBase.js"; -import { getModels, getProviders, type Model } from "@mariozechner/pi-ai"; +import { getModels, getProviders, type Model, modelsAreEqual } from "@mariozechner/pi-ai"; import { html, type PropertyValues, type TemplateResult } from "lit"; import { customElement, state } from "lit/decorators.js"; import { createRef, ref } from "lit/directives/ref.js"; @@ -195,8 +195,8 @@ export class ModelSelector extends DialogBase { // Sort: current model first, then by provider filteredModels.sort((a, b) => { - const aIsCurrent = this.currentModel?.id === a.model.id; - const bIsCurrent = this.currentModel?.id === b.model.id; + const aIsCurrent = modelsAreEqual(this.currentModel, a.model); + const bIsCurrent = modelsAreEqual(this.currentModel, b.model); if (aIsCurrent && !bIsCurrent) return -1; if (!aIsCurrent && bIsCurrent) return 1; return a.provider.localeCompare(b.provider); @@ -270,7 +270,7 @@ export class ModelSelector extends DialogBase {
${filteredModels.map(({ provider, id, model }, index) => { - const isCurrent = this.currentModel?.id === model.id && this.currentModel?.provider === model.provider; + const isCurrent = modelsAreEqual(this.currentModel, model); const isSelected = index === this.selectedIndex; return html`