返回 DeepSeek-Reasonix
run_budget.go
根目录 / internal / event / run_budget.go
1 package event
2
3 import "reasonix/internal/nilutil"
4
5 // RunBudgetTotals is one scope's accumulated spend: counts and money, never
6 // content. Priced is false when no price table covered it, so a reader can
7 // tell a genuinely free stretch from an unpriced one.
8 type RunBudgetTotals struct {
9 Rounds int `json:"rounds"`
10 Requests int `json:"requests"`
11 PromptTokens int `json:"promptTokens"`
12 OutputTokens int `json:"outputTokens"`
13 Cost float64 `json:"cost"`
14 Priced bool `json:"priced"`
15 ElapsedMs int64 `json:"elapsedMs"`
16 }
17
18 // RunBudgetSample reports both scopes after a round. Turn is the current Run;
19 // Task spans every Run continuing the same work, because "continue" starts a
20 // new Run and the spend worth stopping accrues across them.
21 type RunBudgetSample struct {
22 Turn RunBudgetTotals `json:"turn"`
23 Task RunBudgetTotals `json:"task"`
24 Currency string `json:"currency,omitempty"`
25 }
26
27 // RunBudgetSink is an optional sink capability for the per-round spend axis.
28 // Shadow means observed, not enforced: no threshold reads these yet.
29 type RunBudgetSink interface {
30 RecordRunBudget(RunBudgetSample)
31 }
32
33 // RecordRunBudget forwards a turn's spend reading only to sinks that opt in.
34 // Ordinary UI sinks receive nothing.
35 func RecordRunBudget(s Sink, sample RunBudgetSample) {
36 if nilutil.IsNil(s) {
37 return
38 }
39 if rb, ok := s.(RunBudgetSink); ok {
40 rb.RecordRunBudget(sample)
41 }
42 }
43
43 lines GO