返回 DeepSeek-Reasonix
extension_models.go
根目录 / internal / acp / extension_models.go
1 package acp
2
3 import (
4 "strings"
5
6 "reasonix/internal/extension/providerext"
7 "reasonix/internal/provider"
8 )
9
10 // enrichStateWithExtensionModels folds a live controller's merged provider
11 // catalog into a session config state, so plugin/... models contributed by
12 // installed extensions are discoverable (and switchable) in BOTH the legacy
13 // model list and the category:"model" config option — not only when one is
14 // already the current model. The state is otherwise config-derived: config
15 // entries keep precedence and extension refs only fill gaps. A nil catalog
16 // (no extension sidecars, or a controller without one) is a no-op.
17 func enrichStateWithExtensionModels(state SessionConfigState, catalog []provider.Descriptor) SessionConfigState {
18 // Collect the extension refs first: when the config backend gave no model
19 // state at all, extension models still deserve a selector.
20 type extModel struct{ ref, name string }
21 var ext []extModel
22 for _, desc := range catalog {
23 ref := strings.TrimSpace(desc.Ref)
24 if ref == "" || providerext.PluginRefOwner(ref) == "" {
25 continue
26 }
27 name := strings.TrimSpace(desc.DisplayName)
28 if name == "" {
29 name = ref
30 }
31 ext = append(ext, extModel{ref, name})
32 }
33 if len(ext) == 0 {
34 return state
35 }
36 if state.Models == nil {
37 state.Models = &SessionModelState{CurrentModelID: state.Model}
38 }
39 hasModelOption := false
40 for _, m := range ext {
41 if !hasModelInfo(state.Models.AvailableModels, m.ref) {
42 state.Models.AvailableModels = append(state.Models.AvailableModels, ModelInfo{
43 ModelID: m.ref,
44 Name: m.name,
45 Description: m.name,
46 })
47 }
48 for i := range state.ConfigOptions {
49 opt := &state.ConfigOptions[i]
50 if opt.Category != "model" {
51 continue
52 }
53 hasModelOption = true
54 if !hasSelectOption(opt.Options, m.ref) {
55 opt.Options = append(opt.Options, SessionConfigSelectOption{
56 Value: m.ref,
57 Name: m.name,
58 Description: m.name,
59 })
60 }
61 }
62 }
63 if !hasModelOption {
64 options := make([]SessionConfigSelectOption, 0, len(ext))
65 for _, m := range ext {
66 options = append(options, SessionConfigSelectOption{Value: m.ref, Name: m.name, Description: m.name})
67 }
68 state.ConfigOptions = append(state.ConfigOptions, SessionConfigOption{
69 ID: "model", Name: "Model", Category: "model", Type: "select",
70 CurrentValue: state.Model,
71 Options: options,
72 })
73 }
74 return state
75 }
76
77 func hasModelInfo(models []ModelInfo, ref string) bool {
78 for _, m := range models {
79 if m.ModelID == ref {
80 return true
81 }
82 }
83 return false
84 }
85
86 func hasSelectOption(options []SessionConfigSelectOption, ref string) bool {
87 for _, o := range options {
88 if o.Value == ref {
89 return true
90 }
91 }
92 return false
93 }
94
94 lines GO