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({