mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-08-12 11:43:39 +08:00
Go: implement ListModels in Volcengine driver (#14702)
### What problem does this PR solve? The VolcEngine Go driver in `internal/entity/models/volcengine.go` shipped with a `ListModels` stub that returned `volcengine, no such method`. `conf/models/volcengine.json` also did not declare a `models` URL suffix, so the model picker had nothing to call even if the method body were filled in. A tenant who configured Volcengine (Doubao / Ark) as a provider could not see the list of available endpoints from the RAGFlow UI. Several other Go drivers already implement `ListModels` against the OpenAI-compatible `/models` endpoint (deepseek, gitee, nvidia, openai, siliconflow), so the interface and pattern are well-established. This PR fills the gap. ### What this PR includes * `conf/models/volcengine.json`: declare the `models` URL suffix alongside the existing `chat`, `files`, and `embedding` entries. The Ark v3 API exposes `https://ark.cn-beijing.volces.com/api/v3/models`, so the suffix is just `models`. * `internal/entity/models/volcengine.go`: replace the `ListModels` stub with a real implementation. Reuses the package-level `DSModelList` / `DSModel` types that DeepSeek, Gitee, and SiliconFlow already use to parse the OpenAI-compatible models response shape. No factory change. No interface change. ### How the driver works * Resolves the region with a default fallback, the same way the other VolcEngine methods in this driver already do. * Builds the URL from `BaseURL[region] + URLSuffix.Models`, with `strings.TrimSuffix` on the base to keep the join robust. * Issues a `GET` with optional `Authorization: Bearer <api_key>` (the header is omitted when no key is configured, mirroring the existing NVIDIA `ListModels`). * Reads the response body once, surfaces a non-200 with the upstream status line plus body, and parses the JSON via the shared `DSModelList` type. * Returns the model id list in input order. When the response includes an `owned_by` field, the entry is rendered as `id@owned_by`, matching the convention used by the other Go drivers. ### Type of change - [x] New Feature (non-breaking change which adds functionality) ### How was this tested? * `go build ./internal/entity/models/...` returns exit 0. * `go vet ./internal/entity/models/...` is clean. * `gofmt -l internal/entity/models/volcengine.go` is clean. * The full method set on `VolcEngine` still matches the `ModelDriver` interface. * Endpoint reachability check: `GET https://ark.cn-beijing.volces.com/api/v3/models` returns `401 Unauthorized` without an API key, confirming the path exists and accepts Bearer authentication. * Pattern parity with DeepSeek, Gitee, NVIDIA, and SiliconFlow `ListModels`. Fixes #14701 Co-authored-by: Jin Hai <haijin.chn@gmail.com>
This commit is contained in:
@@ -6,7 +6,8 @@
|
||||
"url_suffix": {
|
||||
"chat": "chat/completions",
|
||||
"files": "files",
|
||||
"embedding": "embeddings/multimodal"
|
||||
"embedding": "embeddings/multimodal",
|
||||
"models": "models"
|
||||
},
|
||||
"class": "volcengine",
|
||||
"models": [
|
||||
|
||||
@@ -496,7 +496,60 @@ func (z *VolcEngine) Rerank(modelName *string, query string, documents []string,
|
||||
}
|
||||
|
||||
func (z *VolcEngine) ListModels(apiConfig *APIConfig) ([]string, error) {
|
||||
return nil, fmt.Errorf("%s, no such method", z.Name())
|
||||
var region = "default"
|
||||
if apiConfig != nil && apiConfig.Region != nil && *apiConfig.Region != "" {
|
||||
region = *apiConfig.Region
|
||||
}
|
||||
|
||||
baseURL := z.BaseURL[region]
|
||||
if baseURL == "" {
|
||||
baseURL = z.BaseURL["default"]
|
||||
}
|
||||
if baseURL == "" {
|
||||
return nil, fmt.Errorf("volcengine: no base URL configured for region %q", region)
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/%s", strings.TrimSuffix(baseURL, "/"), z.URLSuffix.Models)
|
||||
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
if apiConfig != nil && apiConfig.ApiKey != nil && *apiConfig.ApiKey != "" {
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", *apiConfig.ApiKey))
|
||||
}
|
||||
|
||||
resp, err := z.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to send request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read response: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("VolcEngine models API error: %s, body: %s", resp.Status, string(body))
|
||||
}
|
||||
|
||||
var modelList DSModelList
|
||||
if err = json.Unmarshal(body, &modelList); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse response: %w", err)
|
||||
}
|
||||
|
||||
models := make([]string, 0, len(modelList.Models))
|
||||
for _, model := range modelList.Models {
|
||||
modelName := model.ID
|
||||
if model.OwnedBy != "" {
|
||||
modelName = model.ID + "@" + model.OwnedBy
|
||||
}
|
||||
models = append(models, modelName)
|
||||
}
|
||||
|
||||
return models, nil
|
||||
}
|
||||
|
||||
func (z *VolcEngine) Balance(apiConfig *APIConfig) (map[string]interface{}, error) {
|
||||
|
||||
Reference in New Issue
Block a user