Files
coder/coderd/x/chatd/chatdebug/context.go
2026-04-13 19:59:47 +02:00

85 lines
2.1 KiB
Go

package chatdebug
import (
"context"
"runtime"
"sync"
"github.com/google/uuid"
)
type (
runContextKey struct{}
stepContextKey struct{}
reuseStepKey struct{}
reuseHolder struct {
mu sync.Mutex
handle *stepHandle
}
)
// ContextWithRun stores rc in ctx.
//
// Step counter cleanup is reference-counted per RunID: each live
// RunContext increments a counter and runtime.AddCleanup decrements
// it when the struct is garbage collected. Shared state (step
// counters) is only deleted when the last RunContext for a given
// RunID becomes unreachable, preventing premature cleanup when
// multiple RunContext instances share the same RunID.
func ContextWithRun(ctx context.Context, rc *RunContext) context.Context {
if rc == nil {
panic("chatdebug: nil RunContext")
}
enriched := context.WithValue(ctx, runContextKey{}, rc)
if rc.RunID != uuid.Nil {
trackRunRef(rc.RunID)
runtime.AddCleanup(rc, func(id uuid.UUID) {
releaseRunRef(id)
}, rc.RunID)
}
return enriched
}
// RunFromContext returns the debug run context stored in ctx.
func RunFromContext(ctx context.Context) (*RunContext, bool) {
rc, ok := ctx.Value(runContextKey{}).(*RunContext)
if !ok {
return nil, false
}
return rc, true
}
// ContextWithStep stores sc in ctx.
func ContextWithStep(ctx context.Context, sc *StepContext) context.Context {
if sc == nil {
panic("chatdebug: nil StepContext")
}
return context.WithValue(ctx, stepContextKey{}, sc)
}
// StepFromContext returns the debug step context stored in ctx.
func StepFromContext(ctx context.Context) (*StepContext, bool) {
sc, ok := ctx.Value(stepContextKey{}).(*StepContext)
if !ok {
return nil, false
}
return sc, true
}
// ReuseStep marks ctx so wrapped model calls under it share one debug step.
func ReuseStep(ctx context.Context) context.Context {
if holder, ok := reuseHolderFromContext(ctx); ok {
return context.WithValue(ctx, reuseStepKey{}, holder)
}
return context.WithValue(ctx, reuseStepKey{}, &reuseHolder{})
}
func reuseHolderFromContext(ctx context.Context) (*reuseHolder, bool) {
holder, ok := ctx.Value(reuseStepKey{}).(*reuseHolder)
if !ok {
return nil, false
}
return holder, true
}