#[non_exhaustive]pub struct ProviderCall {
pub system: Option<String>,
pub operation: Option<String>,
pub request_model: Option<String>,
pub temperature: Option<f64>,
pub response_id: Option<String>,
pub response_model: Option<String>,
pub finish_reasons: Vec<String>,
pub input_tokens: Option<u64>,
pub output_tokens: Option<u64>,
pub input_cached_tokens: Option<u64>,
}Expand description
One provider call, described with the OpenTelemetry GenAI semantic
conventions (gen_ai.*).
These are the attributes every LLM observability backend already reads —
Langfuse, Datadog LLM Observability, Phoenix, Braintrust — so a Turnframe
application shows up in them as a model call without any vendor adapter.
The keys live in crate::attrs so a provider crate and an application
spell them identically.
Token accounting is the part that is easy to get wrong, so it is explicit
here: ProviderCall::input_tokens is always recorded net of cached
tokens. A consumer that reads only that field sees the uncached prompt
cost; one that adds ProviderCall::input_cached_tokens sees the whole
prompt. Neither double counts. Use
ProviderCall::with_cached_usage when the provider reports a total and a
cached figure, and ProviderCall::with_usage when it already reports the
net one.
No member holds prompt or completion text: content is user data and travels
only through ContentRecorder, which is off by default.
Fields (Non-exhaustive)§
This struct is marked as non-exhaustive
Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.system: Option<String>Provider key, recorded as gen_ai.system.
operation: Option<String>Normalized request purpose, recorded as gen_ai.operation.name.
request_model: Option<String>Model asked for, recorded as gen_ai.request.model.
temperature: Option<f64>Sampling temperature, recorded as gen_ai.request.temperature.
response_id: Option<String>The provider’s identifier for the response.
response_model: Option<String>Model that actually answered, which is not always the one asked for.
finish_reasons: Vec<String>Why generation stopped.
input_tokens: Option<u64>Input tokens billed, net of input_cached_tokens.
output_tokens: Option<u64>Output tokens generated.
input_cached_tokens: Option<u64>Input tokens served from the provider’s prompt cache.
Implementations§
Source§impl ProviderCall
impl ProviderCall
Sourcepub fn new(
system: impl Into<String>,
operation: impl Into<String>,
request_model: impl Into<String>,
) -> Self
pub fn new( system: impl Into<String>, operation: impl Into<String>, request_model: impl Into<String>, ) -> Self
A call to system for operation with model.
Sourcepub fn from_attempt(attempt: &ProviderAttemptRecord) -> Self
pub fn from_attempt(attempt: &ProviderAttemptRecord) -> Self
Everything a persisted provider attempt already knows (spec §20.7).
Sourcepub fn with_temperature(self, temperature: f64) -> Self
pub fn with_temperature(self, temperature: f64) -> Self
Sets the requested sampling temperature.
Sourcepub fn with_response(
self,
response_id: impl Into<String>,
response_model: impl Into<String>,
) -> Self
pub fn with_response( self, response_id: impl Into<String>, response_model: impl Into<String>, ) -> Self
Sets the response identifier and the model that answered.
Sourcepub fn with_finish_reason(self, reason: impl Into<String>) -> Self
pub fn with_finish_reason(self, reason: impl Into<String>) -> Self
Adds a finish reason.
Sourcepub fn with_usage(self, input_tokens: u64, output_tokens: u64) -> Self
pub fn with_usage(self, input_tokens: u64, output_tokens: u64) -> Self
Records usage the provider already reports net of its prompt cache.
Sourcepub fn with_cached_usage(
self,
total_input_tokens: u64,
cached_tokens: u64,
output_tokens: u64,
) -> Self
pub fn with_cached_usage( self, total_input_tokens: u64, cached_tokens: u64, output_tokens: u64, ) -> Self
Records usage a provider reports as a total prompt size plus a cached figure, and stores the input tokens net of the cache.
use turnframe_telemetry::tracing::ProviderCall;
let call = ProviderCall::new("openai", "extract", "gpt-x")
.with_cached_usage(1_000, 800, 120);
// 200 uncached + 800 cached = the 1_000 the provider reported.
assert_eq!(call.input_tokens, Some(200));
assert_eq!(call.input_cached_tokens, Some(800));
assert_eq!(call.output_tokens, Some(120));Sourcepub fn total_input_tokens(&self) -> Option<u64>
pub fn total_input_tokens(&self) -> Option<u64>
The total prompt size the provider saw: net input plus cached.
Sourcepub fn attributes(&self) -> Vec<(&'static str, String)>
pub fn attributes(&self) -> Vec<(&'static str, String)>
The call as (attribute key, value) pairs in a stable order, omitting
what is unknown.
This is the exact set the span carries; it is a plain function so a test can assert on it without standing up a subscriber, and an adopter can rename the keys for a backend that wants its own spelling.