Seedframe - Core API Documentation
Seedframe is a clean, macro-driven Rust library for building LLM applications.
Features
- Declarative API through straight forward proc-macros
- Modular Architecture with clearly defined components:
- Loaders: Data ingestion from various sources (files, APIs, etc.)
- Vector Stores: Embedding storage and retrieval (In-memory, Redis, etc.)
- Embedders: Text embedding providers
- LLM Clients: Unified interface for different LLM providers
- Tools: Function calling abstractions with state management and automatic documentation
- Extractors: Structured output generation from LLM responses
Examples
The seedframe repo contains a number of examples that show how to put all the pieces together.
Building a simple RAG
use seedframe::prelude::*;
use seedframe::providers::{completions::OpenAI, embeddings::OpenAIEmbedding};
use seedframe::vector_store::InMemoryVectorStore;
#[loader(kind = "FileOnceLoader", path = "/tmp/data/**/*.txt")]
pub struct MyLoader;
#[vector_store(store = "InMemoryVectorStore")]
pub struct MyVectorStore;
#[embedder(provider = "OpenAIEmbedding")]
struct MyEmbedder {
#[vector_store]
my_vector_store: MyVectorStore,
#[loader]
my_loader: MyLoader,
}
#[client(provider = "OpenAI")]
struct MyClient {
#[embedder]
my_embedder: MyEmbedder,
}
#[tokio::main]
async fn main() {
let mut client = MyClient::build(
"You are a helpful assistant".to_string()
).await;
tokio::time::sleep(Duration::from_secs(5)).await;
let response = client.prompt("Explain quantum computing").send().await.unwrap();
}
Tool calls and Extractors
use seedframe::{providers::completions::OpenAI, prelude::*};
#[client(provider = "OpenAI", tools("analyze"))]
struct ToolClient;
#[tool]
fn analyze(text: String, language: String) -> String {
todo!("implementation");
}
#[derive(Extractor)]
struct PersonData {
age: u8,
email: String
}
#[tokio::main]
async fn main() -> Result<()> {
let mut client = ToolClient::build("You're a data analyst".to_string())
.await
.with_state(AppState::new())?;
client.prompt("Analyze this: 'I love Rust!' (en)")
.append_tool_response(true) .send()
.await?;
let person = client.prompt("John is 30, email john@example.com")
.extract::<PersonData>()
.await?;
}
Sharing state with tools
You can pass state to tools by adding arguments of type State<_>
to them, the only catch is that there can only be one type of State<T> attached to the client.
use seedframe::{providers::completions::OpenAI, prelude::*};
#[client(provider = "OpenAI", tools("greet"))]
struct ToolClient;
#[tool]
fn greet(name: String, State(count): State<u32>) -> String {
for _ in range 0..count { println("Hello {name}!!")};
}
#[tokio::main]
async fn main() -> Result<()> {
let mut client = ToolClient::build("You're a helpful assistant".to_string())
.await
.with_state(3u32)?
.with_state(7u32)? .with_state("some other state".to_string())?;
client.prompt("Say hi to jack for me".to_string())
.send()
.await?;
}
Sharing mutable state with tools
To share mutable state you can use types with interior mutablity, eg Mutex
s
#[tool]
fn greet(name: String, State(count): State<u32>) -> String {
let mut count = count.lock().unwrap();
println!("{name}! This is my {count}th time saying hello");
*count += 1;
}
struct AppState {
count: std::sync::Mutex<u32>
}
#[tokio::main]
async fn main() -> Result<()> {
let mut client = ToolClient::build("You're a helpful assistant".to_string())
.await
.with_state(AppState { count: std::sync::Mutex::new(0u32) })?;
client.prompt("Say hi to jack for me".to_string())
.send()
.await?;
client.prompt("Say hi to jack for me".to_string())
.send()
.await?;
}
Feature flags
seedframe uses a set of [feature flags] to reduce the amount of compiled and
optional dependencies.
The following optional features are available:
Name |
Description |
Default? |
pdf |
enables file loaders to parse PDFs |
No |