diff --git a/package.json b/package.json index 40069a3..8c4beec 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "kimiswitch", "private": true, - "version": "0.6.8", + "version": "0.6.9", "type": "module", "scripts": { "dev": "vite", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 9e5ad0d..be8987d 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kimiswitch" -version = "0.6.8" +version = "0.6.9" description = "Kimi Switch - model config manager" authors = ["you"] edition = "2021" diff --git a/src-tauri/src/dashboard.rs b/src-tauri/src/dashboard.rs index f678796..b11f14e 100644 --- a/src-tauri/src/dashboard.rs +++ b/src-tauri/src/dashboard.rs @@ -610,7 +610,26 @@ fn strip_context_suffix(model_name: &str) -> Option { }) } +/// Memoized price resolution. `match_price` is called once per usage record +/// during the scan, and the models.dev suffix scan is O(5910) per miss — with +/// thousands of records sharing a handful of model names, memoizing the result +/// turns the whole scan from O(records × 5910) into O(unique_models × 5910). +static MATCH_PRICE_MEMO: OnceLock>> = + OnceLock::new(); + +/// Resolve a model's price, caching per-model results (hits and misses). fn match_price(model_name: &str) -> (String, f64, f64, f64, bool) { + let key = model_name.to_ascii_lowercase(); + let memo = MATCH_PRICE_MEMO.get_or_init(|| Mutex::new(HashMap::new())); + if let Some(hit) = memo.lock().unwrap().get(&key) { + return hit.clone(); + } + let result = match_price_inner(model_name); + memo.lock().unwrap().insert(key, result.clone()); + result +} + +fn match_price_inner(model_name: &str) -> (String, f64, f64, f64, bool) { let bare = match model_name.rsplit_once('/') { Some((_, b)) => b, None => model_name, @@ -1537,6 +1556,7 @@ pub fn get_prices() -> PricesResult { #[tauri::command] pub fn get_summary(home_override: Option, range: Option, refresh: Option) -> SummaryResult { + let t0 = std::time::Instant::now(); let refresh = refresh.unwrap_or(false); let home = resolve_kimi_home(home_override); let now_ms = std::time::SystemTime::now() @@ -1544,6 +1564,7 @@ pub fn get_summary(home_override: Option, range: Option, refresh let r = range.unwrap_or_else(|| "30d".into()); let (records, meta) = scan_usage_cached(&home, refresh); + let t1 = std::time::Instant::now(); let stats = aggregate(&records, &r, now_ms); let all_stats = aggregate(&records, "all", now_ms); let heatmap = build_heatmap(&records, now_ms); @@ -1568,6 +1589,15 @@ pub fn get_summary(home_override: Option, range: Option, refresh name, provider: std::env::var("KIMI_MODEL_PROVIDER").ok(), model: std::env::var("KIMI_MODEL_ID").ok(), }); + let t2 = std::time::Instant::now(); + let scan_ms = t1.duration_since(t0).as_secs_f64() * 1000.0; + let aggregate_ms = t2.duration_since(t1).as_secs_f64() * 1000.0; + let total_ms = t2.duration_since(t0).as_secs_f64() * 1000.0; + eprintln!( + "[dashboard] get_summary range={} records={} scan={:.0}ms aggregate={:.0}ms total={:.0}ms", + r, records.len(), scan_ms, aggregate_ms, total_ms + ); + SummaryResult { home: home.to_string_lossy().to_string(), valid: is_kimi_home(&home), diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 4a73a3f..bed448d 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,6 +1,6 @@ { "productName": "Kimi Switch", - "version": "0.6.8", + "version": "0.6.9", "identifier": "com.kimiswitch.app", "build": { "beforeDevCommand": "npm run dev", diff --git a/src/components/dashboard/DailyBars.tsx b/src/components/dashboard/DailyBars.tsx index bded9e5..de3cc0d 100644 --- a/src/components/dashboard/DailyBars.tsx +++ b/src/components/dashboard/DailyBars.tsx @@ -1,7 +1,8 @@ import { useMemo, useState } from "react"; import { createPortal } from "react-dom"; import { useTranslation } from "../../i18n"; -import { fmtPct, fmtTokens, fmtUsd } from "../../lib/dashboard-format"; +import { useCurrency } from "../../hooks/useCurrency"; +import { fmtPct, fmtTokens } from "../../lib/dashboard-format"; import type { DailyRow } from "../../types/dashboard"; import { DailyDetailModal } from "./DailyDetailModal"; @@ -43,6 +44,7 @@ function shortModel(model: string): string { export function DailyBars({ daily, dimension, names, unknownProviderLabel }: DailyBarsProps) { const { t } = useTranslation(); + const { money } = useCurrency(); const [selected, setSelected] = useState(null); const colorMap = useMemo(() => { @@ -103,7 +105,7 @@ export function DailyBars({ daily, dimension, names, unknownProviderLabel }: Dai const tooltipLines = [ d.date, - `${t("totalTokens")}: ${fmtTokens(d.totalTokens)} · ${fmtUsd(d.costUsd)} · ${t("colCacheHit")} ${fmtPct(d.cacheHitRate)}`, + `${t("totalTokens")}: ${fmtTokens(d.totalTokens)} · ${money(d.costUsd)} · ${t("colCacheHit")} ${fmtPct(d.cacheHitRate)}`, ...segments.map((s) => `${displayName(s.key)}: ${fmtTokens(s.tokens)}`), "", t("doubleClickHint"), diff --git a/src/components/dashboard/DailyDetailModal.tsx b/src/components/dashboard/DailyDetailModal.tsx index c90ef1e..517e0f7 100644 --- a/src/components/dashboard/DailyDetailModal.tsx +++ b/src/components/dashboard/DailyDetailModal.tsx @@ -1,6 +1,7 @@ import { useEffect } from "react"; import { useTranslation } from "../../i18n"; -import { fmtPct, fmtTokens, fmtUsd } from "../../lib/dashboard-format"; +import { useCurrency } from "../../hooks/useCurrency"; +import { fmtPct, fmtTokens } from "../../lib/dashboard-format"; import type { DailyRow } from "../../types/dashboard"; import { modelColor } from "./DailyBars"; @@ -22,6 +23,7 @@ function shortModel(model: string): string { export function DailyDetailModal({ day, dimension, colorMap, unknownProviderLabel, onClose }: DailyDetailModalProps) { const { t } = useTranslation(); + const { money } = useCurrency(); useEffect(() => { const onKey = (e: KeyboardEvent) => { @@ -106,7 +108,7 @@ export function DailyDetailModal({ day, dimension, colorMap, unknownProviderLabe {/* Day summary chips */}
- +
diff --git a/src/components/dashboard/DashboardPage.tsx b/src/components/dashboard/DashboardPage.tsx index acc6636..2bb629f 100644 --- a/src/components/dashboard/DashboardPage.tsx +++ b/src/components/dashboard/DashboardPage.tsx @@ -1,13 +1,20 @@ import { useState, type ReactNode } from "react"; import { useDashboard, type DashboardRange } from "../../hooks/useDashboard"; +import { useCurrency } from "../../hooks/useCurrency"; import { useTranslation } from "../../i18n"; -import { fmtInt, fmtPct, fmtTime, fmtTokens, fmtUsd } from "../../lib/dashboard-format"; +import { fmtInt, fmtPct, fmtTime, fmtTokens } from "../../lib/dashboard-format"; import { DailyBars } from "./DailyBars"; import { Heatmap } from "./Heatmap"; import { TrendLineChart } from "./TrendLineChart"; const RANGES: DashboardRange[] = ["today", "7d", "30d", "all"]; +/** Per-request cache hit rate: cached tokens / total input tokens. */ +function cacheHitOf(r: { inputOther: number; inputCacheRead: number }): number { + const denom = r.inputOther + r.inputCacheRead; + return denom > 0 ? r.inputCacheRead / denom : 0; +} + function Card({ title, subtitle, @@ -44,7 +51,8 @@ function KpiCard({ label, value, sub }: { label: string; value: string; sub?: st export function DashboardPage() { const { t } = useTranslation(); - const { range, changeRange, data, loading, error, refresh } = useDashboard(); + const { money } = useCurrency(); + const { range, changeRange, data, loading, error, refresh, loadStats } = useDashboard(); const [showAllModels, setShowAllModels] = useState(false); const [recentPage, setRecentPage] = useState(1); const [trendTab, setTrendTab] = useState<"daily" | "model" | "provider">("daily"); @@ -120,28 +128,35 @@ export function DashboardPage() { { label: t("kpiCacheCreation"), value: fmtTokens(totals.inputCacheCreation), sub: fmtInt(totals.inputCacheCreation) }, { label: t("kpiCacheHit"), value: fmtPct(totals.cacheHitRate) }, { label: t("totalTokens"), value: fmtTokens(totals.totalTokens), sub: fmtInt(totals.totalTokens) }, - { label: t("kpiCost"), value: fmtUsd(totals.costUsd) }, + { label: t("kpiCost"), value: money(totals.costUsd) }, ]; return (
{/* Range tabs + refresh */}
-
- {RANGES.map((r) => ( - - ))} +
+
+ {RANGES.map((r) => ( + + ))} +
+ {loadStats && !loading && ( + + {t("loadStats", { ms: loadStats.ms, kb: loadStats.kb })} + + )}
); @@ -305,7 +320,7 @@ export function DashboardPage() { {fmtInt(m.requests)} {fmtTokens(m.totalTokens)} {fmtPct(m.cacheHitRate)} - {fmtUsd(m.costUsd)} + {money(m.costUsd)} ))} @@ -346,6 +361,7 @@ export function DashboardPage() { {t("colInput")} {t("colOutput")} {t("colCacheRead")} + {t("colCacheHit")} {t("cost")} @@ -359,7 +375,8 @@ export function DashboardPage() { {fmtTokens(r.inputOther)} {fmtTokens(r.output)} {fmtTokens(r.inputCacheRead)} - {fmtUsd(r.costUsd)} + {fmtPct(cacheHitOf(r))} + {money(r.costUsd)} ))} diff --git a/src/components/dashboard/Heatmap.tsx b/src/components/dashboard/Heatmap.tsx index 2c14d53..0092e2b 100644 --- a/src/components/dashboard/Heatmap.tsx +++ b/src/components/dashboard/Heatmap.tsx @@ -1,7 +1,8 @@ import { useEffect, useMemo, useRef, useState, type MouseEvent } from "react"; import { createPortal } from "react-dom"; import { useTranslation } from "../../i18n"; -import { fmtPct, fmtTokens, fmtUsd } from "../../lib/dashboard-format"; +import { useCurrency } from "../../hooks/useCurrency"; +import { fmtPct, fmtTokens } from "../../lib/dashboard-format"; import type { HeatmapCell, HeatmapData } from "../../types/dashboard"; interface HeatmapProps { @@ -220,6 +221,7 @@ export function Heatmap({ heatmap }: HeatmapProps) { function HeatmapTooltip({ state }: { state: HoverState }) { const { t } = useTranslation(); + const { money } = useCurrency(); const { cell, x, y } = state; // Position above the cursor with a small offset; flip below if too close to top. const offset = 14; @@ -239,7 +241,7 @@ function HeatmapTooltip({ state }: { state: HoverState }) { >
{cell.date}
- {fmtTokens(cell.totalTokens)} · {fmtUsd(cell.costUsd)} ·{" "} + {fmtTokens(cell.totalTokens)} · {money(cell.costUsd)} ·{" "} {fmtInt(cell.requests)} {t("requests")}
diff --git a/src/hooks/useCurrency.ts b/src/hooks/useCurrency.ts new file mode 100644 index 0000000..f1f01a4 --- /dev/null +++ b/src/hooks/useCurrency.ts @@ -0,0 +1,83 @@ +import { useCallback, useEffect, useState } from "react"; +import { useTranslation } from "../i18n"; +import { fmtUsd } from "../lib/dashboard-format"; + +/** + * USD → CNY exchange rate for the dashboard. + * Chinese mode shows prices in CNY (latest available rate), English stays in + * USD. The rate is fetched once and cached in localStorage for 12h; any + * failure falls back to a conservative fixed rate so the UI never blocks. + */ + +const FALLBACK_RATE = 7.2; +const CACHE_KEY = "kimi-switch-cny-rate"; +const CACHE_TTL = 12 * 60 * 60 * 1000; // 12 hours + +function readCachedRate(): number | null { + try { + const raw = localStorage.getItem(CACHE_KEY); + if (!raw) return null; + const { ts, rate } = JSON.parse(raw) as { ts: number; rate: number }; + if (Date.now() - ts > CACHE_TTL || !(rate > 1)) return null; + return rate; + } catch { + return null; + } +} + +function writeCachedRate(rate: number) { + try { + localStorage.setItem(CACHE_KEY, JSON.stringify({ ts: Date.now(), rate })); + } catch { + /* ignore */ + } +} + +export function useCurrency() { + const { lang } = useTranslation(); + const [rate, setRate] = useState(null); + + useEffect(() => { + let cancelled = false; + (async () => { + const cached = readCachedRate(); + if (cached) { + setRate(cached); + return; + } + try { + const res = await fetch("https://open.er-api.com/v6/latest/USD"); + if (res.ok) { + const data = (await res.json()) as { rates?: { CNY?: number } }; + const cny = Number(data.rates?.CNY); + if (cny > 1) { + if (!cancelled) setRate(cny); + writeCachedRate(cny); + return; + } + } + } catch { + /* network blocked → fallback */ + } + if (!cancelled) setRate(FALLBACK_RATE); + })(); + return () => { + cancelled = true; + }; + }, []); + + /** Format a USD cost for display: CNY in Chinese mode, USD otherwise. */ + const money = useCallback( + (usd: number): string => { + if (lang !== "zh") return fmtUsd(usd); + const v = (Number(usd) || 0) * (rate ?? FALLBACK_RATE); + if (v === 0) return "¥0.00"; + if (v < 0.1) return "¥" + v.toFixed(3); + if (v < 1) return "¥" + v.toFixed(2); + return "¥" + v.toFixed(2); + }, + [lang, rate], + ); + + return { money, cnyRate: rate ?? FALLBACK_RATE }; +} diff --git a/src/hooks/useDashboard.ts b/src/hooks/useDashboard.ts index e8a9d03..545a2b2 100644 --- a/src/hooks/useDashboard.ts +++ b/src/hooks/useDashboard.ts @@ -22,16 +22,23 @@ export function useDashboard() { const [data, setData] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); + /** Round-trip timing for the last get_summary call (user-perceived lag). */ + const [loadStats, setLoadStats] = useState<{ ms: number; kb: number } | null>(null); const refresh = useCallback(async (force = false) => { setLoading(true); setError(null); + const start = performance.now(); try { const result = await invoke("get_summary", { range, refresh: force, }); setData(result); + setLoadStats({ + ms: Math.round(performance.now() - start), + kb: Math.round(JSON.stringify(result).length / 1024), + }); } catch (err) { const msg = err instanceof Error ? err.message : String(err); setError(msg); @@ -53,5 +60,5 @@ export function useDashboard() { } }, []); - return { range, changeRange, data, loading, error, refresh }; + return { range, changeRange, data, loading, error, refresh, loadStats }; } diff --git a/src/i18n/en.ts b/src/i18n/en.ts index 84bac24..e9d5e31 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -197,6 +197,7 @@ export const enTranslations: Record = { scanning: "Scanning…", refresh: "Refresh", refreshing: "Refreshing…", + loadStats: "Loaded in {ms} ms · {kb} KB payload", refreshed: "Refreshed", cancel: "Cancel", confirmDeleteTitle: "Delete session forever?", diff --git a/src/i18n/zh.ts b/src/i18n/zh.ts index b3e9ac1..ef4a855 100644 --- a/src/i18n/zh.ts +++ b/src/i18n/zh.ts @@ -194,6 +194,7 @@ export const zhTranslations = { scanning: "扫描中…", refresh: "刷新", refreshing: "刷新中…", + loadStats: "加载 {ms} ms · 数据 {kb} KB", refreshed: "已刷新", cancel: "取消", confirmDeleteTitle: "永久删除会话?", diff --git a/src/lib/models-dev-full.json b/src/lib/models-dev-full.json index 878dbd0..0e775ce 100644 --- a/src/lib/models-dev-full.json +++ b/src/lib/models-dev-full.json @@ -17101,7 +17101,9 @@ "output_limit": 8192, "reasoning": true, "tool_call": true, - "structured_output": true + "structured_output": true, + "image": true, + "video": true }, { "id": "qwen3.6-27b", @@ -17175,7 +17177,8 @@ "name": "Abliterated Model Large", "cost": { "input": 5, - "output": 5 + "output": 5, + "cache_read": 0.5 }, "context": 1000000, "input_limit": 1000000, @@ -17189,7 +17192,8 @@ "name": "Abliterated Model", "cost": { "input": 3, - "output": 3 + "output": 3, + "cache_read": 0.3 }, "context": 150000, "input_limit": 150000, @@ -18190,7 +18194,9 @@ "output_limit": 65536, "reasoning": true, "tool_call": true, - "structured_output": true + "structured_output": true, + "image": true, + "video": true }, { "id": "gpt-5.2-chat-latest", @@ -19796,6 +19802,22 @@ "structured_output": true, "image": true }, + { + "id": "kimi-k3-fast", + "name": "Kimi K3", + "cost": { + "input": 4.5, + "output": 22.5, + "cache_read": 0.45 + }, + "context": 1040384, + "output_limit": 131072, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true + }, { "id": "claude-opus-5", "name": "Claude Opus 5", @@ -29065,7 +29087,9 @@ "output_limit": 32768, "reasoning": true, "tool_call": true, - "structured_output": true + "structured_output": true, + "image": true, + "video": true }, { "id": "Qwen/Qwen3-8B", @@ -29441,7 +29465,8 @@ "name": "zai-org/GLM-5", "cost": { "input": 0.95, - "output": 2.55 + "output": 2.55, + "cache_read": 0.2 }, "context": 205000, "output_limit": 205000, @@ -29467,6 +29492,7 @@ "cost": { "input": 1.4, "output": 4.4, + "cache_read": 0.26, "cache_write": 0 }, "context": 1049000, @@ -29481,6 +29507,7 @@ "cost": { "input": 1.4, "output": 4.4, + "cache_read": 0.26, "cache_write": 0 }, "context": 205000, @@ -29495,6 +29522,7 @@ "cost": { "input": 1.2, "output": 4, + "cache_read": 0.24, "cache_write": 0 }, "context": 200000, @@ -29824,7 +29852,8 @@ "name": "MiniMaxAI/MiniMax-M2.5", "cost": { "input": 0.3, - "output": 1.2 + "output": 1.2, + "cache_read": 0.03 }, "context": 197000, "output_limit": 131000, @@ -29992,7 +30021,8 @@ "name": "moonshotai/Kimi-K2.5", "cost": { "input": 0.45, - "output": 2.25 + "output": 2.25, + "cache_read": 0.07 }, "context": 262000, "output_limit": 262000, @@ -36790,8 +36820,9 @@ "id": "zai-org-glm-4.7-flash", "name": "GLM 4.7 Flash", "cost": { - "input": 0.125, - "output": 0.5 + "input": 0.06, + "output": 0.4, + "cache_read": 0.01 }, "context": 128000, "output_limit": 16384, @@ -36825,6 +36856,20 @@ "structured_output": true, "image": true }, + { + "id": "deepseek-v4-flash-0731", + "name": "DeepSeek V4 Flash 0731", + "cost": { + "input": 0.175, + "output": 0.35, + "cache_read": 0.035 + }, + "context": 1000000, + "output_limit": 32768, + "reasoning": true, + "tool_call": true, + "structured_output": true + }, { "id": "qwen3-235b-a22b-instruct-2507", "name": "Qwen 3 235B A22B Instruct 2507", @@ -37751,7 +37796,8 @@ "name": "GLM-5.1", "cost": { "input": 1.4, - "output": 4.4 + "output": 4.4, + "cache_read": 0.26 }, "context": 202752, "output_limit": 131072, @@ -37822,7 +37868,8 @@ "name": "Qwen3.7 Max", "cost": { "input": 1.25, - "output": 3.75 + "output": 3.75, + "cache_read": 0.125 }, "context": 1000000, "output_limit": 500000, @@ -37844,7 +37891,8 @@ "name": "Qwen3.5 397B A17B", "cost": { "input": 0.6, - "output": 3.6 + "output": 3.6, + "cache_read": 0.35 }, "context": 262144, "output_limit": 130000, @@ -40988,6 +41036,21 @@ "reasoning": true, "tool_call": true, "image": true + }, + { + "id": "moonshotai/Kimi-K3", + "name": "Kimi K3", + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3 + }, + "context": 1048576, + "output_limit": 131072, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true } ] }, @@ -41448,7 +41511,9 @@ "name": "MiniMax-M2.1", "cost": { "input": 2.1, - "output": 8.4 + "output": 8.4, + "cache_read": 2.1, + "cache_write": 8.4 }, "context": 204800, "output_limit": 131072, @@ -45670,7 +45735,9 @@ "output_limit": 32000, "reasoning": true, "tool_call": true, - "structured_output": true + "structured_output": true, + "image": true, + "video": true }, { "id": "nemotron-3-ultra", @@ -48709,7 +48776,9 @@ "name": "MiniMax-M2.1", "cost": { "input": 0.3, - "output": 1.2 + "output": 1.2, + "cache_read": 0.03, + "cache_write": 0.375 }, "context": 204800, "output_limit": 131072, @@ -49621,6 +49690,355 @@ } ] }, + "tensorx": { + "id": "tensorx", + "name": "TensorX", + "models": [ + { + "id": "nvidia/nemotron-3-super-120b-a12b", + "name": "Nemotron 3 Super 120B A12B", + "cost": { + "input": 0.3, + "output": 0.9, + "cache_read": 0.075, + "cache_write": 0.375 + }, + "context": 262144, + "output_limit": 262144, + "reasoning": true, + "tool_call": true + }, + { + "id": "qwen/qwen3-coder-30b-a3b-instruct", + "name": "Qwen3-Coder 30B-A3B Instruct", + "cost": { + "input": 0.06, + "output": 0.25, + "cache_read": 0.015, + "cache_write": 0.075 + }, + "context": 262000, + "output_limit": 65536, + "tool_call": true + }, + { + "id": "qwen/qwen3.5-9b", + "name": "Qwen3.5 9B", + "cost": { + "input": 0.15, + "output": 0.2, + "cache_read": 0.0375, + "cache_write": 0.1875 + }, + "context": 262144, + "output_limit": 65536, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true + }, + { + "id": "qwen/qwen3-vl-235b-a22b-instruct", + "name": "Qwen3 VL 235B-A22B Instruct", + "cost": { + "input": 0.21, + "output": 1.9, + "cache_read": 0.0525, + "cache_write": 0.2625 + }, + "context": 131000, + "output_limit": 131072, + "tool_call": true, + "image": true + }, + { + "id": "qwen/qwen3-235b-a22b-2507", + "name": "Qwen3 235B-A22B-2507", + "cost": { + "input": 0.072, + "output": 0.464, + "cache_read": 0.018, + "cache_write": 0.09 + }, + "context": 131000, + "output_limit": 262144, + "tool_call": true + }, + { + "id": "qwen/qwen3.5-122b-a10b", + "name": "Qwen3.5 122B-A10B", + "cost": { + "input": 0.5, + "output": 3.5, + "cache_read": 0.125, + "cache_write": 0.625 + }, + "context": 262144, + "output_limit": 262144, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true + }, + { + "id": "minimax/minimax-m3", + "name": "MiniMax-M3", + "cost": { + "input": 0.4, + "output": 2, + "cache_read": 0.1 + }, + "context": 1048576, + "output_limit": 131072, + "reasoning": true, + "tool_call": true, + "image": true, + "video": true + }, + { + "id": "minimax/minimax-m2.5", + "name": "MiniMax-M2.5", + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.075, + "cache_write": 0.375 + }, + "context": 196608, + "output_limit": 65536, + "reasoning": true, + "tool_call": true + }, + { + "id": "deepseek/deepseek-v4-flash", + "name": "DeepSeek V4 Flash", + "cost": { + "input": 0.15, + "output": 0.3, + "cache_read": 0.0375, + "cache_write": 0.1875 + }, + "context": 1048576, + "output_limit": 384000, + "reasoning": true, + "tool_call": true, + "structured_output": true + }, + { + "id": "deepseek/deepseek-v4-pro", + "name": "DeepSeek V4 Pro", + "cost": { + "input": 1.75, + "output": 3.5, + "cache_read": 0.4375, + "cache_write": 2.185 + }, + "context": 1048576, + "output_limit": 384000, + "reasoning": true, + "tool_call": true, + "structured_output": true + }, + { + "id": "deepseek/deepseek-r1-0528", + "name": "DeepSeek R1-0528", + "cost": { + "input": 0.66, + "output": 2.6, + "cache_read": 0.165, + "cache_write": 0.825 + }, + "context": 164000, + "output_limit": 8192, + "reasoning": true, + "tool_call": true + }, + { + "id": "deepseek/deepseek-v3.2", + "name": "DeepSeek V3.2", + "cost": { + "input": 0.3, + "output": 0.5, + "cache_read": 0.075, + "cache_write": 0.375 + }, + "context": 163840, + "output_limit": 163840, + "reasoning": true, + "tool_call": true + }, + { + "id": "deepseek/deepseek-chat-v3.1", + "name": "DeepSeek Chat V3.1", + "cost": { + "input": 0.2, + "output": 0.8, + "cache_read": 0.05, + "cache_write": 0.25 + }, + "context": 164000, + "output_limit": 163840, + "reasoning": true, + "tool_call": true + }, + { + "id": "z-ai/glm-5", + "name": "GLM-5", + "cost": { + "input": 1, + "output": 3.2, + "cache_read": 0.25, + "cache_write": 1.25 + }, + "context": 202752, + "output_limit": 202752, + "reasoning": true, + "tool_call": true + }, + { + "id": "z-ai/glm-5.1", + "name": "GLM-5.1", + "cost": { + "input": 1.4, + "output": 4.4, + "cache_read": 0.35, + "cache_write": 1.75 + }, + "context": 202752, + "output_limit": 202752, + "reasoning": true, + "tool_call": true, + "structured_output": true + }, + { + "id": "z-ai/glm-5.2", + "name": "GLM-5.2", + "cost": { + "input": 1.5, + "output": 4.5, + "cache_read": 0.375 + }, + "context": 1048576, + "output_limit": 131072, + "reasoning": true, + "tool_call": true, + "structured_output": true + }, + { + "id": "z-ai/glm-4.7", + "name": "GLM-4.7", + "cost": { + "input": 0.6, + "output": 2.2, + "cache_read": 0.15, + "cache_write": 0.75 + }, + "context": 200000, + "output_limit": 200000, + "reasoning": true, + "tool_call": true, + "image": true + }, + { + "id": "z-ai/glm-5-turbo", + "name": "GLM-5-Turbo", + "cost": { + "input": 1.2, + "output": 4, + "cache_read": 0.3, + "cache_write": 1.5 + }, + "context": 202752, + "output_limit": 131072, + "reasoning": true, + "tool_call": true, + "structured_output": true + }, + { + "id": "z-ai/glm-5v-turbo", + "name": "GLM-5V-Turbo", + "cost": { + "input": 1.2, + "output": 4, + "cache_read": 0.3, + "cache_write": 1.5 + }, + "context": 202752, + "output_limit": 131072, + "reasoning": true, + "tool_call": true, + "image": true, + "video": true + }, + { + "id": "moonshotai/kimi-k2.5", + "name": "Kimi K2.5", + "cost": { + "input": 0.5, + "output": 2.8, + "cache_read": 0.125, + "cache_write": 0.625 + }, + "context": 262144, + "output_limit": 262144, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true + }, + { + "id": "moonshotai/kimi-k2.6", + "name": "Kimi K2.6", + "cost": { + "input": 1, + "output": 4, + "cache_read": 0.25, + "cache_write": 1.25 + }, + "context": 262144, + "output_limit": 262144, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true + }, + { + "id": "moonshotai/kimi-k2.7-code", + "name": "Kimi K2.7 Code", + "cost": { + "input": 1.25, + "output": 4.5, + "cache_read": 0.3125 + }, + "context": 262144, + "output_limit": 262144, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true + }, + { + "id": "openai/gpt-oss-120b", + "name": "GPT OSS 120B", + "cost": { + "input": 0.04, + "output": 0.2, + "cache_read": 0.01, + "cache_write": 0.05 + }, + "context": 131072, + "output_limit": 32768, + "reasoning": true, + "tool_call": true, + "structured_output": true + } + ] + }, "llama": { "id": "llama", "name": "Llama", @@ -58117,7 +58535,8 @@ "cache_read": 0.03 }, "context": 32768, - "output_limit": 65536 + "output_limit": 65536, + "image": true }, { "id": "google/imagen-4.0-generate-001", @@ -58133,7 +58552,8 @@ "cache_read": 0.2 }, "context": 65536, - "output_limit": 32768 + "output_limit": 32768, + "image": true }, { "id": "google/gemini-3.1-pro-preview", @@ -58150,6 +58570,10 @@ "structured_output": true, "image": true }, + { + "id": "google/veo-3.1-lite-generate-001", + "name": "Veo 3.1 Lite Generate" + }, { "id": "google/gemini-2.5-flash-lite", "name": "Gemini 2.5 Flash Lite", @@ -58868,7 +59292,8 @@ }, "context": 128000, "output_limit": 128000, - "tool_call": true + "tool_call": true, + "image": true }, { "id": "mistral/mistral-small", @@ -59198,6 +59623,19 @@ "tool_call": true, "structured_output": true }, + { + "id": "deepseek/deepseek-v4-flash-0731", + "name": "DeepSeek V4 Flash 0731", + "cost": { + "input": 0.13, + "output": 0.26, + "cache_read": 0.028 + }, + "context": 1000000, + "output_limit": 384000, + "reasoning": true, + "tool_call": true + }, { "id": "deepseek/deepseek-v3", "name": "DeepSeek V3 0324", @@ -59714,7 +60152,8 @@ "context": 256000, "output_limit": 80000, "reasoning": true, - "tool_call": true + "tool_call": true, + "image": true }, { "id": "kwaipilot/kat-coder-air-v2.5", @@ -59727,7 +60166,8 @@ "context": 256000, "output_limit": 80000, "reasoning": true, - "tool_call": true + "tool_call": true, + "image": true }, { "id": "xiaomi/mimo-v2.5", @@ -60018,7 +60458,8 @@ "name": "Sonar Reasoning Pro", "context": 127000, "output_limit": 8000, - "reasoning": true + "reasoning": true, + "image": true }, { "id": "moonshotai/kimi-k2.5", @@ -61102,7 +61543,8 @@ "context": 262114, "output_limit": 262114, "reasoning": true, - "tool_call": true + "tool_call": true, + "image": true }, { "id": "stepfun/step-3.7-flash", @@ -68833,7 +69275,9 @@ "name": "MiniMax-M2.1", "cost": { "input": 0, - "output": 0 + "output": 0, + "cache_read": 0, + "cache_write": 0 }, "context": 204800, "output_limit": 131072, @@ -68910,6 +69354,7 @@ "cost": { "input": 1.4, "output": 4.4, + "cache_read": 0.26, "cache_write": 0 }, "context": 1049000, @@ -68973,6 +69418,7 @@ "cost": { "input": 1.4, "output": 4.4, + "cache_read": 0.26, "cache_write": 0 }, "context": 205000, @@ -69064,7 +69510,8 @@ "name": "Pro/moonshotai/Kimi-K2.5", "cost": { "input": 0.45, - "output": 2.25 + "output": 2.25, + "cache_read": 0.07 }, "context": 262000, "output_limit": 262000, @@ -72114,20 +72561,6 @@ "structured_output": true, "image": true }, - { - "id": "zai-org/GLM-5-TEE", - "name": "GLM 5 TEE", - "cost": { - "input": 0.95, - "output": 2.55, - "cache_read": 0.475 - }, - "context": 202752, - "output_limit": 65535, - "reasoning": true, - "tool_call": true, - "structured_output": true - }, { "id": "zai-org/GLM-5.1-TEE", "name": "GLM 5.1 TEE", @@ -72225,20 +72658,6 @@ "structured_output": true, "image": true }, - { - "id": "MiniMaxAI/MiniMax-M2.5-TEE", - "name": "MiniMax M2.5 TEE", - "cost": { - "input": 0.15, - "output": 1.2, - "cache_read": 0.075 - }, - "context": 196608, - "output_limit": 65536, - "reasoning": true, - "tool_call": true, - "structured_output": true - }, { "id": "deepseek-ai/DeepSeek-V3.2-TEE", "name": "DeepSeek V3.2 TEE", @@ -72269,22 +72688,6 @@ "image": true, "video": true }, - { - "id": "moonshotai/Kimi-K2.5-TEE", - "name": "Kimi K2.5 TEE", - "cost": { - "input": 0.44, - "output": 2, - "cache_read": 0.22 - }, - "context": 262144, - "output_limit": 65535, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "image": true, - "video": true - }, { "id": "moonshotai/Kimi-K3-TEE", "name": "Kimi K3 TEE", @@ -77090,7 +77493,9 @@ "name": "MiniMax-M2.1", "cost": { "input": 0.3, - "output": 1.2 + "output": 1.2, + "cache_read": 0.03, + "cache_write": 0.375 }, "context": 204800, "output_limit": 131072, @@ -77719,7 +78124,9 @@ "name": "MiniMax-M2.1", "cost": { "input": 0, - "output": 0 + "output": 0, + "cache_read": 0, + "cache_write": 0 }, "context": 204800, "output_limit": 131072, diff --git a/src/lib/models-dev.json b/src/lib/models-dev.json index cc27bb9..c632ad5 100644 --- a/src/lib/models-dev.json +++ b/src/lib/models-dev.json @@ -13610,6 +13610,8 @@ "reasoning": true, "tool_call": true, "structured_output": true, + "image": true, + "video": true, "cost": { "input": 0, "output": 0 @@ -13674,7 +13676,8 @@ "structured_output": true, "cost": { "input": 5, - "output": 5 + "output": 5, + "cache_read": 0.5 } }, "abliteration-ai/abliterated-model": { @@ -13685,7 +13688,8 @@ "image": true, "cost": { "input": 3, - "output": 3 + "output": 3, + "cache_read": 0.3 } }, "alibaba-coding-plan-cn/qwen3.7-plus": { @@ -14520,6 +14524,8 @@ "reasoning": true, "tool_call": true, "structured_output": true, + "image": true, + "video": true, "cost": { "input": 0.1, "output": 0.15 @@ -15883,6 +15889,20 @@ "cache_read": 0.2 } }, + "llmgateway/kimi-k3-fast": { + "name": "Kimi K3", + "context": 1040384, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 4.5, + "output": 22.5, + "cache_read": 0.45 + } + }, "llmgateway/claude-opus-5": { "name": "Claude Opus 5", "context": 1000000, @@ -23525,6 +23545,8 @@ "reasoning": true, "tool_call": true, "structured_output": true, + "image": true, + "video": true, "cost": { "input": 0.3, "output": 0.3, @@ -23853,7 +23875,8 @@ "structured_output": true, "cost": { "input": 0.95, - "output": 2.55 + "output": 2.55, + "cache_read": 0.2 } }, "siliconflow/zai-org/GLM-4.5-Air": { @@ -23875,6 +23898,7 @@ "cost": { "input": 1.4, "output": 4.4, + "cache_read": 0.26, "cache_write": 0 } }, @@ -23887,6 +23911,7 @@ "cost": { "input": 1.4, "output": 4.4, + "cache_read": 0.26, "cache_write": 0 } }, @@ -23899,6 +23924,7 @@ "cost": { "input": 1.2, "output": 4, + "cache_read": 0.24, "cache_write": 0 } }, @@ -24174,7 +24200,8 @@ "tool_call": true, "cost": { "input": 0.3, - "output": 1.2 + "output": 1.2, + "cache_read": 0.03 } }, "siliconflow/inclusionAI/Ling-flash-2.0": { @@ -24319,7 +24346,8 @@ "image": true, "cost": { "input": 0.45, - "output": 2.25 + "output": 2.25, + "cache_read": 0.07 } }, "siliconflow/openai/gpt-oss-20b": { @@ -29988,8 +30016,9 @@ "tool_call": true, "structured_output": true, "cost": { - "input": 0.125, - "output": 0.5 + "input": 0.06, + "output": 0.4, + "cache_read": 0.01 } }, "venice/llama-3.2-3b": { @@ -30013,6 +30042,18 @@ "output": 225 } }, + "venice/deepseek-v4-flash-0731": { + "name": "DeepSeek V4 Flash 0731", + "context": 1000000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "cost": { + "input": 0.175, + "output": 0.35, + "cache_read": 0.035 + } + }, "venice/qwen3-235b-a22b-instruct-2507": { "name": "Qwen 3 235B A22B Instruct 2507", "context": 128000, @@ -30792,7 +30833,8 @@ "structured_output": true, "cost": { "input": 1.4, - "output": 4.4 + "output": 4.4, + "cache_read": 0.26 } }, "togetherai/pearl-ai/gemma-4-31b-it": { @@ -30849,7 +30891,8 @@ "tool_call": true, "cost": { "input": 1.25, - "output": 3.75 + "output": 3.75, + "cache_read": 0.125 } }, "togetherai/Qwen/Qwen3-235B-A22B-Instruct-2507-tput": { @@ -30869,7 +30912,8 @@ "image": true, "cost": { "input": 0.6, - "output": 3.6 + "output": 3.6, + "cache_read": 0.35 } }, "togetherai/Qwen/Qwen3.6-Plus": { @@ -33439,6 +33483,19 @@ "cache_read": 0.27 } }, + "modal/moonshotai/Kimi-K3": { + "name": "Kimi K3", + "context": 1048576, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3 + } + }, "qihang-ai/gemini-2.5-flash": { "name": "Gemini 2.5 Flash", "context": 1048576, @@ -33814,7 +33871,9 @@ "tool_call": true, "cost": { "input": 2.1, - "output": 8.4 + "output": 8.4, + "cache_read": 2.1, + "cache_write": 8.4 } }, "moark/GLM-4.7": { @@ -37298,6 +37357,8 @@ "reasoning": true, "tool_call": true, "structured_output": true, + "image": true, + "video": true, "cost": { "input": 0.16, "output": 0.48 @@ -39859,7 +39920,9 @@ "tool_call": true, "cost": { "input": 0.3, - "output": 1.2 + "output": 1.2, + "cache_read": 0.03, + "cache_write": 0.375 } }, "minimax/MiniMax-M2.5": { @@ -40611,6 +40674,303 @@ "cache_read": 0.11 } }, + "tensorx/nvidia/nemotron-3-super-120b-a12b": { + "name": "Nemotron 3 Super 120B A12B", + "context": 262144, + "reasoning": true, + "tool_call": true, + "cost": { + "input": 0.3, + "output": 0.9, + "cache_read": 0.075, + "cache_write": 0.375 + } + }, + "tensorx/qwen/qwen3-coder-30b-a3b-instruct": { + "name": "Qwen3-Coder 30B-A3B Instruct", + "context": 262000, + "tool_call": true, + "cost": { + "input": 0.06, + "output": 0.25, + "cache_read": 0.015, + "cache_write": 0.075 + } + }, + "tensorx/qwen/qwen3.5-9b": { + "name": "Qwen3.5 9B", + "context": 262144, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 0.15, + "output": 0.2, + "cache_read": 0.0375, + "cache_write": 0.1875 + } + }, + "tensorx/qwen/qwen3-vl-235b-a22b-instruct": { + "name": "Qwen3 VL 235B-A22B Instruct", + "context": 131000, + "tool_call": true, + "image": true, + "cost": { + "input": 0.21, + "output": 1.9, + "cache_read": 0.0525, + "cache_write": 0.2625 + } + }, + "tensorx/qwen/qwen3-235b-a22b-2507": { + "name": "Qwen3 235B-A22B-2507", + "context": 131000, + "tool_call": true, + "cost": { + "input": 0.072, + "output": 0.464, + "cache_read": 0.018, + "cache_write": 0.09 + } + }, + "tensorx/qwen/qwen3.5-122b-a10b": { + "name": "Qwen3.5 122B-A10B", + "context": 262144, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 0.5, + "output": 3.5, + "cache_read": 0.125, + "cache_write": 0.625 + } + }, + "tensorx/minimax/minimax-m3": { + "name": "MiniMax-M3", + "context": 1048576, + "reasoning": true, + "tool_call": true, + "image": true, + "video": true, + "cost": { + "input": 0.4, + "output": 2, + "cache_read": 0.1 + } + }, + "tensorx/minimax/minimax-m2.5": { + "name": "MiniMax-M2.5", + "context": 196608, + "reasoning": true, + "tool_call": true, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.075, + "cache_write": 0.375 + } + }, + "tensorx/deepseek/deepseek-v4-flash": { + "name": "DeepSeek V4 Flash", + "context": 1048576, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "cost": { + "input": 0.15, + "output": 0.3, + "cache_read": 0.0375, + "cache_write": 0.1875 + } + }, + "tensorx/deepseek/deepseek-v4-pro": { + "name": "DeepSeek V4 Pro", + "context": 1048576, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "cost": { + "input": 1.75, + "output": 3.5, + "cache_read": 0.4375, + "cache_write": 2.185 + } + }, + "tensorx/deepseek/deepseek-r1-0528": { + "name": "DeepSeek R1-0528", + "context": 164000, + "reasoning": true, + "tool_call": true, + "cost": { + "input": 0.66, + "output": 2.6, + "cache_read": 0.165, + "cache_write": 0.825 + } + }, + "tensorx/deepseek/deepseek-v3.2": { + "name": "DeepSeek V3.2", + "context": 163840, + "reasoning": true, + "tool_call": true, + "cost": { + "input": 0.3, + "output": 0.5, + "cache_read": 0.075, + "cache_write": 0.375 + } + }, + "tensorx/deepseek/deepseek-chat-v3.1": { + "name": "DeepSeek Chat V3.1", + "context": 164000, + "reasoning": true, + "tool_call": true, + "cost": { + "input": 0.2, + "output": 0.8, + "cache_read": 0.05, + "cache_write": 0.25 + } + }, + "tensorx/z-ai/glm-5": { + "name": "GLM-5", + "context": 202752, + "reasoning": true, + "tool_call": true, + "cost": { + "input": 1, + "output": 3.2, + "cache_read": 0.25, + "cache_write": 1.25 + } + }, + "tensorx/z-ai/glm-5.1": { + "name": "GLM-5.1", + "context": 202752, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "cost": { + "input": 1.4, + "output": 4.4, + "cache_read": 0.35, + "cache_write": 1.75 + } + }, + "tensorx/z-ai/glm-5.2": { + "name": "GLM-5.2", + "context": 1048576, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "cost": { + "input": 1.5, + "output": 4.5, + "cache_read": 0.375 + } + }, + "tensorx/z-ai/glm-4.7": { + "name": "GLM-4.7", + "context": 200000, + "reasoning": true, + "tool_call": true, + "image": true, + "cost": { + "input": 0.6, + "output": 2.2, + "cache_read": 0.15, + "cache_write": 0.75 + } + }, + "tensorx/z-ai/glm-5-turbo": { + "name": "GLM-5-Turbo", + "context": 202752, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "cost": { + "input": 1.2, + "output": 4, + "cache_read": 0.3, + "cache_write": 1.5 + } + }, + "tensorx/z-ai/glm-5v-turbo": { + "name": "GLM-5V-Turbo", + "context": 202752, + "reasoning": true, + "tool_call": true, + "image": true, + "video": true, + "cost": { + "input": 1.2, + "output": 4, + "cache_read": 0.3, + "cache_write": 1.5 + } + }, + "tensorx/moonshotai/kimi-k2.5": { + "name": "Kimi K2.5", + "context": 262144, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 0.5, + "output": 2.8, + "cache_read": 0.125, + "cache_write": 0.625 + } + }, + "tensorx/moonshotai/kimi-k2.6": { + "name": "Kimi K2.6", + "context": 262144, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 1, + "output": 4, + "cache_read": 0.25, + "cache_write": 1.25 + } + }, + "tensorx/moonshotai/kimi-k2.7-code": { + "name": "Kimi K2.7 Code", + "context": 262144, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 1.25, + "output": 4.5, + "cache_read": 0.3125 + } + }, + "tensorx/openai/gpt-oss-120b": { + "name": "GPT OSS 120B", + "context": 131072, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "cost": { + "input": 0.04, + "output": 0.2, + "cache_read": 0.01, + "cache_write": 0.05 + } + }, "llama/cerebras-llama-4-maverick-17b-128e-instruct": { "name": "Cerebras-Llama-4-Maverick-17B-128E-Instruct", "context": 128000, @@ -47689,6 +48049,7 @@ "vercel/google/gemini-2.5-flash-image": { "name": "Nano Banana (Gemini 2.5 Flash Image)", "context": 32768, + "image": true, "cost": { "input": 0.3, "output": 2.5, @@ -47702,6 +48063,7 @@ "vercel/google/gemini-3-pro-image": { "name": "Nano Banana Pro", "context": 65536, + "image": true, "cost": { "input": 2, "output": 12, @@ -47721,6 +48083,9 @@ "cache_read": 0.2 } }, + "vercel/google/veo-3.1-lite-generate-001": { + "name": "Veo 3.1 Lite Generate" + }, "vercel/google/gemini-2.5-flash-lite": { "name": "Gemini 2.5 Flash Lite", "context": 1048576, @@ -48298,6 +48663,7 @@ "name": "Mistral Nemo", "context": 128000, "tool_call": true, + "image": true, "cost": { "input": 0.15, "output": 0.15 @@ -48578,6 +48944,17 @@ "cache_read": 0.0036 } }, + "vercel/deepseek/deepseek-v4-flash-0731": { + "name": "DeepSeek V4 Flash 0731", + "context": 1000000, + "reasoning": true, + "tool_call": true, + "cost": { + "input": 0.13, + "output": 0.26, + "cache_read": 0.028 + } + }, "vercel/deepseek/deepseek-v3": { "name": "DeepSeek V3 0324", "context": 163840, @@ -49002,6 +49379,7 @@ "context": 256000, "reasoning": true, "tool_call": true, + "image": true, "cost": { "input": 0.74, "output": 2.96, @@ -49013,6 +49391,7 @@ "context": 256000, "reasoning": true, "tool_call": true, + "image": true, "cost": { "input": 0.15, "output": 0.6, @@ -49266,7 +49645,8 @@ "vercel/perplexity/sonar-reasoning-pro": { "name": "Sonar Reasoning Pro", "context": 127000, - "reasoning": true + "reasoning": true, + "image": true }, "vercel/moonshotai/kimi-k2.5": { "name": "Kimi K2.5", @@ -50157,6 +50537,7 @@ "context": 262114, "reasoning": true, "tool_call": true, + "image": true, "cost": { "input": 0.09, "output": 0.3, @@ -56667,7 +57048,9 @@ "tool_call": true, "cost": { "input": 0, - "output": 0 + "output": 0, + "cache_read": 0, + "cache_write": 0 } }, "minimax-coding-plan/MiniMax-M2.5": { @@ -56729,6 +57112,7 @@ "cost": { "input": 1.4, "output": 4.4, + "cache_read": 0.26, "cache_write": 0 } }, @@ -56782,6 +57166,7 @@ "cost": { "input": 1.4, "output": 4.4, + "cache_read": 0.26, "cache_write": 0 } }, @@ -56860,7 +57245,8 @@ "image": true, "cost": { "input": 0.45, - "output": 2.25 + "output": 2.25, + "cache_read": 0.07 } }, "siliconflow-cn/Qwen/Qwen3.5-27B": { @@ -59386,18 +59772,6 @@ "cache_read": 0.06 } }, - "chutes/zai-org/GLM-5-TEE": { - "name": "GLM 5 TEE", - "context": 202752, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "cost": { - "input": 0.95, - "output": 2.55, - "cache_read": 0.475 - } - }, "chutes/zai-org/GLM-5.1-TEE": { "name": "GLM 5.1 TEE", "context": 202752, @@ -59481,18 +59855,6 @@ "cache_read": 0.225 } }, - "chutes/MiniMaxAI/MiniMax-M2.5-TEE": { - "name": "MiniMax M2.5 TEE", - "context": 196608, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "cost": { - "input": 0.15, - "output": 1.2, - "cache_read": 0.075 - } - }, "chutes/deepseek-ai/DeepSeek-V3.2-TEE": { "name": "DeepSeek V3.2 TEE", "context": 131072, @@ -59519,20 +59881,6 @@ "cache_read": 0.33 } }, - "chutes/moonshotai/Kimi-K2.5-TEE": { - "name": "Kimi K2.5 TEE", - "context": 262144, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "image": true, - "video": true, - "cost": { - "input": 0.44, - "output": 2, - "cache_read": 0.22 - } - }, "chutes/moonshotai/Kimi-K3-TEE": { "name": "Kimi K3 TEE", "context": 1048576, @@ -63413,7 +63761,9 @@ "tool_call": true, "cost": { "input": 0.3, - "output": 1.2 + "output": 1.2, + "cache_read": 0.03, + "cache_write": 0.375 } }, "minimax-cn/MiniMax-M2.5": { @@ -63930,7 +64280,9 @@ "tool_call": true, "cost": { "input": 0, - "output": 0 + "output": 0, + "cache_read": 0, + "cache_write": 0 } }, "minimax-cn-coding-plan/MiniMax-M2.5": { diff --git a/website/index.html b/website/index.html index 5311bea..a18daef 100644 --- a/website/index.html +++ b/website/index.html @@ -13,7 +13,7 @@ /> @@ -46,7 +46,7 @@ { "@type": "SoftwareApplication", "name": "Kimi Switch", - "alternateName": ["KimiSwitch", "KimiCodeSwitch"], + "alternateName": ["KimiSwitch", "KimiCodeSwitch", "Kimi Switch", "KimiCode Switch"], "url": "https://billowliu2.github.io/KimiSwitch/", "applicationCategory": "DeveloperApplication", "operatingSystem": "Windows 10, Windows 11", diff --git a/website/public/logos/glm.svg b/website/public/logos/glm.svg deleted file mode 100644 index f1b5845..0000000 --- a/website/public/logos/glm.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/website/src/components/Footer.tsx b/website/src/components/Footer.tsx index 20c70a8..8a5cea5 100644 --- a/website/src/components/Footer.tsx +++ b/website/src/components/Footer.tsx @@ -5,7 +5,7 @@ import { asset } from "../lib/asset"; /** Map recommendLink.name → logo file in public/logos (only when we have one). */ const logoFor: Record = { "Kimi": "logos/kimi.svg", - "智谱 GLM": "logos/glm.svg", + "智谱 GLM": "logos/zhipu.svg", "DeepSeek": "logos/deepseek.svg", "OpenCodeGo": "logos/opencode.svg", "MiniMax": "logos/minimax.svg", diff --git a/website/src/i18n.tsx b/website/src/i18n.tsx index 62137d2..22d5921 100644 --- a/website/src/i18n.tsx +++ b/website/src/i18n.tsx @@ -16,10 +16,10 @@ const zh = { performance: "性能", changelog: "更新日志", download: "下载", - downloadBtn: "下载 v0.6.8", + downloadBtn: "下载 v0.6.9", }, hero: { - badge: "开源 MIT · Windows 10 / 11", + badge: "v0.6.9 · 开源 MIT · Windows / macOS / Linux", titleBefore: "统一管理你的", titleAccent: "AI 供应商", titleAfter: "", @@ -140,6 +140,15 @@ const zh = { syncedNote: "数据已同步 GitHub Releases", fallbackNote: "内置版本记录", entries: [ + { + version: "v0.6.9", + date: "2026-08-01", + items: [ + "最近请求新增缓存命中率列,每次请求命中一目了然", + "中文模式费用按最新汇率换算人民币(¥)显示,英文模式保持美元", + "仪表盘性能再优化:价格解析记忆化缓存,扫描从秒级降到百毫秒级;右上角新增加载耗时/数据量跟踪", + ], + }, { version: "v0.6.8", date: "2026-08-01", @@ -150,14 +159,6 @@ const zh = { "官网更新:Kimi 风格重设计、中英文切换、更新日志同步 GitHub Releases、三平台下载", ], }, - { - version: "v0.6.7", - date: "2026-08-01", - items: [ - "应用内置 Kimi 设备授权登录(等同 kimi login),浏览器授权即用,过期自动续期", - "外链打开统一走 Rust 命令,修复部分链接无法跳转", - ], - }, { version: "v0.6.6", date: "2026-08-01", @@ -244,7 +245,7 @@ const zh = { }, download: { title: "下载 Kimi Switch", - subtitle: "当前版本 v0.6.8 · Windows / macOS / Linux 三平台已发布", + subtitle: "当前版本 v0.6.9 · Windows / macOS / Linux 三平台已发布", autoUpdate: "应用内置自动检测更新,新版本发布后在设置页一键升级。", ready: "已发布", wip: "开发中", @@ -312,10 +313,10 @@ const en: Dict = { performance: "Performance", changelog: "Changelog", download: "Download", - downloadBtn: "Download v0.6.8", + downloadBtn: "Download v0.6.9", }, hero: { - badge: "Open source MIT · Windows 10 / 11", + badge: "v0.6.9 · Open source MIT · Windows / macOS / Linux", titleBefore: "One app for all your ", titleAccent: "AI providers", titleAfter: "", @@ -436,6 +437,15 @@ const en: Dict = { syncedNote: "Synced from GitHub Releases", fallbackNote: "Built-in release notes", entries: [ + { + version: "v0.6.9", + date: "2026-08-01", + items: [ + "Recent requests gain a cache-hit-rate column, per request at a glance", + "Chinese UI now shows costs in CNY (latest exchange rate); English keeps USD", + "Dashboard speedup: memoized price resolution drops the scan from seconds to milliseconds; load time / payload tracking in the top-right corner", + ], + }, { version: "v0.6.8", date: "2026-08-01", @@ -540,7 +550,7 @@ const en: Dict = { }, download: { title: "Download Kimi Switch", - subtitle: "Current version v0.6.8 · Windows, macOS and Linux now released", + subtitle: "Current version v0.6.9 · Windows, macOS and Linux now released", autoUpdate: "Built-in update detection: upgrade in one click from Settings when a new version ships.", ready: "Available", wip: "In development", diff --git a/website/src/sections/Changelog.tsx b/website/src/sections/Changelog.tsx index e1960f6..2eddcf4 100644 --- a/website/src/sections/Changelog.tsx +++ b/website/src/sections/Changelog.tsx @@ -10,9 +10,14 @@ interface ChangelogEntry { const API_URL = "https://api.github.com/repos/billowliu2/KimiSwitch/releases?per_page=20"; -const CACHE_KEY = "kimi-switch-changelog"; const CACHE_TTL = 60 * 60 * 1000; // 1 hour +/** Cache key is versioned by the newest embedded release so redeploys + * invalidate stale caches in the browser immediately. */ +function cacheKey(embedded: ChangelogEntry[]) { + return `kimi-switch-changelog-${embedded[0]?.version ?? "v1"}`; +} + /** Parse a GitHub release body into bullet items. */ function parseBody(body: string): string[] { return body @@ -36,9 +41,9 @@ async function fetchReleases(): Promise { })); } -function readCache(): ChangelogEntry[] | null { +function readCache(embedded: ChangelogEntry[]): ChangelogEntry[] | null { try { - const raw = localStorage.getItem(CACHE_KEY); + const raw = localStorage.getItem(cacheKey(embedded)); if (!raw) return null; const { ts, data } = JSON.parse(raw) as { ts: number; data: ChangelogEntry[] }; if (Date.now() - ts > CACHE_TTL) return null; @@ -48,10 +53,10 @@ function readCache(): ChangelogEntry[] | null { } } -function writeCache(data: ChangelogEntry[]) { +function writeCache(data: ChangelogEntry[], embedded: ChangelogEntry[]) { try { localStorage.setItem( - CACHE_KEY, + cacheKey(embedded), JSON.stringify({ ts: Date.now(), data }), ); } catch { @@ -67,7 +72,7 @@ export default function Changelog() { useEffect(() => { let cancelled = false; - const cached = readCache(); + const cached = readCache(embedded); if (cached) { setRemote(cached); setSynced(true); @@ -78,7 +83,7 @@ export default function Changelog() { if (cancelled) return; setRemote(data); setSynced(true); - writeCache(data); + writeCache(data, embedded); }) .catch(() => { /* 网络不通时使用内置列表兜底 */ diff --git a/website/src/sections/Download.tsx b/website/src/sections/Download.tsx index 0f7f63a..d5144fc 100644 --- a/website/src/sections/Download.tsx +++ b/website/src/sections/Download.tsx @@ -8,25 +8,25 @@ const icons: Record = { linux: LinuxLogo, }; -const VERSION = "0.6.8"; +const VERSION = "0.6.9"; const GITHUB = "https://github.com/billowliu2/KimiSwitch"; const MIRROR_RELEASES = "https://git.codingplan.site/admin/KimiCodeSwitch/releases"; const dl = (file: string) => `${GITHUB}/releases/download/v${VERSION}/${file}`; /** Per-platform download assets for the current release (names match the CI - * release workflow, e.g. Kimi.Switch_0.6.8_x64_en-US.msi). */ + * release workflow, e.g. Kimi.Switch_0.6.9_x64_en-US.msi). */ const assets: Record = { windows: [ - { label: "MSI", href: dl("Kimi.Switch_0.6.8_x64_en-US.msi") }, + { label: "MSI", href: dl("Kimi.Switch_0.6.9_x64_en-US.msi") }, { label: "镜像", href: MIRROR_RELEASES }, ], macos: [ - { label: "Apple Silicon (.dmg)", href: dl("Kimi.Switch_0.6.8_aarch64.dmg") }, + { label: "Apple Silicon (.dmg)", href: dl("Kimi.Switch_0.6.9_aarch64.dmg") }, ], linux: [ - { label: ".deb", href: dl("Kimi.Switch_0.6.8_amd64.deb") }, - { label: ".AppImage", href: dl("Kimi.Switch_0.6.8_amd64.AppImage") }, - { label: ".rpm", href: dl("Kimi.Switch-0.6.8-1.x86_64.rpm") }, + { label: ".deb", href: dl("Kimi.Switch_0.6.9_amd64.deb") }, + { label: ".AppImage", href: dl("Kimi.Switch_0.6.9_amd64.AppImage") }, + { label: ".rpm", href: dl("Kimi.Switch-0.6.9-1.x86_64.rpm") }, ], };