mirror of
https://github.com/openai/codex.git
synced 2026-11-05 20:10:33 +00:00
Merge branch 'main' into rhan/fix-image-poisoning
This commit is contained in:
@@ -134,7 +134,7 @@ pub fn map_api_error(err: ApiError) -> CodexErr {
|
||||
status: http::StatusCode::INTERNAL_SERVER_ERROR,
|
||||
request_id: None,
|
||||
}),
|
||||
TransportError::Timeout => CodexErr::Timeout,
|
||||
TransportError::Timeout => CodexErr::RequestTimeout,
|
||||
TransportError::Network(msg) | TransportError::Build(msg) => {
|
||||
CodexErr::Stream(msg, None)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ use http::Method;
|
||||
use serde::Deserialize;
|
||||
use serde_json::to_value;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
pub struct CompactClient<T: HttpTransport> {
|
||||
session: EndpointSession<T>,
|
||||
@@ -37,10 +38,19 @@ impl<T: HttpTransport> CompactClient<T> {
|
||||
&self,
|
||||
body: serde_json::Value,
|
||||
extra_headers: HeaderMap,
|
||||
request_timeout: Duration,
|
||||
) -> Result<Vec<ResponseItem>, ApiError> {
|
||||
let resp = self
|
||||
.session
|
||||
.execute(Method::POST, Self::path(), extra_headers, Some(body))
|
||||
.execute_with(
|
||||
Method::POST,
|
||||
Self::path(),
|
||||
extra_headers,
|
||||
Some(body),
|
||||
|req| {
|
||||
req.timeout = Some(request_timeout);
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
let parsed: CompactHistoryResponse =
|
||||
serde_json::from_slice(&resp.body).map_err(|e| ApiError::Stream(e.to_string()))?;
|
||||
@@ -51,10 +61,11 @@ impl<T: HttpTransport> CompactClient<T> {
|
||||
&self,
|
||||
input: &CompactionInput<'_>,
|
||||
extra_headers: HeaderMap,
|
||||
request_timeout: Duration,
|
||||
) -> Result<Vec<ResponseItem>, ApiError> {
|
||||
let body = to_value(input)
|
||||
.map_err(|e| ApiError::Stream(format!("failed to encode compaction input: {e}")))?;
|
||||
self.compact(body, extra_headers).await
|
||||
self.compact(body, extra_headers, request_timeout).await
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ pub(crate) mod realtime_call;
|
||||
pub(crate) mod realtime_websocket;
|
||||
pub(crate) mod responses;
|
||||
pub(crate) mod responses_websocket;
|
||||
pub(crate) mod search;
|
||||
mod session;
|
||||
|
||||
pub use compact::CompactClient;
|
||||
@@ -27,3 +28,4 @@ pub use responses_websocket::ResponsesWebsocketClient;
|
||||
pub use responses_websocket::ResponsesWebsocketClose;
|
||||
pub use responses_websocket::ResponsesWebsocketConnection;
|
||||
pub use responses_websocket::ResponsesWebsocketProbe;
|
||||
pub use search::SearchClient;
|
||||
|
||||
@@ -0,0 +1,264 @@
|
||||
use crate::auth::SharedAuthProvider;
|
||||
use crate::endpoint::session::EndpointSession;
|
||||
use crate::error::ApiError;
|
||||
use crate::provider::Provider;
|
||||
use crate::search::SearchRequest;
|
||||
use crate::search::SearchResponse;
|
||||
use codex_client::HttpTransport;
|
||||
use codex_client::RequestTelemetry;
|
||||
use http::HeaderMap;
|
||||
use http::Method;
|
||||
use serde_json::to_value;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct SearchClient<T: HttpTransport> {
|
||||
session: EndpointSession<T>,
|
||||
}
|
||||
|
||||
impl<T: HttpTransport> SearchClient<T> {
|
||||
pub fn new(transport: T, provider: Provider, auth: SharedAuthProvider) -> Self {
|
||||
Self {
|
||||
session: EndpointSession::new(transport, provider, auth),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_telemetry(self, request: Option<Arc<dyn RequestTelemetry>>) -> Self {
|
||||
Self {
|
||||
session: self.session.with_request_telemetry(request),
|
||||
}
|
||||
}
|
||||
|
||||
fn path() -> &'static str {
|
||||
"alpha/search"
|
||||
}
|
||||
|
||||
pub async fn search(
|
||||
&self,
|
||||
request: &SearchRequest,
|
||||
extra_headers: HeaderMap,
|
||||
) -> Result<SearchResponse, ApiError> {
|
||||
let body = to_value(request)
|
||||
.map_err(|e| ApiError::Stream(format!("failed to encode search request: {e}")))?;
|
||||
let resp = self
|
||||
.session
|
||||
.execute(Method::POST, Self::path(), extra_headers, Some(body))
|
||||
.await?;
|
||||
serde_json::from_slice(&resp.body)
|
||||
.map_err(|e| ApiError::Stream(format!("failed to decode search response: {e}")))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::auth::AuthProvider;
|
||||
use crate::provider::RetryConfig;
|
||||
use crate::search::AllowedCaller;
|
||||
use crate::search::ApproximateLocation;
|
||||
use crate::search::LocationType;
|
||||
use crate::search::OpenOperation;
|
||||
use crate::search::SearchCommands;
|
||||
use crate::search::SearchContextSize;
|
||||
use crate::search::SearchFilters;
|
||||
use crate::search::SearchImageSettings;
|
||||
use crate::search::SearchInput;
|
||||
use crate::search::SearchQuery;
|
||||
use crate::search::SearchSettings;
|
||||
use async_trait::async_trait;
|
||||
use codex_client::Request;
|
||||
use codex_client::RequestBody;
|
||||
use codex_client::Response;
|
||||
use codex_client::StreamResponse;
|
||||
use codex_client::TransportError;
|
||||
use codex_protocol::models::ContentItem;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use http::StatusCode;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::json;
|
||||
use std::sync::Mutex;
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
struct DummyAuth;
|
||||
|
||||
impl AuthProvider for DummyAuth {
|
||||
fn add_auth_headers(&self, _headers: &mut HeaderMap) {}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct CapturingTransport {
|
||||
last_request: Arc<Mutex<Option<Request>>>,
|
||||
response_body: Arc<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl CapturingTransport {
|
||||
fn new(response_body: Vec<u8>) -> Self {
|
||||
Self {
|
||||
last_request: Arc::new(Mutex::new(None)),
|
||||
response_body: Arc::new(response_body),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl HttpTransport for CapturingTransport {
|
||||
async fn execute(&self, req: Request) -> Result<Response, TransportError> {
|
||||
*self.last_request.lock().expect("lock request store") = Some(req);
|
||||
Ok(Response {
|
||||
status: StatusCode::OK,
|
||||
headers: HeaderMap::new(),
|
||||
body: self.response_body.as_ref().clone().into(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn stream(&self, _req: Request) -> Result<StreamResponse, TransportError> {
|
||||
Err(TransportError::Build("stream should not run".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
fn provider() -> Provider {
|
||||
Provider {
|
||||
name: "test".to_string(),
|
||||
base_url: "https://example.com/v1".to_string(),
|
||||
query_params: None,
|
||||
headers: HeaderMap::new(),
|
||||
retry: RetryConfig {
|
||||
max_attempts: 1,
|
||||
base_delay: Duration::from_millis(1),
|
||||
retry_429: false,
|
||||
retry_5xx: true,
|
||||
retry_transport: true,
|
||||
},
|
||||
stream_idle_timeout: Duration::from_secs(1),
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn search_posts_typed_request_and_parses_encrypted_output() {
|
||||
let transport = CapturingTransport::new(
|
||||
serde_json::to_vec(&json!({"encrypted_output": "ciphertext"}))
|
||||
.expect("serialize response"),
|
||||
);
|
||||
let client = SearchClient::new(transport.clone(), provider(), Arc::new(DummyAuth));
|
||||
|
||||
let response = client
|
||||
.search(
|
||||
&SearchRequest {
|
||||
id: "search-session".to_string(),
|
||||
model: Some("gpt-test".to_string()),
|
||||
reasoning: None,
|
||||
input: Some(SearchInput::Items(vec![ResponseItem::Message {
|
||||
id: None,
|
||||
role: "user".to_string(),
|
||||
content: vec![
|
||||
ContentItem::InputText {
|
||||
text: "find this".to_string(),
|
||||
},
|
||||
ContentItem::InputImage {
|
||||
image_url: "https://example.com/image.png".to_string(),
|
||||
detail: None,
|
||||
},
|
||||
],
|
||||
phase: None,
|
||||
}])),
|
||||
commands: Some(SearchCommands {
|
||||
search_query: Some(vec![SearchQuery {
|
||||
q: "OpenAI news".to_string(),
|
||||
recency: Some(7),
|
||||
domains: Some(vec!["openai.com".to_string()]),
|
||||
}]),
|
||||
open: Some(vec![OpenOperation {
|
||||
ref_id: "https://openai.com".to_string(),
|
||||
lineno: Some(12),
|
||||
}]),
|
||||
..Default::default()
|
||||
}),
|
||||
settings: Some(SearchSettings {
|
||||
user_location: Some(ApproximateLocation {
|
||||
r#type: LocationType::Approximate,
|
||||
country: Some("US".to_string()),
|
||||
region: None,
|
||||
city: Some("San Francisco".to_string()),
|
||||
timezone: None,
|
||||
}),
|
||||
search_context_size: Some(SearchContextSize::Low),
|
||||
filters: Some(SearchFilters {
|
||||
allowed_domains: Some(vec!["openai.com".to_string()]),
|
||||
blocked_domains: Some(vec!["example.com".to_string()]),
|
||||
}),
|
||||
image_settings: Some(SearchImageSettings {
|
||||
max_results: Some(4),
|
||||
caption: Some(true),
|
||||
}),
|
||||
allowed_callers: Some(vec![AllowedCaller::Direct]),
|
||||
external_web_access: Some(true),
|
||||
}),
|
||||
max_output_tokens: Some(2500),
|
||||
},
|
||||
HeaderMap::new(),
|
||||
)
|
||||
.await
|
||||
.expect("search request should succeed");
|
||||
|
||||
assert_eq!(
|
||||
response,
|
||||
SearchResponse {
|
||||
encrypted_output: "ciphertext".to_string(),
|
||||
}
|
||||
);
|
||||
|
||||
let request = transport
|
||||
.last_request
|
||||
.lock()
|
||||
.expect("lock request store")
|
||||
.clone()
|
||||
.expect("request should be captured");
|
||||
let body = request
|
||||
.body
|
||||
.as_ref()
|
||||
.and_then(RequestBody::json)
|
||||
.expect("request body should be JSON");
|
||||
assert_eq!(
|
||||
body,
|
||||
&json!({
|
||||
"id": "search-session",
|
||||
"model": "gpt-test",
|
||||
"input": [{
|
||||
"type": "message",
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "input_text", "text": "find this"},
|
||||
{
|
||||
"type": "input_image",
|
||||
"image_url": "https://example.com/image.png"
|
||||
}
|
||||
]
|
||||
}],
|
||||
"commands": {
|
||||
"search_query": [{
|
||||
"q": "OpenAI news",
|
||||
"recency": 7,
|
||||
"domains": ["openai.com"]
|
||||
}],
|
||||
"open": [{"ref_id": "https://openai.com", "lineno": 12}]
|
||||
},
|
||||
"settings": {
|
||||
"user_location": {
|
||||
"type": "approximate",
|
||||
"country": "US",
|
||||
"city": "San Francisco"
|
||||
},
|
||||
"search_context_size": "low",
|
||||
"filters": {
|
||||
"allowed_domains": ["openai.com"],
|
||||
"blocked_domains": ["example.com"]
|
||||
},
|
||||
"image_settings": {"max_results": 4, "caption": true},
|
||||
"allowed_callers": ["direct"],
|
||||
"external_web_access": true
|
||||
},
|
||||
"max_output_tokens": 2500
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ pub(crate) mod files;
|
||||
pub(crate) mod provider;
|
||||
pub(crate) mod rate_limits;
|
||||
pub(crate) mod requests;
|
||||
pub(crate) mod search;
|
||||
pub(crate) mod sse;
|
||||
pub(crate) mod telemetry;
|
||||
|
||||
@@ -58,6 +59,7 @@ pub use crate::endpoint::ResponsesWebsocketClient;
|
||||
pub use crate::endpoint::ResponsesWebsocketClose;
|
||||
pub use crate::endpoint::ResponsesWebsocketConnection;
|
||||
pub use crate::endpoint::ResponsesWebsocketProbe;
|
||||
pub use crate::endpoint::SearchClient;
|
||||
pub use crate::endpoint::session_update_session_json;
|
||||
pub use crate::error::ApiError;
|
||||
pub use crate::files::upload_local_file;
|
||||
@@ -65,6 +67,31 @@ pub use crate::provider::Provider;
|
||||
pub use crate::provider::RetryConfig;
|
||||
pub use crate::provider::is_azure_responses_provider;
|
||||
pub use crate::requests::Compression;
|
||||
pub use crate::search::AllowedCaller;
|
||||
pub use crate::search::ApproximateLocation;
|
||||
pub use crate::search::ClickOperation;
|
||||
pub use crate::search::FinanceAssetType;
|
||||
pub use crate::search::FinanceOperation;
|
||||
pub use crate::search::FindOperation;
|
||||
pub use crate::search::LocationType;
|
||||
pub use crate::search::OpenOperation;
|
||||
pub use crate::search::ScreenshotOperation;
|
||||
pub use crate::search::SearchCommands;
|
||||
pub use crate::search::SearchContextSize;
|
||||
pub use crate::search::SearchFilters;
|
||||
pub use crate::search::SearchImageSettings;
|
||||
pub use crate::search::SearchInput;
|
||||
pub use crate::search::SearchQuery;
|
||||
pub use crate::search::SearchRequest;
|
||||
pub use crate::search::SearchResponse;
|
||||
pub use crate::search::SearchResponseLength;
|
||||
pub use crate::search::SearchSettings;
|
||||
pub use crate::search::SportsFunction;
|
||||
pub use crate::search::SportsLeague;
|
||||
pub use crate::search::SportsOperation;
|
||||
pub use crate::search::SportsToolName;
|
||||
pub use crate::search::TimeOperation;
|
||||
pub use crate::search::WeatherOperation;
|
||||
pub use crate::telemetry::SseTelemetry;
|
||||
pub use crate::telemetry::WebsocketTelemetry;
|
||||
pub use codex_protocol::protocol::RealtimeAudioFrame;
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
use crate::common::Reasoning;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
||||
pub struct SearchRequest {
|
||||
pub id: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub model: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub reasoning: Option<Reasoning>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub input: Option<SearchInput>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub commands: Option<SearchCommands>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub settings: Option<SearchSettings>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub max_output_tokens: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
||||
#[serde(untagged)]
|
||||
pub enum SearchInput {
|
||||
Text(String),
|
||||
Items(Vec<ResponseItem>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
|
||||
pub struct SearchCommands {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub search_query: Option<Vec<SearchQuery>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub image_query: Option<Vec<SearchQuery>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub open: Option<Vec<OpenOperation>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub click: Option<Vec<ClickOperation>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub find: Option<Vec<FindOperation>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub screenshot: Option<Vec<ScreenshotOperation>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub finance: Option<Vec<FinanceOperation>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub weather: Option<Vec<WeatherOperation>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub sports: Option<Vec<SportsOperation>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub time: Option<Vec<TimeOperation>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub response_length: Option<SearchResponseLength>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct SearchQuery {
|
||||
pub q: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub recency: Option<u64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub domains: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct OpenOperation {
|
||||
pub ref_id: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub lineno: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct ClickOperation {
|
||||
pub ref_id: String,
|
||||
pub id: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct FindOperation {
|
||||
pub ref_id: String,
|
||||
pub pattern: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct ScreenshotOperation {
|
||||
pub ref_id: String,
|
||||
pub pageno: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct FinanceOperation {
|
||||
pub ticker: String,
|
||||
pub r#type: FinanceAssetType,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub market: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum FinanceAssetType {
|
||||
Equity,
|
||||
Fund,
|
||||
Crypto,
|
||||
Index,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct WeatherOperation {
|
||||
pub location: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub start: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub duration: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct SportsOperation {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub tool: Option<SportsToolName>,
|
||||
pub r#fn: SportsFunction,
|
||||
pub league: SportsLeague,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub team: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub opponent: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub date_from: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub date_to: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub num_games: Option<u64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub locale: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum SportsToolName {
|
||||
Sports,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum SportsFunction {
|
||||
Schedule,
|
||||
Standings,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum SportsLeague {
|
||||
Nba,
|
||||
Wnba,
|
||||
Nfl,
|
||||
Nhl,
|
||||
Mlb,
|
||||
Epl,
|
||||
Ncaamb,
|
||||
Ncaawb,
|
||||
Ipl,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct TimeOperation {
|
||||
pub utc_offset: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum SearchResponseLength {
|
||||
Short,
|
||||
Medium,
|
||||
Long,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
|
||||
pub struct SearchSettings {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub user_location: Option<ApproximateLocation>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub search_context_size: Option<SearchContextSize>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub filters: Option<SearchFilters>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub image_settings: Option<SearchImageSettings>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub allowed_callers: Option<Vec<AllowedCaller>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub external_web_access: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct ApproximateLocation {
|
||||
pub r#type: LocationType,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub country: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub region: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub city: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub timezone: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum LocationType {
|
||||
Approximate,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum SearchContextSize {
|
||||
Low,
|
||||
Medium,
|
||||
High,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
|
||||
pub struct SearchFilters {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub allowed_domains: Option<Vec<String>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub blocked_domains: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
|
||||
pub struct SearchImageSettings {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub max_results: Option<u64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub caption: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum AllowedCaller {
|
||||
Direct,
|
||||
Shell,
|
||||
CodeInterpreter,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
|
||||
pub struct SearchResponse {
|
||||
pub encrypted_output: String,
|
||||
}
|
||||
Reference in New Issue
Block a user