use crate::diagnostics::ParseDiagnostics;
use crate::error::Result;
use crate::exec_ctx::ExecCtx;
#[allow(deprecated)]
use crate::trace::TraceId;
use crate::PipelineError;
use serde::de::DeserializeOwned;
use serde_json::Value;
use stack_ids::TraceCtx;
use std::future::Future;
use std::pin::Pin;
pub type BoxFut<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
pub trait Payload: Send + Sync {
fn kind(&self) -> &'static str;
fn name(&self) -> &str;
fn invoke<'a>(&'a self, ctx: &'a ExecCtx, input: Value) -> BoxFut<'a, Result<PayloadOutput>>;
}
#[derive(Debug, Clone)]
pub struct PayloadOutput {
pub value: Value,
pub raw_response: String,
pub thinking: Option<String>,
pub model: Option<String>,
pub diagnostics: Option<ParseDiagnostics>,
pub trace_id: Option<TraceId>,
pub trace_ctx: Option<TraceCtx>,
pub transport_retries_used: u32,
pub semantic_retries_used: u32,
pub response_bytes: usize,
pub wall_time_ms: u64,
}
impl PayloadOutput {
pub fn from_value(value: Value) -> Self {
let raw = value.to_string();
Self {
value,
raw_response: raw,
thinking: None,
model: None,
diagnostics: None,
trace_id: None,
trace_ctx: None,
transport_retries_used: 0,
semantic_retries_used: 0,
response_bytes: 0,
wall_time_ms: 0,
}
}
pub fn parse_as<T: DeserializeOwned>(&self) -> Result<T> {
serde_json::from_value(self.value.clone()).map_err(|e| {
let snippet = self.value.to_string();
let snippet = &snippet[..snippet.len().min(200)];
PipelineError::Other(format!(
"Failed to parse PayloadOutput into target type: {}. Value (truncated): {}",
e, snippet
))
})
}
}