返回 DeepSeek-Reasonix
goal_rebuild.go
根目录 / internal / control / goal_rebuild.go
1 package control
2
3 import (
4 goaldomain "reasonix/internal/goal"
5 "reasonix/internal/session"
6 )
7
8 // InheritLifecycleFrom carries same-session lifecycle state across controller
9 // rebuilds, such as model switches that preserve the conversation.
10 func (c *Controller) InheritLifecycleFrom(prev *Controller) error {
11 if prev == nil {
12 return nil
13 }
14 if c.workspaceRoot == prev.workspaceRoot && c.executor != nil {
15 c.executor.InheritFileObservationsFrom(prev.executor)
16 }
17 prev.mu.Lock()
18 started := prev.startedOnce
19 turn := prev.turn
20 prev.mu.Unlock()
21
22 c.mu.Lock()
23 c.startedOnce = started
24 if c.turn < turn {
25 c.turn = turn
26 }
27 c.mu.Unlock()
28
29 _, currentRuntime, currentExclusive := c.v3Binding()
30 _, previousRuntime, previousExclusive := prev.v3Binding()
31 if !currentExclusive || !previousExclusive || currentRuntime == nil || currentRuntime != previousRuntime {
32 return nil
33 }
34 prev.goalDriverMu.Lock()
35 defer prev.goalDriverMu.Unlock()
36 if prev.goalDriverPending || prev.goalDriverActive != nil {
37 return session.ErrRuntimeBusy
38 }
39 prev.goalLifecycleMu.RLock()
40 previousMachine, previousLoadErr := prev.goalLifecycle, prev.goalLifecycleLoadErr
41 prev.goalLifecycleMu.RUnlock()
42 if previousLoadErr != nil {
43 return previousLoadErr
44 }
45 c.goalLifecycleMu.Lock()
46 if c.goalLifecycleLoadErr != nil {
47 err := c.goalLifecycleLoadErr
48 c.goalLifecycleMu.Unlock()
49 return err
50 }
51 candidate := c.goalLifecycle.Clone()
52 if err := candidate.InheritRuntimeFrom(previousMachine); err != nil {
53 c.goalLifecycleMu.Unlock()
54 return err
55 }
56 c.goalLifecycle = candidate
57 c.goalLifecycleMu.Unlock()
58
59 prev.goalResourceMu.Lock()
60 tokensUsed, requestsUsed := prev.goalTokensUsed, prev.goalRequestsUsed
61 tokenLimit, extensions := prev.goalTokenLimit, prev.goalBudgetExtensions
62 previousBudget := prev.goalTokenBudget
63 prev.goalResourceMu.Unlock()
64 c.goalResourceMu.Lock()
65 c.goalTokensUsed = tokensUsed
66 c.goalRequestsUsed = requestsUsed
67 c.goalBudgetExtensions = extensions
68 if c.goalTokenBudget == previousBudget {
69 c.goalTokenLimit = tokenLimit
70 } else if c.goalTokenBudget <= 0 {
71 c.goalTokenLimit = 0
72 } else {
73 c.goalTokenLimit = c.goalTokenBudget * (extensions + 1)
74 }
75 c.goalResourceMu.Unlock()
76 if view := candidate.Get(); view != nil && view.Phase == goaldomain.PhaseActive && view.Activation == goaldomain.ActivationArmed {
77 c.goalDriverControl.inherited.Store(true)
78 }
79 return nil
80 }
81
82 // ActivateGoalDriverAfterRebuild is called only after a host has published the
83 // replacement controller. It keeps an unpublished build from racing the old
84 // controller for the shared SessionRuntime.
85 func (c *Controller) ActivateGoalDriverAfterRebuild() {
86 if c != nil && c.goalDriverControl.inherited.Swap(false) {
87 c.kickGoalDriver()
88 }
89 }
90
90 lines GO