Skip to main content

ares_store/
lib.rs

1//! Database Clients and Vector Stores
2//!
3//! This module provides database abstractions for:
4//! - **PostgreSQL**: Relational database for conversations, users, etc.
5//! - **Vector Stores**: Multi-provider vector database support
6//!
7//! # Relational Database
8//!
9//! The [`PostgresClient`] provides async access to PostgreSQL for:
10//! - User management (registration, authentication)
11//! - Conversation storage and retrieval
12//! - Message history
13//! - User memory (facts, preferences)
14//!
15//! # Vector Store Providers
16//!
17//! The following vector store backends are supported:
18//! - `ares-vector` (default) - Pure Rust embedded HNSW vector database
19//! - `lancedb` - Serverless, embedded vector database (may have build issues on Windows)
20//! - `qdrant` - High-performance vector search engine
21//! - `pgvector` - PostgreSQL extension
22//! - `chromadb` - Simple embedding database
23//! - `pinecone` - Managed cloud service
24//!
25//! Enable providers via Cargo features:
26//! ```toml
27//! ares = { version = "*", features = ["ares-vector", "qdrant"] }
28//! ```
29//!
30//! # Example
31//!
32//! ```ignore
33//! use ares::db::{PostgresClient, VectorStore, AresVectorStore};
34//!
35//! // Relational database
36//! let db = PostgresClient::new("postgres://user:pass@localhost:5432/ares").await?;
37//! let user = db.get_user_by_id(user_id).await?;
38//!
39//! // Vector store
40//! let vector_store = AresVectorStore::new("./vectors").await?;
41//! vector_store.upsert("docs", embeddings, metadata).await?;
42//! let results = vector_store.search("docs", query_embedding, 10).await?;
43//! ```
44
45#![allow(clippy::too_many_arguments)]
46#![allow(clippy::type_complexity)]
47#![allow(clippy::redundant_closure)]
48#![allow(unused_imports)]
49#![allow(clippy::needless_borrows_for_generic_args)]
50#![allow(clippy::option_as_ref_deref)]
51#![allow(clippy::map_flatten)]
52#![allow(clippy::for_kv_map)]
53
54/// Canonical embedded migrator shared by every crate that needs the schema.
55///
56/// Built once from the crate-local `./migrations` directory, which ships in
57/// the published `ares-store` package so `cargo install` works from anywhere.
58#[cfg(feature = "postgres")]
59pub static MIGRATOR: sqlx::migrate::Migrator = sqlx::migrate!("./migrations");
60
61pub mod billing_config;
62pub mod config;
63pub mod fleet_secrets;
64pub use billing_config::{BillingConfig, ModelPricingConfig};
65pub use config::{default_qdrant_url, DatabaseConfig, QdrantConfig};
66pub use fleet_secrets::{
67    decrypt_api_key, encrypt_api_key, last_n_visible, EncryptedPayload, FleetSecrets,
68    FleetSecretsError, MasterKey, ProviderOverride,
69};
70
71// Vector store abstraction layer
72pub mod vectorstore;
73
74// Provider implementations
75#[cfg(feature = "ares-vector")]
76pub mod ares_vector;
77#[cfg(feature = "chromadb")]
78pub mod chromadb;
79#[cfg(any(feature = "lancedb", feature = "postgres"))]
80pub mod lancedb;
81#[cfg(any(feature = "pgvector", feature = "postgres"))]
82pub mod pgvector;
83#[cfg(any(feature = "pinecone", feature = "postgres"))]
84pub mod pinecone;
85#[cfg(any(feature = "qdrant", feature = "postgres"))]
86pub mod qdrant;
87
88// Relational database (requires postgres feature for sqlx)
89#[cfg(feature = "postgres")]
90/// Reviewer and quality feedback attached to agent runs.
91pub mod agent_feedback;
92#[cfg(feature = "postgres")]
93/// Agent run tracking (execution history).
94pub mod agent_runs;
95#[cfg(feature = "postgres")]
96/// Platform alerts (health, quota, errors).
97pub mod alerts;
98#[cfg(feature = "postgres")]
99/// Admin audit log (mutation tracking).
100pub mod audit_log;
101#[cfg(feature = "postgres")]
102/// PostgreSQL database client implementation.
103pub mod postgres;
104#[cfg(feature = "postgres")]
105/// Per-tenant Cordis child contexts (temporal tenancy).
106pub mod realms;
107#[cfg(feature = "postgres")]
108/// Per-tenant agent instance management.
109pub mod tenant_agents;
110#[cfg(feature = "postgres")]
111/// Multi-tenant tenant management.
112pub mod tenants;
113#[cfg(feature = "postgres")]
114pub use realms::TenantRealms;
115#[cfg(feature = "postgres")]
116/// Agent config version history (Sprint 11).
117pub mod agent_versions;
118/// Database traits and common types shared across providers.
119#[cfg(feature = "postgres")]
120pub mod traits;
121/// Turso/libSQL database client (alternative to PostgreSQL).
122#[cfg(feature = "turso")]
123pub mod turso;
124#[cfg(feature = "postgres")]
125pub use agent_versions::AgentVersionInput;
126#[cfg(feature = "postgres")]
127/// Fleet-wide, tenant-agnostic provider API key & config storage.
128pub mod fleet_provider_secrets;
129#[cfg(feature = "postgres")]
130/// OAuth credential storage for third-party connectors.
131pub mod oauth_credentials;
132#[cfg(feature = "postgres")]
133/// Pure SQL builders and row conversions (testable without a live DB).
134pub mod query_builders;
135#[cfg(feature = "postgres")]
136/// Detailed run history: LLM calls, tool calls, costs, budgets, health metrics.
137pub mod run_history;
138#[cfg(feature = "postgres")]
139/// Runtime-defined LLM provider configurations.
140pub mod runtime_providers;
141#[cfg(feature = "postgres")]
142/// Runtime-defined tools (HTTP, MCP, Script, SQL).
143pub mod runtime_tools;
144#[cfg(feature = "postgres")]
145/// Agent schedules, event triggers, and pipeline links.
146pub mod schedules;
147#[cfg(feature = "postgres")]
148/// Custom skills and connector configurations.
149pub mod skills;
150#[cfg(feature = "postgres")]
151/// Per-tenant allowlist for tools, models, and RAG sources.
152pub mod tenant_allowlist;
153#[cfg(feature = "postgres")]
154/// Per-tenant model tier mapping (abstract tier -> concrete provider/model).
155pub mod tenant_model_tiers;
156#[cfg(feature = "postgres")]
157/// Per-tenant LLM token budget tracking.
158pub mod token_budgets;
159
160// Re-exports
161pub use vectorstore::{CollectionInfo, CollectionStats, VectorStore, VectorStoreProvider};
162
163#[cfg(feature = "ares-vector")]
164pub use ares_vector::AresVectorStore;
165#[cfg(feature = "lancedb")]
166pub use lancedb::LanceDBStore;
167#[cfg(feature = "postgres")]
168pub use postgres::PostgresClient;
169#[cfg(feature = "qdrant")]
170pub use qdrant::QdrantVectorStore;
171#[cfg(feature = "postgres")]
172pub use tenants::{TenantDb, UsageSummary};
173#[cfg(feature = "turso")]
174pub use turso::TursoClient;
175
176#[cfg(feature = "postgres")]
177pub type Store = TenantDb;
178
179mod plugins;
180pub use plugins::register_plugins;
181
182/// Cordis Service for postgres availability — runtime check replaces `#[cfg(feature = "postgres")]` in handlers.
183///
184/// `check()` returns `cfg!(feature = "postgres")` so both `cargo check --no-default-features` and
185/// `cargo check --features postgres` compile; handlers branch via `PostgresService::check()` or `cfg!`.
186pub struct PostgresService;
187impl cordis::Service for PostgresService {
188    fn name(&self) -> &'static str {
189        "postgres"
190    }
191    fn check(&self) -> bool {
192        cfg!(feature = "postgres")
193    }
194}
195
196#[cfg(test)]
197mod tests {
198    #[cfg(feature = "ares-vector")]
199    use super::VectorStoreProvider;
200    use super::{CollectionInfo, CollectionStats};
201    #[cfg(feature = "ares-vector")]
202    use serde_json::json;
203
204    #[test]
205    fn collection_stats_serde_roundtrip() {
206        let stats = CollectionStats {
207            name: "docs".into(),
208            document_count: 42,
209            dimensions: 384,
210            index_size_bytes: Some(1024),
211            distance_metric: "cosine".into(),
212        };
213        let value = serde_json::to_value(&stats).expect("serialize");
214        let back: CollectionStats = serde_json::from_value(value).expect("deserialize");
215        assert_eq!(back.name, "docs");
216        assert_eq!(back.document_count, 42);
217        assert_eq!(back.dimensions, 384);
218    }
219
220    #[test]
221    fn collection_info_serde_roundtrip() {
222        let info = CollectionInfo {
223            name: "embeddings".into(),
224            dimensions: 768,
225            document_count: 10,
226        };
227        let json = serde_json::to_string(&info).expect("serialize");
228        let back: CollectionInfo = serde_json::from_str(&json).expect("deserialize");
229        assert_eq!(back.name, "embeddings");
230        assert_eq!(back.dimensions, 768);
231    }
232
233    #[cfg(feature = "ares-vector")]
234    #[test]
235    fn ares_vector_provider_tagged_json() {
236        let provider = VectorStoreProvider::AresVector {
237            path: Some("./data/vectors".into()),
238        };
239        let value = serde_json::to_value(&provider).expect("serialize");
240        assert_eq!(value["provider"], json!("aresvector"));
241    }
242}