返回 DeepSeek-Reasonix
remote_model_ownership.go
根目录 / desktop / remote_model_ownership.go
1 package main
2
3 import (
4 "context"
5 "errors"
6 "fmt"
7 "net/http"
8
9 "reasonix/internal/config"
10 )
11
12 type credentialProxyOwnership struct {
13 config.ModelSettingsOwnership
14 pending map[string]string
15 latest remoteModelSettingsStatus
16 }
17
18 func (a *App) installRemoteModelSettingsSnapshot(ctx context.Context, client *http.Client, base, host, workspace, path, ref string, bundle *config.ModelRuntimeSettings, prior remoteModelSettingsStatus) (remoteModelSettingsStatus, error) {
19 if !a.reserveCredentialProxyInstall(host, workspace, bundle.OfferID, bundle.Revision, prior.OwnershipIncarnation) {
20 a.finishCredentialProxyOffer(host, workspace, bundle.OfferID)
21 return prior, fmt.Errorf("remote ownership changed while preparing model settings")
22 }
23 status, err := applyRemoteModelSettingsSnapshot(ctx, client, base, path, ref, bundle, prior)
24 if err != nil {
25 if status.OwnershipIncarnation == prior.OwnershipIncarnation && status.OwnershipSeq > prior.OwnershipSeq {
26 prior = status
27 }
28 var rejected *remoteModelSettingsRejection
29 if errors.As(err, &rejected) {
30 a.reconcileCredentialProxyGenerations(host, workspace, prior, bundle.OfferID)
31 } else {
32 a.reconcileCredentialProxyGenerations(host, workspace, prior)
33 }
34 return status, err
35 }
36 a.reconcileCredentialProxyGenerations(host, workspace, status, bundle.OfferID)
37 return status, nil
38 }
39
40 func (a *App) reserveCredentialProxyInstall(host, workspace, offer, revision, incarnation string) bool {
41 a.credProxyMu.Lock()
42 p := a.credProxy
43 a.credProxyMu.Unlock()
44 if p == nil {
45 return false
46 }
47 p.mu.Lock()
48 defer p.mu.Unlock()
49 if owner := p.ownership[credentialProxyScope(host, workspace)]; owner != nil && owner.OwnershipIncarnation == incarnation {
50 if owner.pending == nil {
51 owner.pending = map[string]string{}
52 }
53 owner.pending[offer] = revision
54 return true
55 }
56 return false
57 }
58
59 func (m *desktopRemoteManager) pinModelSettingsOwnership(app *App, host, workspace string, managed *managedHost, status remoteModelSettingsStatus) bool {
60 // No route-lock holder calls into the manager. Keep identity validation and
61 // pin together so a replaced connection cannot restore an old incarnation.
62 m.mu.Lock()
63 defer m.mu.Unlock()
64 return m.hosts[host] == managed && app.pinCredentialProxyOwnership(host, workspace, status)
65 }
66
67 // Only the authenticated GET performed under the current managed connection's
68 // serve gate may establish a Serve incarnation. Source calls cannot replace it.
69 func (a *App) pinCredentialProxyOwnership(host, workspace string, status remoteModelSettingsStatus) bool {
70 if status.OwnershipIncarnation == "" || status.OwnershipSeq == 0 {
71 return false
72 }
73 a.credProxyMu.Lock()
74 p := a.credProxy
75 a.credProxyMu.Unlock()
76 if p == nil {
77 return false
78 }
79 p.updateMu.Lock()
80 defer p.updateMu.Unlock()
81 p.mu.Lock()
82 defer p.mu.Unlock()
83 if p.ownership == nil {
84 p.ownership = map[string]*credentialProxyOwnership{}
85 }
86 scope := credentialProxyScope(host, workspace)
87 if old := p.ownership[scope]; old == nil || old.OwnershipIncarnation != status.OwnershipIncarnation {
88 // A new Serve cannot own an unfinished build in the previous process.
89 // Preserve routes until its full receipt is reconciled, but release all
90 // old-process reservations. Late builders recheck incarnation below.
91 if old != nil {
92 for _, route := range p.routes {
93 if route.scope == scope {
94 clear(route.holds)
95 }
96 }
97 }
98 p.ownership[scope] = &credentialProxyOwnership{ModelSettingsOwnership: config.ModelSettingsOwnership{OwnershipIncarnation: status.OwnershipIncarnation}}
99 }
100 return true
101 }
102
103 // Retirement, receipt ordering, and offer release share one route transaction.
104 // An accepted HTTP request retains its route until it returns. An uncertain
105 // install retains its hold until a receipt positively identifies that revision.
106 func (a *App) reconcileCredentialProxyGenerations(host, workspace string, status remoteModelSettingsStatus, offers ...string) bool {
107 if status.Version != 1 {
108 return false
109 }
110 a.credProxyMu.Lock()
111 p := a.credProxy
112 a.credProxyMu.Unlock()
113 if p == nil {
114 return false
115 }
116 p.updateMu.Lock()
117 defer p.updateMu.Unlock()
118 p.mu.Lock()
119 defer p.mu.Unlock()
120 scope := credentialProxyScope(host, workspace)
121 authority := p.ownership[scope]
122 if authority == nil || authority.OwnershipIncarnation != status.OwnershipIncarnation {
123 return false
124 }
125 accepted := status.OwnershipSeq > authority.OwnershipSeq
126 if accepted {
127 authority.OwnershipSeq = status.OwnershipSeq
128 authority.latest = status
129 authority.latest.OwnedRevisions = append([]string(nil), status.OwnedRevisions...)
130 } else {
131 // A confirmed older install may release its own reservation, but only
132 // the newest complete ownership snapshot may drive retirement.
133 if len(offers) == 0 {
134 return false
135 }
136 status = authority.latest
137 }
138 owned := map[string]bool{}
139 for _, revision := range status.OwnedRevisions {
140 owned[revision] = true
141 }
142 for offer, revision := range authority.pending {
143 if owned[revision] {
144 offers = append(offers, offer)
145 }
146 }
147 for _, offer := range offers {
148 delete(authority.pending, offer)
149 }
150 for token, route := range p.routes {
151 if route.scope != scope {
152 continue
153 }
154 for _, offer := range offers {
155 delete(route.holds, offer)
156 }
157 if status.UnversionedOwners || owned[route.revision] || len(route.holds) > 0 {
158 continue
159 }
160 route.retired = true
161 if route.active == 0 {
162 delete(p.routes, token)
163 }
164 }
165 return accepted
166 }
167
167 lines GO