feat: add FIM, files, fine-tuning, batch, OCR, audio, moderation, and agent endpoints
New API endpoints with sync + async support: - FIM (fill-in-the-middle) completions for code completion - Files API: upload, list, get, delete, get URL - Fine-tuning: create, list, get, cancel, start jobs - Batch jobs: create, list, get, cancel - OCR: document text extraction with page/table options - Audio transcription: speech-to-text with diarization - Moderations and classifications: content safety - Agent completions: run pre-configured agents
This commit is contained in:
98
src/v1/agents.rs
Normal file
98
src/v1/agents.rs
Normal file
@@ -0,0 +1,98 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::v1::{chat, common, constants, tool};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Request
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AgentCompletionParams {
|
||||
pub max_tokens: Option<u32>,
|
||||
pub min_tokens: Option<u32>,
|
||||
pub temperature: Option<f32>,
|
||||
pub top_p: Option<f32>,
|
||||
pub random_seed: Option<u32>,
|
||||
pub stop: Option<Vec<String>>,
|
||||
pub response_format: Option<chat::ResponseFormat>,
|
||||
pub tools: Option<Vec<tool::Tool>>,
|
||||
pub tool_choice: Option<tool::ToolChoice>,
|
||||
}
|
||||
impl Default for AgentCompletionParams {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
max_tokens: None,
|
||||
min_tokens: None,
|
||||
temperature: None,
|
||||
top_p: None,
|
||||
random_seed: None,
|
||||
stop: None,
|
||||
response_format: None,
|
||||
tools: None,
|
||||
tool_choice: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct AgentCompletionRequest {
|
||||
pub agent_id: String,
|
||||
pub messages: Vec<chat::ChatMessage>,
|
||||
pub stream: bool,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub max_tokens: Option<u32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub min_tokens: Option<u32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub temperature: Option<f32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub top_p: Option<f32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub random_seed: Option<u32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub stop: Option<Vec<String>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub response_format: Option<chat::ResponseFormat>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub tools: Option<Vec<tool::Tool>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub tool_choice: Option<tool::ToolChoice>,
|
||||
}
|
||||
impl AgentCompletionRequest {
|
||||
pub fn new(
|
||||
agent_id: String,
|
||||
messages: Vec<chat::ChatMessage>,
|
||||
stream: bool,
|
||||
options: Option<AgentCompletionParams>,
|
||||
) -> Self {
|
||||
let opts = options.unwrap_or_default();
|
||||
|
||||
Self {
|
||||
agent_id,
|
||||
messages,
|
||||
stream,
|
||||
max_tokens: opts.max_tokens,
|
||||
min_tokens: opts.min_tokens,
|
||||
temperature: opts.temperature,
|
||||
top_p: opts.top_p,
|
||||
random_seed: opts.random_seed,
|
||||
stop: opts.stop,
|
||||
response_format: opts.response_format,
|
||||
tools: opts.tools,
|
||||
tool_choice: opts.tool_choice,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Response (same shape as chat completions)
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct AgentCompletionResponse {
|
||||
pub id: String,
|
||||
pub object: String,
|
||||
pub created: u64,
|
||||
pub model: constants::Model,
|
||||
pub choices: Vec<chat::ChatResponseChoice>,
|
||||
pub usage: common::ResponseUsage,
|
||||
}
|
||||
78
src/v1/audio.rs
Normal file
78
src/v1/audio.rs
Normal file
@@ -0,0 +1,78 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::v1::constants;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Request (multipart form, but we define the params struct)
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AudioTranscriptionParams {
|
||||
pub model: constants::Model,
|
||||
pub language: Option<String>,
|
||||
pub temperature: Option<f32>,
|
||||
pub diarize: Option<bool>,
|
||||
pub timestamp_granularities: Option<Vec<TimestampGranularity>>,
|
||||
}
|
||||
impl Default for AudioTranscriptionParams {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
model: constants::Model::voxtral_mini_transcribe(),
|
||||
language: None,
|
||||
temperature: None,
|
||||
diarize: None,
|
||||
timestamp_granularities: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum TimestampGranularity {
|
||||
Segment,
|
||||
Word,
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Response
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct AudioTranscriptionResponse {
|
||||
pub text: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub model: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub language: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub segments: Option<Vec<TranscriptionSegment>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub words: Option<Vec<TranscriptionWord>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub usage: Option<AudioUsage>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct TranscriptionSegment {
|
||||
pub id: u32,
|
||||
pub start: f32,
|
||||
pub end: f32,
|
||||
pub text: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub speaker: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct TranscriptionWord {
|
||||
pub word: String,
|
||||
pub start: f32,
|
||||
pub end: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct AudioUsage {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub prompt_audio_seconds: Option<f32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub prompt_tokens: Option<u32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub total_tokens: Option<u32>,
|
||||
}
|
||||
53
src/v1/batch.rs
Normal file
53
src/v1/batch.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::v1::constants;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Request
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct BatchJobRequest {
|
||||
pub input_files: Vec<String>,
|
||||
pub model: constants::Model,
|
||||
pub endpoint: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub metadata: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Response
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct BatchJobResponse {
|
||||
pub id: String,
|
||||
pub object: String,
|
||||
pub model: constants::Model,
|
||||
pub endpoint: String,
|
||||
pub input_files: Vec<String>,
|
||||
pub status: String,
|
||||
pub created_at: u64,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub completed_at: Option<u64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub output_file: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub error_file: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub metadata: Option<serde_json::Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub total_requests: Option<u64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub completed_requests: Option<u64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub succeeded_requests: Option<u64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub failed_requests: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct BatchJobListResponse {
|
||||
pub data: Vec<BatchJobResponse>,
|
||||
pub object: String,
|
||||
#[serde(default)]
|
||||
pub total: u32,
|
||||
}
|
||||
619
src/v1/client.rs
619
src/v1/client.rs
@@ -5,10 +5,14 @@ use reqwest::Error as ReqwestError;
|
||||
use std::{
|
||||
any::Any,
|
||||
collections::HashMap,
|
||||
path::Path,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use crate::v1::{chat, chat_stream, constants, embedding, error, model_list, tool, utils};
|
||||
use crate::v1::{
|
||||
agents, audio, batch, chat, chat_stream, constants, embedding, error, files, fim, fine_tuning,
|
||||
model_list, moderation, ocr, tool, utils,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Client {
|
||||
@@ -200,6 +204,48 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// FIM (Fill-in-the-Middle)
|
||||
// =========================================================================
|
||||
|
||||
pub fn fim(
|
||||
&self,
|
||||
model: constants::Model,
|
||||
prompt: String,
|
||||
options: Option<fim::FimParams>,
|
||||
) -> Result<fim::FimResponse, error::ApiError> {
|
||||
let request = fim::FimRequest::new(model, prompt, false, options);
|
||||
|
||||
let response = self.post_sync("/fim/completions", &request)?;
|
||||
let result = response.json::<fim::FimResponse>();
|
||||
match result {
|
||||
Ok(data) => {
|
||||
utils::debug_pretty_json_from_struct("Response Data", &data);
|
||||
Ok(data)
|
||||
}
|
||||
Err(error) => Err(self.to_api_error(error)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn fim_async(
|
||||
&self,
|
||||
model: constants::Model,
|
||||
prompt: String,
|
||||
options: Option<fim::FimParams>,
|
||||
) -> Result<fim::FimResponse, error::ApiError> {
|
||||
let request = fim::FimRequest::new(model, prompt, false, options);
|
||||
|
||||
let response = self.post_async("/fim/completions", &request).await?;
|
||||
let result = response.json::<fim::FimResponse>().await;
|
||||
match result {
|
||||
Ok(data) => {
|
||||
utils::debug_pretty_json_from_struct("Response Data", &data);
|
||||
Ok(data)
|
||||
}
|
||||
Err(error) => Err(self.to_api_error(error)),
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Models
|
||||
// =========================================================================
|
||||
@@ -283,6 +329,577 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Files
|
||||
// =========================================================================
|
||||
|
||||
pub fn list_files(&self) -> Result<files::FileListResponse, error::ApiError> {
|
||||
let response = self.get_sync("/files")?;
|
||||
let result = response.json::<files::FileListResponse>();
|
||||
match result {
|
||||
Ok(data) => Ok(data),
|
||||
Err(error) => Err(self.to_api_error(error)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn list_files_async(&self) -> Result<files::FileListResponse, error::ApiError> {
|
||||
let response = self.get_async("/files").await?;
|
||||
let result = response.json::<files::FileListResponse>().await;
|
||||
match result {
|
||||
Ok(data) => Ok(data),
|
||||
Err(error) => Err(self.to_api_error(error)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn upload_file(
|
||||
&self,
|
||||
file_path: &Path,
|
||||
purpose: files::FilePurpose,
|
||||
) -> Result<files::FileObject, error::ApiError> {
|
||||
let reqwest_client = reqwest::blocking::Client::new();
|
||||
let url = format!("{}/files", self.endpoint);
|
||||
|
||||
let purpose_str = serde_json::to_value(&purpose)
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
let file_bytes = std::fs::read(file_path).map_err(|e| error::ApiError {
|
||||
message: format!("Failed to read file: {}", e),
|
||||
})?;
|
||||
|
||||
let file_name = file_path
|
||||
.file_name()
|
||||
.unwrap_or_default()
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
let part = reqwest::blocking::multipart::Part::bytes(file_bytes).file_name(file_name);
|
||||
|
||||
let form = reqwest::blocking::multipart::Form::new()
|
||||
.text("purpose", purpose_str)
|
||||
.part("file", part);
|
||||
|
||||
let request = self.build_request_sync_no_accept(reqwest_client.post(url).multipart(form));
|
||||
let result = request.send();
|
||||
match result {
|
||||
Ok(response) => {
|
||||
if response.status().is_success() {
|
||||
response
|
||||
.json::<files::FileObject>()
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
} else {
|
||||
let status = response.status();
|
||||
let body = response.text().unwrap_or_default();
|
||||
Err(error::ApiError {
|
||||
message: format!("{}: {}", status, body),
|
||||
})
|
||||
}
|
||||
}
|
||||
Err(error) => Err(error::ApiError {
|
||||
message: error.to_string(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn upload_file_async(
|
||||
&self,
|
||||
file_path: &Path,
|
||||
purpose: files::FilePurpose,
|
||||
) -> Result<files::FileObject, error::ApiError> {
|
||||
let reqwest_client = reqwest::Client::new();
|
||||
let url = format!("{}/files", self.endpoint);
|
||||
|
||||
let purpose_str = serde_json::to_value(&purpose)
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
let file_bytes = tokio::fs::read(file_path).await.map_err(|e| error::ApiError {
|
||||
message: format!("Failed to read file: {}", e),
|
||||
})?;
|
||||
|
||||
let file_name = file_path
|
||||
.file_name()
|
||||
.unwrap_or_default()
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
let part = reqwest::multipart::Part::bytes(file_bytes).file_name(file_name);
|
||||
|
||||
let form = reqwest::multipart::Form::new()
|
||||
.text("purpose", purpose_str)
|
||||
.part("file", part);
|
||||
|
||||
let request = self.build_request_async_no_accept(reqwest_client.post(url).multipart(form));
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(response) => {
|
||||
if response.status().is_success() {
|
||||
response
|
||||
.json::<files::FileObject>()
|
||||
.await
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
} else {
|
||||
let status = response.status();
|
||||
let body = response.text().await.unwrap_or_default();
|
||||
Err(error::ApiError {
|
||||
message: format!("{}: {}", status, body),
|
||||
})
|
||||
}
|
||||
}
|
||||
Err(error) => Err(error::ApiError {
|
||||
message: error.to_string(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_file(&self, file_id: &str) -> Result<files::FileObject, error::ApiError> {
|
||||
let response = self.get_sync(&format!("/files/{}", file_id))?;
|
||||
response
|
||||
.json::<files::FileObject>()
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub async fn get_file_async(
|
||||
&self,
|
||||
file_id: &str,
|
||||
) -> Result<files::FileObject, error::ApiError> {
|
||||
let response = self.get_async(&format!("/files/{}", file_id)).await?;
|
||||
response
|
||||
.json::<files::FileObject>()
|
||||
.await
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub fn delete_file(&self, file_id: &str) -> Result<files::FileDeleteResponse, error::ApiError> {
|
||||
let response = self.delete_sync(&format!("/files/{}", file_id))?;
|
||||
response
|
||||
.json::<files::FileDeleteResponse>()
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub async fn delete_file_async(
|
||||
&self,
|
||||
file_id: &str,
|
||||
) -> Result<files::FileDeleteResponse, error::ApiError> {
|
||||
let response = self.delete_async(&format!("/files/{}", file_id)).await?;
|
||||
response
|
||||
.json::<files::FileDeleteResponse>()
|
||||
.await
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub fn get_file_url(&self, file_id: &str) -> Result<files::FileUrlResponse, error::ApiError> {
|
||||
let response = self.get_sync(&format!("/files/{}/url", file_id))?;
|
||||
response
|
||||
.json::<files::FileUrlResponse>()
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub async fn get_file_url_async(
|
||||
&self,
|
||||
file_id: &str,
|
||||
) -> Result<files::FileUrlResponse, error::ApiError> {
|
||||
let response = self.get_async(&format!("/files/{}/url", file_id)).await?;
|
||||
response
|
||||
.json::<files::FileUrlResponse>()
|
||||
.await
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Fine-Tuning
|
||||
// =========================================================================
|
||||
|
||||
pub fn create_fine_tuning_job(
|
||||
&self,
|
||||
request: &fine_tuning::FineTuningJobRequest,
|
||||
) -> Result<fine_tuning::FineTuningJobResponse, error::ApiError> {
|
||||
let response = self.post_sync("/fine_tuning/jobs", request)?;
|
||||
response
|
||||
.json::<fine_tuning::FineTuningJobResponse>()
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub async fn create_fine_tuning_job_async(
|
||||
&self,
|
||||
request: &fine_tuning::FineTuningJobRequest,
|
||||
) -> Result<fine_tuning::FineTuningJobResponse, error::ApiError> {
|
||||
let response = self.post_async("/fine_tuning/jobs", request).await?;
|
||||
response
|
||||
.json::<fine_tuning::FineTuningJobResponse>()
|
||||
.await
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub fn list_fine_tuning_jobs(
|
||||
&self,
|
||||
) -> Result<fine_tuning::FineTuningJobListResponse, error::ApiError> {
|
||||
let response = self.get_sync("/fine_tuning/jobs")?;
|
||||
response
|
||||
.json::<fine_tuning::FineTuningJobListResponse>()
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub async fn list_fine_tuning_jobs_async(
|
||||
&self,
|
||||
) -> Result<fine_tuning::FineTuningJobListResponse, error::ApiError> {
|
||||
let response = self.get_async("/fine_tuning/jobs").await?;
|
||||
response
|
||||
.json::<fine_tuning::FineTuningJobListResponse>()
|
||||
.await
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub fn get_fine_tuning_job(
|
||||
&self,
|
||||
job_id: &str,
|
||||
) -> Result<fine_tuning::FineTuningJobResponse, error::ApiError> {
|
||||
let response = self.get_sync(&format!("/fine_tuning/jobs/{}", job_id))?;
|
||||
response
|
||||
.json::<fine_tuning::FineTuningJobResponse>()
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub async fn get_fine_tuning_job_async(
|
||||
&self,
|
||||
job_id: &str,
|
||||
) -> Result<fine_tuning::FineTuningJobResponse, error::ApiError> {
|
||||
let response = self
|
||||
.get_async(&format!("/fine_tuning/jobs/{}", job_id))
|
||||
.await?;
|
||||
response
|
||||
.json::<fine_tuning::FineTuningJobResponse>()
|
||||
.await
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub fn cancel_fine_tuning_job(
|
||||
&self,
|
||||
job_id: &str,
|
||||
) -> Result<fine_tuning::FineTuningJobResponse, error::ApiError> {
|
||||
let response = self.post_sync_empty(&format!("/fine_tuning/jobs/{}/cancel", job_id))?;
|
||||
response
|
||||
.json::<fine_tuning::FineTuningJobResponse>()
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub async fn cancel_fine_tuning_job_async(
|
||||
&self,
|
||||
job_id: &str,
|
||||
) -> Result<fine_tuning::FineTuningJobResponse, error::ApiError> {
|
||||
let response = self
|
||||
.post_async_empty(&format!("/fine_tuning/jobs/{}/cancel", job_id))
|
||||
.await?;
|
||||
response
|
||||
.json::<fine_tuning::FineTuningJobResponse>()
|
||||
.await
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub fn start_fine_tuning_job(
|
||||
&self,
|
||||
job_id: &str,
|
||||
) -> Result<fine_tuning::FineTuningJobResponse, error::ApiError> {
|
||||
let response = self.post_sync_empty(&format!("/fine_tuning/jobs/{}/start", job_id))?;
|
||||
response
|
||||
.json::<fine_tuning::FineTuningJobResponse>()
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub async fn start_fine_tuning_job_async(
|
||||
&self,
|
||||
job_id: &str,
|
||||
) -> Result<fine_tuning::FineTuningJobResponse, error::ApiError> {
|
||||
let response = self
|
||||
.post_async_empty(&format!("/fine_tuning/jobs/{}/start", job_id))
|
||||
.await?;
|
||||
response
|
||||
.json::<fine_tuning::FineTuningJobResponse>()
|
||||
.await
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Batch Jobs
|
||||
// =========================================================================
|
||||
|
||||
pub fn create_batch_job(
|
||||
&self,
|
||||
request: &batch::BatchJobRequest,
|
||||
) -> Result<batch::BatchJobResponse, error::ApiError> {
|
||||
let response = self.post_sync("/batch/jobs", request)?;
|
||||
response
|
||||
.json::<batch::BatchJobResponse>()
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub async fn create_batch_job_async(
|
||||
&self,
|
||||
request: &batch::BatchJobRequest,
|
||||
) -> Result<batch::BatchJobResponse, error::ApiError> {
|
||||
let response = self.post_async("/batch/jobs", request).await?;
|
||||
response
|
||||
.json::<batch::BatchJobResponse>()
|
||||
.await
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub fn list_batch_jobs(&self) -> Result<batch::BatchJobListResponse, error::ApiError> {
|
||||
let response = self.get_sync("/batch/jobs")?;
|
||||
response
|
||||
.json::<batch::BatchJobListResponse>()
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub async fn list_batch_jobs_async(
|
||||
&self,
|
||||
) -> Result<batch::BatchJobListResponse, error::ApiError> {
|
||||
let response = self.get_async("/batch/jobs").await?;
|
||||
response
|
||||
.json::<batch::BatchJobListResponse>()
|
||||
.await
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub fn get_batch_job(
|
||||
&self,
|
||||
job_id: &str,
|
||||
) -> Result<batch::BatchJobResponse, error::ApiError> {
|
||||
let response = self.get_sync(&format!("/batch/jobs/{}", job_id))?;
|
||||
response
|
||||
.json::<batch::BatchJobResponse>()
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub async fn get_batch_job_async(
|
||||
&self,
|
||||
job_id: &str,
|
||||
) -> Result<batch::BatchJobResponse, error::ApiError> {
|
||||
let response = self
|
||||
.get_async(&format!("/batch/jobs/{}", job_id))
|
||||
.await?;
|
||||
response
|
||||
.json::<batch::BatchJobResponse>()
|
||||
.await
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub fn cancel_batch_job(
|
||||
&self,
|
||||
job_id: &str,
|
||||
) -> Result<batch::BatchJobResponse, error::ApiError> {
|
||||
let response = self.post_sync_empty(&format!("/batch/jobs/{}/cancel", job_id))?;
|
||||
response
|
||||
.json::<batch::BatchJobResponse>()
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub async fn cancel_batch_job_async(
|
||||
&self,
|
||||
job_id: &str,
|
||||
) -> Result<batch::BatchJobResponse, error::ApiError> {
|
||||
let response = self
|
||||
.post_async_empty(&format!("/batch/jobs/{}/cancel", job_id))
|
||||
.await?;
|
||||
response
|
||||
.json::<batch::BatchJobResponse>()
|
||||
.await
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// OCR
|
||||
// =========================================================================
|
||||
|
||||
pub fn ocr(
|
||||
&self,
|
||||
request: &ocr::OcrRequest,
|
||||
) -> Result<ocr::OcrResponse, error::ApiError> {
|
||||
let response = self.post_sync("/ocr", request)?;
|
||||
response
|
||||
.json::<ocr::OcrResponse>()
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub async fn ocr_async(
|
||||
&self,
|
||||
request: &ocr::OcrRequest,
|
||||
) -> Result<ocr::OcrResponse, error::ApiError> {
|
||||
let response = self.post_async("/ocr", request).await?;
|
||||
response
|
||||
.json::<ocr::OcrResponse>()
|
||||
.await
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Audio Transcription
|
||||
// =========================================================================
|
||||
|
||||
pub async fn transcribe_audio_async(
|
||||
&self,
|
||||
file_path: &Path,
|
||||
params: Option<audio::AudioTranscriptionParams>,
|
||||
) -> Result<audio::AudioTranscriptionResponse, error::ApiError> {
|
||||
let opts = params.unwrap_or_default();
|
||||
let reqwest_client = reqwest::Client::new();
|
||||
let url = format!("{}/audio/transcriptions", self.endpoint);
|
||||
|
||||
let file_bytes = tokio::fs::read(file_path).await.map_err(|e| error::ApiError {
|
||||
message: format!("Failed to read file: {}", e),
|
||||
})?;
|
||||
|
||||
let file_name = file_path
|
||||
.file_name()
|
||||
.unwrap_or_default()
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
let part = reqwest::multipart::Part::bytes(file_bytes).file_name(file_name);
|
||||
|
||||
let mut form = reqwest::multipart::Form::new()
|
||||
.text("model", opts.model.0)
|
||||
.part("file", part);
|
||||
|
||||
if let Some(lang) = opts.language {
|
||||
form = form.text("language", lang);
|
||||
}
|
||||
if let Some(temp) = opts.temperature {
|
||||
form = form.text("temperature", temp.to_string());
|
||||
}
|
||||
if let Some(diarize) = opts.diarize {
|
||||
form = form.text("diarize", diarize.to_string());
|
||||
}
|
||||
|
||||
let request = self.build_request_async_no_accept(reqwest_client.post(url).multipart(form));
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(response) => {
|
||||
if response.status().is_success() {
|
||||
response
|
||||
.json::<audio::AudioTranscriptionResponse>()
|
||||
.await
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
} else {
|
||||
let status = response.status();
|
||||
let body = response.text().await.unwrap_or_default();
|
||||
Err(error::ApiError {
|
||||
message: format!("{}: {}", status, body),
|
||||
})
|
||||
}
|
||||
}
|
||||
Err(error) => Err(error::ApiError {
|
||||
message: error.to_string(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Moderations & Classifications
|
||||
// =========================================================================
|
||||
|
||||
pub fn moderations(
|
||||
&self,
|
||||
model: constants::Model,
|
||||
input: Vec<String>,
|
||||
) -> Result<moderation::ModerationResponse, error::ApiError> {
|
||||
let request = moderation::ModerationRequest::new(model, input);
|
||||
let response = self.post_sync("/moderations", &request)?;
|
||||
response
|
||||
.json::<moderation::ModerationResponse>()
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub async fn moderations_async(
|
||||
&self,
|
||||
model: constants::Model,
|
||||
input: Vec<String>,
|
||||
) -> Result<moderation::ModerationResponse, error::ApiError> {
|
||||
let request = moderation::ModerationRequest::new(model, input);
|
||||
let response = self.post_async("/moderations", &request).await?;
|
||||
response
|
||||
.json::<moderation::ModerationResponse>()
|
||||
.await
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub fn chat_moderations(
|
||||
&self,
|
||||
request: &moderation::ChatModerationRequest,
|
||||
) -> Result<moderation::ModerationResponse, error::ApiError> {
|
||||
let response = self.post_sync("/chat/moderations", request)?;
|
||||
response
|
||||
.json::<moderation::ModerationResponse>()
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub async fn chat_moderations_async(
|
||||
&self,
|
||||
request: &moderation::ChatModerationRequest,
|
||||
) -> Result<moderation::ModerationResponse, error::ApiError> {
|
||||
let response = self.post_async("/chat/moderations", request).await?;
|
||||
response
|
||||
.json::<moderation::ModerationResponse>()
|
||||
.await
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub fn classifications(
|
||||
&self,
|
||||
request: &moderation::ClassificationRequest,
|
||||
) -> Result<moderation::ClassificationResponse, error::ApiError> {
|
||||
let response = self.post_sync("/classifications", request)?;
|
||||
response
|
||||
.json::<moderation::ClassificationResponse>()
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub async fn classifications_async(
|
||||
&self,
|
||||
request: &moderation::ClassificationRequest,
|
||||
) -> Result<moderation::ClassificationResponse, error::ApiError> {
|
||||
let response = self.post_async("/classifications", request).await?;
|
||||
response
|
||||
.json::<moderation::ClassificationResponse>()
|
||||
.await
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Agent Completions
|
||||
// =========================================================================
|
||||
|
||||
pub fn agent_completion(
|
||||
&self,
|
||||
agent_id: String,
|
||||
messages: Vec<chat::ChatMessage>,
|
||||
options: Option<agents::AgentCompletionParams>,
|
||||
) -> Result<agents::AgentCompletionResponse, error::ApiError> {
|
||||
let request = agents::AgentCompletionRequest::new(agent_id, messages, false, options);
|
||||
let response = self.post_sync("/agents/completions", &request)?;
|
||||
response
|
||||
.json::<agents::AgentCompletionResponse>()
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
pub async fn agent_completion_async(
|
||||
&self,
|
||||
agent_id: String,
|
||||
messages: Vec<chat::ChatMessage>,
|
||||
options: Option<agents::AgentCompletionParams>,
|
||||
) -> Result<agents::AgentCompletionResponse, error::ApiError> {
|
||||
let request = agents::AgentCompletionRequest::new(agent_id, messages, false, options);
|
||||
let response = self.post_async("/agents/completions", &request).await?;
|
||||
response
|
||||
.json::<agents::AgentCompletionResponse>()
|
||||
.await
|
||||
.map_err(|e| self.to_api_error(e))
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Function Calling
|
||||
// =========================================================================
|
||||
|
||||
55
src/v1/files.rs
Normal file
55
src/v1/files.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Request
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum FilePurpose {
|
||||
FineTune,
|
||||
Batch,
|
||||
Ocr,
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Response
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct FileListResponse {
|
||||
pub data: Vec<FileObject>,
|
||||
pub object: String,
|
||||
#[serde(default)]
|
||||
pub total: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct FileObject {
|
||||
pub id: String,
|
||||
pub object: String,
|
||||
pub bytes: u64,
|
||||
pub created_at: u64,
|
||||
pub filename: String,
|
||||
pub purpose: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub sample_type: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub source: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub num_lines: Option<u64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub mimetype: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct FileDeleteResponse {
|
||||
pub id: String,
|
||||
pub object: String,
|
||||
pub deleted: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct FileUrlResponse {
|
||||
pub url: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub expires_at: Option<u64>,
|
||||
}
|
||||
101
src/v1/fim.rs
Normal file
101
src/v1/fim.rs
Normal file
@@ -0,0 +1,101 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::v1::{common, constants};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Request
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FimParams {
|
||||
pub suffix: Option<String>,
|
||||
pub max_tokens: Option<u32>,
|
||||
pub min_tokens: Option<u32>,
|
||||
pub temperature: Option<f32>,
|
||||
pub top_p: Option<f32>,
|
||||
pub stop: Option<Vec<String>>,
|
||||
pub random_seed: Option<u32>,
|
||||
}
|
||||
impl Default for FimParams {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
suffix: None,
|
||||
max_tokens: None,
|
||||
min_tokens: None,
|
||||
temperature: None,
|
||||
top_p: None,
|
||||
stop: None,
|
||||
random_seed: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct FimRequest {
|
||||
pub model: constants::Model,
|
||||
pub prompt: String,
|
||||
pub stream: bool,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub suffix: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub max_tokens: Option<u32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub min_tokens: Option<u32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub temperature: Option<f32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub top_p: Option<f32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub stop: Option<Vec<String>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub random_seed: Option<u32>,
|
||||
}
|
||||
impl FimRequest {
|
||||
pub fn new(
|
||||
model: constants::Model,
|
||||
prompt: String,
|
||||
stream: bool,
|
||||
options: Option<FimParams>,
|
||||
) -> Self {
|
||||
let opts = options.unwrap_or_default();
|
||||
|
||||
Self {
|
||||
model,
|
||||
prompt,
|
||||
stream,
|
||||
suffix: opts.suffix,
|
||||
max_tokens: opts.max_tokens,
|
||||
min_tokens: opts.min_tokens,
|
||||
temperature: opts.temperature,
|
||||
top_p: opts.top_p,
|
||||
stop: opts.stop,
|
||||
random_seed: opts.random_seed,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Response
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct FimResponse {
|
||||
pub id: String,
|
||||
pub object: String,
|
||||
pub created: u64,
|
||||
pub model: constants::Model,
|
||||
pub choices: Vec<FimResponseChoice>,
|
||||
pub usage: common::ResponseUsage,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct FimResponseChoice {
|
||||
pub index: u32,
|
||||
pub message: FimResponseMessage,
|
||||
pub finish_reason: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct FimResponseMessage {
|
||||
pub role: String,
|
||||
pub content: String,
|
||||
}
|
||||
101
src/v1/fine_tuning.rs
Normal file
101
src/v1/fine_tuning.rs
Normal file
@@ -0,0 +1,101 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::v1::constants;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Request
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct FineTuningJobRequest {
|
||||
pub model: constants::Model,
|
||||
pub training_files: Vec<TrainingFile>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub validation_files: Option<Vec<String>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub hyperparameters: Option<Hyperparameters>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub suffix: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub auto_start: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub job_type: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub integrations: Option<Vec<Integration>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct TrainingFile {
|
||||
pub file_id: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub weight: Option<f32>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Hyperparameters {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub learning_rate: Option<f64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub training_steps: Option<u32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub warmup_fraction: Option<f64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub epochs: Option<f64>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Integration {
|
||||
pub r#type: String,
|
||||
pub project: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub name: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub api_key: Option<String>,
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Response
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct FineTuningJobResponse {
|
||||
pub id: String,
|
||||
pub object: String,
|
||||
pub model: constants::Model,
|
||||
pub status: FineTuningJobStatus,
|
||||
pub created_at: u64,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub modified_at: Option<u64>,
|
||||
pub training_files: Vec<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub validation_files: Option<Vec<String>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub hyperparameters: Option<Hyperparameters>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub fine_tuned_model: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub suffix: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub integrations: Option<Vec<Integration>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub trained_tokens: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum FineTuningJobStatus {
|
||||
Queued,
|
||||
Running,
|
||||
Success,
|
||||
Failed,
|
||||
TimeoutExceeded,
|
||||
CancellationRequested,
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct FineTuningJobListResponse {
|
||||
pub data: Vec<FineTuningJobResponse>,
|
||||
pub object: String,
|
||||
#[serde(default)]
|
||||
pub total: u32,
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
pub mod agents;
|
||||
pub mod audio;
|
||||
pub mod batch;
|
||||
pub mod chat;
|
||||
pub mod chat_stream;
|
||||
pub mod client;
|
||||
@@ -5,6 +8,11 @@ pub mod common;
|
||||
pub mod constants;
|
||||
pub mod embedding;
|
||||
pub mod error;
|
||||
pub mod files;
|
||||
pub mod fim;
|
||||
pub mod fine_tuning;
|
||||
pub mod model_list;
|
||||
pub mod moderation;
|
||||
pub mod ocr;
|
||||
pub mod tool;
|
||||
pub mod utils;
|
||||
|
||||
70
src/v1/moderation.rs
Normal file
70
src/v1/moderation.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::v1::constants;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Request
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ModerationRequest {
|
||||
pub model: constants::Model,
|
||||
pub input: Vec<String>,
|
||||
}
|
||||
impl ModerationRequest {
|
||||
pub fn new(model: constants::Model, input: Vec<String>) -> Self {
|
||||
Self { model, input }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ChatModerationRequest {
|
||||
pub model: constants::Model,
|
||||
pub input: Vec<ChatModerationInput>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ChatModerationInput {
|
||||
pub role: String,
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ClassificationRequest {
|
||||
pub model: constants::Model,
|
||||
pub input: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ChatClassificationRequest {
|
||||
pub model: constants::Model,
|
||||
pub input: Vec<ChatModerationInput>,
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Response
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct ModerationResponse {
|
||||
pub id: String,
|
||||
pub model: constants::Model,
|
||||
pub results: Vec<ModerationResult>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct ModerationResult {
|
||||
pub categories: serde_json::Value,
|
||||
pub category_scores: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct ClassificationResponse {
|
||||
pub id: String,
|
||||
pub model: constants::Model,
|
||||
pub results: Vec<ClassificationResult>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct ClassificationResult {
|
||||
pub categories: serde_json::Value,
|
||||
pub category_scores: serde_json::Value,
|
||||
}
|
||||
96
src/v1/ocr.rs
Normal file
96
src/v1/ocr.rs
Normal file
@@ -0,0 +1,96 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::v1::constants;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Request
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct OcrRequest {
|
||||
pub model: constants::Model,
|
||||
pub document: OcrDocument,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub pages: Option<Vec<u32>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub table_format: Option<OcrTableFormat>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub include_image_base64: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub image_limit: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct OcrDocument {
|
||||
#[serde(rename = "type")]
|
||||
pub type_: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub document_url: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub file_id: Option<String>,
|
||||
}
|
||||
impl OcrDocument {
|
||||
pub fn from_url(url: &str) -> Self {
|
||||
Self {
|
||||
type_: "document_url".to_string(),
|
||||
document_url: Some(url.to_string()),
|
||||
file_id: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_file_id(file_id: &str) -> Self {
|
||||
Self {
|
||||
type_: "file_id".to_string(),
|
||||
document_url: None,
|
||||
file_id: Some(file_id.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum OcrTableFormat {
|
||||
Markdown,
|
||||
Html,
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Response
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct OcrResponse {
|
||||
pub pages: Vec<OcrPage>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub usage_info: Option<OcrUsageInfo>,
|
||||
pub model: constants::Model,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct OcrPage {
|
||||
pub index: u32,
|
||||
pub markdown: String,
|
||||
#[serde(default)]
|
||||
pub images: Vec<OcrImage>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub dimensions: Option<OcrPageDimensions>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct OcrImage {
|
||||
pub id: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub image_base64: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct OcrPageDimensions {
|
||||
pub width: f32,
|
||||
pub height: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct OcrUsageInfo {
|
||||
pub pages_processed: u32,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub doc_size_bytes: Option<u64>,
|
||||
}
|
||||
Reference in New Issue
Block a user