Files
kandev/apps/backend/internal/orchestrator/context_window.go
T
2026-08-02 17:25:55 +01:00

81 lines
2.6 KiB
Go

package orchestrator
import (
"context"
"sync"
"github.com/kandev/kandev/internal/task/models"
)
const contextWindowMetadataKey = models.SessionMetaKeyContextWindow
// contextWindowWriteGuard serializes context-window metadata writes for one
// session and advances its generation at each agent reset boundary. A context
// update that was observed before a reset cannot write after the reset's clear.
type contextWindowWriteGuard struct {
mu sync.Mutex
generation uint64
}
func (s *Service) contextWindowGuard(sessionID string) *contextWindowWriteGuard {
guard := &contextWindowWriteGuard{}
actual, _ := s.contextWindowGuards.LoadOrStore(sessionID, guard)
return actual.(*contextWindowWriteGuard)
}
func (s *Service) captureContextWindowGeneration(sessionID string) uint64 {
guard := s.contextWindowGuard(sessionID)
guard.mu.Lock()
defer guard.mu.Unlock()
return guard.generation
}
// clearContextWindowForReset advances the session's context generation while
// holding the same lock used by asynchronous context-window persistence.
func (s *Service) clearContextWindowForReset(ctx context.Context, sessionID string) error {
guard := s.contextWindowGuard(sessionID)
guard.mu.Lock()
defer guard.mu.Unlock()
guard.generation++
return s.repo.SetSessionMetadataKey(ctx, sessionID, models.SessionMetaKeyContextWindow, nil)
}
// ResetContextWindow clears the persisted context-window reading while
// invalidating any update captured before the reset. It is exposed as a narrow
// callback for services that can reset a session without going through the
// orchestrator's reset state machine.
func (s *Service) ResetContextWindow(ctx context.Context, sessionID string) error {
return s.clearContextWindowForReset(ctx, sessionID)
}
// persistContextWindowUpdate stores an update only when it belongs to the
// current context generation. The bool is false when a reset superseded it.
func (s *Service) persistContextWindowUpdate(
ctx context.Context,
sessionID string,
generation uint64,
contextWindowData map[string]interface{},
) (bool, int64, error) {
guard := s.contextWindowGuard(sessionID)
guard.mu.Lock()
defer guard.mu.Unlock()
if guard.generation != generation {
return false, 0, nil
}
count, err := s.repo.UpdateSessionContextWindow(ctx, sessionID, contextWindowData)
if err != nil {
return false, 0, err
}
return true, count, nil
}
func clearInMemoryContextWindow(session *models.TaskSession) {
if session == nil {
return
}
if session.Metadata == nil {
session.Metadata = make(map[string]interface{})
}
session.Metadata[contextWindowMetadataKey] = nil
}