返回 DeepSeek-Reasonix
maintenance_commit.go
根目录 / internal / agent / maintenance_commit.go
1 package agent
2
3 import (
4 "context"
5 "errors"
6 "fmt"
7 "time"
8
9 "reasonix/internal/provider"
10 )
11
12 // maintenanceInstall is one free projection rewrite (no summarizer call): the
13 // visible view it started from and the projected view replacing it.
14 type maintenanceInstall struct {
15 trigger, action string
16 state CompactionState
17 canonical []provider.Message
18 transcriptVersion uint64
19 visible, projected []provider.Message
20 affected int
21 }
22
23 // installMaintenanceProjection CAS-installs a free projection under
24 // compactionMu. The caller owns compactionRunMu for the whole maintenance run;
25 // canonical storage, including RawContent, is never modified.
26 func (a *Agent) installMaintenanceProjection(in maintenanceInstall) (bool, error) {
27 projected := projectionMessagesPreservingPinnedContext(in.projected)
28 projected, _, err := rebasePinnedContextProjection(projected, in.canonical, len(in.canonical))
29 if err != nil {
30 return false, err
31 }
32 sourceTokens := a.estimatedVisibleRequestTokens(in.visible)
33 resultTokens := a.estimatedVisibleRequestTokens(projected)
34 inputHash := a.contextMaintenanceInputHash(modelInputMessages(in.visible))
35 outputHash := providerVisibleFingerprint(modelInputMessages(projected))
36 projectionVersion := in.state.Projection.ProjectionVersion + 1
37 now := time.Now().UTC()
38 coveredHash := coveredPrefixHash(in.canonical, len(in.canonical))
39 receipt := &ContextMaintenanceReceipt{
40 OperationID: fmt.Sprintf("%s-%d-%s", in.action, projectionVersion, outputHash), Status: "applied", Action: in.action,
41 Trigger: in.trigger, SourceProjection: in.state.Projection.ProjectionVersion, ProjectionVersion: projectionVersion,
42 CoveredCount: len(in.canonical), CoveredPrefixHash: coveredHash, InputHash: inputHash, OutputHash: outputHash,
43 InputTokens: sourceTokens, ResultTokens: resultTokens, SavedTokens: max(0, sourceTokens-resultTokens),
44 AffectedToolResults: in.affected, CacheBreak: true, CreatedAt: now,
45 }
46 next := in.state
47 next.SchemaVersion = compactionStateSchemaCurrent
48 next.TranscriptVersion = in.transcriptVersion
49 next.Generation++
50 next.PromptCacheKey = a.currentPromptCacheKey()
51 next.Projection = ContextProjection{
52 Messages: projected, TranscriptVersion: in.transcriptVersion, ProjectionVersion: projectionVersion,
53 CoveredCount: len(in.canonical), CoveredPrefixHash: coveredHash, SourceTokens: sourceTokens,
54 PinnedContextHash: pinnedContextCoverageHash(in.canonical, len(in.canonical)),
55 ProjectionTokens: resultTokens, ViewInputHash: inputHash, ViewOutputHash: outputHash, CreatedAt: now,
56 }
57 next.LastReceipt = receipt
58 next.UpdatedAt = now
59
60 a.sess.compactionMu.Lock()
61 current, currentVersion := a.sess.conversation.snapshotMessagesVersion()
62 if currentVersion != in.transcriptVersion || len(current) != len(in.canonical) ||
63 coveredPrefixHash(current, len(current)) != coveredHash ||
64 a.sess.compactionState.Projection.ProjectionVersion != in.state.Projection.ProjectionVersion ||
65 a.sess.compactionState.Generation != in.state.Generation {
66 a.sess.compactionMu.Unlock()
67 return false, errCompressStaleContext
68 }
69 previous := a.sess.compactionState
70 a.sess.compactionState = next
71 accepted, err := a.persistInstalledProjectionLocked(context.Background(), next, current)
72 if err != nil {
73 if accepted {
74 a.sess.checkpointState = "pending"
75 a.sess.compactionMu.Unlock()
76 return false, fmt.Errorf("persist %s projection: %w", in.action, err)
77 }
78 a.sess.compactionState = previous
79 a.sess.compactionMu.Unlock()
80 if errors.Is(err, errCompressStaleContext) {
81 return false, err
82 }
83 return false, fmt.Errorf("persist %s projection: %w", in.action, err)
84 }
85 a.sess.checkpointState = "applied"
86 a.sess.compactionMu.Unlock()
87 a.emitContextMaintenance(receipt)
88 return true, nil
89 }
90
90 lines GO