返回 DeepSeek-Reasonix
model_provider.go
根目录 / internal / boot / model_provider.go
1 package boot
2
3 import (
4 "net/http"
5 "reasonix/internal/config"
6 "reasonix/internal/netclient"
7 "reasonix/internal/provider"
8 )
9
10 // NewProvider builds a provider.Provider from a configured entry. Exported so
11 // custom assemblers (e.g. the ACP per-session factory) can reuse it without
12 // going through the full Build.
13 func NewProvider(e *config.ProviderEntry) (provider.Provider, error) {
14 return NewProviderWithProxy(e, netclient.ProxySpec{Mode: netclient.ModeAuto})
15 }
16
17 // NewProviderWithProxy builds a provider.Provider with the configured ordinary
18 // network proxy settings.
19 func NewProviderWithProxy(e *config.ProviderEntry, proxy netclient.ProxySpec) (provider.Provider, error) {
20 return NewProviderWithProxyAndModelInfo(e, proxy, nil)
21 }
22
23 // NewProviderWithProxyAndModelInfo builds a provider while preserving the
24 // adapter-resolved metadata for the exact model instance.
25 func NewProviderWithProxyAndModelInfo(e *config.ProviderEntry, proxy netclient.ProxySpec, modelInfo *provider.ModelInfo) (provider.Provider, error) {
26 return newProviderWithSearchMode(e, proxy, modelInfo, true)
27 }
28
29 // clientSearch suppresses new native searches while retaining the adapter's
30 // ability to read and replay existing native search history.
31 func newProviderWithSearchMode(e *config.ProviderEntry, proxy netclient.ProxySpec, modelInfo *provider.ModelInfo, clientSearch bool) (provider.Provider, error) {
32 // Runtime callers may share the resolved config entry across role/provider
33 // factories. Repair a private copy so construction cannot race or mutate the
34 // caller while still honoring exact catalog routes.
35 e = config.ResolveReasoningEntry(e)
36 if err := config.ValidateProviderEndpoint(e); err != nil {
37 return nil, err
38 }
39 reasoning := config.ReasoningCapabilityForEntry(e)
40 if err := reasoning.Validate(e.Model, config.EffectiveEffort(e)); err != nil {
41 return nil, err
42 }
43 if modelInfo == nil {
44 resolved := config.NewModelCapabilityResolver().Resolve(e)
45 modelInfo = &resolved.ModelInfo
46 }
47 var tunnelClient *http.Client
48 if endpoint := e.CredentialProxyURL(); endpoint != "" {
49 var err error
50 tunnelClient, err = netclient.NewModelCredentialProxyClient(endpoint, e.APIKey())
51 if err != nil {
52 return nil, err
53 }
54 }
55 return provider.New(e.Kind, provider.Config{
56 HTTPClient: tunnelClient,
57 Name: e.Name, DisplayName: e.DisplayName, Protocol: e.Kind,
58 BaseURL: e.BaseURL, Model: e.Model, APIKey: e.APIKey(), ModelInfo: modelInfo,
59 // Pass the key's env var so auth failures can name where to fix it, plus
60 // provider-kind-specific knobs. EffectiveEffort applies a configured
61 // default_effort when the user has not explicitly selected /effort.
62 Extra: map[string]any{
63 "api_key_env": e.APIKeyEnv,
64 "api_key_source": e.APIKeySourceLabel(),
65 "thinking": e.Thinking,
66 "effort": config.EffectiveEffort(e),
67 "supported_efforts": e.SupportedEfforts,
68 "default_effort": reasoning.Default,
69 "reasoning_protocol": config.ReasoningProtocolForEntry(e),
70 "max_output_tokens": e.MaxOutputTokens,
71 "chat_url": e.ChatURL,
72 "request_url": e.RequestURL,
73 "headers": e.Headers,
74 "extra_body": e.ExtraBody,
75 "auth_header": e.AuthHeader,
76 "proxy_spec": proxy,
77 "vision": config.EffectiveVision(e),
78 "vision_detail": e.VisionDetail,
79 "web_search": config.EffectiveWebSearch(e),
80 "client_web_search": clientSearch,
81 "reject_redirects": !clientSearch,
82 "mode": e.ResponsesMode,
83 // Keep nil as nil so the responses provider can vendor-detect its
84 // default instead of accidentally treating every endpoint as stateful.
85 "stateful": e.ResponsesStateful,
86 },
87 })
88 }
89
89 lines GO