gaze_proxy/adapters/
openai.rs1use http::Method;
2use serde_json::Value;
3use url::Url;
4
5use crate::adapter::{
6 push_string, push_text_blocks, walk_all_strings, PiiSurface, ProviderAdapter, SseEvent,
7};
8
9#[derive(Clone, Debug)]
10#[non_exhaustive]
11pub struct OpenAiAdapter {
12 upstream: Url,
13}
14
15impl OpenAiAdapter {
16 pub fn new(upstream: Url) -> Self {
17 Self { upstream }
18 }
19}
20
21#[async_trait::async_trait]
22impl ProviderAdapter for OpenAiAdapter {
23 fn name(&self) -> &'static str {
24 "openai"
25 }
26
27 fn matches_path(&self, method: &Method, path: &str) -> bool {
28 method == Method::POST
29 && matches!(
30 path,
31 "/v1/chat/completions" | "/v1/completions" | "/v1/responses"
32 )
33 }
34
35 fn upstream_base(&self) -> &Url {
36 &self.upstream
37 }
38
39 fn request_pii_surfaces<'a>(&self, body: &'a mut Value) -> Vec<PiiSurface<'a>> {
51 let mut surfaces = Vec::new();
52 if let Value::Object(root) = body {
53 for (key, value) in root {
54 match key.as_str() {
55 "system" => push_string(&mut surfaces, "system", value),
56 "prompt" => push_text_blocks(&mut surfaces, "prompt", value),
57 "instructions" => push_string(&mut surfaces, "instructions", value),
58 "messages" => {
59 if let Value::Array(messages) = value {
60 for (index, message) in messages.iter_mut().enumerate() {
61 collect_message_surfaces(
62 &mut surfaces,
63 format!("messages[{index}]"),
64 message,
65 );
66 }
67 }
68 }
69 "input" => push_text_blocks(&mut surfaces, "input", value),
70 "user" => push_string(&mut surfaces, "user", value),
74 "metadata" => walk_all_strings(&mut surfaces, "metadata".to_string(), value),
76 "stop" => walk_all_strings(&mut surfaces, "stop".to_string(), value),
78 "tools" => walk_all_strings(&mut surfaces, "tools".to_string(), value),
81 "functions" => walk_all_strings(&mut surfaces, "functions".to_string(), value),
82 "response_format" => {
83 walk_all_strings(&mut surfaces, "response_format".to_string(), value);
84 }
85 "prediction" => {
86 walk_all_strings(&mut surfaces, "prediction".to_string(), value);
87 }
88 _ => {}
89 }
90 }
91 }
92 surfaces
93 }
94
95 fn response_pii_surfaces<'a>(&self, body: &'a mut Value) -> Vec<PiiSurface<'a>> {
96 let mut surfaces = Vec::new();
97 if let Value::Object(root) = body {
98 for (key, value) in root {
99 match key.as_str() {
100 "choices" => {
101 if let Value::Array(choices) = value {
102 for (index, choice) in choices.iter_mut().enumerate() {
103 if let Value::Object(choice) = choice {
104 for (choice_key, choice_value) in choice {
105 match choice_key.as_str() {
106 "message" => collect_message_surfaces(
107 &mut surfaces,
108 format!("choices[{index}].message"),
109 choice_value,
110 ),
111 "delta" => collect_message_surfaces(
112 &mut surfaces,
113 format!("choices[{index}].delta"),
114 choice_value,
115 ),
116 "text" => push_string(
117 &mut surfaces,
118 format!("choices[{index}].text"),
119 choice_value,
120 ),
121 _ => {}
122 }
123 }
124 }
125 }
126 }
127 }
128 "output" => walk_all_strings(&mut surfaces, "output".to_string(), value),
129 _ => {}
130 }
131 }
132 }
133 surfaces
134 }
135
136 fn sse_event_pii_surfaces<'a>(&self, event: &'a mut SseEvent) -> Vec<PiiSurface<'a>> {
137 self.response_pii_surfaces(&mut event.data)
138 }
139}
140
141fn collect_message_surfaces<'a>(
142 surfaces: &mut Vec<PiiSurface<'a>>,
143 prefix: String,
144 message: &'a mut Value,
145) {
146 let Value::Object(map) = message else {
147 return;
148 };
149 for (key, value) in map {
150 match key.as_str() {
151 "content" => push_text_blocks(surfaces, &format!("{prefix}.content"), value),
152 "tool_results" => push_text_blocks(surfaces, &format!("{prefix}.tool_results"), value),
153 "name" => push_string(surfaces, format!("{prefix}.name"), value),
156 "tool_calls" => {
157 if let Value::Array(tool_calls) = value {
158 for (index, tool_call) in tool_calls.iter_mut().enumerate() {
159 if let Value::Object(tool_call) = tool_call {
160 if let Some(Value::Object(function)) = tool_call.get_mut("function") {
161 if let Some(args) = function.get_mut("arguments") {
162 push_string(
163 surfaces,
164 format!("{prefix}.tool_calls[{index}].function.arguments"),
165 args,
166 );
167 }
168 }
169 }
170 }
171 }
172 }
173 _ => {}
174 }
175 }
176}