From 2e4f88c612fd2f5170602f5dd024baafa4ce15ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=80=AA=E7=A8=8B=E4=BC=9F?= Date: Wed, 20 May 2026 10:22:44 +0800 Subject: [PATCH] fix(llm): switch slash-bearing modelId from path variable to query parameter (#177) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #174 Model identifiers like 'Qwen/Qwen3-Embedding-8B' or 'Pro/deepseek-ai/DeepSeek-V3' carry forward slashes that Spring MVC decodes from %2F before path matching, so even with the frontend's encodeURIComponent the request never reaches the handler and 404s out. The two affected endpoints take modelId as a request param instead: DELETE /{providerId}/models/{modelId} -> DELETE /{providerId}/models?modelId=... POST /{providerId}/models/{modelId}/test -> POST /{providerId}/models/test?modelId=... modelApi.removeProviderModel / testModel in the UI follow suit, passing the id via axios params so axios handles the URL encoding consistently. providerId stays as a path variable — provider ids are kebab-case and never contain slashes. --- .../vip/mate/llm/controller/ModelConfigController.java | 8 ++++---- mateclaw-ui/src/api/index.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mateclaw-server/src/main/java/vip/mate/llm/controller/ModelConfigController.java b/mateclaw-server/src/main/java/vip/mate/llm/controller/ModelConfigController.java index be82b239..b1921472 100644 --- a/mateclaw-server/src/main/java/vip/mate/llm/controller/ModelConfigController.java +++ b/mateclaw-server/src/main/java/vip/mate/llm/controller/ModelConfigController.java @@ -156,10 +156,10 @@ public class ModelConfigController { } @Operation(summary = "从 Provider 删除模型") - @DeleteMapping("/{providerId}/models/{modelId}") + @DeleteMapping("/{providerId}/models") @RequireWorkspaceRole("admin") public R removeProviderModel(@PathVariable String providerId, - @PathVariable String modelId) { + @RequestParam String modelId) { return R.ok(modelProviderService.removeModel(providerId, modelId)); } @@ -226,10 +226,10 @@ public class ModelConfigController { } @Operation(summary = "测试单个模型可用性") - @PostMapping("/{providerId}/models/{modelId}/test") + @PostMapping("/{providerId}/models/test") @RequireWorkspaceRole("admin") public R testModel(@PathVariable String providerId, - @PathVariable String modelId) { + @RequestParam String modelId) { return R.ok(modelDiscoveryService.testModel(providerId, modelId)); } diff --git a/mateclaw-ui/src/api/index.ts b/mateclaw-ui/src/api/index.ts index 483de0c6..6317968d 100644 --- a/mateclaw-ui/src/api/index.ts +++ b/mateclaw-ui/src/api/index.ts @@ -494,7 +494,7 @@ export const modelApi = { addProviderModel: (providerId: string, data: any) => http.post(`/models/${providerId}/models`, data), removeProviderModel: (providerId: string, modelId: string) => - http.delete(`/models/${providerId}/models/${encodeURIComponent(modelId)}`), + http.delete(`/models/${providerId}/models`, { params: { modelId } }), getActive: () => http.get('/models/active'), setActive: (data: { providerId: string; model: string }) => http.put('/models/active', data), @@ -506,7 +506,7 @@ export const modelApi = { testConnection: (providerId: string) => http.post(`/models/${providerId}/test-connection`), testModel: (providerId: string, modelId: string) => - http.post(`/models/${providerId}/models/${encodeURIComponent(modelId)}/test`), + http.post(`/models/${providerId}/models/test`, null, { params: { modelId } }), // ==================== RFC-074: enabled / catalog ==================== /** Full provider catalog including enabled=false rows; powers the Add Provider drawer. */