package promptkit import ( "go.maximumdirect.net/promptkit/internal/domain" "go.maximumdirect.net/promptkit/internal/observation" ) // ExecutionGroup collects facts from explicitly selected execution events. // Its zero value is ready for concurrent use. Do not copy it after first use. type ExecutionGroup struct{ group observation.Group } // ExecutionGroupMember identifies an observed run and its terminal coverage. type ExecutionGroupMember struct { RunID string `json:"run_id"` TerminalObserved bool `json:"terminal_observed"` } // ExecutionGroupSummary is a detached snapshot of terminal-only totals. // Usage sums known terminal values, with Known as their logical OR and // Complete as their logical AND. Coverage describes whether every observed // member has a terminal, independently of usage completeness. An empty group // has complete coverage and known complete zero usage; a group with members // but no terminals has unknown, incomplete usage. Overlapping groups are not // generally additive. type ExecutionGroupSummary struct { Members []ExecutionGroupMember `json:"members"` TerminalCoverageComplete bool `json:"terminal_coverage_complete"` Attempts uint64 `json:"attempts"` TransientRetries uint64 `json:"transient_retries"` RepairCalls uint64 `json:"repair_calls"` Usage ObservedTokenUsage `json:"usage"` } // Observe enrolls an event's run, counting its first terminal event only. func (g *ExecutionGroup) Observe(event ExecutionEvent) { count := func(c ObservedTokenCount) domain.ObservedTokenCount { return domain.ObservedTokenCount{Value: c.Value, Known: c.Known, Complete: c.Complete} } g.group.Observe(domain.ExecutionEvent{Kind: domain.ExecutionEventKind(event.Kind), RunID: event.RunID, Attempts: event.Attempts, TransientRetries: event.TransientRetries, RepairCalls: event.RepairCalls, Usage: domain.ObservedTokenUsage{PromptTokens: count(event.Usage.PromptTokens), CompletionTokens: count(event.Usage.CompletionTokens), TotalTokens: count(event.Usage.TotalTokens), CachedTokens: count(event.Usage.CachedTokens), CacheWriteTokens: count(event.Usage.CacheWriteTokens)}}) } // Snapshot returns an internally consistent, caller-owned summary. Coverage // describes observed members only, not future members or application progress. func (g *ExecutionGroup) Snapshot() ExecutionGroupSummary { value := g.group.Snapshot() count := func(c domain.ObservedTokenCount) ObservedTokenCount { return ObservedTokenCount{Value: c.Value, Known: c.Known, Complete: c.Complete} } members := make([]ExecutionGroupMember, len(value.Members)) for i, member := range value.Members { members[i] = ExecutionGroupMember{RunID: member.RunID, TerminalObserved: member.TerminalObserved} } return ExecutionGroupSummary{Members: members, TerminalCoverageComplete: value.TerminalCoverageComplete, Attempts: value.Attempts, TransientRetries: value.TransientRetries, RepairCalls: value.RepairCalls, Usage: ObservedTokenUsage{PromptTokens: count(value.Usage.PromptTokens), CompletionTokens: count(value.Usage.CompletionTokens), TotalTokens: count(value.Usage.TotalTokens), CachedTokens: count(value.Usage.CachedTokens), CacheWriteTokens: count(value.Usage.CacheWriteTokens)}} }