fix(llm): switch slash-bearing modelId from path variable to query parameter (#177)

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.
This commit is contained in:
倪程伟 2026-05-20 10:22:44 +08:00 committed by GitHub
parent 461f81ccb5
commit 2e4f88c612
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 6 additions and 6 deletions

View File

@ -156,10 +156,10 @@ public class ModelConfigController {
}
@Operation(summary = "从 Provider 删除模型")
@DeleteMapping("/{providerId}/models/{modelId}")
@DeleteMapping("/{providerId}/models")
@RequireWorkspaceRole("admin")
public R<ProviderInfoDTO> 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<TestResult> testModel(@PathVariable String providerId,
@PathVariable String modelId) {
@RequestParam String modelId) {
return R.ok(modelDiscoveryService.testModel(providerId, modelId));
}

View File

@ -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. */