返回 DeepSeek-Reasonix
runtime_model_switch.go
根目录 / internal / cli / runtime_model_switch.go
1 package cli
2
3 import (
4 "fmt"
5
6 tea "charm.land/bubbletea/v2"
7
8 "reasonix/internal/control"
9 "reasonix/internal/i18n"
10 )
11
12 func (m *chatTUI) handleModelSwitch(msg modelSwitchMsg) []tea.Cmd {
13 var cmds []tea.Cmd
14 if msg.resumeTurn != nil && msg.err == nil && m.ctrl != msg.oldCtrl {
15 // The user left the originating session while the replacement built.
16 if concrete, ok := msg.ctrl.(*control.Controller); ok {
17 concrete.ReleaseResources()
18 } else if msg.ctrl != nil {
19 msg.ctrl.Close()
20 }
21 return nil
22 }
23 m.modelSwitchPending = false
24 m.pendingModelSwitch = nil
25 if msg.err != nil {
26 prefix := msg.failurePrefix
27 if prefix == "" {
28 prefix = "model"
29 }
30 m.notice(prefix + ": " + msg.err.Error())
31 // Build failed — no old controller to retire. The kept controller
32 // may still have been retargeted to a recovery branch by the
33 // pre-switch snapshot, so the lease must follow it.
34 m.followSessionLease()
35 } else {
36 if err := control.ActivateSessionAPIReplacement(msg.oldCtrl, msg.ctrl); err != nil {
37 if concrete, ok := msg.ctrl.(*control.Controller); ok {
38 concrete.ReleaseResources()
39 } else if msg.ctrl != nil {
40 msg.ctrl.Close()
41 }
42 m.notice("runtime activation: " + err.Error())
43 m.followSessionLease()
44 return cmds
45 }
46 m.ctrl = activateGoalDriverAfterRebuild(msg.ctrl)
47 if m.takeover != nil {
48 m.takeover.AttachController(msg.ctrl)
49 }
50 m.updateWatchdogStatusProvider()
51 m.label = msg.label
52 m.commands = msg.commands
53 m.skills = msg.skills
54 m.setHostAndInvalidateSlashCatalog(msg.host)
55 m.modelRef = msg.ref
56 m.refreshEffortStatus()
57 // Defer Close to exit; skip when subgraph rebuild reused the pointer.
58 if msg.oldCtrl != nil && msg.oldCtrl != msg.ctrl {
59 m.oldControllers = append(m.oldControllers, msg.oldCtrl)
60 }
61 // Follow the session file, including a recovery branch created by
62 // the pre-switch snapshot.
63 m.followSessionLease()
64 if msg.successNotice != "" {
65 m.notice(msg.successNotice)
66 } else {
67 m.notice(fmt.Sprintf(i18n.M.ModelSwitchedFmt, m.label))
68 }
69 cmds = append(cmds, fetchBalance(m.ctrl))
70 if msg.resumeTurn != nil {
71 cmds = append(cmds, m.resumeControllerTurn(*msg.resumeTurn, false))
72 }
73 if c := m.runStatusline(); c != nil {
74 cmds = append(cmds, c)
75 }
76 // Keep the existing waitForAgentEvent reader: a second consumer
77 // would race it and deliver streamed events out of order.
78 }
79
80 // A /reload queued behind this switch runs now that it settled. On a
81 // failed switch the old controller still serves, so the reload simply
82 // retries against it.
83 if c := m.drainQueuedRuntimeReload(); c != nil {
84 cmds = append(cmds, c)
85 }
86 return cmds
87 }
88
88 lines GO