diff --git a/src/package.json b/src/package.json index 9893c5886d..0ba23426e9 100644 --- a/src/package.json +++ b/src/package.json @@ -3,7 +3,7 @@ "displayName": "%extension.displayName%", "description": "%extension.description%", "publisher": "matterai", - "version": "6.7.0", + "version": "6.7.1", "icon": "assets/icons/matterai-ic.png", "galleryBanner": { "color": "#FFFFFF", diff --git a/webview-ui/src/components/chat/kilocode/AcceptRejectButtons.tsx b/webview-ui/src/components/chat/kilocode/AcceptRejectButtons.tsx index efaa0338fb..cc7d83705b 100644 --- a/webview-ui/src/components/chat/kilocode/AcceptRejectButtons.tsx +++ b/webview-ui/src/components/chat/kilocode/AcceptRejectButtons.tsx @@ -216,6 +216,7 @@ export const AcceptRejectButtons = ({ onDismiss }: { onDismiss?: () => void }) = style={{ background: "#1f932f", color: "#ffffff", + border: "none", fontWeight: 600, }}> Accept all diff --git a/webview-ui/src/components/kilocode/chat/ModelSelector.tsx b/webview-ui/src/components/kilocode/chat/ModelSelector.tsx index 1577cf86c5..0874bcf95d 100644 --- a/webview-ui/src/components/kilocode/chat/ModelSelector.tsx +++ b/webview-ui/src/components/kilocode/chat/ModelSelector.tsx @@ -15,7 +15,12 @@ import { useAppTranslation } from "@src/i18n/TranslationContext" import { cn } from "@src/lib/utils" import { vscode } from "@src/utils/vscode" import { useMemo, useCallback, useEffect } from "react" -import { prettyModelName, AXON_MODEL_TOOLTIPS } from "../../../utils/prettyModelName" +import { + prettyModelName, + removeContextSuffix, + formatSelectedModelLabel, + AXON_MODEL_TOOLTIPS, +} from "../../../utils/prettyModelName" import { useProviderModels } from "../hooks/useProviderModels" import { getModelIdKey, getSelectedModelId } from "../hooks/useSelectedModel" import { useExtensionState } from "@/context/ExtensionStateContext" @@ -218,7 +223,7 @@ export const ModelSelector = ({ : [selectedModelId] const allOptions = missingModelIds.concat(modelsIds).map((modelId) => { const baseLabel = providerModels[modelId]?.displayName ?? prettyModelName(modelId) - const label = baseLabel + const label = removeContextSuffix(baseLabel) const isProModel = proModelIds?.includes(modelId) const isProModelDisabled = isProModel && !proModelsEnabled const isExtendedContextDisabled = is400kAxonModel(modelId) && !has400kAccess @@ -552,7 +557,14 @@ export const ModelSelector = ({ } if (isError || options.length <= 0) { - return {fallbackText} + const is400k = selectedModelId + ? providerModels[selectedModelId]?.contextWindow === 400000 || is400kAxonModel(selectedModelId) + : false + return ( + + {formatSelectedModelLabel(fallbackText, is400k)} + + ) } return ( @@ -569,7 +581,10 @@ export const ModelSelector = ({ triggerIcon={false} itemClassName="group" renderItem={renderItem} - renderValue={(option) => } + renderValue={(option) => { + const is400k = providerModels[option.value]?.contextWindow === 400000 || is400kAxonModel(option.value) + return + }} onRefresh={handleRefreshModels} // Always show refresh since matterai3p is always enabled /> ) diff --git a/webview-ui/src/utils/__tests__/prettyModelName.spec.ts b/webview-ui/src/utils/__tests__/prettyModelName.spec.ts new file mode 100644 index 0000000000..d0ec5a1f2f --- /dev/null +++ b/webview-ui/src/utils/__tests__/prettyModelName.spec.ts @@ -0,0 +1,57 @@ +import { describe, expect, it } from "vitest" +import { prettyModelName, removeAxonPrefix, formatSelectedModelLabel } from "../prettyModelName" + +describe("prettyModelName", () => { + it("should format simple model names", () => { + expect(prettyModelName("axon-eido-3-flash")).toBe("Axon Eido 3 Flash") + expect(prettyModelName("axon-code-2-pro")).toBe("Axon Code 2 Pro") + }) + + it("should return empty string for empty input", () => { + expect(prettyModelName("")).toBe("") + }) +}) + +describe("removeAxonPrefix", () => { + it("should remove Axon prefix from display labels", () => { + expect(removeAxonPrefix("Axon Eido 3 Flash (200K context)")).toBe("Eido 3 Flash (200K context)") + expect(removeAxonPrefix("Axon Code 2 Pro")).toBe("Code 2 Pro") + expect(removeAxonPrefix("Axon Eido 3 Mini")).toBe("Eido 3 Mini") + }) + + it("should remove axon prefix from hyphenated or lowercased model ids", () => { + expect(removeAxonPrefix("axon-eido-3-flash")).toBe("eido-3-flash") + expect(removeAxonPrefix("AXON Eido 3")).toBe("Eido 3") + }) + + it("should preserve non-Axon model labels", () => { + expect(removeAxonPrefix("Claude 3.5 Sonnet")).toBe("Claude 3.5 Sonnet") + expect(removeAxonPrefix("Kimi K2.5 (Moonshotai)")).toBe("Kimi K2.5 (Moonshotai)") + }) + + it("should return empty string for empty input", () => { + expect(removeAxonPrefix("")).toBe("") + }) +}) + +describe("formatSelectedModelLabel", () => { + it("should remove Axon prefix and hide 200k context specifiers", () => { + expect(formatSelectedModelLabel("Axon Eido 3 Flash (200K context)")).toBe("Eido 3 Flash") + expect(formatSelectedModelLabel("Axon Eido 3 Mini (200k context)")).toBe("Eido 3 Mini") + expect(formatSelectedModelLabel("Axon Eido 3 Code Mini 200k")).toBe("Eido 3 Code Mini") + }) + + it("should remove Axon prefix but keep 400k context specifiers", () => { + expect(formatSelectedModelLabel("Axon Eido 3 Pro (400K context)", true)).toBe("Eido 3 Pro (400k context)") + expect(formatSelectedModelLabel("Axon Lumen 4 (400k context)", true)).toBe("Lumen 4 (400k context)") + }) + + it("should handle model labels without context specifiers", () => { + expect(formatSelectedModelLabel("Axon Code 2 Pro")).toBe("Code 2 Pro") + expect(formatSelectedModelLabel("Claude 3.5 Sonnet")).toBe("Claude 3.5 Sonnet") + }) + + it("should return empty string for empty input", () => { + expect(formatSelectedModelLabel("")).toBe("") + }) +}) diff --git a/webview-ui/src/utils/prettyModelName.ts b/webview-ui/src/utils/prettyModelName.ts index 2cc0ff5998..4f6828d73e 100644 --- a/webview-ui/src/utils/prettyModelName.ts +++ b/webview-ui/src/utils/prettyModelName.ts @@ -88,6 +88,35 @@ export const prettyModelName = (modelId: string): string => { return formattedName + formattedTag } +export const removeAxonPrefix = (text: string): string => { + if (!text) return "" + return text + .replace(/^axon[\s-/_]*/i, "") + .replace(/\bAxon\b\s*/gi, "") + .trim() +} + +/** + * Removes context window suffixes like "(200k context)", "(400k context)", "200k context", etc. + */ +export const removeContextSuffix = (text: string): string => { + if (!text) return "" + return text.replace(/\s*\(?(?:200k|400k)(?:\s*context)?\)?/gi, "").trim() +} + +/** + * Formats model label for displaying as selected model in ChatTextArea. + * Removes "Axon" prefix. Hides 200k context, but appends 400k context if is400k is true. + */ +export const formatSelectedModelLabel = (text: string, is400k?: boolean): string => { + if (!text) return "" + let result = removeContextSuffix(removeAxonPrefix(text)) + if (is400k) { + result += " (400k context)" + } + return result +} + // Function to get credits for Axon models export const getModelCredits = (modelId: string): string | null => { return AXON_MODEL_CREDITS[modelId]