返回 DeepSeek-Reasonix
project_providers.go
根目录 / internal / config / project_providers.go
1 package config
2
3 import (
4 "reflect"
5 "slices"
6 "strings"
7 )
8
9 // ProjectProgramProvider is a model provider a workspace declares.
10 const ProjectProgramProvider ProjectProgramKind = "provider"
11
12 // heldEndpoints is what the user chose for model routing before the project
13 // merge: the providers in force and every setting that names a model.
14 type heldEndpoints struct {
15 providers []ProviderEntry
16 defaultModel string
17 agent AgentConfig
18 }
19
20 func holdUserEndpoints(c *Config) heldEndpoints {
21 agent := c.Agent
22 agent.SubagentModels = cloneStringMap(agent.SubagentModels)
23 return heldEndpoints{
24 providers: slices.Clone(c.Providers),
25 defaultModel: c.DefaultModel,
26 agent: agent,
27 }
28 }
29
30 // gateEndpoints keeps a provider a workspace declares out of the config until
31 // it is approved: resolving it would hand the user's stored key to whatever
32 // address the checkout wrote. Model settings that then name nothing fall back.
33 func (h heldEndpoints) gateEndpoints(c *Config, store *ProjectProgramStore, ws string) {
34 var trusted, approved, heldBack []ProviderEntry
35 for _, p := range c.Providers {
36 if h.trustedProvider(c, p) {
37 trusted = append(trusted, p)
38 continue
39 }
40 detail := strings.TrimSpace(p.Name + " " + p.BaseURL + " " + p.RequestURL + " " + p.ChatURL + " key=" + p.APIKeyEnv)
41 prog := NewProjectProgram(ProjectProgramProvider, p.Name, detail, ws, ws, p, nil)
42 if c.admit(store, ws, "providers."+p.Name, prog) {
43 approved = append(approved, p)
44 } else {
45 heldBack = append(heldBack, p)
46 delete(c.providerSources, providerMergeKey(p))
47 }
48 }
49 if len(heldBack) > 0 {
50 if len(trusted) == 0 || !slices.ContainsFunc(trusted, func(p ProviderEntry) bool { return c.providerSources[providerMergeKey(p)] == providerSourceUser }) {
51 trusted = slices.Clone(h.providers)
52 }
53 for _, p := range approved {
54 trusted = slices.DeleteFunc(trusted, func(t ProviderEntry) bool { return providerMergeKey(t) == providerMergeKey(p) })
55 trusted = append(trusted, p)
56 }
57 c.Providers = trusted
58 h.restoreUnresolvedModels(c, heldBack)
59 }
60 }
61
62 // trustedProvider is one the user declared, or one they already had in force.
63 func (h heldEndpoints) trustedProvider(c *Config, p ProviderEntry) bool {
64 if c.providerSources[providerMergeKey(p)] == providerSourceUser {
65 return true
66 }
67 return slices.ContainsFunc(h.providers, func(u ProviderEntry) bool { return reflect.DeepEqual(u, p) })
68 }
69
70 // restoreUnresolvedModels puts back the user's choice wherever a setting named a
71 // model only a held-back provider served; a reference broken for other reasons
72 // stays as written, so its own error still reaches the user.
73 func (h heldEndpoints) restoreUnresolvedModels(c *Config, heldBack []ProviderEntry) {
74 withHeld := *c
75 withHeld.Providers = heldBack
76 orphaned := func(ref string) bool {
77 return !c.resolves(ref) && strings.TrimSpace(ref) != "" && withHeld.resolves(ref)
78 }
79 refs := []struct {
80 key string
81 value *string
82 userChoice string
83 }{
84 {"default_model", &c.DefaultModel, h.defaultModel},
85 {"agent.planner_model", &c.Agent.PlannerModel, h.agent.PlannerModel},
86 {"agent.guardian_model", &c.Agent.GuardianModel, h.agent.GuardianModel},
87 {"agent.recovery_model", &c.Agent.RecoveryModel, h.agent.RecoveryModel},
88 {"agent.subagent_model", &c.Agent.SubagentModel, h.agent.SubagentModel},
89 {"agent.vision_model", &c.Agent.VisionModel, h.agent.VisionModel},
90 {"agent.web_search_model", &c.Agent.WebSearchModel, h.agent.WebSearchModel},
91 }
92 for _, ref := range refs {
93 if *ref.value != ref.userChoice && orphaned(*ref.value) {
94 c.ignoreProject(ref.key, *ref.value, ProjectAwaitingApproval)
95 *ref.value = ref.userChoice
96 }
97 }
98 for name, model := range c.Agent.SubagentModels {
99 if user, ok := h.agent.SubagentModels[name]; (!ok || user != model) && orphaned(model) {
100 c.ignoreProject("agent.subagent_models."+name, model, ProjectAwaitingApproval)
101 if ok {
102 c.Agent.SubagentModels[name] = user
103 } else {
104 delete(c.Agent.SubagentModels, name)
105 }
106 }
107 }
108 }
109
110 func (c *Config) resolves(ref string) bool {
111 if strings.TrimSpace(ref) == "" {
112 return true
113 }
114 _, ok := c.ResolveModel(ref)
115 return ok
116 }
117
117 lines GO