Skip to main content

nemo_relay/api/
optimization.rs

1// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Managed, bounded LLM optimization accounting.
5
6use std::collections::BTreeSet;
7use std::sync::{Arc, Condvar, Mutex};
8
9use chrono::{DateTime, Utc};
10use serde::Serialize;
11use uuid::Uuid;
12
13use crate::codec::optimization::{
14    LlmOptimizationContribution, LlmOptimizationModel, LlmOptimizationSummary,
15    LlmOptimizationSummaryStatus, LlmOptimizationTokens,
16};
17use crate::codec::response::{
18    AnnotatedLlmResponse, CostEstimate, CostSource, PricingResolver, Usage,
19};
20
21/// Maximum contributions retained for one LLM call.
22pub const MAX_LLM_OPTIMIZATION_CONTRIBUTIONS: usize = 64;
23/// Maximum serialized size of one complete contribution envelope.
24pub const MAX_LLM_OPTIMIZATION_CONTRIBUTION_BYTES: usize = 16 * 1024;
25/// Maximum aggregate serialized size of all contribution envelopes for one call.
26pub const MAX_LLM_OPTIMIZATION_TOTAL_CONTRIBUTION_BYTES: usize = 256 * 1024;
27/// Maximum contribution records inspected before the recorder seals itself.
28///
29/// Invalid records count toward this bound even though they do not consume an
30/// accepted sequence number.
31pub const MAX_LLM_OPTIMIZATION_CONTRIBUTION_ATTEMPTS: usize = 64;
32
33#[derive(Debug, Default)]
34struct AccumulatorState {
35    contributions: Vec<LlmOptimizationContribution>,
36    recorded_at: Vec<DateTime<Utc>>,
37    total_contribution_bytes: usize,
38    attempted_contributions: usize,
39    in_flight_records: usize,
40    emitted: usize,
41    closed: bool,
42    finished: bool,
43    limitations: BTreeSet<String>,
44    contribution_limit_exceeded: bool,
45    invalid_payload_schema: bool,
46}
47
48#[derive(Debug, Default)]
49struct Accumulator {
50    state: Mutex<AccumulatorState>,
51    records_settled: Condvar,
52}
53
54/// Cloneable capability for adding evidence to the current managed LLM call.
55///
56/// A streaming execution intercept may capture this value before returning its
57/// stream and use it when the route is committed by the first upstream item.
58#[derive(Debug, Clone, Default)]
59pub struct LlmOptimizationRecorder {
60    state: Arc<Accumulator>,
61}
62
63impl LlmOptimizationRecorder {
64    /// Record one contribution without blocking on I/O or exporter delivery.
65    ///
66    /// Returns `false` when the contribution is rejected by a payload/schema
67    /// invariant, a per-call bound, or because accounting has already closed.
68    /// Rejection never affects LLM execution and does not consume a sequence.
69    #[must_use]
70    pub fn record(&self, contribution: LlmOptimizationContribution) -> bool {
71        let Some(_attempt) = self.reserve_record_attempt() else {
72            return false;
73        };
74        if !self.payload_schema_is_valid(&contribution) {
75            return false;
76        }
77        // Relay always replaces producer-supplied identity. Serialization is
78        // deliberately outside the accumulator lock; if another writer wins
79        // the next sequence while we measure, retry with the new sequence.
80        let mut contribution = Some(contribution);
81        let Some(record) = contribution.as_mut() else {
82            return false;
83        };
84        record.id = Some(Uuid::now_v7());
85        loop {
86            let Some(sequence) = self.next_contribution_sequence() else {
87                return false;
88            };
89            let Some(record) = contribution.as_mut() else {
90                return false;
91            };
92            record.sequence = Some(sequence);
93            let Some(contribution_bytes) = self.serialized_contribution_size(record) else {
94                return false;
95            };
96            match self.commit_contribution(&mut contribution, contribution_bytes, sequence) {
97                ContributionCommit::Committed => return true,
98                ContributionCommit::Retry => continue,
99                ContributionCommit::Rejected => return false,
100            }
101        }
102    }
103
104    fn reserve_record_attempt(&self) -> Option<RecordAttempt> {
105        let Ok(mut state) = self.state.state.lock() else {
106            return None;
107        };
108        if state.closed {
109            return None;
110        }
111        if state.attempted_contributions >= MAX_LLM_OPTIMIZATION_CONTRIBUTION_ATTEMPTS {
112            seal_for_contribution_limit(&mut state);
113            return None;
114        }
115        state.attempted_contributions += 1;
116        state.in_flight_records += 1;
117        Some(RecordAttempt {
118            state: Arc::clone(&self.state),
119        })
120    }
121
122    #[cfg(test)]
123    pub(crate) fn reserve_record_attempt_for_test(&self) -> Option<RecordAttempt> {
124        self.reserve_record_attempt()
125    }
126
127    fn payload_schema_is_valid(&self, contribution: &LlmOptimizationContribution) -> bool {
128        if contribution.payload.is_some() && contribution.payload_schema.is_none() {
129            self.note_invalid_payload_schema();
130            return false;
131        }
132        true
133    }
134
135    fn next_contribution_sequence(&self) -> Option<u64> {
136        let Ok(state) = self.state.state.lock() else {
137            return None;
138        };
139        if state.closed {
140            return None;
141        }
142        if state.contributions.len() >= MAX_LLM_OPTIMIZATION_CONTRIBUTIONS {
143            drop(state);
144            self.note_contribution_limit_exceeded();
145            return None;
146        }
147        Some(state.contributions.len() as u64)
148    }
149
150    fn serialized_contribution_size(
151        &self,
152        contribution: &LlmOptimizationContribution,
153    ) -> Option<usize> {
154        match bounded_json_size(contribution, MAX_LLM_OPTIMIZATION_CONTRIBUTION_BYTES) {
155            Ok(size) => Some(size),
156            Err(SerializedSizeError::LimitExceeded) => {
157                self.note_contribution_limit_exceeded();
158                None
159            }
160            Err(SerializedSizeError::Serialization) => {
161                self.note_invalid_payload_schema();
162                None
163            }
164        }
165    }
166
167    fn commit_contribution(
168        &self,
169        contribution: &mut Option<LlmOptimizationContribution>,
170        contribution_bytes: usize,
171        sequence: u64,
172    ) -> ContributionCommit {
173        let Ok(mut state) = self.state.state.lock() else {
174            return ContributionCommit::Rejected;
175        };
176        if state.closed {
177            return ContributionCommit::Rejected;
178        }
179        if state.contributions.len() as u64 != sequence {
180            return ContributionCommit::Retry;
181        }
182        let Some(total_contribution_bytes) = state
183            .total_contribution_bytes
184            .checked_add(contribution_bytes)
185        else {
186            seal_for_contribution_limit(&mut state);
187            return ContributionCommit::Rejected;
188        };
189        if total_contribution_bytes > MAX_LLM_OPTIMIZATION_TOTAL_CONTRIBUTION_BYTES {
190            seal_for_contribution_limit(&mut state);
191            return ContributionCommit::Rejected;
192        }
193        let Some(contribution) = contribution.take() else {
194            return ContributionCommit::Rejected;
195        };
196        state.total_contribution_bytes = total_contribution_bytes;
197        state.contributions.push(contribution);
198        state.recorded_at.push(Utc::now());
199        ContributionCommit::Committed
200    }
201
202    fn note_invalid_payload_schema(&self) {
203        if let Ok(mut state) = self.state.state.lock()
204            && !state.finished
205        {
206            state.invalid_payload_schema = true;
207        }
208    }
209
210    fn note_contribution_limit_exceeded(&self) {
211        if let Ok(mut state) = self.state.state.lock()
212            && !state.finished
213        {
214            seal_for_contribution_limit(&mut state);
215        }
216    }
217
218    pub(crate) fn record_all(
219        &self,
220        contributions: impl IntoIterator<Item = LlmOptimizationContribution>,
221    ) {
222        for contribution in contributions {
223            if !self.record(contribution) && self.is_closed() {
224                break;
225            }
226        }
227    }
228
229    fn is_closed(&self) -> bool {
230        self.state
231            .state
232            .lock()
233            .map(|state| state.closed)
234            .unwrap_or(true)
235    }
236
237    #[cfg(test)]
238    pub(crate) fn is_closed_for_test(&self) -> bool {
239        self.is_closed()
240    }
241
242    /// Snapshot contributions not yet accepted by mark delivery.
243    ///
244    /// This does not move the cursor. Call [`Self::mark_emitted`] only after
245    /// the asynchronous dispatcher accepts an item.
246    #[cfg(test)]
247    pub(crate) fn unemitted(&self) -> Vec<LlmOptimizationContribution> {
248        self.unemitted_with_timestamps()
249            .into_iter()
250            .map(|(contribution, _)| contribution)
251            .collect()
252    }
253
254    /// Snapshot unacknowledged contributions with their acceptance time.
255    ///
256    /// The timestamp is captured only after the contribution wins its final
257    /// sequence and size checks, so execution-time marks retain commit-time
258    /// ordering even when they are emitted at the LLM close boundary.
259    pub(crate) fn unemitted_with_timestamps(
260        &self,
261    ) -> Vec<(LlmOptimizationContribution, DateTime<Utc>)> {
262        let Ok(state) = self.state.state.lock() else {
263            return Vec::new();
264        };
265        let start = state.emitted.min(state.contributions.len());
266        state.contributions[start..]
267            .iter()
268            .cloned()
269            .zip(state.recorded_at[start..].iter().copied())
270            .collect()
271    }
272
273    /// Advance the delivery cursor for a bounded number of accepted marks.
274    pub(crate) fn mark_emitted(&self, count: usize) {
275        let Ok(mut state) = self.state.state.lock() else {
276            return;
277        };
278        state.emitted = state
279            .emitted
280            .saturating_add(count)
281            .min(state.contributions.len());
282    }
283
284    /// Add a best-effort lifecycle limitation to the eventual summary.
285    #[cfg(test)]
286    pub(crate) fn note_limitation(&self, limitation: impl Into<String>) {
287        if let Ok(mut state) = self.state.state.lock()
288            && !state.closed
289        {
290            state.limitations.insert(limitation.into());
291        }
292    }
293
294    /// Atomically seal contribution acceptance at an LLM close boundary.
295    ///
296    /// When `conditional_limitation` is supplied, it is added only if the call
297    /// already has optimization evidence or accounting limitations. This keeps
298    /// an interrupted but otherwise unoptimized stream from manufacturing an
299    /// optimization summary.
300    pub(crate) fn close_for_finalization(&self, conditional_limitation: Option<&str>) -> bool {
301        let Ok(mut state) = self.state.state.lock() else {
302            return false;
303        };
304        if state.finished {
305            return false;
306        }
307        let has_evidence = state.has_evidence();
308        if has_evidence && let Some(limitation) = conditional_limitation {
309            state.limitations.insert(limitation.to_string());
310        }
311        state.closed = true;
312        has_evidence
313    }
314
315    fn finish(&self) -> FinishedContributions {
316        let Ok(mut state) = self.state.state.lock() else {
317            return FinishedContributions {
318                contributions: Vec::new(),
319                limitations: vec!["optimization_accumulator_unavailable".to_string()],
320            };
321        };
322        if state.finished {
323            return FinishedContributions {
324                contributions: Vec::new(),
325                limitations: Vec::new(),
326            };
327        }
328        state.closed = true;
329        while state.in_flight_records > 0 {
330            let Ok(waiting) = self.state.records_settled.wait(state) else {
331                return FinishedContributions {
332                    contributions: Vec::new(),
333                    limitations: vec!["optimization_accumulator_unavailable".to_string()],
334                };
335            };
336            state = waiting;
337            if state.finished {
338                return FinishedContributions {
339                    contributions: Vec::new(),
340                    limitations: Vec::new(),
341                };
342            }
343        }
344        state.finished = true;
345        let mut limitations = std::mem::take(&mut state.limitations)
346            .into_iter()
347            .collect::<Vec<_>>();
348        if state.contribution_limit_exceeded {
349            limitations.push("contribution_limit_exceeded".to_string());
350            state.contribution_limit_exceeded = false;
351        }
352        if state.invalid_payload_schema {
353            limitations.push("invalid_contribution_payload_schema".to_string());
354            state.invalid_payload_schema = false;
355        }
356        state.recorded_at.clear();
357        FinishedContributions {
358            contributions: std::mem::take(&mut state.contributions),
359            limitations,
360        }
361    }
362}
363
364enum ContributionCommit {
365    Committed,
366    Retry,
367    Rejected,
368}
369
370pub(crate) struct RecordAttempt {
371    state: Arc<Accumulator>,
372}
373
374impl Drop for RecordAttempt {
375    fn drop(&mut self) {
376        let Ok(mut state) = self.state.state.lock() else {
377            return;
378        };
379        state.in_flight_records = state.in_flight_records.saturating_sub(1);
380        self.state.records_settled.notify_all();
381    }
382}
383
384impl AccumulatorState {
385    fn has_evidence(&self) -> bool {
386        !self.contributions.is_empty()
387            || !self.limitations.is_empty()
388            || self.contribution_limit_exceeded
389            || self.invalid_payload_schema
390    }
391}
392
393fn seal_for_contribution_limit(state: &mut AccumulatorState) {
394    state.contribution_limit_exceeded = true;
395    state.closed = true;
396}
397
398#[derive(Debug)]
399enum SerializedSizeError {
400    LimitExceeded,
401    Serialization,
402}
403
404fn bounded_json_size<T: Serialize>(value: &T, limit: usize) -> Result<usize, SerializedSizeError> {
405    struct CountingWriter {
406        size: usize,
407        limit: usize,
408        exceeded: bool,
409    }
410
411    impl std::io::Write for CountingWriter {
412        fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
413            if self.size.saturating_add(bytes.len()) > self.limit {
414                self.exceeded = true;
415                return Err(std::io::Error::other(
416                    "optimization contribution limit exceeded",
417                ));
418            }
419            self.size += bytes.len();
420            Ok(bytes.len())
421        }
422
423        fn flush(&mut self) -> std::io::Result<()> {
424            Ok(())
425        }
426    }
427
428    let mut writer = CountingWriter {
429        size: 0,
430        limit,
431        exceeded: false,
432    };
433    if serde_json::to_writer(&mut writer, value).is_err() {
434        return Err(if writer.exceeded {
435            SerializedSizeError::LimitExceeded
436        } else {
437            SerializedSizeError::Serialization
438        });
439    }
440    Ok(writer.size)
441}
442
443struct FinishedContributions {
444    contributions: Vec<LlmOptimizationContribution>,
445    limitations: Vec<String>,
446}
447
448tokio::task_local! {
449    static CURRENT_LLM_OPTIMIZATION_RECORDER: LlmOptimizationRecorder;
450}
451
452/// Return a recorder for the current execution intercept, if it is managed by Relay.
453#[must_use]
454pub fn current_llm_optimization_recorder() -> Option<LlmOptimizationRecorder> {
455    CURRENT_LLM_OPTIMIZATION_RECORDER
456        .try_with(Clone::clone)
457        .ok()
458}
459
460/// Best-effort shorthand for recording evidence on the current managed call.
461#[must_use]
462pub fn record_llm_optimization_contribution(contribution: LlmOptimizationContribution) -> bool {
463    current_llm_optimization_recorder().is_some_and(|recorder| recorder.record(contribution))
464}
465
466pub(crate) async fn scope_llm_optimization_recorder<F: std::future::Future>(
467    recorder: LlmOptimizationRecorder,
468    future: F,
469) -> F::Output {
470    CURRENT_LLM_OPTIMIZATION_RECORDER
471        .scope(recorder, future)
472        .await
473}
474
475struct ContributionAnalysis {
476    limitations: Vec<String>,
477    token_totals: CheckedTokenTotals,
478    baseline_model: Option<LlmOptimizationModel>,
479    contributed_effective_model: Option<LlmOptimizationModel>,
480    use_effective_as_baseline: bool,
481}
482
483fn analyze_contributions(finished: &FinishedContributions) -> ContributionAnalysis {
484    let applied_routing = finished
485        .contributions
486        .iter()
487        .filter(|contribution| contribution.applied)
488        .filter(|contribution| {
489            contribution.kind.as_str()
490                == crate::codec::optimization::LlmOptimizationKind::MODEL_ROUTING
491        })
492        .collect::<Vec<_>>();
493    let routing_ambiguous = applied_routing.len() > 1;
494    let mut limitations = finished.limitations.clone();
495    if routing_ambiguous {
496        limitations.push("multiple_routing_contributions".to_string());
497    }
498    let mut token_totals = CheckedTokenTotals::default();
499    for contribution in finished
500        .contributions
501        .iter()
502        .filter(|contribution| contribution.applied)
503    {
504        let is_routing = contribution.kind.as_str()
505            == crate::codec::optimization::LlmOptimizationKind::MODEL_ROUTING;
506        if routing_ambiguous && is_routing {
507            continue;
508        }
509        if let Some(saved) = contribution
510            .token_impact
511            .as_ref()
512            .and_then(|impact| impact.saved.as_ref())
513        {
514            token_totals.add_contribution(saved);
515        }
516    }
517    if token_totals.missing_total {
518        limitations.push("missing_token_savings_total".to_string());
519    }
520    if token_totals.inconsistent_total {
521        limitations.push("inconsistent_token_savings_total".to_string());
522    }
523    let authoritative_transition = (applied_routing.len() == 1)
524        .then(|| applied_routing[0].model_transition.as_ref())
525        .flatten();
526    ContributionAnalysis {
527        limitations,
528        token_totals,
529        baseline_model: authoritative_transition.and_then(|route| route.baseline.clone()),
530        contributed_effective_model: authoritative_transition
531            .and_then(|route| route.effective.clone()),
532        use_effective_as_baseline: applied_routing.is_empty() || routing_ambiguous,
533    }
534}
535
536fn resolve_effective_model(
537    contributed: Option<LlmOptimizationModel>,
538    response: Option<&AnnotatedLlmResponse>,
539    requested_model: Option<&str>,
540) -> Option<LlmOptimizationModel> {
541    contributed
542        .or_else(|| {
543            response
544                .and_then(|response| response.model.as_ref())
545                .map(|model| LlmOptimizationModel::new(model.clone()))
546        })
547        .or_else(|| requested_model.map(LlmOptimizationModel::new))
548}
549
550struct UsageAnalysis {
551    effective: Option<Usage>,
552    baseline: Option<Usage>,
553    token_count_overflow: bool,
554    baseline_derivation_incomplete: bool,
555}
556
557fn derive_optimization_usage(
558    mut response: Option<&mut AnnotatedLlmResponse>,
559    tokens_saved: &LlmOptimizationTokens,
560    token_totals: &CheckedTokenTotals,
561    limitations: &mut Vec<String>,
562) -> UsageAnalysis {
563    let mut effective = response
564        .as_ref()
565        .and_then(|response| response.usage.clone());
566    let mut token_count_overflow = token_totals.overflow.any();
567    let mut baseline_derivation_incomplete =
568        token_totals.missing_total || token_totals.inconsistent_total;
569    if let Some(usage) = effective.as_mut() {
570        note_missing_core_usage(usage, limitations, &mut token_count_overflow);
571    }
572    if let (Some(inferred), Some(response_usage)) = (
573        effective.as_ref().and_then(|usage| usage.total_tokens),
574        response
575            .as_mut()
576            .and_then(|response| response.usage.as_mut()),
577    ) && response_usage.total_tokens.is_none()
578    {
579        response_usage.total_tokens = Some(inferred);
580    }
581    let baseline = effective.as_ref().map(|usage| {
582        derive_baseline_usage(
583            usage,
584            tokens_saved,
585            token_totals,
586            limitations,
587            &mut token_count_overflow,
588            &mut baseline_derivation_incomplete,
589        )
590    });
591    if token_count_overflow {
592        limitations.push("token_count_overflow".to_string());
593    }
594    UsageAnalysis {
595        effective,
596        baseline,
597        token_count_overflow,
598        baseline_derivation_incomplete,
599    }
600}
601
602fn note_missing_core_usage(
603    usage: &mut Usage,
604    limitations: &mut Vec<String>,
605    token_count_overflow: &mut bool,
606) {
607    if usage.prompt_tokens.is_none() {
608        limitations.push("missing_effective_prompt_tokens".to_string());
609    }
610    if usage.completion_tokens.is_none() {
611        limitations.push("missing_effective_completion_tokens".to_string());
612    }
613    if usage.total_tokens.is_none() {
614        match (usage.prompt_tokens, usage.completion_tokens) {
615            (Some(prompt), Some(completion)) => match prompt.checked_add(completion) {
616                Some(total) => usage.total_tokens = Some(total),
617                None => *token_count_overflow = true,
618            },
619            _ => limitations.push("missing_effective_total_tokens".to_string()),
620        }
621    }
622}
623
624fn derive_baseline_usage(
625    usage: &Usage,
626    tokens_saved: &LlmOptimizationTokens,
627    token_totals: &CheckedTokenTotals,
628    limitations: &mut Vec<String>,
629    token_count_overflow: &mut bool,
630    baseline_derivation_incomplete: &mut bool,
631) -> Usage {
632    let mut baseline = usage.clone();
633    baseline.cost = None;
634    let fields = [
635        (
636            &mut baseline.prompt_tokens,
637            tokens_saved.prompt_tokens,
638            token_totals.overflow.prompt,
639            "missing_effective_prompt_tokens",
640        ),
641        (
642            &mut baseline.completion_tokens,
643            tokens_saved.completion_tokens,
644            token_totals.overflow.completion,
645            "missing_effective_completion_tokens",
646        ),
647        (
648            &mut baseline.cache_read_tokens,
649            tokens_saved.cache_read_tokens,
650            token_totals.overflow.cache_read,
651            "missing_effective_cache_read_tokens",
652        ),
653        (
654            &mut baseline.cache_write_tokens,
655            tokens_saved.cache_write_tokens,
656            token_totals.overflow.cache_write,
657            "missing_effective_cache_write_tokens",
658        ),
659        (
660            &mut baseline.total_tokens,
661            tokens_saved.total_tokens,
662            token_totals.overflow.total,
663            "missing_effective_total_tokens",
664        ),
665    ];
666    for (observed, saved, overflowed, missing_limitation) in fields {
667        *token_count_overflow |= checked_add_observed_tokens(
668            observed,
669            saved,
670            overflowed,
671            missing_limitation,
672            limitations,
673            baseline_derivation_incomplete,
674        );
675    }
676    baseline
677}
678
679struct PricingAnalysis {
680    baseline_cost: Option<CostEstimate>,
681    actual_cost: Option<CostEstimate>,
682    complete_core_usage: bool,
683}
684
685#[allow(clippy::too_many_arguments)]
686fn price_optimization_usage(
687    effective_usage: &mut Option<Usage>,
688    response: Option<&mut AnnotatedLlmResponse>,
689    effective_model: Option<&LlmOptimizationModel>,
690    baseline_model: Option<&LlmOptimizationModel>,
691    baseline_usage: Option<&Usage>,
692    token_count_overflow: bool,
693    baseline_derivation_incomplete: bool,
694    pricing: &PricingResolver,
695) -> PricingAnalysis {
696    let provider_reported_cost = effective_usage
697        .as_ref()
698        .and_then(|usage| usage.cost.as_ref())
699        .filter(|cost| cost.source == CostSource::ProviderReported)
700        .cloned();
701    let complete_core_usage = effective_usage
702        .as_ref()
703        .is_some_and(|usage| usage.prompt_tokens.is_some() && usage.completion_tokens.is_some());
704    let actual_cost = provider_reported_cost.or_else(|| {
705        let model = complete_core_usage.then_some(effective_model).flatten()?;
706        pricing.estimate_cost_for_provider(
707            model.provider.as_deref(),
708            &model.model,
709            effective_usage.as_ref()?,
710        )
711    });
712    if let Some(usage) = effective_usage.as_mut() {
713        usage.cost.clone_from(&actual_cost);
714    }
715    if let Some(usage) = response.and_then(|response| response.usage.as_mut()) {
716        usage.cost.clone_from(&actual_cost);
717    }
718    let baseline_cost =
719        (!token_count_overflow && !baseline_derivation_incomplete && complete_core_usage)
720            .then_some(baseline_model)
721            .flatten()
722            .and_then(|model| {
723                pricing.estimate_cost_for_provider(
724                    model.provider.as_deref(),
725                    &model.model,
726                    baseline_usage?,
727                )
728            });
729    PricingAnalysis {
730        baseline_cost,
731        actual_cost,
732        complete_core_usage,
733    }
734}
735
736#[allow(clippy::too_many_arguments)]
737fn add_summary_limitations(
738    limitations: &mut Vec<String>,
739    effective_usage: Option<&Usage>,
740    effective_model: Option<&LlmOptimizationModel>,
741    baseline_model: Option<&LlmOptimizationModel>,
742    baseline_cost: Option<&CostEstimate>,
743    actual_cost: Option<&CostEstimate>,
744    token_count_overflow: bool,
745    baseline_derivation_incomplete: bool,
746    complete_core_usage: bool,
747) {
748    if effective_usage.is_none() {
749        limitations.push("missing_effective_usage".to_string());
750    }
751    if effective_model.is_none() {
752        limitations.push("missing_effective_model".to_string());
753    }
754    if baseline_model.is_none() {
755        limitations.push("missing_baseline_model".to_string());
756    }
757    if baseline_cost.is_none()
758        && baseline_model.is_some()
759        && !token_count_overflow
760        && !baseline_derivation_incomplete
761        && complete_core_usage
762    {
763        limitations.push("missing_baseline_pricing".to_string());
764    }
765    if actual_cost.is_none() {
766        limitations.push("missing_actual_cost".to_string());
767    }
768}
769
770pub(crate) fn finalize_optimization_summary(
771    recorder: &LlmOptimizationRecorder,
772    mut response: Option<&mut AnnotatedLlmResponse>,
773    requested_model: Option<&str>,
774    pricing: &PricingResolver,
775) -> Option<LlmOptimizationSummary> {
776    let finished = recorder.finish();
777    if finished.contributions.is_empty() && finished.limitations.is_empty() {
778        return None;
779    }
780
781    let mut analysis = analyze_contributions(&finished);
782    let effective_model = resolve_effective_model(
783        analysis.contributed_effective_model.take(),
784        response.as_deref(),
785        requested_model,
786    );
787    if analysis.use_effective_as_baseline && analysis.baseline_model.is_none() {
788        analysis.baseline_model = effective_model.clone();
789    }
790    let baseline_model = analysis.baseline_model;
791    let tokens_saved = analysis.token_totals.values.clone();
792    let mut limitations = analysis.limitations;
793    let token_totals = analysis.token_totals;
794    let mut token_count_overflow = token_totals.overflow.any();
795
796    let usage = derive_optimization_usage(
797        response.as_deref_mut(),
798        &tokens_saved,
799        &token_totals,
800        &mut limitations,
801    );
802    let mut effective_usage = usage.effective;
803    let baseline_usage = usage.baseline;
804    token_count_overflow |= usage.token_count_overflow;
805    let baseline_derivation_incomplete = usage.baseline_derivation_incomplete;
806
807    let pricing_analysis = price_optimization_usage(
808        &mut effective_usage,
809        response.as_deref_mut(),
810        effective_model.as_ref(),
811        baseline_model.as_ref(),
812        baseline_usage.as_ref(),
813        token_count_overflow,
814        baseline_derivation_incomplete,
815        pricing,
816    );
817    let baseline_cost = pricing_analysis.baseline_cost;
818    let actual_cost = pricing_analysis.actual_cost;
819    add_summary_limitations(
820        &mut limitations,
821        effective_usage.as_ref(),
822        effective_model.as_ref(),
823        baseline_model.as_ref(),
824        baseline_cost.as_ref(),
825        actual_cost.as_ref(),
826        token_count_overflow,
827        baseline_derivation_incomplete,
828        pricing_analysis.complete_core_usage,
829    );
830
831    let (estimated_cost_saved, currency) = calculate_estimated_cost_saved(
832        baseline_cost.as_ref(),
833        actual_cost.as_ref(),
834        &mut limitations,
835    );
836
837    limitations.sort();
838    limitations.dedup();
839    let summary = LlmOptimizationSummary {
840        schema_version: "1".to_string(),
841        calculation_version: "1".to_string(),
842        status: if limitations.is_empty() {
843            LlmOptimizationSummaryStatus::Complete
844        } else {
845            LlmOptimizationSummaryStatus::Partial
846        },
847        limitations,
848        baseline_model,
849        effective_model,
850        effective_usage,
851        baseline_usage,
852        tokens_saved,
853        baseline_cost,
854        actual_cost,
855        estimated_cost_saved,
856        currency,
857        contributions: finished.contributions,
858    };
859    if let Some(response) = response {
860        response.optimization_summary = Some(summary.clone());
861    }
862    Some(summary)
863}
864
865fn calculate_estimated_cost_saved(
866    baseline_cost: Option<&crate::codec::response::CostEstimate>,
867    actual_cost: Option<&crate::codec::response::CostEstimate>,
868    limitations: &mut Vec<String>,
869) -> (Option<f64>, Option<String>) {
870    let baseline_total = baseline_cost.and_then(|cost| cost.total_or_component_sum());
871    let actual_total = actual_cost.and_then(|cost| cost.total_or_component_sum());
872    if baseline_cost.is_some() && baseline_total.is_none() {
873        limitations.push("missing_baseline_cost_total".to_string());
874    }
875    if actual_cost.is_some() && actual_total.is_none() {
876        limitations.push("missing_actual_cost_total".to_string());
877    }
878
879    match (baseline_cost, actual_cost) {
880        (Some(baseline), Some(actual))
881            if baseline.currency.eq_ignore_ascii_case(&actual.currency) =>
882        {
883            let saved = baseline_total
884                .zip(actual_total)
885                .map(|(baseline, actual)| baseline - actual);
886            let currency = saved.is_some().then(|| baseline.currency.clone());
887            (saved, currency)
888        }
889        (Some(_), Some(_)) => {
890            limitations.push("cost_currency_mismatch".to_string());
891            (None, None)
892        }
893        _ => (None, None),
894    }
895}
896
897#[derive(Debug, Clone, Copy, Default)]
898struct TokenOverflow {
899    prompt: bool,
900    completion: bool,
901    cache_read: bool,
902    cache_write: bool,
903    total: bool,
904}
905
906impl TokenOverflow {
907    fn any(self) -> bool {
908        self.prompt || self.completion || self.cache_read || self.cache_write || self.total
909    }
910}
911
912#[derive(Debug, Default)]
913struct CheckedTokenTotals {
914    values: LlmOptimizationTokens,
915    overflow: TokenOverflow,
916    missing_total: bool,
917    inconsistent_total: bool,
918}
919
920impl CheckedTokenTotals {
921    fn add_contribution(&mut self, other: &LlmOptimizationTokens) {
922        checked_accumulate(
923            &mut self.values.prompt_tokens,
924            other.prompt_tokens,
925            &mut self.overflow.prompt,
926        );
927        checked_accumulate(
928            &mut self.values.completion_tokens,
929            other.completion_tokens,
930            &mut self.overflow.completion,
931        );
932        checked_accumulate(
933            &mut self.values.cache_read_tokens,
934            other.cache_read_tokens,
935            &mut self.overflow.cache_read,
936        );
937        checked_accumulate(
938            &mut self.values.cache_write_tokens,
939            other.cache_write_tokens,
940            &mut self.overflow.cache_write,
941        );
942
943        let (derived_total, derived_overflow) =
944            checked_option_sum([other.prompt_tokens, other.completion_tokens]);
945        self.overflow.total |= derived_overflow;
946        if derived_overflow {
947            self.values.total_tokens = None;
948        }
949        if let (Some(explicit), Some(prompt), Some(completion)) = (
950            other.total_tokens,
951            other.prompt_tokens,
952            other.completion_tokens,
953        ) && prompt
954            .checked_add(completion)
955            .is_some_and(|derived| derived != explicit)
956        {
957            self.inconsistent_total = true;
958        }
959        let contribution_total = other.total_tokens.or(derived_total);
960        if contribution_total.is_none() {
961            self.missing_total = true;
962        }
963        checked_accumulate(
964            &mut self.values.total_tokens,
965            contribution_total,
966            &mut self.overflow.total,
967        );
968    }
969}
970
971fn checked_accumulate(target: &mut Option<u64>, value: Option<u64>, overflowed: &mut bool) {
972    if *overflowed {
973        return;
974    }
975    let Some(value) = value else {
976        return;
977    };
978    match target.unwrap_or(0).checked_add(value) {
979        Some(total) => *target = Some(total),
980        None => {
981            *target = None;
982            *overflowed = true;
983        }
984    }
985}
986
987fn checked_add_observed_tokens(
988    target: &mut Option<u64>,
989    value: Option<u64>,
990    value_overflowed: bool,
991    missing_limitation: &'static str,
992    limitations: &mut Vec<String>,
993    baseline_derivation_incomplete: &mut bool,
994) -> bool {
995    if value_overflowed {
996        *target = None;
997        *baseline_derivation_incomplete = true;
998        return true;
999    }
1000    let Some(value) = value else {
1001        return false;
1002    };
1003    let Some(observed) = *target else {
1004        limitations.push(missing_limitation.to_string());
1005        *baseline_derivation_incomplete = true;
1006        return false;
1007    };
1008    match observed.checked_add(value) {
1009        Some(total) => {
1010            *target = Some(total);
1011            false
1012        }
1013        None => {
1014            *target = None;
1015            true
1016        }
1017    }
1018}
1019
1020fn checked_option_sum(values: impl IntoIterator<Item = Option<u64>>) -> (Option<u64>, bool) {
1021    let mut present = false;
1022    let mut total = 0_u64;
1023    for value in values.into_iter().flatten() {
1024        present = true;
1025        let Some(next) = total.checked_add(value) else {
1026            return (None, true);
1027        };
1028        total = next;
1029    }
1030    (present.then_some(total), false)
1031}
1032
1033#[cfg(test)]
1034#[path = "../../tests/unit/optimization_tests.rs"]
1035mod tests;