use super::client_profile::{self, ClientEfficiencyProfile};
use super::context_broker::{BrokerBudget, ContextBroker};
use super::coverage_class::{self, CoverageClass};
use super::etpao_live::{EtpaoLive, OutcomeMetrics, RequestMetrics};
use super::identity::CallerIdentity;
use super::identity_resolver;
#[derive(Debug, Clone)]
pub(crate) struct RequestContext {
pub identity: CallerIdentity,
pub profile: ClientEfficiencyProfile,
pub coverage: CoverageClass,
pub broker_budget: BrokerBudget,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub(crate) enum OptimizationLevel {
Full,
#[default]
Partial,
ObserveOnly,
None,
}
#[must_use]
pub(crate) fn build_request_context(
headers: &[(String, String)],
has_proxy: bool,
has_mcp: bool,
has_hooks: bool,
) -> RequestContext {
let identity = identity_resolver::resolve_with_defaults(headers);
let coverage = coverage_class::detect_coverage(has_proxy, has_mcp, has_hooks);
let mut profile = client_profile::detect_from_headers(headers);
profile.coverage = coverage;
let broker_budget = ContextBroker::new(profile.clone()).compute_budget();
RequestContext {
identity,
profile,
coverage,
broker_budget,
}
}
#[must_use]
pub(crate) fn should_optimize(ctx: &RequestContext) -> bool {
coverage_class::is_addressable(ctx.coverage)
}
#[must_use]
pub(crate) fn optimization_level(ctx: &RequestContext) -> OptimizationLevel {
match ctx.coverage {
CoverageClass::FullInline => OptimizationLevel::Full,
CoverageClass::ContextControlled => OptimizationLevel::Partial,
CoverageClass::ObserveOnly => OptimizationLevel::ObserveOnly,
CoverageClass::Unmanaged => OptimizationLevel::None,
}
}
pub(crate) fn record_request_etpao(
etpao: &mut EtpaoLive,
ctx: &RequestContext,
input_tokens: usize,
output_tokens: usize,
) {
etpao.record_request(RequestMetrics {
input_tokens,
output_tokens,
reasoning_tokens: 0,
schema_tokens: 0,
cache_write_tokens: 0,
retry_count: 0,
client_id: ctx.profile.client_id.clone(),
coverage_class: ctx.coverage,
});
}
pub(crate) fn record_outcome_etpao(
etpao: &mut EtpaoLive,
ctx: &RequestContext,
accepted: bool,
quality: f64,
) {
etpao.record_outcome(OutcomeMetrics {
accepted,
quality_score: quality,
first_pass: accepted,
client_id: ctx.profile.client_id.clone(),
});
}
#[must_use]
pub(crate) fn format_request_summary(ctx: &RequestContext) -> String {
format!(
"[{}] user={} team={} budget={}",
coverage_class::coverage_label(ctx.coverage),
ctx.identity.user_id.as_deref().unwrap_or("-"),
ctx.identity.team_id.as_deref().unwrap_or("-"),
ctx.broker_budget.context_tokens
)
}
#[cfg(test)]
mod tests {
use super::{
OptimizationLevel, build_request_context, format_request_summary, optimization_level,
record_outcome_etpao, record_request_etpao, should_optimize,
};
use crate::core::context_kernel::coverage_class::CoverageClass;
use crate::core::context_kernel::etpao_live::EtpaoLive;
fn context(coverage: CoverageClass) -> super::RequestContext {
let (has_proxy, has_mcp, has_hooks) = match coverage {
CoverageClass::FullInline => (true, false, false),
CoverageClass::ContextControlled => (false, true, false),
CoverageClass::ObserveOnly => (false, false, true),
CoverageClass::Unmanaged => (false, false, false),
};
build_request_context(&[], has_proxy, has_mcp, has_hooks)
}
#[test]
fn build_context_from_empty_headers() {
let ctx = build_request_context(&[], false, false, false);
assert_eq!(ctx.profile.client_id, "unknown");
assert_eq!(ctx.profile.coverage, CoverageClass::Unmanaged);
assert_eq!(ctx.coverage, CoverageClass::Unmanaged);
assert!(ctx.broker_budget.context_tokens > 0);
}
#[test]
fn build_context_with_proxy() {
let ctx = build_request_context(&[], true, false, false);
assert_eq!(ctx.coverage, CoverageClass::FullInline);
assert_eq!(ctx.profile.coverage, CoverageClass::FullInline);
}
#[test]
fn should_optimize_full_inline() {
assert!(should_optimize(&context(CoverageClass::FullInline)));
}
#[test]
fn should_optimize_unmanaged() {
assert!(!should_optimize(&context(CoverageClass::Unmanaged)));
}
#[test]
fn optimization_level_mapping() {
for (coverage, expected) in [
(CoverageClass::FullInline, OptimizationLevel::Full),
(CoverageClass::ContextControlled, OptimizationLevel::Partial),
(CoverageClass::ObserveOnly, OptimizationLevel::ObserveOnly),
(CoverageClass::Unmanaged, OptimizationLevel::None),
] {
assert_eq!(optimization_level(&context(coverage)), expected);
}
}
#[test]
fn format_summary_includes_identity() {
let ctx = build_request_context(&[], false, true, false);
let expected_user = format!("user={}", ctx.identity.user_id.as_deref().unwrap_or("-"));
let expected_team = format!("team={}", ctx.identity.team_id.as_deref().unwrap_or("-"));
let summary = format_request_summary(&ctx);
assert!(summary.contains(&expected_user));
assert!(summary.contains(&expected_team));
assert!(summary.contains("budget="));
}
#[test]
fn etpao_recording_works() {
let ctx = context(CoverageClass::FullInline);
let mut etpao = EtpaoLive::new();
record_request_etpao(&mut etpao, &ctx, 100, 25);
record_outcome_etpao(&mut etpao, &ctx, true, 0.9);
let summary = etpao.summary();
assert_eq!(etpao.request_count(), 1);
assert_eq!(etpao.outcome_count(), 1);
assert_eq!(summary.total_tokens, 125);
assert_eq!(summary.accepted_outcomes, 1);
}
}