feat: initial commit — antigravity proxy with MITM, standalone LS, and snapshot tooling
This commit is contained in:
241
src/api/types.rs
Normal file
241
src/api/types.rs
Normal file
@@ -0,0 +1,241 @@
|
||||
//! Request/response types for the OpenAI-compatible API.
|
||||
//!
|
||||
//! All response shapes strictly match the official OpenAI Responses API spec:
|
||||
//! https://platform.openai.com/docs/api-reference/responses
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// ─── Request types ───────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub(crate) struct ResponsesRequest {
|
||||
pub model: Option<String>,
|
||||
pub input: serde_json::Value,
|
||||
#[serde(default)]
|
||||
pub instructions: Option<String>,
|
||||
#[serde(default)]
|
||||
pub stream: bool,
|
||||
#[serde(default = "default_timeout")]
|
||||
pub timeout: u64,
|
||||
pub conversation: Option<serde_json::Value>,
|
||||
#[serde(default = "default_true")]
|
||||
pub store: bool,
|
||||
#[serde(default)]
|
||||
pub temperature: Option<f64>,
|
||||
#[serde(default)]
|
||||
pub top_p: Option<f64>,
|
||||
#[serde(default)]
|
||||
pub max_output_tokens: Option<u64>,
|
||||
#[serde(default)]
|
||||
pub previous_response_id: Option<String>,
|
||||
#[serde(default)]
|
||||
pub metadata: Option<serde_json::Value>,
|
||||
#[serde(default)]
|
||||
pub user: Option<String>,
|
||||
}
|
||||
|
||||
/// Chat Completions request (OpenAI-compatible).
|
||||
#[derive(Deserialize)]
|
||||
pub(crate) struct CompletionRequest {
|
||||
pub model: Option<String>,
|
||||
pub messages: Vec<CompletionMessage>,
|
||||
#[serde(default)]
|
||||
pub stream: bool,
|
||||
#[serde(default = "default_timeout")]
|
||||
pub timeout: u64,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub(crate) struct CompletionMessage {
|
||||
pub role: String,
|
||||
pub content: serde_json::Value,
|
||||
}
|
||||
|
||||
fn default_timeout() -> u64 {
|
||||
120
|
||||
}
|
||||
|
||||
fn default_true() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
// ─── Response types (official OpenAI Responses API shape) ────────────────────
|
||||
|
||||
/// Top-level Response object — matches OpenAI exactly.
|
||||
#[derive(Serialize, Clone)]
|
||||
pub(crate) struct ResponsesResponse {
|
||||
pub id: String,
|
||||
pub object: &'static str,
|
||||
pub created_at: u64,
|
||||
pub status: &'static str,
|
||||
#[serde(serialize_with = "serialize_option_u64")]
|
||||
pub completed_at: Option<u64>,
|
||||
pub error: Option<serde_json::Value>,
|
||||
pub incomplete_details: Option<serde_json::Value>,
|
||||
pub instructions: Option<String>,
|
||||
#[serde(serialize_with = "serialize_option_u64")]
|
||||
pub max_output_tokens: Option<u64>,
|
||||
pub model: String,
|
||||
pub output: Vec<ResponseOutput>,
|
||||
pub parallel_tool_calls: bool,
|
||||
pub previous_response_id: Option<String>,
|
||||
pub reasoning: Reasoning,
|
||||
pub store: bool,
|
||||
pub temperature: f64,
|
||||
pub text: TextFormat,
|
||||
pub tool_choice: &'static str,
|
||||
pub tools: Vec<serde_json::Value>,
|
||||
pub top_p: f64,
|
||||
pub truncation: &'static str,
|
||||
pub usage: Option<Usage>,
|
||||
pub user: Option<String>,
|
||||
pub metadata: serde_json::Value,
|
||||
/// Proxy extension: opaque thinking verification signature.
|
||||
/// Present for all models. Required for multi-turn chaining with thinking models.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub thinking_signature: Option<String>,
|
||||
/// Proxy extension: the model's internal reasoning/thinking content.
|
||||
/// Available for all models (Opus, Gemini Flash, Gemini Pro).
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub thinking: Option<String>,
|
||||
/// Proxy extension: time spent thinking (e.g. "0.041999832s").
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub thinking_duration: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
pub(crate) struct ResponseOutput {
|
||||
#[serde(rename = "type")]
|
||||
pub output_type: &'static str,
|
||||
pub id: String,
|
||||
pub status: &'static str,
|
||||
pub role: &'static str,
|
||||
pub content: Vec<OutputContent>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
pub(crate) struct OutputContent {
|
||||
#[serde(rename = "type")]
|
||||
pub content_type: &'static str,
|
||||
pub text: String,
|
||||
pub annotations: Vec<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
pub(crate) struct Usage {
|
||||
pub input_tokens: u64,
|
||||
pub input_tokens_details: InputTokensDetails,
|
||||
pub output_tokens: u64,
|
||||
pub output_tokens_details: OutputTokensDetails,
|
||||
pub total_tokens: u64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
pub(crate) struct InputTokensDetails {
|
||||
pub cached_tokens: u64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
pub(crate) struct OutputTokensDetails {
|
||||
pub reasoning_tokens: u64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
pub(crate) struct Reasoning {
|
||||
pub effort: Option<String>,
|
||||
pub summary: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
pub(crate) struct TextFormat {
|
||||
pub format: TextFormatInner,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
pub(crate) struct TextFormatInner {
|
||||
#[serde(rename = "type")]
|
||||
pub format_type: &'static str,
|
||||
}
|
||||
|
||||
impl Usage {
|
||||
/// Estimate token counts from actual text.
|
||||
/// Uses ~4 chars/token heuristic (standard GPT tokenizer average).
|
||||
pub fn estimate(input_text: &str, output_text: &str) -> Self {
|
||||
let input_tokens = (input_text.len() as u64 + 3) / 4;
|
||||
let output_tokens = (output_text.len() as u64 + 3) / 4;
|
||||
Self {
|
||||
input_tokens,
|
||||
input_tokens_details: InputTokensDetails { cached_tokens: 0 },
|
||||
output_tokens,
|
||||
output_tokens_details: OutputTokensDetails {
|
||||
reasoning_tokens: 0,
|
||||
},
|
||||
total_tokens: input_tokens + output_tokens,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Usage {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
input_tokens: 0,
|
||||
input_tokens_details: InputTokensDetails { cached_tokens: 0 },
|
||||
output_tokens: 0,
|
||||
output_tokens_details: OutputTokensDetails {
|
||||
reasoning_tokens: 0,
|
||||
},
|
||||
total_tokens: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Reasoning {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
effort: None,
|
||||
summary: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TextFormat {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
format: TextFormatInner {
|
||||
format_type: "text",
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Serialize Option<u64> as either the number or JSON null (not omitted).
|
||||
fn serialize_option_u64<S>(val: &Option<u64>, s: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
match val {
|
||||
Some(v) => s.serialize_u64(*v),
|
||||
None => s.serialize_none(),
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Shared types ────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub(crate) struct TokenRequest {
|
||||
pub token: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub(crate) struct ErrorResponse {
|
||||
pub error: ErrorDetail,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub(crate) struct ErrorDetail {
|
||||
pub message: String,
|
||||
#[serde(rename = "type")]
|
||||
pub error_type: String,
|
||||
}
|
||||
Reference in New Issue
Block a user