Skip to main content

gproxy_protocol/transform/openai/websocket/to_http/
request.rs

1use crate::openai::create_response::request::{OpenAiCreateResponseRequest, RequestBody};
2use crate::openai::create_response::types::Metadata;
3use crate::openai::create_response::websocket::request::OpenAiCreateResponseWebSocketConnectRequest;
4use crate::openai::create_response::websocket::types::OpenAiCreateResponseWebSocketClientMessage;
5use crate::transform::openai::websocket::context::OpenAiWebsocketTransformContext;
6use crate::transform::openai::websocket::from_http::request::OPENAI_CLIENT_METADATA_TUNNEL_PREFIX;
7use crate::transform::utils::TransformError;
8
9fn inject_client_metadata_into_request_body(
10    body: &mut RequestBody,
11    client_metadata: Option<&Metadata>,
12    ctx: &mut OpenAiWebsocketTransformContext,
13) {
14    let Some(client_metadata) = client_metadata else {
15        return;
16    };
17    if client_metadata.is_empty() {
18        return;
19    }
20
21    let metadata = body.metadata.get_or_insert_with(Metadata::new);
22    for (key, value) in client_metadata {
23        let tunnel_key = format!("{OPENAI_CLIENT_METADATA_TUNNEL_PREFIX}{key}");
24        if metadata.contains_key(&tunnel_key) {
25            ctx.push_warning(format!(
26                "openai websocket to_http request: metadata tunnel key conflict overwritten `{tunnel_key}`"
27            ));
28        }
29        metadata.insert(tunnel_key, value.clone());
30    }
31    ctx.push_warning(
32        "openai websocket to_http request: client_metadata tunneled through metadata".to_string(),
33    );
34}
35
36pub fn websocket_client_message_to_openai_request_with_context(
37    value: &OpenAiCreateResponseWebSocketClientMessage,
38) -> Result<(OpenAiCreateResponseRequest, OpenAiWebsocketTransformContext), TransformError> {
39    let mut ctx = OpenAiWebsocketTransformContext::default();
40
41    let request = match value {
42        OpenAiCreateResponseWebSocketClientMessage::ResponseCreate(payload) => {
43            let mut body = payload.request.clone();
44            inject_client_metadata_into_request_body(
45                &mut body,
46                payload.client_metadata.as_ref(),
47                &mut ctx,
48            );
49            if payload.generate == Some(false) {
50                ctx.push_warning(
51                    "openai websocket to_http request: dropped generate=false flag".to_string(),
52                );
53            }
54            OpenAiCreateResponseRequest {
55                body,
56                ..OpenAiCreateResponseRequest::default()
57            }
58        }
59        OpenAiCreateResponseWebSocketClientMessage::ResponseAppend(payload) => {
60            let mut body = RequestBody {
61                input: Some(payload.input.clone()),
62                stream: Some(true),
63                ..RequestBody::default()
64            };
65            inject_client_metadata_into_request_body(
66                &mut body,
67                payload.client_metadata.as_ref(),
68                &mut ctx,
69            );
70            OpenAiCreateResponseRequest {
71                body,
72                ..OpenAiCreateResponseRequest::default()
73            }
74        }
75    };
76
77    Ok((request, ctx))
78}
79
80pub fn websocket_connect_to_openai_request_with_context(
81    value: &OpenAiCreateResponseWebSocketConnectRequest,
82) -> Result<(OpenAiCreateResponseRequest, OpenAiWebsocketTransformContext), TransformError> {
83    let Some(message) = value.body.as_ref() else {
84        let mut ctx = OpenAiWebsocketTransformContext::default();
85        ctx.push_warning(
86            "openai websocket to_http request: missing initial body, downgraded to empty request"
87                .to_string(),
88        );
89        return Ok((OpenAiCreateResponseRequest::default(), ctx));
90    };
91    websocket_client_message_to_openai_request_with_context(message)
92}
93
94impl TryFrom<&OpenAiCreateResponseWebSocketClientMessage> for OpenAiCreateResponseRequest {
95    type Error = TransformError;
96
97    fn try_from(
98        value: &OpenAiCreateResponseWebSocketClientMessage,
99    ) -> Result<Self, TransformError> {
100        Ok(websocket_client_message_to_openai_request_with_context(value)?.0)
101    }
102}
103
104impl TryFrom<&OpenAiCreateResponseWebSocketConnectRequest> for OpenAiCreateResponseRequest {
105    type Error = TransformError;
106
107    fn try_from(
108        value: &OpenAiCreateResponseWebSocketConnectRequest,
109    ) -> Result<Self, TransformError> {
110        Ok(websocket_connect_to_openai_request_with_context(value)?.0)
111    }
112}
113
114impl TryFrom<OpenAiCreateResponseWebSocketConnectRequest> for OpenAiCreateResponseRequest {
115    type Error = TransformError;
116
117    fn try_from(
118        value: OpenAiCreateResponseWebSocketConnectRequest,
119    ) -> Result<Self, TransformError> {
120        OpenAiCreateResponseRequest::try_from(&value)
121    }
122}