use std::time::Duration;
use crate::core::Transport;
use crate::error::BigRagError;
use crate::files::FileInput;
use crate::resources::collections::Collections;
use crate::resources::documents::Documents;
use crate::resources::queries::Queries;
use crate::resources::vectors::Vectors;
use crate::resources::webhooks::Webhooks;
use crate::sse::SseStream;
use crate::types::analytics::AnalyticsResponse;
use crate::types::collections::CollectionStatsResponse;
use crate::types::common::{HealthResponse, PlatformStatsResponse, ReadinessResponse, StatusResponse};
use crate::types::documents::{
BatchDeleteDocumentsResponse, BatchGetDocumentsResponse, BatchStatusResponse, Document,
DocumentChunkListResponse, DocumentListOptions, DocumentListResponse, S3IngestBody,
S3IngestResponse, S3JobListResponse,
};
use crate::types::embeddings::EmbeddingModelListResponse;
use crate::types::query::{QueryBody, QueryResponse};
const DEFAULT_BASE_URL: &str = "http://localhost:6100";
const DEFAULT_TIMEOUT_SECS: u64 = 120;
const DEFAULT_MAX_RETRIES: u32 = 2;
#[derive(Debug, Clone)]
pub struct BigRagConfig {
pub base_url: String,
pub api_key: Option<String>,
pub timeout: Duration,
pub max_retries: u32,
}
pub struct BigRag {
pub(crate) transport: Transport,
pub(crate) config: BigRagConfig,
}
impl BigRag {
pub fn new(base_url: &str, api_key: &str) -> Self {
let config = BigRagConfig {
base_url: base_url.trim_end_matches('/').to_string(),
api_key: Some(api_key.to_string()),
timeout: Duration::from_secs(DEFAULT_TIMEOUT_SECS),
max_retries: DEFAULT_MAX_RETRIES,
};
let transport = Transport::new(
&config.base_url,
config.api_key.clone(),
config.timeout,
config.max_retries,
);
Self { transport, config }
}
pub fn from_env() -> Result<Self, BigRagError> {
let base_url =
std::env::var("BIGRAG_BASE_URL").unwrap_or_else(|_| DEFAULT_BASE_URL.to_string());
let api_key = std::env::var("BIGRAG_API_KEY").ok();
let config = BigRagConfig {
base_url: base_url.trim_end_matches('/').to_string(),
api_key,
timeout: Duration::from_secs(DEFAULT_TIMEOUT_SECS),
max_retries: DEFAULT_MAX_RETRIES,
};
let transport = Transport::new(
&config.base_url,
config.api_key.clone(),
config.timeout,
config.max_retries,
);
Ok(Self { transport, config })
}
pub fn builder() -> BigRagBuilder {
BigRagBuilder::default()
}
pub fn collections(&self) -> Collections<'_> {
Collections { client: self }
}
pub fn documents(&self) -> Documents<'_> {
Documents { client: self }
}
pub fn queries(&self) -> Queries<'_> {
Queries { client: self }
}
pub fn vectors(&self) -> Vectors<'_> {
Vectors { client: self }
}
pub fn webhooks(&self) -> Webhooks<'_> {
Webhooks { client: self }
}
pub fn collection(&self, name: &str) -> CollectionClient<'_> {
CollectionClient {
client: self,
name: name.to_string(),
}
}
pub async fn health(&self) -> Result<HealthResponse, BigRagError> {
self.transport.get("/health", vec![]).await
}
pub async fn readiness(&self) -> Result<ReadinessResponse, BigRagError> {
self.transport.get("/health/ready", vec![]).await
}
pub async fn stats(&self) -> Result<PlatformStatsResponse, BigRagError> {
self.transport.get("/v1/stats", vec![]).await
}
pub async fn embedding_models(&self) -> Result<EmbeddingModelListResponse, BigRagError> {
self.transport.get("/v1/embeddings/models", vec![]).await
}
pub async fn analytics(&self, collection: &str) -> Result<AnalyticsResponse, BigRagError> {
let path = format!("/v1/collections/{}/analytics", crate::core::urlencode(collection));
self.transport.get(&path, vec![]).await
}
}
#[derive(Default)]
pub struct BigRagBuilder {
base_url: Option<String>,
api_key: Option<String>,
timeout: Option<Duration>,
max_retries: Option<u32>,
reqwest_client: Option<reqwest::Client>,
}
impl BigRagBuilder {
pub fn base_url(mut self, url: &str) -> Self {
self.base_url = Some(url.to_string());
self
}
pub fn api_key(mut self, key: &str) -> Self {
self.api_key = Some(key.to_string());
self
}
pub fn timeout(mut self, timeout: Duration) -> Self {
self.timeout = Some(timeout);
self
}
pub fn max_retries(mut self, retries: u32) -> Self {
self.max_retries = Some(retries);
self
}
pub fn reqwest_client(mut self, client: reqwest::Client) -> Self {
self.reqwest_client = Some(client);
self
}
pub fn build(self) -> Result<BigRag, BigRagError> {
let config = BigRagConfig {
base_url: self
.base_url
.unwrap_or_else(|| DEFAULT_BASE_URL.to_string())
.trim_end_matches('/')
.to_string(),
api_key: self.api_key,
timeout: self
.timeout
.unwrap_or(Duration::from_secs(DEFAULT_TIMEOUT_SECS)),
max_retries: self.max_retries.unwrap_or(DEFAULT_MAX_RETRIES),
};
let transport = if let Some(http) = self.reqwest_client {
Transport::with_client(
http,
&config.base_url,
config.api_key.clone(),
config.timeout,
config.max_retries,
)
} else {
Transport::new(
&config.base_url,
config.api_key.clone(),
config.timeout,
config.max_retries,
)
};
Ok(BigRag { transport, config })
}
}
pub struct CollectionClient<'a> {
pub(crate) client: &'a BigRag,
pub(crate) name: String,
}
impl CollectionClient<'_> {
pub async fn upload(
&self,
file: impl Into<FileInput>,
metadata: Option<serde_json::Value>,
) -> Result<Document, BigRagError> {
self.client.documents().upload(&self.name, file, metadata).await
}
pub async fn batch_upload(
&self,
files: Vec<FileInput>,
metadata: Option<serde_json::Value>,
) -> Result<DocumentListResponse, BigRagError> {
self.client.documents().batch_upload(&self.name, files, metadata).await
}
pub async fn list_documents(
&self,
options: Option<DocumentListOptions>,
) -> Result<DocumentListResponse, BigRagError> {
self.client.documents().list(&self.name, options).await
}
pub async fn get_document(&self, document_id: &str) -> Result<Document, BigRagError> {
self.client.documents().get(&self.name, document_id).await
}
pub async fn delete_document(&self, document_id: &str) -> Result<StatusResponse, BigRagError> {
self.client.documents().delete(&self.name, document_id).await
}
pub async fn reprocess_document(&self, document_id: &str) -> Result<StatusResponse, BigRagError> {
self.client.documents().reprocess(&self.name, document_id).await
}
pub async fn get_document_chunks(&self, document_id: &str) -> Result<DocumentChunkListResponse, BigRagError> {
self.client.documents().get_chunks(&self.name, document_id).await
}
pub async fn batch_get_status(&self, document_ids: &[&str]) -> Result<BatchStatusResponse, BigRagError> {
self.client.documents().batch_get_status(&self.name, document_ids).await
}
pub async fn batch_get_documents(&self, document_ids: &[&str]) -> Result<BatchGetDocumentsResponse, BigRagError> {
self.client.documents().batch_get(&self.name, document_ids).await
}
pub async fn batch_delete(&self, document_ids: &[&str]) -> Result<BatchDeleteDocumentsResponse, BigRagError> {
self.client.documents().batch_delete(&self.name, document_ids).await
}
pub async fn ingest_s3(&self, body: S3IngestBody) -> Result<S3IngestResponse, BigRagError> {
self.client.documents().ingest_s3(&self.name, body).await
}
pub async fn list_s3_jobs(&self) -> Result<S3JobListResponse, BigRagError> {
self.client.documents().list_s3_jobs(&self.name).await
}
pub async fn query(&self, body: QueryBody) -> Result<QueryResponse, BigRagError> {
self.client.queries().query(&self.name, body).await
}
pub async fn stats(&self) -> Result<CollectionStatsResponse, BigRagError> {
self.client.collections().stats(&self.name).await
}
pub async fn analytics(&self) -> Result<AnalyticsResponse, BigRagError> {
let path = format!("/v1/collections/{}/analytics", &self.name);
self.client.transport.get(&path, vec![]).await
}
pub async fn stream_events(&self) -> Result<SseStream, BigRagError> {
self.client.collections().stream_events(&self.name).await
}
pub async fn stream_document_progress(&self, document_id: &str) -> Result<SseStream, BigRagError> {
self.client.documents().stream_progress(&self.name, document_id).await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_sets_defaults() {
let client = BigRag::new("http://localhost:6100", "sk-test");
assert_eq!(client.config.base_url, "http://localhost:6100");
assert_eq!(client.config.api_key.as_deref(), Some("sk-test"));
}
#[test]
fn test_builder_defaults() {
let client = BigRag::builder().api_key("sk-test").build().unwrap();
assert_eq!(client.config.base_url, "http://localhost:6100");
}
#[test]
fn test_builder_custom_base_url() {
let client = BigRag::builder()
.base_url("https://custom.example.com")
.api_key("sk-test")
.build()
.unwrap();
assert_eq!(client.config.base_url, "https://custom.example.com");
}
#[test]
fn test_builder_strips_trailing_slash() {
let client = BigRag::builder()
.base_url("https://example.com/")
.api_key("sk-test")
.build()
.unwrap();
assert_eq!(client.config.base_url, "https://example.com");
}
#[test]
fn test_resource_accessors_compile() {
let client = BigRag::new("http://localhost:6100", "sk-test");
let _collections = client.collections();
let _documents = client.documents();
let _queries = client.queries();
let _vectors = client.vectors();
let _webhooks = client.webhooks();
let _col_client = client.collection("test");
}
}