Skip to main content

gproxy_protocol/adapt/generate/stream/event/
mod.rs

1//! Concrete native event collection. The associated payloads remain vendor wire types.
2
3mod chat;
4mod claude;
5mod gemini;
6mod responses;
7use super::super::{
8    ToolCallKind,
9    identity_facts::{IdentityFacts, ToolIdentity},
10};
11use crate::{
12    Dialect,
13    transform::{
14        Converted, TransformError,
15        identity::{IdentityFlow, TargetIdPolicy},
16    },
17    wire::DeclaredFields,
18};
19use serde::{Serialize, de::DeserializeOwned};
20
21#[derive(Debug, Clone, Copy)]
22pub struct EventLimits {
23    pub max_bytes: usize,
24    pub max_pending_bytes: usize,
25}
26
27impl EventLimits {}
28
29#[derive(Debug, Clone)]
30pub struct Collected<T> {
31    pub value: T,
32    /// Actual source observations override IDs synthesized only to construct a
33    /// complete native Chat DTO. These are never invented upstream identities.
34    pub(crate) original_tool_ids: Option<Vec<Option<String>>>,
35}
36
37impl<T> Collected<T> {
38    fn native(value: T) -> Self {
39        Self {
40            value,
41            original_tool_ids: None,
42        }
43    }
44}
45
46impl<T: IdentityFacts> IdentityFacts for Collected<T> {
47    fn dialect(&self) -> Dialect {
48        self.value.dialect()
49    }
50    fn tools(&self) -> Vec<ToolIdentity> {
51        let mut tools = self.value.tools();
52        if let Some(ids) = &self.original_tool_ids {
53            // The DTO's native legacy/modern form remains intact. Only IDs
54            // synthesized for DTO completeness are replaced by actual observations.
55            for (tool, id) in tools.iter_mut().zip(ids) {
56                tool.call_id.clone_from(id);
57            }
58        }
59        tools
60    }
61}
62
63pub(crate) mod sealed {
64    pub trait Event {}
65}
66
67/// Sealed over the four actual generation event types.
68pub trait NativeEvent:
69    sealed::Event + Serialize + DeserializeOwned + DeclaredFields + Clone
70{
71    type Full: Serialize + Clone + DeclaredFields;
72    type Collector;
73    fn responses_history(
74        _value: &Self::Full,
75    ) -> Option<&crate::wire::openai::responses::GenerateContentResponseBody> {
76        None
77    }
78    const DIALECT: Dialect;
79    const DONE: bool = false;
80    fn collector(
81        flow: IdentityFlow,
82        policy: TargetIdPolicy,
83        limits: EventLimits,
84    ) -> Self::Collector;
85    fn collect(collector: &mut Self::Collector, event: Self) -> Result<(), TransformError>;
86    fn collect_done(_collector: &mut Self::Collector) -> Result<(), TransformError> {
87        Err(invalid("unexpected DONE for native dialect"))
88    }
89    fn collected(
90        collector: Self::Collector,
91    ) -> Result<Converted<Collected<Self::Full>>, TransformError>;
92    fn event_name(&self) -> Option<&'static str>;
93    fn is_terminal(&self) -> bool;
94    fn is_usage_only(&self) -> bool {
95        false
96    }
97    /// Complete emitted ID, actual call kind, and complete name if already known.
98    fn tool_declarations(
99        &self,
100        complete_tool_names: bool,
101    ) -> Vec<(String, ToolCallKind, Option<String>)>;
102}
103
104fn invalid(message: impl Into<String>) -> TransformError {
105    TransformError::invalid_result("generation.stream.event", message)
106}