diff --git a/memhub/apps/web/src/ModelSettings.test.tsx b/memhub/apps/web/src/ModelSettings.test.tsx index 23e6d87..83f9d4b 100644 --- a/memhub/apps/web/src/ModelSettings.test.tsx +++ b/memhub/apps/web/src/ModelSettings.test.tsx @@ -1482,7 +1482,12 @@ describe("ModelSettings", () => { ...profile, profile_id: "created", revision: 1, - capabilities, + capabilities: { + ...capabilities, + endpoint_reachable: false, + model_available: false, + embedding_available: false, + }, }; profiles = []; create.mockImplementation(async () => { @@ -1491,11 +1496,17 @@ describe("ModelSettings", () => { }); probeProfile.mockResolvedValue({ profile_id: created.profile_id, - profile_revision: created.revision, + input_profile_revision: created.revision, + profile_revision: 2, stages: [], capabilities, }); - activate.mockResolvedValue({ ...created, is_active: true }); + activate.mockResolvedValue({ + ...created, + revision: 2, + capabilities, + is_active: true, + }); render(); await user.type(screen.getByLabelText("配置名称"), "Current model"); @@ -1504,10 +1515,7 @@ describe("ModelSettings", () => { await user.click(screen.getByRole("button", { name: "测试并完成配置" })); await waitFor(() => expect(onSetupComplete).toHaveBeenCalledOnce()); - expect(planActivation).toHaveBeenCalledWith( - created.profile_id, - created.revision, - ); + expect(planActivation).toHaveBeenCalledWith(created.profile_id, 2); expect(activate).toHaveBeenCalledWith(created.profile_id); }); diff --git a/memhub/apps/web/src/ModelSettings.tsx b/memhub/apps/web/src/ModelSettings.tsx index 6318648..0c6c459 100644 --- a/memhub/apps/web/src/ModelSettings.tsx +++ b/memhub/apps/web/src/ModelSettings.tsx @@ -27,6 +27,7 @@ import { presetForGeneration, validateGenerationModel, } from "./GenerationModelPrimitives"; +import { modelProfileReady, profileFromProbe } from "./model-profile-readiness"; import { OptionMenu } from "./OptionMenu"; import { useOnboardingTranslation } from "./onboarding-language"; import { PROVIDER_PRESETS } from "./provider-presets"; @@ -174,27 +175,6 @@ function validEndpoint(value: string): boolean { } } -function ready( - profile: ModelProfile, - probe: ModelProbeResult | null = null, - error: string | null = null, -): boolean { - return ( - !error && - (!probe || - (probe.profile_id === profile.profile_id && - probe.profile_revision === profile.revision && - probe.capabilities.endpoint_reachable && - probe.capabilities.model_available && - probe.capabilities.embedding_available)) && - profile.credential_state !== "unavailable" && - !(profile.has_configured_api_key && !profile.has_api_key) && - profile.capabilities.endpoint_reachable && - profile.capabilities.model_available && - profile.capabilities.embedding_available - ); -} - function endpointLabel(value: string | null): string { if (!value) return "未提供接口地址"; try { @@ -467,7 +447,11 @@ export function ModelSettings({ technicalTouched.current.clear(); }; const isReady = (profile: ModelProfile) => - ready(profile, models.probeFor(profile), models.probeErrorFor(profile)); + modelProfileReady( + profile, + models.probeFor(profile), + models.probeErrorFor(profile), + ); function editProfile(profile: ModelProfile) { if (ownerLocked) return; @@ -626,7 +610,7 @@ export function ModelSettings({ !active || active.profile_id !== profile.profile_id || !active.is_active || - !ready(active, result) + !modelProfileReady(active, result) ) { setSetupError("配置已保存,尚未确认启用成功。请核对配置后重试。"); return false; @@ -640,38 +624,15 @@ export function ModelSettings({ setSetupPhase("testing"); const result = await models.probeProfile(profile.profile_id); if (!alive.current) return false; - if ( - result && - result.profile_id === profile.profile_id && - result.profile_revision !== profile.revision - ) { - // A newer revision used to be accepted here, and then activation turned - // on whatever the configuration had become. The user is told the - // settings they filled in passed, so this hands them a model they never - // tested. `ready` below cannot catch it either: by then the probe and - // the activated profile are both on the new revision and agree. - setSetupError( - result.profile_revision > profile.revision - ? "这份配置在测试期间被改过,刚才测的是改动后的版本。配置已保存但未启用,请重新测试并启用。" - : "测试结果来自这份配置更早的版本。配置已保存但未启用,请重新测试并启用。", - ); - return false; - } - if ( - !result || - result.profile_id !== profile.profile_id || - result.profile_revision !== profile.revision || - !result.capabilities.endpoint_reachable || - !result.capabilities.model_available || - !result.capabilities.embedding_available - ) { + const testedProfile = profileFromProbe(profile, result); + if (!testedProfile || !modelProfileReady(testedProfile, result ?? null)) { setSetupError( "配置已保存,连接测试未通过。请核对配置后重试,历史对话尚未发送。", ); setFields(probeFieldErrors(result)); return false; } - return activateWithPlan(profile, result); + return activateWithPlan(testedProfile, result ?? null); } async function activateTested(profile: ModelProfile) { @@ -1940,7 +1901,11 @@ function PresetProfileCard({