mirror of
https://github.com/samvallad33/vestige.git
synced 2026-06-20 21:18:08 +02:00
feat(embedder): swap async-trait for trait_variant + dyn adapter (0001c)
Mirror of the 0001a pattern for the Embedder side. - embedder/mod.rs: LocalEmbedder is the source trait declared with native async-fn-in-trait. #[trait_variant::make(EmbedderSend: Send)] derives the Send-bounded variant that backends implement. A hand-written Embedder trait wraps each async method in BoxedEmbedderFuture<'a, T> and forwards sync methods through a blanket impl<T: EmbedderSend> Embedder for T, so Box<dyn Embedder> / Arc<dyn Embedder> stay dyn-safe -- trait_variant 0.1 alone does NOT produce a dyn-safe variant (RPITIT), so the hand-written adapter is required. - embedder/fastembed.rs: drop the #[async_trait::async_trait] attribute and retarget the impl block to EmbedderSend. Adjust the top-level use to bring EmbedderSend into scope (also keeps fastembed::tests' use super::* trait lookups working). - lib.rs: export EmbedderSend alongside the existing Embedder / LocalEmbedder re-exports. The async-trait Cargo dependency is dropped in a follow-up commit so the manifest change stays visible on its own. Verification: cargo test -p vestige-core --features embeddings,vector-search (428) and --no-default-features (370) both green. cargo test --test embedder_trait green (2/2 including Box<dyn Embedder> cast). cargo build --workspace --release green. cargo clippy --workspace --features embeddings,vector-search -- -D warnings clean. grep -rn async_trait crates/ returns zero.
This commit is contained in:
parent
a4a6e877c5
commit
194fc6e4c0
3 changed files with 72 additions and 12 deletions
|
|
@ -4,7 +4,7 @@
|
|||
#[cfg(feature = "embeddings")]
|
||||
use crate::embeddings::{EMBEDDING_DIMENSIONS, EmbeddingService};
|
||||
|
||||
use super::{EmbedderError, EmbedderResult, LocalEmbedder};
|
||||
use super::{EmbedderError, EmbedderResult, EmbedderSend};
|
||||
|
||||
pub struct FastembedEmbedder {
|
||||
#[cfg(feature = "embeddings")]
|
||||
|
|
@ -41,8 +41,7 @@ impl Default for FastembedEmbedder {
|
|||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl LocalEmbedder for FastembedEmbedder {
|
||||
impl EmbedderSend for FastembedEmbedder {
|
||||
async fn embed(&self, text: &str) -> EmbedderResult<Vec<f32>> {
|
||||
#[cfg(feature = "embeddings")]
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
//! Text-to-vector encoding trait. Pluggable per-install.
|
||||
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
|
||||
mod fastembed;
|
||||
|
||||
pub use fastembed::FastembedEmbedder;
|
||||
|
|
@ -18,14 +21,23 @@ pub enum EmbedderError {
|
|||
|
||||
pub type EmbedderResult<T> = std::result::Result<T, EmbedderError>;
|
||||
|
||||
/// Boxed Send future returning an `EmbedderResult<T>`, bound to the lifetime
|
||||
/// of the borrows captured by the call. Used as the return type of every
|
||||
/// async method on the dyn-compatible `Embedder` trait below.
|
||||
pub type BoxedEmbedderFuture<'a, T> =
|
||||
Pin<Box<dyn Future<Output = EmbedderResult<T>> + Send + 'a>>;
|
||||
|
||||
/// Pluggable embedder. The storage layer NEVER calls fastembed directly;
|
||||
/// callers compute vectors via this trait and pass them into `MemoryStore`.
|
||||
///
|
||||
/// `#[async_trait::async_trait]` makes every `async fn` return a
|
||||
/// `Pin<Box<dyn Future + Send>>`, which is required for `Box<dyn Embedder>`
|
||||
/// and `Arc<dyn Embedder>` to be dyn-compatible.
|
||||
#[async_trait::async_trait]
|
||||
pub trait LocalEmbedder: Send + Sync + 'static {
|
||||
/// `LocalEmbedder` is the source-of-truth trait declared with native
|
||||
/// async-fn-in-trait. `#[trait_variant::make(EmbedderSend: Send)]` derives
|
||||
/// a Send-bounded variant that backends actually implement (the
|
||||
/// trait_variant 0.1.x blanket goes variant -> source). The dyn-compatible
|
||||
/// public surface is the `Embedder` trait declared below, which wraps every
|
||||
/// async method in `Pin<Box<dyn Future + Send + '_>>`.
|
||||
#[trait_variant::make(EmbedderSend: Send)]
|
||||
pub trait LocalEmbedder: Sync + 'static {
|
||||
async fn embed(&self, text: &str) -> EmbedderResult<Vec<f32>>;
|
||||
|
||||
fn model_name(&self) -> &str;
|
||||
|
|
@ -52,6 +64,53 @@ pub trait LocalEmbedder: Send + Sync + 'static {
|
|||
}
|
||||
}
|
||||
|
||||
/// Type alias: `Embedder` is the dyn-compatible, Send+Sync variant.
|
||||
/// Both names refer to the same `async_trait`-annotated trait.
|
||||
pub use LocalEmbedder as Embedder;
|
||||
/// Dyn-compatible embedder trait.
|
||||
///
|
||||
/// `EmbedderSend` above is the trait users implement; it uses native
|
||||
/// async-fn-in-trait return types (RPITIT), which gives zero-allocation
|
||||
/// static dispatch but is not dyn-safe. This trait wraps every async
|
||||
/// method in `Pin<Box<dyn Future + Send + '_>>` so `Box<dyn Embedder>`
|
||||
/// and `Arc<dyn Embedder>` work for the cognitive module surface and
|
||||
/// the Phase 1 integration tests.
|
||||
///
|
||||
/// Implementations should not target this trait directly; the blanket
|
||||
/// `impl<T: EmbedderSend> Embedder for T` adapts every Send-variant
|
||||
/// implementation automatically.
|
||||
pub trait Embedder: Send + Sync + 'static {
|
||||
fn embed<'a>(&'a self, text: &'a str) -> BoxedEmbedderFuture<'a, Vec<f32>>;
|
||||
fn embed_batch<'a>(
|
||||
&'a self,
|
||||
texts: &'a [&'a str],
|
||||
) -> BoxedEmbedderFuture<'a, Vec<Vec<f32>>>;
|
||||
fn model_name(&self) -> &str;
|
||||
fn dimension(&self) -> usize;
|
||||
fn model_hash(&self) -> String;
|
||||
fn signature(&self) -> crate::storage::ModelSignature;
|
||||
}
|
||||
|
||||
impl<T> Embedder for T
|
||||
where
|
||||
T: EmbedderSend,
|
||||
{
|
||||
fn embed<'a>(&'a self, text: &'a str) -> BoxedEmbedderFuture<'a, Vec<f32>> {
|
||||
Box::pin(<T as EmbedderSend>::embed(self, text))
|
||||
}
|
||||
fn embed_batch<'a>(
|
||||
&'a self,
|
||||
texts: &'a [&'a str],
|
||||
) -> BoxedEmbedderFuture<'a, Vec<Vec<f32>>> {
|
||||
Box::pin(<T as EmbedderSend>::embed_batch(self, texts))
|
||||
}
|
||||
fn model_name(&self) -> &str {
|
||||
<T as EmbedderSend>::model_name(self)
|
||||
}
|
||||
fn dimension(&self) -> usize {
|
||||
<T as EmbedderSend>::dimension(self)
|
||||
}
|
||||
fn model_hash(&self) -> String {
|
||||
<T as EmbedderSend>::model_hash(self)
|
||||
}
|
||||
fn signature(&self) -> crate::storage::ModelSignature {
|
||||
<T as EmbedderSend>::signature(self)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -198,7 +198,9 @@ pub use storage::{
|
|||
};
|
||||
|
||||
// Embedder trait and implementations
|
||||
pub use embedder::{Embedder, EmbedderError, EmbedderResult, FastembedEmbedder, LocalEmbedder};
|
||||
pub use embedder::{
|
||||
Embedder, EmbedderError, EmbedderResult, EmbedderSend, FastembedEmbedder, LocalEmbedder,
|
||||
};
|
||||
|
||||
// Consolidation (sleep-inspired memory processing)
|
||||
pub use consolidation::SleepConsolidation;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue