Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13e15657af |
No files matched your search
@@ -6,6 +6,10 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## v0.7.22 (2026-09-24)
|
||||||
|
|
||||||
|
- **修复 Sub2API / NewAPI 模板查询不显示**:为无预设识别类型的自定义供应商配置 Sub2API(或 NewAPI)模板后,弹窗测试可查但供应商列表始终不显示——前端列表的查询/显示门槛只看预设 `usageKinds`,显式模板被漏掉;现在与后端口径对齐(模板查询绕过 usage_kinds),列表正常显示余额 / 配额
|
||||||
|
|
||||||
## v0.7.21 (2026-09-24)
|
## v0.7.21 (2026-09-24)
|
||||||
|
|
||||||
- **models.dev 在线同步**:高级设置新增「模型参考数据」卡片——一键在线拉取 models.dev 最新快照(上下文长度 / 能力标记 / 单价),前端与仪表盘计价即时生效,无需等待新版本;支持一键恢复随版本内置数据;显示当前数据来源与版本(同步 / 内置 + 日期 + 模型/供应商数)
|
- **models.dev 在线同步**:高级设置新增「模型参考数据」卡片——一键在线拉取 models.dev 最新快照(上下文长度 / 能力标记 / 单价),前端与仪表盘计价即时生效,无需等待新版本;支持一键恢复随版本内置数据;显示当前数据来源与版本(同步 / 内置 + 日期 + 模型/供应商数)
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# KimiSwitch v0.7.22
|
||||||
|
|
||||||
|
## 修复
|
||||||
|
|
||||||
|
- **Sub2API / NewAPI 模板查询在供应商列表不显示**:为没有预设识别类型的自定义供应商(如自家中转站)配置「Sub2API 中转站」或「NewAPI 中转站」模板后,配置弹窗里「测试查询」正常返回余额,但回到供应商列表却始终空白。根因:列表的查询与显示门槛只认预设注入的 `usageKinds`,而后端模板查询本就绕过 `usageKinds` 直接执行——前端口径与后端不一致。现在二者对齐,显式模板的供应商在列表正常显示余额 / 配额窗口、支持自动查询间隔与预警阈值。
|
||||||
|
|
||||||
|
## 备注
|
||||||
|
|
||||||
|
- 仅前端修复(`useUsageQuery` 支持判定 + 卡片渲染门槛),后端查询逻辑无变化;已补 3 个单测覆盖支持判定。
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "kimiswitch",
|
"name": "kimiswitch",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.7.21",
|
"version": "0.7.22",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
Generated
+1
-1
@@ -1978,7 +1978,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kimiswitch"
|
name = "kimiswitch"
|
||||||
version = "0.7.21"
|
version = "0.7.22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kimiswitch"
|
name = "kimiswitch"
|
||||||
version = "0.7.21"
|
version = "0.7.22"
|
||||||
description = "Kimi Switch - model config manager"
|
description = "Kimi Switch - model config manager"
|
||||||
authors = ["codingplan.site"]
|
authors = ["codingplan.site"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"productName": "Kimi Switch",
|
"productName": "Kimi Switch",
|
||||||
"version": "0.7.21",
|
"version": "0.7.22",
|
||||||
"identifier": "com.kimiswitch.app",
|
"identifier": "com.kimiswitch.app",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "npm run dev",
|
"beforeDevCommand": "npm run dev",
|
||||||
|
|||||||
@@ -96,7 +96,9 @@ function ProviderCard({
|
|||||||
provider.usageKinds,
|
provider.usageKinds,
|
||||||
provider.usageConfig?.autoQueryIntervalMinutes,
|
provider.usageConfig?.autoQueryIntervalMinutes,
|
||||||
provider.usageConfig?.enabled === false,
|
provider.usageConfig?.enabled === false,
|
||||||
provider.usageConfig?.threshold
|
provider.usageConfig?.threshold,
|
||||||
|
provider.usageConfig?.templateType === "newapi" ||
|
||||||
|
provider.usageConfig?.templateType === "sub2api"
|
||||||
);
|
);
|
||||||
const providerModels = Object.values(models).filter(
|
const providerModels = Object.values(models).filter(
|
||||||
(m) => m.provider === provider.name
|
(m) => m.provider === provider.name
|
||||||
@@ -221,7 +223,7 @@ function ProviderCard({
|
|||||||
)}
|
)}
|
||||||
{/* compact usage summary — sits left of the switch button,
|
{/* compact usage summary — sits left of the switch button,
|
||||||
mirroring cc-switch's card layout (usage → action buttons) */}
|
mirroring cc-switch's card layout (usage → action buttons) */}
|
||||||
{(provider.usageKinds?.length ?? 0) > 0 && (
|
{usage.supported && (
|
||||||
<UsageFooter usage={usage} variant="compact" />
|
<UsageFooter usage={usage} variant="compact" />
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { isAlertTier, type UsageData } from "./useUsageQuery";
|
import { isAlertTier, usageQuerySupported, type UsageData } from "./useUsageQuery";
|
||||||
|
|
||||||
/** 百分比窗口行(five_hour 等);套餐类 tier 的 unit 恒为 "%"。 */
|
/** 百分比窗口行(five_hour 等);套餐类 tier 的 unit 恒为 "%"。 */
|
||||||
function percentTier(used: number): UsageData {
|
function percentTier(used: number): UsageData {
|
||||||
@@ -42,3 +42,22 @@ describe("isAlertTier", () => {
|
|||||||
expect(isAlertTier({ planName: "five_hour", unit: "%", remaining: 20 }, 50)).toBe(false);
|
expect(isAlertTier({ planName: "five_hour", unit: "%", remaining: 20 }, 50)).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("usageQuerySupported", () => {
|
||||||
|
it("有识别类型即可查询", () => {
|
||||||
|
expect(usageQuerySupported(["balance:deepseek"])).toBe(true);
|
||||||
|
expect(usageQuerySupported([], true)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("显式模板(newapi/sub2api)绕过 usage_kinds——自定义供应商也该查", () => {
|
||||||
|
expect(usageQuerySupported(undefined, true)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("无识别类型且非显式模板时不查询", () => {
|
||||||
|
expect(usageQuerySupported(undefined)).toBe(false);
|
||||||
|
expect(usageQuerySupported([])).toBe(false);
|
||||||
|
// auto 模板没有识别到类型 = 不支持(与弹窗短路口径一致)
|
||||||
|
expect(usageQuerySupported([], false)).toBe(false);
|
||||||
|
expect(usageQuerySupported(undefined, undefined)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -75,15 +75,30 @@ export function isAlertTier(
|
|||||||
return d.unit === "%" && d.used != null && d.used >= threshold;
|
return d.unit === "%" && d.used != null && d.used >= threshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否值得为该供应商发起用量查询:有预设识别类型,或后端按显式模板
|
||||||
|
* (newapi / sub2api,二者绕过 usage_kinds)查询。
|
||||||
|
*/
|
||||||
|
export function usageQuerySupported(
|
||||||
|
usageKinds?: string[],
|
||||||
|
explicitTemplate?: boolean
|
||||||
|
): boolean {
|
||||||
|
return (usageKinds?.length ?? 0) > 0 || explicitTemplate === true;
|
||||||
|
}
|
||||||
|
|
||||||
export function useUsageQuery(
|
export function useUsageQuery(
|
||||||
agent: Agent,
|
agent: Agent,
|
||||||
providerName: string,
|
providerName: string,
|
||||||
usageKinds?: string[],
|
usageKinds?: string[],
|
||||||
autoIntervalMinutes?: number,
|
autoIntervalMinutes?: number,
|
||||||
disabled?: boolean,
|
disabled?: boolean,
|
||||||
threshold?: number
|
threshold?: number,
|
||||||
|
/** Backend resolves the query from an explicit template (newapi / sub2api)
|
||||||
|
* regardless of usageKinds — those bypass kinds entirely, so the row is
|
||||||
|
* supported even when the provider has no preset-detected kinds. */
|
||||||
|
explicitTemplate?: boolean
|
||||||
): UsageQueryState {
|
): UsageQueryState {
|
||||||
const supported = (usageKinds?.length ?? 0) > 0;
|
const supported = usageQuerySupported(usageKinds, explicitTemplate);
|
||||||
// Cache key includes the agent so a Kimi Code provider and a Pi provider
|
// Cache key includes the agent so a Kimi Code provider and a Pi provider
|
||||||
// with the same name do not clobber each other's cached result.
|
// with the same name do not clobber each other's cached result.
|
||||||
const cacheKey = `${agent}:${providerName}`;
|
const cacheKey = `${agent}:${providerName}`;
|
||||||
|
|||||||
Reference in new issue
Block a user