mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-09 04:44:49 +08:00
refactor(go-models): add unsupported model driver defaults (#15431)
### What problem does this PR solve?
Adds a shared safe default implementation for unsupported Go
model-driver capability methods and migrates the confirmed panic-stub
providers to use it.
The Go `ModelDriver` interface requires providers to implement many
capability methods even when the provider does not support them. XunFei
had unsupported capability methods implemented as `panic("implement
me")`, Mistral still had a panic in `ParseFile`, and HuaweiCloud carried
an unreachable `panic("implement me")` after a normal chat return.
### Type of change
- [x] Refactoring
Co-authored-by: Haruko386 <tryeverypossible@163.com>
This commit is contained in:
@@ -280,7 +280,6 @@ func (h *HuaweiCloudModel) ChatWithMessages(modelName string, messages []Message
|
||||
Answer: &content,
|
||||
ReasonContent: &reasonContent,
|
||||
}, nil
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (h *HuaweiCloudModel) ChatStreamlyWithSender(modelName string, messages []Message, apiConfig *APIConfig, chatModelConfig *ChatConfig, sender func(*string, *string) error) error {
|
||||
|
||||
@@ -609,7 +609,7 @@ func (m *MistralModel) ListModels(apiConfig *APIConfig) ([]string, error) {
|
||||
|
||||
// Balance is not exposed by the Mistral API, so this returns "no such method".
|
||||
func (m *MistralModel) Balance(apiConfig *APIConfig) (map[string]interface{}, error) {
|
||||
return nil, fmt.Errorf("no such method")
|
||||
return nil, fmt.Errorf("%s, no such method", m.Name())
|
||||
}
|
||||
|
||||
// CheckConnection runs a lightweight ListModels call to verify the API key.
|
||||
@@ -624,7 +624,7 @@ func (m *MistralModel) CheckConnection(apiConfig *APIConfig) error {
|
||||
// Rerank calculates similarity scores between query and documents. Mistral
|
||||
// does not expose a public rerank API, so this returns "no such method".
|
||||
func (m *MistralModel) Rerank(modelName *string, query string, documents []string, apiConfig *APIConfig, rerankConfig *RerankConfig) (*RerankResponse, error) {
|
||||
return nil, fmt.Errorf("no such method")
|
||||
return nil, fmt.Errorf("%s, no such method", m.Name())
|
||||
}
|
||||
|
||||
// TranscribeAudio transcribe audio
|
||||
@@ -731,8 +731,7 @@ func (m *MistralModel) OCRFile(modelName *string, content []byte, urls *string,
|
||||
}
|
||||
|
||||
func (m *MistralModel) ParseFile(modelName *string, content []byte, url *string, apiConfig *APIConfig, parseFileConfig *ParseFileConfig) (*ParseFileResponse, error) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
return nil, fmt.Errorf("%s, no such method", m.Name())
|
||||
}
|
||||
|
||||
func (m *MistralModel) ListTasks(apiConfig *APIConfig) ([]ListTaskStatus, error) {
|
||||
|
||||
@@ -390,6 +390,49 @@ func TestMistralRerankReturnsNoSuchMethod(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMistralUnsupportedDefaultsReturnNoSuchMethod(t *testing.T) {
|
||||
m := newMistralForTest("http://unused")
|
||||
modelName := "mistral-large-latest"
|
||||
|
||||
checks := []struct {
|
||||
name string
|
||||
call func() error
|
||||
}{
|
||||
{"TranscribeAudio", func() error {
|
||||
_, err := m.TranscribeAudio(&modelName, nil, &APIConfig{}, nil)
|
||||
return err
|
||||
}},
|
||||
{"TranscribeAudioWithSender", func() error {
|
||||
return m.TranscribeAudioWithSender(&modelName, nil, &APIConfig{}, nil, nil)
|
||||
}},
|
||||
{"AudioSpeech", func() error {
|
||||
_, err := m.AudioSpeech(&modelName, nil, &APIConfig{}, nil)
|
||||
return err
|
||||
}},
|
||||
{"AudioSpeechWithSender", func() error {
|
||||
return m.AudioSpeechWithSender(&modelName, nil, &APIConfig{}, nil, nil)
|
||||
}},
|
||||
{"ParseFile", func() error {
|
||||
_, err := m.ParseFile(&modelName, nil, nil, &APIConfig{}, nil)
|
||||
return err
|
||||
}},
|
||||
{"ListTasks", func() error {
|
||||
_, err := m.ListTasks(&APIConfig{})
|
||||
return err
|
||||
}},
|
||||
{"ShowTask", func() error {
|
||||
_, err := m.ShowTask("task-id", &APIConfig{})
|
||||
return err
|
||||
}},
|
||||
}
|
||||
|
||||
for _, check := range checks {
|
||||
t.Run(check.name, func(t *testing.T) {
|
||||
requireNoSuchMethod(t, check.name, check.call())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMistralEmbedHappyPath(t *testing.T) {
|
||||
srv := newMistralServer(t, "/embeddings", func(t *testing.T, body map[string]interface{}, w http.ResponseWriter) {
|
||||
if body["model"] != "mistral-embed" {
|
||||
|
||||
16
internal/entity/models/model_test_helpers_test.go
Normal file
16
internal/entity/models/model_test_helpers_test.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func requireNoSuchMethod(t *testing.T, name string, err error) {
|
||||
t.Helper()
|
||||
if err == nil {
|
||||
t.Fatalf("%s: expected no such method error, got nil", name)
|
||||
}
|
||||
if !strings.Contains(err.Error(), "no such method") {
|
||||
t.Fatalf("%s: expected no such method error, got %v", name, err)
|
||||
}
|
||||
}
|
||||
@@ -51,18 +51,7 @@ func NewXunFeiModel(baseURL map[string]string, urlSuffix URLSuffix) *XunFeiModel
|
||||
}
|
||||
|
||||
func (x *XunFeiModel) NewInstance(baseURL map[string]string) ModelDriver {
|
||||
return &XunFeiModel{
|
||||
BaseURL: baseURL,
|
||||
URLSuffix: x.URLSuffix,
|
||||
httpClient: &http.Client{
|
||||
Transport: &http.Transport{
|
||||
MaxIdleConns: 10,
|
||||
MaxIdleConnsPerHost: 100,
|
||||
IdleConnTimeout: time.Second * 90,
|
||||
DisableCompression: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
return NewXunFeiModel(baseURL, x.URLSuffix)
|
||||
}
|
||||
|
||||
func (x *XunFeiModel) Name() string {
|
||||
@@ -358,43 +347,35 @@ func (x *XunFeiModel) ChatStreamlyWithSender(modelName string, messages []Messag
|
||||
}
|
||||
|
||||
func (x *XunFeiModel) Embed(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([]EmbeddingData, error) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
return nil, fmt.Errorf("%s, no such method", x.Name())
|
||||
}
|
||||
|
||||
func (x *XunFeiModel) Rerank(modelName *string, query string, documents []string, apiConfig *APIConfig, rerankConfig *RerankConfig) (*RerankResponse, error) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
return nil, fmt.Errorf("%s, no such method", x.Name())
|
||||
}
|
||||
|
||||
func (x *XunFeiModel) TranscribeAudio(modelName *string, file *string, apiConfig *APIConfig, asrConfig *ASRConfig) (*ASRResponse, error) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
return nil, fmt.Errorf("%s, no such method", x.Name())
|
||||
}
|
||||
|
||||
func (x *XunFeiModel) TranscribeAudioWithSender(modelName *string, file *string, apiConfig *APIConfig, asrConfig *ASRConfig, sender func(*string, *string) error) error {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
return fmt.Errorf("%s, no such method", x.Name())
|
||||
}
|
||||
|
||||
func (x *XunFeiModel) AudioSpeech(modelName *string, audioContent *string, apiConfig *APIConfig, ttsConfig *TTSConfig) (*TTSResponse, error) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
return nil, fmt.Errorf("%s, no such method", x.Name())
|
||||
}
|
||||
|
||||
func (x *XunFeiModel) AudioSpeechWithSender(modelName *string, audioContent *string, apiConfig *APIConfig, ttsConfig *TTSConfig, sender func(*string, *string) error) error {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
return fmt.Errorf("%s, no such method", x.Name())
|
||||
}
|
||||
|
||||
func (x *XunFeiModel) OCRFile(modelName *string, content []byte, url *string, apiConfig *APIConfig, ocrConfig *OCRConfig) (*OCRFileResponse, error) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
return nil, fmt.Errorf("%s, no such method", x.Name())
|
||||
}
|
||||
|
||||
func (x *XunFeiModel) ParseFile(modelName *string, content []byte, url *string, apiConfig *APIConfig, parseFileConfig *ParseFileConfig) (*ParseFileResponse, error) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
return nil, fmt.Errorf("%s, no such method", x.Name())
|
||||
}
|
||||
|
||||
func (x *XunFeiModel) ListModels(apiConfig *APIConfig) ([]string, error) {
|
||||
@@ -457,21 +438,17 @@ func (x *XunFeiModel) ListModels(apiConfig *APIConfig) ([]string, error) {
|
||||
}
|
||||
|
||||
func (x *XunFeiModel) Balance(apiConfig *APIConfig) (map[string]interface{}, error) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
return nil, fmt.Errorf("%s, no such method", x.Name())
|
||||
}
|
||||
|
||||
func (x *XunFeiModel) CheckConnection(apiConfig *APIConfig) error {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
return fmt.Errorf("%s, no such method", x.Name())
|
||||
}
|
||||
|
||||
func (x *XunFeiModel) ListTasks(apiConfig *APIConfig) ([]ListTaskStatus, error) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
return nil, fmt.Errorf("%s, no such method", x.Name())
|
||||
}
|
||||
|
||||
func (x *XunFeiModel) ShowTask(taskID string, apiConfig *APIConfig) (*TaskResponse, error) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
return nil, fmt.Errorf("%s, no such method", x.Name())
|
||||
}
|
||||
|
||||
67
internal/entity/models/xunfei_test.go
Normal file
67
internal/entity/models/xunfei_test.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package models
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestXunFeiUnsupportedMethodsReturnNoSuchMethod(t *testing.T) {
|
||||
driver := NewXunFeiModel(map[string]string{"default": "http://unused"}, URLSuffix{}).
|
||||
NewInstance(map[string]string{"default": "http://unused"})
|
||||
modelName := "spark"
|
||||
text := "hello"
|
||||
|
||||
checks := []struct {
|
||||
name string
|
||||
call func() error
|
||||
}{
|
||||
{"Embed", func() error {
|
||||
_, err := driver.Embed(&modelName, []string{text}, &APIConfig{}, nil)
|
||||
return err
|
||||
}},
|
||||
{"Rerank", func() error {
|
||||
_, err := driver.Rerank(&modelName, text, []string{text}, &APIConfig{}, nil)
|
||||
return err
|
||||
}},
|
||||
{"TranscribeAudio", func() error {
|
||||
_, err := driver.TranscribeAudio(&modelName, &text, &APIConfig{}, nil)
|
||||
return err
|
||||
}},
|
||||
{"TranscribeAudioWithSender", func() error {
|
||||
return driver.TranscribeAudioWithSender(&modelName, &text, &APIConfig{}, nil, nil)
|
||||
}},
|
||||
{"AudioSpeech", func() error {
|
||||
_, err := driver.AudioSpeech(&modelName, &text, &APIConfig{}, nil)
|
||||
return err
|
||||
}},
|
||||
{"AudioSpeechWithSender", func() error {
|
||||
return driver.AudioSpeechWithSender(&modelName, &text, &APIConfig{}, nil, nil)
|
||||
}},
|
||||
{"OCRFile", func() error {
|
||||
_, err := driver.OCRFile(&modelName, nil, &text, &APIConfig{}, nil)
|
||||
return err
|
||||
}},
|
||||
{"ParseFile", func() error {
|
||||
_, err := driver.ParseFile(&modelName, nil, &text, &APIConfig{}, nil)
|
||||
return err
|
||||
}},
|
||||
{"Balance", func() error {
|
||||
_, err := driver.Balance(&APIConfig{})
|
||||
return err
|
||||
}},
|
||||
{"CheckConnection", func() error {
|
||||
return driver.CheckConnection(&APIConfig{})
|
||||
}},
|
||||
{"ListTasks", func() error {
|
||||
_, err := driver.ListTasks(&APIConfig{})
|
||||
return err
|
||||
}},
|
||||
{"ShowTask", func() error {
|
||||
_, err := driver.ShowTask("task-id", &APIConfig{})
|
||||
return err
|
||||
}},
|
||||
}
|
||||
|
||||
for _, check := range checks {
|
||||
t.Run(check.name, func(t *testing.T) {
|
||||
requireNoSuchMethod(t, check.name, check.call())
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user