Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ export const AcceptRejectButtons = ({ onDismiss }: { onDismiss?: () => void }) =
style={{
background: "#1f932f",
color: "#ffffff",
border: "none",
fontWeight: 600,
}}>
Accept all
Expand Down
23 changes: 19 additions & 4 deletions webview-ui/src/components/kilocode/chat/ModelSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -552,7 +557,14 @@ export const ModelSelector = ({
}

if (isError || options.length <= 0) {
return <span className="text-xs text-vscode-descriptionForeground opacity-70 truncate">{fallbackText}</span>
const is400k = selectedModelId
? providerModels[selectedModelId]?.contextWindow === 400000 || is400kAxonModel(selectedModelId)
: false
return (
<span className="text-xs text-vscode-descriptionForeground opacity-70 truncate">
{formatSelectedModelLabel(fallbackText, is400k)}
</span>
)
}

return (
Expand All @@ -569,7 +581,10 @@ export const ModelSelector = ({
triggerIcon={false}
itemClassName="group"
renderItem={renderItem}
renderValue={(option) => <ModelLabel label={option.label} />}
renderValue={(option) => {
const is400k = providerModels[option.value]?.contextWindow === 400000 || is400kAxonModel(option.value)
return <ModelLabel label={formatSelectedModelLabel(option.label, is400k)} />
}}
onRefresh={handleRefreshModels} // Always show refresh since matterai3p is always enabled
/>
)
Expand Down
57 changes: 57 additions & 0 deletions webview-ui/src/utils/__tests__/prettyModelName.spec.ts
Original file line number Diff line number Diff line change
@@ -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("")
})
})
29 changes: 29 additions & 0 deletions webview-ui/src/utils/prettyModelName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
Loading