fixed PR comments and added more trace attributes

This commit is contained in:
Salman Paracha 2025-12-11 13:53:44 -08:00
parent c0cf877b4f
commit 28b674454b
21 changed files with 565 additions and 26 deletions

View file

@ -14,7 +14,7 @@ use tokio::sync::RwLock;
use tracing::{debug, warn};
use crate::router::llm_router::RouterService;
use crate::handlers::utils::{create_streaming_response, PassthroughProcessor, truncate_message};
use crate::handlers::utils::{create_streaming_response, ObservableStreamProcessor, truncate_message};
use crate::handlers::router_chat::router_chat_get_upstream_model;
use crate::tracing::operation_component;
@ -24,7 +24,7 @@ fn full<T: Into<Bytes>>(chunk: T) -> BoxBody<Bytes, hyper::Error> {
.boxed()
}
pub async fn chat(
pub async fn llm_chat(
request: Request<hyper::body::Incoming>,
router_service: Arc<RouterService>,
full_qualified_llm_provider_url: String,
@ -36,12 +36,19 @@ pub async fn chat(
let request_path = request.uri().path().to_string();
let request_headers = request.headers().clone();
// Extract traceparent header early (Envoy should have added this)
let traceparent = request_headers
// Extract or generate traceparent - this establishes the trace context for all spans
let traceparent: String = request_headers
.get("traceparent")
.and_then(|h| h.to_str().ok())
.unwrap_or("00-00000000000000000000000000000000-0000000000000000-01")
.to_string();
.map(|s| s.to_string())
.unwrap_or_else(|| {
// No traceparent - this is a root span, generate a new trace ID
use uuid::Uuid;
let trace_id = Uuid::new_v4().to_string().replace("-", "");
let span_id = Uuid::new_v4().to_string().replace("-", "")[..16].to_string();
// Format: version-trace_id-parent_span_id-trace_flags
format!("00-{}-{}-01", trace_id, span_id)
});
let mut request_headers = request_headers;
let chat_request_bytes = request.collect().await?.to_bytes();
@ -68,6 +75,7 @@ pub async fn chat(
// Model alias resolution: update model field in client_request immediately
// This ensures all downstream objects use the resolved model
let model_from_request = client_request.model().to_string();
let temperature = client_request.get_temperature();
let is_streaming_request = client_request.is_streaming();
let resolved_model = resolve_model_alias(&model_from_request, &model_aliases);
@ -177,11 +185,12 @@ pub async fn chat(
request_start_system_time,
tool_names,
user_message_preview,
temperature,
&llm_providers,
).await;
// Use PassthroughProcessor to track streaming metrics and finalize the span
let processor = PassthroughProcessor::new(
let processor = ObservableStreamProcessor::new(
trace_collector,
operation_component::LLM,
llm_span,
@ -230,6 +239,7 @@ async fn build_llm_span(
start_time: std::time::SystemTime,
tool_names: Option<Vec<String>>,
user_message_preview: Option<String>,
temperature: Option<f32>,
llm_providers: &Arc<RwLock<Vec<LlmProvider>>>,
) -> common::traces::Span {
use common::traces::{SpanBuilder, SpanKind, parse_traceparent};
@ -274,6 +284,10 @@ async fn build_llm_span(
.with_attribute(llm::IS_STREAMING, is_streaming.to_string());
// Add optional attributes
if let Some(temp) = temperature {
span_builder = span_builder.with_attribute(llm::TEMPERATURE, temp.to_string());
}
if let Some(tools) = tool_names {
let formatted_tools = tools.iter()
.map(|name| format!("{}(...)", name))

View file

@ -1,6 +1,6 @@
pub mod agent_chat_completions;
pub mod agent_selector;
pub mod router;
pub mod llm;
pub mod router_chat;
pub mod models;
pub mod function_calling;

View file

@ -30,7 +30,7 @@ pub trait StreamProcessor: Send + 'static {
}
/// A processor that tracks streaming metrics and finalizes the span
pub struct PassthroughProcessor {
pub struct ObservableStreamProcessor {
collector: Arc<TraceCollector>,
service_name: String,
span: Span,
@ -40,7 +40,7 @@ pub struct PassthroughProcessor {
time_to_first_token: Option<u128>,
}
impl PassthroughProcessor {
impl ObservableStreamProcessor {
/// Create a new passthrough processor
///
/// # Arguments
@ -66,7 +66,7 @@ impl PassthroughProcessor {
}
}
impl StreamProcessor for PassthroughProcessor {
impl StreamProcessor for ObservableStreamProcessor {
fn process_chunk(&mut self, chunk: Bytes) -> Result<Option<Bytes>, String> {
self.total_bytes += chunk.len();
self.chunk_count += 1;

View file

@ -1,5 +1,5 @@
use brightstaff::handlers::agent_chat_completions::agent_chat;
use brightstaff::handlers::router::chat;
use brightstaff::handlers::llm::llm_chat;
use brightstaff::handlers::models::list_models;
use brightstaff::handlers::function_calling::{function_calling_chat_handler};
use brightstaff::router::llm_router::RouterService;
@ -130,7 +130,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
(&Method::POST, CHAT_COMPLETIONS_PATH | MESSAGES_PATH | OPENAI_RESPONSES_API_PATH) => {
let fully_qualified_url =
format!("{}{}", llm_provider_url, req.uri().path());
chat(req, router_service, fully_qualified_url, model_aliases, llm_providers, trace_collector)
llm_chat(req, router_service, fully_qualified_url, model_aliases, llm_providers, trace_collector)
.with_context(parent_cx)
.await
}

View file

@ -83,19 +83,19 @@ pub mod llm {
pub const TOTAL_TOKENS: &str = "llm.usage.total_tokens";
/// Temperature parameter used
pub const TEMPERATURE: &str = "llm.request.temperature";
pub const TEMPERATURE: &str = "llm.temperature";
/// Max tokens parameter used
pub const MAX_TOKENS: &str = "llm.request.max_tokens";
pub const MAX_TOKENS: &str = "llm.max_tokens";
/// Top-p parameter used
pub const TOP_P: &str = "llm.request.top_p";
pub const TOP_P: &str = "llm.top_p";
/// List of tool names provided in the request
pub const TOOLS: &str = "llm.tools";
/// Preview of the user message (truncated)
pub const USER_MESSAGE_PREVIEW: &str = "llm.request.user_message_preview";
pub const USER_MESSAGE_PREVIEW: &str = "llm.user_message_preview";
}
// =============================================================================