Skip to content

Commit be46f74

Browse files
authored
Merge pull request #566 from phantom5099/file-checkpoint
fix(provider):删除误加入模型发现逻辑问题
2 parents 0f3f9eb + 47a0d9e commit be46f74

23 files changed

Lines changed: 824 additions & 432 deletions

docs/repository-design.md

Lines changed: 642 additions & 53 deletions
Large diffs are not rendered by default.

internal/config/provider.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -699,13 +699,15 @@ func GeminiProvider() ProviderConfig {
699699
func QiniuProvider() ProviderConfig {
700700
cfg := newBuiltinOpenAICompatProvider(QiniuName, QiniuDefaultBaseURL, QiniuDefaultModel, QiniuDefaultAPIKeyEnv)
701701
cfg.Models = cloneBuiltinModels(qiniuStaticModels)
702+
cfg.ModelSource = ModelSourceManual
702703
return cfg
703704
}
704705

705706
// ModelScopeProvider 返回内置的 ModelScope provider 配置。
706707
func ModelScopeProvider() ProviderConfig {
707708
cfg := newBuiltinOpenAICompatProvider(ModelScopeName, ModelScopeDefaultBaseURL, ModelScopeDefaultModel, ModelScopeDefaultAPIKeyEnv)
708709
cfg.Models = cloneBuiltinModels(modelScopeStaticModels)
710+
cfg.ModelSource = ModelSourceManual
709711
return cfg
710712
}
711713

@@ -951,7 +953,7 @@ func DeepSeekProvider() ProviderConfig {
951953
APIKeyEnv: DeepSeekDefaultAPIKeyEnv,
952954
ChatAPIMode: provider.ChatAPIModeChatCompletions,
953955
ChatEndpointPath: "/chat/completions",
954-
ModelSource: ModelSourceDiscover,
956+
ModelSource: ModelSourceManual,
955957
Models: cloneBuiltinModels(deepSeekStaticModels),
956958
Source: ProviderSourceBuiltin,
957959
}
@@ -967,7 +969,7 @@ func KimiProvider() ProviderConfig {
967969
ChatAPIMode: provider.ChatAPIModeChatCompletions,
968970
ChatEndpointPath: "/chat/completions",
969971
DiscoveryEndpointPath: provider.DiscoveryEndpointPathModels,
970-
ModelSource: ModelSourceDiscover,
972+
ModelSource: ModelSourceManual,
971973
Models: cloneBuiltinModels(kimiStaticModels),
972974
Source: ProviderSourceBuiltin,
973975
}
@@ -982,7 +984,7 @@ func QwenProvider() ProviderConfig {
982984
APIKeyEnv: QwenDefaultAPIKeyEnv,
983985
ChatAPIMode: provider.ChatAPIModeChatCompletions,
984986
ChatEndpointPath: "/chat/completions",
985-
ModelSource: ModelSourceDiscover,
987+
ModelSource: ModelSourceManual,
986988
Models: cloneBuiltinModels(qwenStaticModels),
987989
Source: ProviderSourceBuiltin,
988990
}
@@ -997,7 +999,7 @@ func GLMProvider() ProviderConfig {
997999
APIKeyEnv: GLMDefaultAPIKeyEnv,
9981000
ChatAPIMode: provider.ChatAPIModeChatCompletions,
9991001
ChatEndpointPath: "/chat/completions",
1000-
ModelSource: ModelSourceDiscover,
1002+
ModelSource: ModelSourceManual,
10011003
Models: cloneBuiltinModels(glmStaticModels),
10021004
Source: ProviderSourceBuiltin,
10031005
}
@@ -1012,7 +1014,7 @@ func MiMoProvider() ProviderConfig {
10121014
APIKeyEnv: MiMoDefaultAPIKeyEnv,
10131015
ChatAPIMode: provider.ChatAPIModeChatCompletions,
10141016
ChatEndpointPath: "/chat/completions",
1015-
ModelSource: ModelSourceDiscover,
1017+
ModelSource: ModelSourceManual,
10161018
Models: cloneBuiltinModels(miMoStaticModels),
10171019
Source: ProviderSourceBuiltin,
10181020
}
@@ -1027,7 +1029,7 @@ func MiniMaxProvider() ProviderConfig {
10271029
APIKeyEnv: MiniMaxDefaultAPIKeyEnv,
10281030
ChatAPIMode: provider.ChatAPIModeChatCompletions,
10291031
ChatEndpointPath: "/",
1030-
ModelSource: ModelSourceDiscover,
1032+
ModelSource: ModelSourceManual,
10311033
Models: cloneBuiltinModels(miniMaxStaticModels),
10321034
Source: ProviderSourceBuiltin,
10331035
}

internal/provider/catalog/service.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,9 @@ func (s *Service) discoverAndPersist(ctx context.Context, input provider.Catalog
158158
if !s.registry.Supports(input.Identity.Driver) {
159159
return nil, nil
160160
}
161+
if !s.registry.SupportsDiscovery(input.Identity.Driver) {
162+
return nil, provider.NewDiscoveryConfigError(fmt.Sprintf("driver %q does not support model discovery", input.Identity.Driver))
163+
}
161164

162165
if input.ResolveDiscoveryConfig == nil {
163166
return nil, errors.New("provider catalog: discovery config resolver is nil")

internal/provider/catalog/service_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,32 @@ func TestDiscoverAndPersistFailurePaths(t *testing.T) {
524524
}
525525
})
526526

527+
t.Run("driver without discovery support", func(t *testing.T) {
528+
t.Setenv(testAPIKeyEnv, "test-key")
529+
530+
registry := provider.NewRegistry()
531+
if err := registry.Register(provider.DriverDefinition{
532+
Name: "manual-only",
533+
Build: func(ctx context.Context, cfg provider.RuntimeConfig) (provider.Provider, error) {
534+
return catalogTestProvider{}, nil
535+
},
536+
}); err != nil {
537+
t.Fatalf("register driver: %v", err)
538+
}
539+
540+
service := NewService("", registry, newMemoryStore())
541+
providerCfg := customGatewayProvider()
542+
providerCfg.Driver = "manual-only"
543+
544+
discovered, err := service.discoverAndPersist(context.Background(), mustCatalogInput(t, providerCfg))
545+
if err == nil || discovered != nil {
546+
t.Fatalf("expected discovery config error, got err=%v models=%+v", err, discovered)
547+
}
548+
if !provider.IsDiscoveryConfigError(err) {
549+
t.Fatalf("expected discovery config error type, got %v", err)
550+
}
551+
})
552+
527553
t.Run("discovery error", func(t *testing.T) {
528554
t.Setenv(testAPIKeyEnv, "test-key")
529555
service := NewService("", newRegistry(t, openaicompat.DriverName, func(ctx context.Context, cfg provider.RuntimeConfig) ([]providertypes.ModelDescriptor, error) {

internal/provider/conformance/conformance_test.go

Lines changed: 33 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -320,23 +320,6 @@ func TestDiscoverContractAcrossDrivers(t *testing.T) {
320320
expectedHeader: "Authorization",
321321
responseBody: `{"data":[{"id":"gpt-4.1","name":"GPT 4.1"}]}`,
322322
},
323-
{
324-
name: "deepseek_discover",
325-
driver: deepseek.Driver(),
326-
buildConfig: func(baseURL string) provider.RuntimeConfig {
327-
return provider.RuntimeConfig{
328-
Name: deepseek.DriverName,
329-
Driver: provider.DriverDeepSeek,
330-
BaseURL: baseURL,
331-
APIKeyEnv: "DEEPSEEK_TEST_KEY",
332-
APIKeyResolver: provider.StaticAPIKeyResolver("test-key"),
333-
DiscoveryEndpointPath: "/models",
334-
}
335-
},
336-
expectedPath: "/models",
337-
expectedHeader: "Authorization",
338-
responseBody: `{"data":[{"id":"deepseek-v4","name":"DeepSeek V4"}]}`,
339-
},
340323
{
341324
name: "gemini_discover",
342325
driver: gemini.Driver(),
@@ -371,74 +354,6 @@ func TestDiscoverContractAcrossDrivers(t *testing.T) {
371354
expectedHeader: "x-api-key",
372355
responseBody: `{"data":[{"id":"claude-3-7-sonnet","display_name":"Claude 3.7 Sonnet"}],"has_more":false}`,
373356
},
374-
{
375-
name: "mimo_discover",
376-
driver: mimo.Driver(),
377-
buildConfig: func(baseURL string) provider.RuntimeConfig {
378-
return provider.RuntimeConfig{
379-
Name: mimo.DriverName,
380-
Driver: provider.DriverMiMo,
381-
BaseURL: baseURL,
382-
APIKeyEnv: "MIMO_TEST_KEY",
383-
APIKeyResolver: provider.StaticAPIKeyResolver("test-key"),
384-
DiscoveryEndpointPath: "/models",
385-
}
386-
},
387-
expectedPath: "/models",
388-
expectedHeader: "Authorization",
389-
responseBody: `{"data":[{"id":"mimo-v2.5","name":"MiMo V2.5"}]}`,
390-
},
391-
{
392-
name: "minimax_discover",
393-
driver: minimax.Driver(),
394-
buildConfig: func(baseURL string) provider.RuntimeConfig {
395-
return provider.RuntimeConfig{
396-
Name: minimax.DriverName,
397-
Driver: provider.DriverMiniMax,
398-
BaseURL: baseURL,
399-
APIKeyEnv: "MINIMAX_TEST_KEY",
400-
APIKeyResolver: provider.StaticAPIKeyResolver("test-key"),
401-
DiscoveryEndpointPath: "/models",
402-
}
403-
},
404-
expectedPath: "/models",
405-
expectedHeader: "Authorization",
406-
responseBody: `{"data":[{"id":"minimax-m2.7","name":"MiniMax M2.7"}]}`,
407-
},
408-
{
409-
name: "qwen_discover",
410-
driver: qwen.Driver(),
411-
buildConfig: func(baseURL string) provider.RuntimeConfig {
412-
return provider.RuntimeConfig{
413-
Name: qwen.DriverName,
414-
Driver: provider.DriverQwen,
415-
BaseURL: baseURL,
416-
APIKeyEnv: "QWEN_TEST_KEY",
417-
APIKeyResolver: provider.StaticAPIKeyResolver("test-key"),
418-
DiscoveryEndpointPath: "/models",
419-
}
420-
},
421-
expectedPath: "/models",
422-
expectedHeader: "Authorization",
423-
responseBody: `{"data":[{"id":"qwen3","name":"Qwen 3"}]}`,
424-
},
425-
{
426-
name: "glm_discover",
427-
driver: glm.Driver(),
428-
buildConfig: func(baseURL string) provider.RuntimeConfig {
429-
return provider.RuntimeConfig{
430-
Name: glm.DriverName,
431-
Driver: provider.DriverGLM,
432-
BaseURL: baseURL,
433-
APIKeyEnv: "GLM_TEST_KEY",
434-
APIKeyResolver: provider.StaticAPIKeyResolver("test-key"),
435-
DiscoveryEndpointPath: "/models",
436-
}
437-
},
438-
expectedPath: "/models",
439-
expectedHeader: "Authorization",
440-
responseBody: `{"data":[{"id":"glm-5.1","name":"GLM 5.1"}]}`,
441-
},
442357
}
443358

444359
for _, tt := range testCases {
@@ -467,6 +382,39 @@ func TestDiscoverContractAcrossDrivers(t *testing.T) {
467382
}
468383
}
469384

385+
func TestDiscoverUnsupportedDrivers(t *testing.T) {
386+
testCases := []struct {
387+
name string
388+
driver provider.DriverDefinition
389+
}{
390+
{name: "deepseek_discover_unsupported", driver: deepseek.Driver()},
391+
{name: "minimax_discover_unsupported", driver: minimax.Driver()},
392+
{name: "qwen_discover_unsupported", driver: qwen.Driver()},
393+
{name: "glm_discover_unsupported", driver: glm.Driver()},
394+
{name: "mimo_discover_unsupported", driver: mimo.Driver()},
395+
}
396+
397+
for _, tt := range testCases {
398+
tt := tt
399+
t.Run(tt.name, func(t *testing.T) {
400+
reg := provider.NewRegistry()
401+
if err := reg.Register(tt.driver); err != nil {
402+
t.Fatalf("Register() error = %v", err)
403+
}
404+
if reg.SupportsDiscovery(tt.driver.Name) {
405+
t.Fatalf("expected driver %q to not support discovery", tt.driver.Name)
406+
}
407+
_, err := reg.DiscoverModels(context.Background(), provider.RuntimeConfig{Driver: tt.driver.Name})
408+
if err == nil {
409+
t.Fatal("expected discovery unsupported error, got nil")
410+
}
411+
if !provider.IsDiscoveryConfigError(err) {
412+
t.Fatalf("expected discovery config error, got %T: %v", err, err)
413+
}
414+
})
415+
}
416+
}
417+
470418
func TestGenerateErrorClassificationAcrossDrivers(t *testing.T) {
471419
testCases := []struct {
472420
name string

internal/provider/deepseek/driver.go

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55

66
"neo-code/internal/provider"
7-
providertypes "neo-code/internal/provider/types"
87
)
98

109
const DriverName = provider.DriverDeepSeek
@@ -15,13 +14,6 @@ func Driver() provider.DriverDefinition {
1514
Build: func(ctx context.Context, cfg provider.RuntimeConfig) (provider.Provider, error) {
1615
return New(cfg)
1716
},
18-
Discover: func(ctx context.Context, cfg provider.RuntimeConfig) ([]providertypes.ModelDescriptor, error) {
19-
p, err := New(cfg)
20-
if err != nil {
21-
return nil, err
22-
}
23-
return p.DiscoverModels(ctx)
24-
},
2517
ValidateCatalogIdentity: func(identity provider.ProviderIdentity) error {
2618
return nil
2719
},

internal/provider/deepseek/driver_test.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ func TestDriverName(t *testing.T) {
1414
if d.Build == nil {
1515
t.Fatal("build func is nil")
1616
}
17-
if d.Discover == nil {
18-
t.Fatal("discover func is nil")
19-
}
2017
}
2118

2219
func TestNewValidatesBaseURL(t *testing.T) {

internal/provider/deepseek/provider.go

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,15 @@ import (
1010
"strings"
1111

1212
"neo-code/internal/provider"
13-
"neo-code/internal/provider/openaicompat"
1413
"neo-code/internal/provider/openaicompat/chatcompletions"
1514
providertypes "neo-code/internal/provider/types"
1615
)
1716

1817
const errorPrefix = "deepseek provider: "
1918

2019
type Provider struct {
21-
cfg provider.RuntimeConfig
22-
generateClient *http.Client
23-
discoveryClient *http.Client
20+
cfg provider.RuntimeConfig
21+
generateClient *http.Client
2422
}
2523

2624
func New(cfg provider.RuntimeConfig) (*Provider, error) {
@@ -35,10 +33,6 @@ func New(cfg provider.RuntimeConfig) (*Provider, error) {
3533
generateClient: &http.Client{
3634
Transport: http.DefaultTransport,
3735
},
38-
discoveryClient: &http.Client{
39-
Timeout: provider.DefaultSDKRequestTimeout,
40-
Transport: http.DefaultTransport,
41-
},
4236
}, nil
4337
}
4438

@@ -61,14 +55,6 @@ func (p *Provider) EstimateInputTokens(
6155
}, nil
6256
}
6357

64-
func (p *Provider) DiscoverModels(ctx context.Context) ([]providertypes.ModelDescriptor, error) {
65-
requestCfg, err := openaicompat.RequestConfigFromRuntime(p.cfg)
66-
if err != nil {
67-
return nil, err
68-
}
69-
return openaicompat.DiscoverModelDescriptors(ctx, p.discoveryClient, requestCfg)
70-
}
71-
7258
func (p *Provider) Generate(ctx context.Context, req providertypes.GenerateRequest, events chan<- providertypes.StreamEvent) error {
7359
payload, err := BuildRequest(ctx, p.cfg, req)
7460
if err != nil {

0 commit comments

Comments
 (0)