agent-infra-sdk 0.2.0

Unified Rust SDK for Gateway-backed and local Agent Infra APIs
use crate::transport::{
    CallOptions, ClientOptions, HttpTransport, InfraClientError, ServiceEndpoint,
};
use agent_trace_contract::{
    AppendEventsRequest, AppendResponse, EVENTS_APPEND_PATH, EVENTS_SEARCH_PATH, EventsResponse,
    RuntimeEventRecord, SearchEventsRequest, TraceScope, redact_payload,
};
use reqwest::Client;

/// Client for the Trace service (`agent-trace-server`).
#[derive(Clone, Debug)]
pub struct TraceClient {
    transport: HttpTransport,
}

impl TraceClient {
    pub(crate) fn new_with_endpoint(
        http: Client,
        endpoint: ServiceEndpoint,
        options: ClientOptions,
    ) -> Self {
        Self {
            transport: HttpTransport::new_with_options(
                http,
                agent_trace_contract::SERVICE_NAME,
                endpoint,
                options,
            ),
        }
    }

    /// Append trace events to a run. Idempotent: duplicate IDs are skipped.
    pub async fn append_events(
        &self,
        events: Vec<RuntimeEventRecord>,
    ) -> Result<AppendResponse, InfraClientError> {
        self.append_events_with_options(events, CallOptions::default())
            .await
    }

    pub async fn append_events_with_options(
        &self,
        mut events: Vec<RuntimeEventRecord>,
        options: CallOptions,
    ) -> Result<AppendResponse, InfraClientError> {
        for event in &mut events {
            redact_payload(&mut event.payload);
            redact_payload(&mut event.metadata);
        }
        self.transport
            .post_json_with_options(
                EVENTS_APPEND_PATH,
                &AppendEventsRequest { events },
                options.idempotent(true),
            )
            .await
    }

    /// Search trace events in a run.
    pub async fn search_events(
        &self,
        conversation_id: &str,
        run_id: &str,
        event_type: Option<&str>,
        limit: Option<usize>,
    ) -> Result<Vec<RuntimeEventRecord>, InfraClientError> {
        let request = SearchEventsRequest {
            scope: TraceScope::run(conversation_id, run_id),
            event_type: event_type.map(ToOwned::to_owned),
            limit,
        };
        let response: EventsResponse = self
            .transport
            .post_json(EVENTS_SEARCH_PATH, &request)
            .await?;
        Ok(response.items)
    }
}