1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//! Priority packing under a token budget.
//!
//! Invariant: the sum of per-piece token estimates of everything packed,
//! plus one joiner token per packed fragment, never exceeds the usable
//! budget. For a superadditive estimator (the default char-ratio one rounds
//! every piece up) this bounds the estimate of the assembled text, so the
//! output provably fits.
//!
//! Selection is a deterministic total order — critical first, then caller
//! priority, then a cache/non-cache tier, then relevance (non-cache items
//! only), then input order — with `seq` as the final tie-break so equal
//! fragments can never swap places between runs.
//!
//! **Trade-off (issue #1455): cache stability over relevance, for
//! cache-marked fragments only.** A `cache: true` fragment (the
//! `cache.stable_prefix` classification) forms the provider prompt-cache
//! prefix, whose entire value is being byte-identical across turns. Ranking
//! it by lexical relevance to the query — like every other fragment — would
//! let a query change alone decide which of two same-priority cache
//! fragments wins a tight budget, silently changing the prefix's bytes and
//! defeating the provider cache on exactly the turn a new question is
//! asked. So a cache-marked fragment's rank never consults relevance, in
//! either direction: it always outranks a non-cache fragment of the same
//! criticality/priority (a fixed, query-independent tier — see
//! [`selection_order`]), and two cache-marked fragments tied on priority
//! fall straight to `seq`. Non-cache fragments are unaffected: relevance
//! remains their tie-break, exactly as before. The accepted cost: a
//! more-relevant non-cache fragment can lose a tight-budget race it would
//! have won pre-#1455 against a same-tier cache fragment.
use TokenEstimator;
/// The separator emitted between packed fragments — the single source both
/// the packing accountant and the assembly joins use, so the accounted cost
/// and the emitted bytes can never drift apart.
pub const JOINER: &str = "\n\n";
/// One emission piece: its text, and — for a piece whose token cost is
/// precomputed outside the injected estimator (a media fragment's single
/// atomic piece, US-009 PR1) — that fixed cost. `None` (every non-media
/// piece) means "measure `text` with the injected estimator", the unchanged
/// pre-media behavior; `Some` must never be re-derived from `text` (for
/// media, `text` is only the caption, which on its own would grossly
/// under-price the piece).
pub
/// One packable fragment: its emission pieces plus its selection keys.
pub
/// How many leading pieces of each item fit under `usable` tokens. The
/// result is aligned with `items` (input order), not with selection order.
pub
/// Item indices in packing order: critical desc, priority desc, cache
/// (cache-marked before non-cache) desc, relevance desc (non-cache items
/// only — see the module trade-off note), seq asc.
///
/// The cache tier is decided purely from each item's own `cache` flag, never
/// from `relevance`, so it can never be perturbed by a query change: two
/// cache-marked items always tie it (falling to `seq`), and a cache-marked
/// item always beats a non-cache one at the same criticality/priority. Only
/// once both sides of a comparison are confirmed non-cache does `relevance`
/// enter at all — the query can reorder non-cache fragments among
/// themselves exactly as before, but can never move a cache-marked fragment
/// relative to anything else.
/// Greedily take leading pieces while they fit; the first piece also pays
/// the fragment's joiner cost. A piece's own cost is its precomputed
/// [`Piece::cost`] when set, otherwise the injected estimator over its text.