返回 DeepSeek-Reasonix
model_application.go
根目录 / internal / serve / model_application.go
1 package serve
2
3 import (
4 "context"
5 "errors"
6 "fmt"
7 "net/http"
8
9 "reasonix/internal/config"
10 "reasonix/internal/control"
11 "reasonix/internal/jobs"
12 )
13
14 const modelApplicationCapability = "model-application-v1"
15
16 // Validate the source revision immediately before publication. A candidate's
17 // virtual resolver is immutable, so its local fingerprint alone cannot detect
18 // a newer save on Desktop while the remote build was in progress.
19 func validateModelCandidate(ctx context.Context, candidate control.SessionAPI, settings *config.ModelRuntimeSettings) error {
20 if changed, err := runtimeModelSettingsChanged(candidate); err != nil {
21 return err
22 } else if changed {
23 return control.ErrModelChoiceStale
24 }
25 if settings == nil || settings.SourceToken == "" {
26 return nil
27 }
28 offer, err := config.NewModelSettingsOfferID()
29 if err != nil {
30 return err
31 }
32 response, err := requestModelSettingsSource(ctx, settings, config.ModelSettingsSourceRequest{Mode: "inspect", OfferID: offer, AppliedRevision: settings.Revision, Model: sourceModelRef(settings, candidate.ModelRef()), OwnedRevisions: []string{}})
33 if err != nil {
34 return err
35 }
36 if response.Revision != settings.Revision {
37 return control.ErrModelChoiceStale
38 }
39 return nil
40 }
41
42 func (s *Server) modelApplicationDetailsLocked(ctx context.Context) *control.ModelApplicationDetails {
43 c, ok := s.ctl().(*control.Controller)
44 if !ok {
45 return nil
46 }
47 d := &control.ModelApplicationDetails{Code: "model_settings_pending", RuntimeIdentity: c.ModelRuntimeIdentity(), Model: c.ModelRef(), BlockingJobs: append([]jobs.View{}, c.ModelReplacementJobs()...)}
48 defer d.SetAvailableActions()
49 if scope := c.BackgroundScope(); scope != nil {
50 d.Applying = scope.Manager.ReplacementInProgress()
51 }
52 d.ConnectionTarget = c.ModelConnectionTarget()
53 var err error
54 d.AppliedRevision, d.DesiredRevision, err = c.ModelSettingsState()
55 if err != nil {
56 d.Code = "model_settings_read_failed"
57 return d
58 }
59 if err = c.ValidateModelContinuation(); err != nil {
60 d.ContinuationUnavailable = err.Error()
61 } else {
62 d.CanUseApplied = true
63 }
64 if settings := s.managedModels; settings != nil && settings.SourceToken != "" {
65 d.AppliedRevision = settings.Revision
66 offer, offerErr := config.NewModelSettingsOfferID()
67 if offerErr != nil {
68 d.CanUseApplied = false
69 return d
70 }
71 response, sourceErr := requestModelSettingsSource(ctx, settings, config.ModelSettingsSourceRequest{Mode: "inspect", OfferID: offer, AppliedRevision: settings.Revision, Model: sourceModelRef(settings, c.ModelRef()), OwnedRevisions: []string{}})
72 if sourceErr != nil {
73 d.CanUseApplied = false
74 d.ContinuationUnavailable = "Desktop cannot verify the current configuration"
75 return d
76 }
77 d.DesiredRevision = response.Revision
78 d.ConnectionTarget = response.ConnectionTarget
79 d.CanUseApplied = d.CanUseApplied && response.CanContinue
80 if !response.CanContinue {
81 d.ContinuationUnavailable = response.ContinuationUnavailable
82 }
83 }
84 r := &s.modelApplicationRetry
85 r.mu.Lock()
86 if r.owner == c && r.failedRevision != "" && r.failedRevision == d.DesiredRevision {
87 d.Code = "model_settings_apply_failed"
88 }
89 r.mu.Unlock()
90 return d
91 }
92
93 func (s *Server) validateAppliedModelChoiceLocked(ctx context.Context, choice control.ModelApplicationChoice) error {
94 d := s.modelApplicationDetailsLocked(ctx)
95 if d == nil || choice.Mode != "applied_once" || d.RuntimeIdentity != choice.ExpectedRuntimeIdentity || d.AppliedRevision != choice.ExpectedAppliedRevision || d.DesiredRevision != choice.ExpectedDesiredRevision {
96 return control.ErrModelChoiceStale
97 }
98 if !d.CanUseApplied {
99 return fmt.Errorf("%w: %s", control.ErrModelContinuationDenied, d.ContinuationUnavailable)
100 }
101 return nil
102 }
103
104 func (s *Server) rejectModelApplication(w http.ResponseWriter, r *http.Request, err error) {
105 d := s.modelApplicationDetailsLocked(r.Context())
106 if d != nil && !control.ModelReplacementBlocked(s.ctl()) {
107 d.Code = "model_settings_apply_failed"
108 }
109 if d != nil && errors.Is(err, control.ErrModelChoiceStale) {
110 d.Code = "model_choice_stale"
111 }
112 if d != nil && errors.Is(err, control.ErrModelContinuationDenied) {
113 d.Code = "model_continuation_denied"
114 }
115 if d != nil && d.Code == "model_settings_pending" {
116 s.deferModelApplicationLocked()
117 }
118 w.Header().Set("Content-Type", "application/json")
119 w.WriteHeader(http.StatusConflict)
120 writeJSON(w, map[string]any{"message": err.Error(), "data": map[string]any{"submissionOutcome": "not_accepted", "kind": "model_application", "modelApplication": d}})
121 }
122
122 lines GO