Feat/provider icon color (#385)

* feat(ui): add color prop support to ProviderIcon component

* feat(health): add stream check core functionality

Add new stream-based health check module to replace model_test:
- Add stream_check command layer with single and batch provider checks
- Add stream_check DAO layer for config and log persistence
- Add stream_check service layer with retry mechanism and health status evaluation
- Add frontend HealthStatusIndicator component
- Add frontend useStreamCheck hook

This provides more comprehensive health checking capabilities.

* refactor(health): replace model_test with stream_check

Replace model_test module with stream_check across the codebase:
- Remove model_test command and service modules
- Update command registry in lib.rs to use stream_check commands
- Update module exports in commands/mod.rs and services/mod.rs
- Remove frontend useModelTest hook
- Update stream_check command implementation

This refactoring provides clearer naming and better separation of concerns.

* refactor(db): clean up unused database tables and optimize schema

Remove deprecated and unused database tables:
- Remove proxy_usage table (replaced by proxy_request_logs)
- Remove usage_daily_stats table (aggregation done on-the-fly)
- Rename model_test_logs to stream_check_logs with updated schema
- Remove related DAO methods for proxy_usage
- Update usage_stats service to use proxy_request_logs only
- Refactor usage_script to work with new schema

This simplifies the database schema and removes redundant data storage.

* refactor(ui): update frontend components for stream check

Update frontend components to use stream check API:
- Refactor ModelTestConfigPanel to use stream check config
- Update API layer to use stream_check commands
- Add HealthStatus type and StreamCheckResult interface
- Update ProviderList to use new health check integration
- Update AutoFailoverConfigPanel with stream check references
- Improve UI layout and configuration options

This completes the frontend migration from model_test to stream_check.

* feat(health): add configurable test models and reasoning effort support

Enhance stream check service with configurable test models:
- Add claude_model, codex_model, gemini_model to StreamCheckConfig
- Support reasoning effort syntax (model@level or model#level)
- Parse and apply reasoning_effort for OpenAI-compatible models
- Remove hardcoded model names from check functions
- Add unit tests for model parsing logic
- Remove obsolete model_test source files

This allows users to customize which models are used for health checks.
This commit is contained in:
YoVinchen
2025-12-11 17:20:44 +08:00
committed by GitHub
parent 038b74b844
commit 395783e22a
21 changed files with 883 additions and 1102 deletions
-66
View File
@@ -1,66 +0,0 @@
import { useState, useCallback } from "react";
import { toast } from "sonner";
import { useTranslation } from "react-i18next";
import { testProviderModel, type ModelTestResult } from "@/lib/api/model-test";
import type { AppId } from "@/lib/api";
export function useModelTest(appId: AppId) {
const { t } = useTranslation();
const [testingIds, setTestingIds] = useState<Set<string>>(new Set());
const testProvider = useCallback(
async (
providerId: string,
providerName: string,
): Promise<ModelTestResult | null> => {
setTestingIds((prev) => new Set(prev).add(providerId));
try {
const result = await testProviderModel(appId, providerId);
if (result.success) {
toast.success(
t("modelTest.success", {
name: providerName,
time: result.responseTimeMs,
defaultValue: `${providerName} 测试成功 (${result.responseTimeMs}ms)`,
}),
);
} else {
toast.error(
t("modelTest.failed", {
name: providerName,
error: result.message,
defaultValue: `${providerName} 测试失败: ${result.message}`,
}),
);
}
return result;
} catch (e) {
toast.error(
t("modelTest.error", {
name: providerName,
error: String(e),
defaultValue: `${providerName} 测试出错: ${String(e)}`,
}),
);
return null;
} finally {
setTestingIds((prev) => {
const next = new Set(prev);
next.delete(providerId);
return next;
});
}
},
[appId, t],
);
const isTesting = useCallback(
(providerId: string) => testingIds.has(providerId),
[testingIds],
);
return { testProvider, isTesting };
}
+77
View File
@@ -0,0 +1,77 @@
import { useState, useCallback } from "react";
import { toast } from "sonner";
import { useTranslation } from "react-i18next";
import {
streamCheckProvider,
type StreamCheckResult,
} from "@/lib/api/model-test";
import type { AppId } from "@/lib/api";
export function useStreamCheck(appId: AppId) {
const { t } = useTranslation();
const [checkingIds, setCheckingIds] = useState<Set<string>>(new Set());
const checkProvider = useCallback(
async (
providerId: string,
providerName: string,
): Promise<StreamCheckResult | null> => {
setCheckingIds((prev) => new Set(prev).add(providerId));
try {
const result = await streamCheckProvider(appId, providerId);
if (result.status === "operational") {
toast.success(
t("streamCheck.operational", {
name: providerName,
time: result.responseTimeMs,
defaultValue: `${providerName} 运行正常 (${result.responseTimeMs}ms)`,
}),
);
} else if (result.status === "degraded") {
toast.warning(
t("streamCheck.degraded", {
name: providerName,
time: result.responseTimeMs,
defaultValue: `${providerName} 响应较慢 (${result.responseTimeMs}ms)`,
}),
);
} else {
toast.error(
t("streamCheck.failed", {
name: providerName,
error: result.message,
defaultValue: `${providerName} 检查失败: ${result.message}`,
}),
);
}
return result;
} catch (e) {
toast.error(
t("streamCheck.error", {
name: providerName,
error: String(e),
defaultValue: `${providerName} 检查出错: ${String(e)}`,
}),
);
return null;
} finally {
setCheckingIds((prev) => {
const next = new Set(prev);
next.delete(providerId);
return next;
});
}
},
[appId, t],
);
const isChecking = useCallback(
(providerId: string) => checkingIds.has(providerId),
[checkingIds],
);
return { checkProvider, isChecking };
}