返回 DeepSeek-Reasonix
attachment_service.go
根目录 / internal / control / attachment_service.go
1 package control
2
3 import (
4 "context"
5 "path/filepath"
6 "sync"
7 "sync/atomic"
8
9 "reasonix/internal/attachment"
10 "reasonix/internal/sessioncontent"
11 )
12
13 var hostVariantCache = attachment.NewVariantCache(attachment.DefaultCacheBytes, attachment.DefaultTransforms)
14
15 type controllerAttachmentState struct {
16 attachments atomic.Pointer[attachment.Service]
17 attachmentOwnerMu sync.Mutex
18 attachmentOwnerScope string
19 imageRoutesOnce sync.Once
20 imageRoutes map[string]ImageRequestRoute
21 imageRoutesErr error
22 }
23
24 func (c *Controller) AttachmentOwnerIdentity() string {
25 ref, _ := c.SessionRef()
26 return ref.HostID + "\x00" + c.attachmentScope()
27 }
28
29 // NewAttachmentOperationContext binds an operation to both caller and owner.
30 func (c *Controller) NewAttachmentOperationContext(parent context.Context) (context.Context, context.CancelFunc) {
31 ctx, cancel := context.WithCancel(parent)
32 stop := context.AfterFunc(c.attachmentContext(), cancel)
33 return ctx, func() { stop(); cancel() }
34 }
35
36 func (c *Controller) attachmentContext() context.Context {
37 if c.goalDriverControl.ctx != nil {
38 return c.goalDriverControl.ctx
39 }
40 return context.Background()
41 }
42
43 func (c *Controller) attachmentService() *attachment.Service {
44 if c == nil {
45 return nil
46 }
47 return c.attachments.Load()
48 }
49
50 // Only the session publication owner changes this binding. Request goroutines
51 // retain their service snapshot; they never lazily choose another content root.
52 func (c *Controller) bindAttachmentService() {
53 c.attachmentOwnerMu.Lock()
54 defer c.attachmentOwnerMu.Unlock()
55 content := c.sessionContentStore()
56 scope := c.attachmentScope()
57 current := c.attachments.Load()
58 if current != nil && c.attachmentOwnerScope != scope {
59 current.Drafts().ReleaseScope(c.attachmentOwnerScope)
60 }
61 c.attachmentOwnerScope = scope
62 if current != nil && current.Store().Root() == content.Root() {
63 return
64 }
65 c.attachments.Store(attachment.NewService(content, hostVariantCache))
66 }
67
68 func (c *Controller) sessionContentStore() *sessioncontent.Store {
69 if c == nil {
70 return nil
71 }
72 if _, runtime, _ := c.v3Binding(); runtime != nil {
73 return runtime.Session().ContentStore()
74 }
75 if store := c.sessionEventStore(); store != nil {
76 if content := store.ContentStore(); content != nil {
77 return content
78 }
79 }
80 if c.sessionDir != "" {
81 return sessioncontent.New(filepath.Join(c.sessionDir, ".content-v1"))
82 }
83 if c.sessionPath != "" {
84 return sessioncontent.New(filepath.Join(filepath.Dir(c.sessionPath), ".content-v1"))
85 }
86 if c.workspaceRoot != "" {
87 return sessioncontent.New(filepath.Join(c.workspaceRoot, ".reasonix", "content-v1"))
88 }
89 return nil
90 }
91
92 func (c *Controller) attachmentScope() string {
93 if c == nil {
94 return ""
95 }
96 if _, runtime, _ := c.v3Binding(); runtime != nil {
97 store := runtime.Session()
98 return store.ID() + ":" + store.StorageGeneration()
99 }
100 if store := c.sessionEventStore(); store != nil {
101 return store.ID() + ":" + store.StorageGeneration()
102 }
103 return c.sessionPath
104 }
105
105 lines GO