RAG¶
Tulip's RAG stack is built around pluggable embedders and vector
stores behind one interface. The BaseVectorStore / BaseEmbedding
contracts are identical across backends so you can swap stores with a
one-line import change.
Retriever¶
The unified interface — combines an embedder and a store into the
retrieve() call that returns ranked, optionally reranked results.
RAGRetriever ¶
Bases: BaseModel
RAG Retriever combining embedding and vector store.
Provides a unified interface for: - Adding documents (with automatic embedding) - Retrieving relevant context for queries - Chunking large documents
Example
from tulip.rag import RAGRetriever, OpenAIEmbeddings, InMemoryVectorStore
retriever = RAGRetriever( ... embedder=OpenAIEmbeddings(model="text-embedding-3-small"), ... store=InMemoryVectorStore(), ... )
Add documents¶
await retriever.add_documents( ... [ ... "Python is a programming language.", ... "Vector search powers retrieval-augmented generation.", ... ] ... )
Retrieve relevant context¶
results = await retriever.retrieve("What is Python?", limit=3) for r in results.documents: ... print(f"{r.score:.2f}: {r.document.content[:50]}...")
Example with chunking
retriever = RAGRetriever( ... embedder=embedder, ... store=store, ... chunk_size=500, ... chunk_overlap=50, ... ) await retriever.add_document(long_document, metadata={"source": "manual"})
add_document
async
¶
add_document(content: str, doc_id: str | None = None, metadata: dict[str, Any] | None = None, chunk: bool = True) -> list[str]
Add a document, optionally chunking it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
content
|
str
|
Document text |
required |
doc_id
|
str | None
|
Optional document ID (auto-generated if not provided) |
None
|
metadata
|
dict[str, Any] | None
|
Optional metadata |
None
|
chunk
|
bool
|
Whether to chunk large documents |
True
|
Returns:
| Type | Description |
|---|---|
list[str]
|
List of document IDs (multiple if chunked) |
Source code in .sdk/src/tulip/rag/retriever.py
add_documents
async
¶
add_documents(contents: list[str], metadata: dict[str, Any] | None = None, chunk: bool = True) -> list[str]
Add multiple documents.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
contents
|
list[str]
|
List of document texts |
required |
metadata
|
dict[str, Any] | None
|
Optional metadata (applied to all) |
None
|
chunk
|
bool
|
Whether to chunk large documents |
True
|
Returns:
| Type | Description |
|---|---|
list[str]
|
List of all document IDs |
Source code in .sdk/src/tulip/rag/retriever.py
add_file
async
¶
add_file(file_path: str | Path, doc_id: str | None = None, metadata: dict[str, Any] | None = None, chunk: bool = True) -> list[str]
Add a file (text, PDF, image, or audio).
Automatically detects content type and processes accordingly: - PDFs: Extracts text (with OCR fallback for scanned docs) - Images: OCR text extraction + optional description - Audio: Speech-to-text transcription - Text: Direct processing
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
file_path
|
str | Path
|
Path to the file |
required |
doc_id
|
str | None
|
Optional document ID |
None
|
metadata
|
dict[str, Any] | None
|
Optional metadata |
None
|
chunk
|
bool
|
Whether to chunk large documents |
True
|
Returns:
| Type | Description |
|---|---|
list[str]
|
List of document IDs |
Example
await retriever.add_file("manual.pdf") await retriever.add_file("diagram.png") await retriever.add_file("meeting.mp3")
Source code in .sdk/src/tulip/rag/retriever.py
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 | |
add_image
async
¶
add_image(image: bytes | str | Path, doc_id: str | None = None, metadata: dict[str, Any] | None = None, use_ocr: bool = True) -> str
Add an image document.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
image
|
bytes | str | Path
|
Image bytes, base64 string, or file path |
required |
doc_id
|
str | None
|
Optional document ID |
None
|
metadata
|
dict[str, Any] | None
|
Optional metadata |
None
|
use_ocr
|
bool
|
Whether to use OCR for text extraction |
True
|
Returns:
| Type | Description |
|---|---|
str
|
Document ID |
Source code in .sdk/src/tulip/rag/retriever.py
add_pdf
async
¶
add_pdf(pdf: bytes | str | Path, doc_id: str | None = None, metadata: dict[str, Any] | None = None, chunk: bool = True) -> list[str]
Add a PDF document.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pdf
|
bytes | str | Path
|
PDF bytes, base64 string, or file path |
required |
doc_id
|
str | None
|
Optional document ID |
None
|
metadata
|
dict[str, Any] | None
|
Optional metadata |
None
|
chunk
|
bool
|
Whether to chunk the document |
True
|
Returns:
| Type | Description |
|---|---|
list[str]
|
List of document IDs (multiple if chunked) |
Source code in .sdk/src/tulip/rag/retriever.py
add_audio
async
¶
add_audio(audio: bytes | str | Path, doc_id: str | None = None, metadata: dict[str, Any] | None = None) -> str
Add an audio/voice document.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
audio
|
bytes | str | Path
|
Audio bytes, base64 string, or file path |
required |
doc_id
|
str | None
|
Optional document ID |
None
|
metadata
|
dict[str, Any] | None
|
Optional metadata |
None
|
Returns:
| Type | Description |
|---|---|
str
|
Document ID |
Source code in .sdk/src/tulip/rag/retriever.py
retrieve
async
¶
retrieve(query: str, limit: int = 5, threshold: float | None = None, metadata_filter: dict[str, Any] | None = None) -> RetrievalResult
Retrieve relevant documents for a query.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
Query text |
required |
limit
|
int
|
Maximum documents to return |
5
|
threshold
|
float | None
|
Minimum similarity score (0.0-1.0) |
None
|
metadata_filter
|
dict[str, Any] | None
|
Filter by metadata fields |
None
|
Returns:
| Type | Description |
|---|---|
RetrievalResult
|
RetrievalResult with ranked documents |
Source code in .sdk/src/tulip/rag/retriever.py
437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 | |
retrieve_text
async
¶
retrieve_text(query: str, limit: int = 5, threshold: float | None = None, separator: str = '\n\n---\n\n', spotlight: bool = True) -> str
Retrieve and concatenate relevant documents as text.
Convenience method for injecting context into prompts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
Query text |
required |
limit
|
int
|
Maximum documents to return |
5
|
threshold
|
float | None
|
Minimum similarity score |
None
|
separator
|
str
|
Text to join documents |
'\n\n---\n\n'
|
spotlight
|
bool
|
When True (default), wrap each document in
|
True
|
Returns:
| Type | Description |
|---|---|
str
|
Concatenated document contents. |
Security note
Retrieved content is untrusted data — a poisoned document can attempt an indirect prompt-injection. The spotlight wrappers let you instruct the model (in the system prompt) to treat anything inside those tags as data only, never as instructions, and to refuse to perform tool calls whose arguments are quoted verbatim from retrieved content.
Source code in .sdk/src/tulip/rag/retriever.py
delete_document
async
¶
clear
async
¶
count
async
¶
close
async
¶
as_tool ¶
Create a tool function for agent use.
Returns a tool that can be registered with an agent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Tool name |
'search_knowledge'
|
description
|
str | None
|
Tool description |
None
|
Returns:
| Type | Description |
|---|---|
Any
|
Tool function decorated with @tool |
Source code in .sdk/src/tulip/rag/retriever.py
RetrievalResult
dataclass
¶
Result from RAG retrieval.
Attributes:
| Name | Type | Description |
|---|---|---|
documents |
list[SearchResult]
|
Retrieved documents sorted by relevance |
query |
str
|
Original query text |
total_results |
int
|
Total number of matches (may be > len(documents)) |
Embeddings¶
OpenAIEmbeddings covers the text-embedding-3-* family;
CohereEmbeddings wraps Cohere's direct embeddings API.
Providers¶
OpenAIEmbeddings ¶
OpenAIEmbeddings(model: str = 'text-embedding-3-small', api_key: str | None = None, dimensions: int | None = None, base_url: str | None = None, **kwargs: Any)
Bases: BaseEmbedding
OpenAI Embeddings provider.
Uses OpenAI's text-embedding models for generating embeddings.
Example
embedder = OpenAIEmbeddings( ... model="text-embedding-3-small", ... api_key="sk-...", ... ) result = await embedder.embed("Hello world") print(len(result.embedding)) # 1536
Initialize OpenAI embeddings.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
str
|
OpenAI embedding model ID |
'text-embedding-3-small'
|
api_key
|
str | None
|
API key (defaults to OPENAI_API_KEY env var) |
None
|
dimensions
|
int | None
|
Output dimensions (for supported models) |
None
|
base_url
|
str | None
|
Custom base URL |
None
|
**kwargs
|
Any
|
Additional configuration |
{}
|
Source code in .sdk/src/tulip/rag/embeddings/openai.py
capabilities
property
¶
OpenAI embeddings: text-only, native batching, no separate query/doc spaces (text-embedding-3-* use the same space).
embed
async
¶
Embed a single text.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
Text to embed |
required |
Returns:
| Type | Description |
|---|---|
EmbeddingResult
|
EmbeddingResult with vector and metadata |
Source code in .sdk/src/tulip/rag/embeddings/openai.py
embed_batch
async
¶
Embed multiple texts in a single request.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
texts
|
list[str]
|
List of texts to embed |
required |
Returns:
| Type | Description |
|---|---|
list[EmbeddingResult]
|
List of EmbeddingResult, one per input text |
Source code in .sdk/src/tulip/rag/embeddings/openai.py
close
async
¶
embed_query
async
¶
embed_documents
async
¶
Embed documents. Override if model has document-specific embeddings.
CohereEmbeddings ¶
CohereEmbeddings(model: str = DEFAULT_COHERE_EMBED_MODEL, api_key: str | None = None, dimension: int | None = None, _client: Any = None)
Bases: BaseEmbedding
Embedding provider backed by Cohere's hosted embed endpoint.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
str
|
Cohere embedding model id. Defaults to |
DEFAULT_COHERE_EMBED_MODEL
|
api_key
|
str | None
|
Cohere API key. Defaults to the |
None
|
dimension
|
int | None
|
Override the output dimension. Defaults to the known
dimension for |
None
|
_client
|
Any
|
Injection seam for tests — an object exposing an async
|
None
|
Source code in .sdk/src/tulip/rag/embeddings/cohere.py
embed
async
¶
Embed a single text as a document.
Source code in .sdk/src/tulip/rag/embeddings/cohere.py
embed_batch
async
¶
Embed multiple texts as documents in one request.
Source code in .sdk/src/tulip/rag/embeddings/cohere.py
embed_query
async
¶
Embed a query in Cohere's query space (input_type=search_query).
Source code in .sdk/src/tulip/rag/embeddings/cohere.py
embed_documents
async
¶
close
async
¶
Close the underlying client if it exposes a close().
Source code in .sdk/src/tulip/rag/embeddings/cohere.py
Embeddings base contract¶
BaseEmbedding ¶
Bases: ABC
Abstract base class for embedding providers.
Provides default implementations for common methods. Subclasses
override :meth:capabilities to advertise optional features, and
override :meth:embed_query/:meth:embed_documents only if
supports_query_vs_doc is True.
capabilities
property
¶
Advertised capabilities. Default is text-only, no batching, and no query/doc differentiation — override in subclasses.
embed
abstractmethod
async
¶
embed_batch
async
¶
Embed multiple texts. Override for batch optimization.
Source code in .sdk/src/tulip/rag/embeddings/base.py
embed_query
async
¶
embed_documents
async
¶
Embed documents. Override if model has document-specific embeddings.
EmbeddingConfig
dataclass
¶
Configuration for embedding providers.
Attributes:
| Name | Type | Description |
|---|---|---|
dimension |
int
|
Vector dimension size |
max_tokens |
int
|
Maximum tokens per request |
batch_size |
int
|
Maximum texts per batch |
EmbeddingProvider ¶
Bases: Protocol
Protocol for embedding providers.
Embedding providers convert text into dense vectors that capture semantic meaning, enabling similarity search.
Example
embedder = OpenAIEmbeddings(model="text-embedding-3-small") result = await embedder.embed("Hello world") print(len(result.embedding)) # 1024
capabilities
property
¶
Advertised capabilities. See :class:EmbeddingCapabilities.
embed
async
¶
Embed a single text.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
Text to embed |
required |
Returns:
| Type | Description |
|---|---|
EmbeddingResult
|
EmbeddingResult with vector and metadata |
embed_batch
async
¶
Embed multiple texts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
texts
|
list[str]
|
List of texts to embed |
required |
Returns:
| Type | Description |
|---|---|
list[EmbeddingResult]
|
List of EmbeddingResult, one per input text |
embed_query
async
¶
Embed a query for retrieval.
Some models use different embeddings for queries vs documents. Default implementation calls embed().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
Query text to embed |
required |
Returns:
| Type | Description |
|---|---|
EmbeddingResult
|
EmbeddingResult optimized for query |
Source code in .sdk/src/tulip/rag/embeddings/base.py
embed_documents
async
¶
Embed documents for storage.
Some models use different embeddings for queries vs documents. Default implementation calls embed_batch().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
documents
|
list[str]
|
Document texts to embed |
required |
Returns:
| Type | Description |
|---|---|
list[EmbeddingResult]
|
List of EmbeddingResult optimized for storage |
Source code in .sdk/src/tulip/rag/embeddings/base.py
EmbeddingResult
dataclass
¶
Result from embedding operation.
Attributes:
| Name | Type | Description |
|---|---|---|
embedding |
list[float]
|
The embedding vector |
text |
str
|
Original text that was embedded |
model |
str
|
Model used for embedding |
tokens |
int | None
|
Number of tokens used (if available) |
Vector stores¶
Backends¶
InMemoryVectorStore ¶
Bases: BaseVectorStore
In-memory vector store for testing and development.
Fast but not persistent - data is lost when process exits.
Example
store = InMemoryVectorStore(dimension=1024) await store.add(document) results = await store.search(query_embedding, limit=5)
Source code in .sdk/src/tulip/rag/stores/memory.py
add
async
¶
add_batch
async
¶
get
async
¶
delete
async
¶
search
async
¶
search(query_embedding: list[float], limit: int = 10, threshold: float | None = None, metadata_filter: dict[str, Any] | None = None) -> list[SearchResult]
Search for similar documents.
Source code in .sdk/src/tulip/rag/stores/memory.py
count
async
¶
clear
async
¶
PgVectorStore ¶
PgVectorStore(dsn: str | None = None, host: str = 'localhost', port: int = 5432, database: str = 'postgres', user: str = 'postgres', password: str | SecretStr = '', table_name: str = 'tulip_vectors', dimension: int = 1536, distance_metric: str = 'cosine', **kwargs: Any)
Bases: BaseModel, BaseVectorStore
PostgreSQL pgvector store.
pgvector adds vector similarity search to PostgreSQL with: - IVFFlat and HNSW indexing - Cosine, L2, and inner product distance - Integration with existing PostgreSQL data - ACID transactions
Prerequisites
- Install pgvector extension: CREATE EXTENSION vector;
- Install asyncpg: pip install asyncpg
Example (DSN): >>> store = PgVectorStore( ... dsn="postgresql://user:pass@localhost:5432/mydb", ... table_name="documents", ... dimension=1536, ... ) >>> await store.add(document) >>> results = await store.search(query_embedding, limit=5)
Example (Individual params): >>> store = PgVectorStore( ... host="localhost", ... database="mydb", ... user="postgres", ... password="secret", ... dimension=1536, ... )
Note
The pgvector extension must be installed in your PostgreSQL database. Run: CREATE EXTENSION IF NOT EXISTS vector;
Source code in .sdk/src/tulip/rag/stores/pgvector.py
add
async
¶
Add a document.
Source code in .sdk/src/tulip/rag/stores/pgvector.py
add_batch
async
¶
Add multiple documents.
Source code in .sdk/src/tulip/rag/stores/pgvector.py
get
async
¶
Get a document by ID.
Source code in .sdk/src/tulip/rag/stores/pgvector.py
delete
async
¶
Delete a document.
Source code in .sdk/src/tulip/rag/stores/pgvector.py
search
async
¶
search(query_embedding: list[float], limit: int = 10, threshold: float | None = None, metadata_filter: dict[str, Any] | None = None) -> list[SearchResult]
Search for similar documents.
Source code in .sdk/src/tulip/rag/stores/pgvector.py
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 | |
count
async
¶
Count documents.
Source code in .sdk/src/tulip/rag/stores/pgvector.py
clear
async
¶
Delete all documents.
Source code in .sdk/src/tulip/rag/stores/pgvector.py
create_index
async
¶
Create vector index for faster similarity search.
Should be called after loading data. IVFFlat indexes require data to determine optimal list assignments.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
index_type
|
str | None
|
Override index type ("ivfflat" or "hnsw") |
None
|
Returns:
| Type | Description |
|---|---|
bool
|
True if index was created, False if already exists |
Example
await store.add_batch(documents) await store.create_index() # Now create the index
Source code in .sdk/src/tulip/rag/stores/pgvector.py
has_index
async
¶
Check if vector index exists.
Source code in .sdk/src/tulip/rag/stores/pgvector.py
OpenSearchVectorStore ¶
OpenSearchVectorStore(hosts: list[str] | None = None, http_auth: tuple[str, str] | None = None, use_ssl: bool = False, index_name: str = 'tulip_vectors', dimension: int = 1024, distance_metric: str = 'cosinesimil', **kwargs: Any)
Bases: BaseModel, BaseVectorStore
OpenSearch vector store with k-NN plugin.
Uses OpenSearch's k-NN plugin for efficient approximate nearest neighbor search. Supports hybrid search combining vectors with full-text search.
Example
store = OpenSearchVectorStore( ... hosts=["localhost:9200"], ... index_name="my_vectors", ... dimension=1024, ... ) await store.add(document) results = await store.search(query_embedding, limit=5)
Example with authentication
store = OpenSearchVectorStore( ... hosts=["search.example.com:443"], ... http_auth=("admin", "password"), ... use_ssl=True, ... )
Source code in .sdk/src/tulip/rag/stores/opensearch.py
add
async
¶
Add a document.
Source code in .sdk/src/tulip/rag/stores/opensearch.py
add_batch
async
¶
Add multiple documents using bulk API.
Source code in .sdk/src/tulip/rag/stores/opensearch.py
get
async
¶
Get a document by ID.
Source code in .sdk/src/tulip/rag/stores/opensearch.py
delete
async
¶
Delete a document.
Source code in .sdk/src/tulip/rag/stores/opensearch.py
search
async
¶
search(query_embedding: list[float], limit: int = 10, threshold: float | None = None, metadata_filter: dict[str, Any] | None = None) -> list[SearchResult]
Search for similar documents using k-NN.
Source code in .sdk/src/tulip/rag/stores/opensearch.py
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 | |
count
async
¶
Count documents.
clear
async
¶
Delete all documents.
Source code in .sdk/src/tulip/rag/stores/opensearch.py
QdrantVectorStore ¶
QdrantVectorStore(dimension: int = 1024, collection_name: str = 'tulip', distance_metric: str = 'cosine', *, location: str | None = None, url: str | None = None, api_key: str | None = None, _client: Any = None)
Bases: BaseVectorStore
Vector store backed by Qdrant.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dimension
|
int
|
Embedding dimension. Used when the collection is created. |
1024
|
collection_name
|
str
|
Qdrant collection. Defaults to |
'tulip'
|
distance_metric
|
str
|
|
'cosine'
|
location
|
str | None
|
Qdrant location string — |
None
|
url
|
str | None
|
Qdrant server URL (e.g. |
None
|
api_key
|
str | None
|
Qdrant Cloud API key. |
None
|
_client
|
Any
|
Injection seam for tests — a pre-built
|
None
|
Source code in .sdk/src/tulip/rag/stores/qdrant.py
ChromaVectorStore ¶
ChromaVectorStore(dimension: int = 1024, collection_name: str = 'tulip', distance_metric: str = 'cosine', *, persist_directory: str | None = None, host: str | None = None, port: int = 8000, _client: Any = None)
Bases: BaseVectorStore
Vector store backed by Chroma.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dimension
|
int
|
Embedding dimension (advertised in |
1024
|
collection_name
|
str
|
Chroma collection. Defaults to |
'tulip'
|
distance_metric
|
str
|
|
'cosine'
|
persist_directory
|
str | None
|
On-disk path for a |
None
|
host
|
str | None
|
Chroma server host (uses |
None
|
port
|
int
|
Chroma server port. |
8000
|
_client
|
Any
|
Injection seam for tests — a pre-built Chroma client bypasses the lazy import. |
None
|
Source code in .sdk/src/tulip/rag/stores/chroma.py
Vector store base contract¶
BaseVectorStore ¶
Bases: ABC
Abstract base class for vector stores.
Provides default implementations for common methods.
add
abstractmethod
async
¶
add_batch
async
¶
Add multiple documents. Override for batch optimization.
get
abstractmethod
async
¶
delete
abstractmethod
async
¶
search
abstractmethod
async
¶
search(query_embedding: list[float], limit: int = 10, threshold: float | None = None, metadata_filter: dict[str, Any] | None = None) -> list[SearchResult]
Search for similar documents.
Source code in .sdk/src/tulip/rag/stores/base.py
count
async
¶
clear
async
¶
VectorStore ¶
Bases: Protocol
Protocol for vector stores.
Vector stores persist documents with embeddings and enable fast similarity search.
Example
store = InMemoryVectorStore() await store.add(doc) results = await store.search(query_embedding, limit=5)
add
async
¶
Add a document.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
document
|
Document
|
Document with embedding |
required |
Returns:
| Type | Description |
|---|---|
str
|
Document ID |
add_batch
async
¶
Add multiple documents.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
documents
|
list[Document]
|
Documents with embeddings |
required |
Returns:
| Type | Description |
|---|---|
list[str]
|
List of document IDs |
get
async
¶
Get a document by ID.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
doc_id
|
str
|
Document identifier |
required |
Returns:
| Type | Description |
|---|---|
Document | None
|
Document or None if not found |
delete
async
¶
Delete a document.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
doc_id
|
str
|
Document identifier |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if deleted, False if not found |
search
async
¶
search(query_embedding: list[float], limit: int = 10, threshold: float | None = None, metadata_filter: dict[str, Any] | None = None) -> list[SearchResult]
Search for similar documents.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query_embedding
|
list[float]
|
Query vector |
required |
limit
|
int
|
Maximum results |
10
|
threshold
|
float | None
|
Minimum similarity score (0.0-1.0) |
None
|
metadata_filter
|
dict[str, Any] | None
|
Filter by metadata fields |
None
|
Returns:
| Type | Description |
|---|---|
list[SearchResult]
|
List of SearchResult sorted by similarity |
Source code in .sdk/src/tulip/rag/stores/base.py
count
async
¶
clear
async
¶
VectorStoreConfig
dataclass
¶
Configuration for vector stores.
Attributes:
| Name | Type | Description |
|---|---|---|
dimension |
int
|
Expected embedding dimension |
distance_metric |
str
|
Distance metric (cosine, l2, dot_product) |
index_type |
str
|
Index type (flat, ivf, hnsw) |
Document
dataclass
¶
Document(id: str, content: str, embedding: list[float] | None = None, metadata: dict[str, Any] = dict(), created_at: datetime = (lambda: datetime.now(UTC))(), content_type: str = 'text', raw_content: bytes | None = None)
A document with optional embedding.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
str
|
Unique document identifier |
content |
str
|
Document text content (or extracted text for multimodal) |
embedding |
list[float] | None
|
Optional embedding vector |
metadata |
dict[str, Any]
|
Optional metadata for filtering |
created_at |
datetime
|
Creation timestamp |
content_type |
str
|
Type of content (text, image, pdf, audio) |
raw_content |
bytes | None
|
Original binary content for multimodal documents |
to_dict ¶
Convert to dictionary.
Source code in .sdk/src/tulip/rag/stores/base.py
from_dict
classmethod
¶
Create from dictionary.
Source code in .sdk/src/tulip/rag/stores/base.py
SearchResult
dataclass
¶
Result from similarity search.
Attributes:
| Name | Type | Description |
|---|---|---|
document |
Document
|
The matching document |
score |
float
|
Similarity score (0.0 to 1.0, higher is more similar) |
distance |
float | None
|
Raw distance metric (interpretation depends on distance type) |
Reranker¶
Re-score candidates after the initial vector search. The Reranker
Protocol lets you plug in any scorer; two implementations ship:
CrossEncoderReranker (local, offline) and CohereReranker (Cohere's
direct rerank API).
Reranker ¶
Bases: ABC
Cross-encoder reranker over retriever candidates.
Implementations score each SearchResult.document.content against
the query string and return the candidates reordered (and optionally
truncated). The SearchResult.score field is replaced with the
reranker's relevance score (typically 0.0-1.0, higher = more relevant).
Two contracts every implementation honours:
- Empty input → empty output.
rerank("...", [])returns[]without making an API call. - No mutation of input. The original list is left untouched; a
new list of new
SearchResultinstances is returned. Safe to call from concurrent contexts.
Example::
from tulip.rag.reranker import CohereReranker
reranker = CrossEncoderReranker(top_n=5)
top = await reranker.rerank("lateral movement over SMB", candidates)
rerank
abstractmethod
async
¶
Return candidates reordered (and optionally truncated) by
relevance to query.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
The query string to score candidates against. |
required |
candidates
|
list[SearchResult]
|
The retriever's hits, typically wide (top-K=50). |
required |
Returns:
| Type | Description |
|---|---|
list[SearchResult]
|
A new list of |
list[SearchResult]
|
length ≤ |
list[SearchResult]
|
Each returned |
list[SearchResult]
|
relevance score in |
list[SearchResult]
|
is preserved on |
Source code in .sdk/src/tulip/rag/reranker/base.py
CrossEncoderReranker ¶
CrossEncoderReranker(*, model: str = DEFAULT_CROSS_ENCODER_MODEL, top_n: int | None = None, device: str | None = None, max_length: int | None = None, _model: Any = None)
Bases: Reranker
Reranker backed by a local sentence-transformers CrossEncoder.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
str
|
HuggingFace cross-encoder model id. Defaults to
|
DEFAULT_CROSS_ENCODER_MODEL
|
top_n
|
int | None
|
Trim the reranked output to the top N candidates. |
None
|
device
|
str | None
|
Torch device string ( |
None
|
max_length
|
int | None
|
Max sequence length for the cross-encoder. |
None
|
_model
|
Any
|
Injection seam for tests — a pre-built object exposing a
|
None
|
Notes
The model's predict call is synchronous; rerank dispatches
it to a threadpool via :func:asyncio.to_thread so it composes
with the async embedding + vector-store calls in a retriever.
Source code in .sdk/src/tulip/rag/reranker/cross_encoder.py
rerank
async
¶
Reorder candidates by local cross-encoder relevance.
Source code in .sdk/src/tulip/rag/reranker/cross_encoder.py
CohereReranker ¶
CohereReranker(*, model: str = DEFAULT_COHERE_RERANK_MODEL, api_key: str | None = None, top_n: int | None = None, max_tokens_per_doc: int | None = None, _client: Any = None)
Bases: Reranker
Reranker backed by Cohere's hosted rerank endpoint.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
str
|
Cohere rerank model id. Defaults to |
DEFAULT_COHERE_RERANK_MODEL
|
api_key
|
str | None
|
Cohere API key. Defaults to the |
None
|
top_n
|
int | None
|
Trim the reranked output to the top N candidates. |
None
|
max_tokens_per_doc
|
int | None
|
Optional per-document truncation passed to the Cohere API. |
None
|
_client
|
Any
|
Injection seam for tests — an object exposing an async
|
None
|
Source code in .sdk/src/tulip/rag/reranker/cohere.py
rerank
async
¶
Reorder candidates by Cohere relevance score.
Source code in .sdk/src/tulip/rag/reranker/cohere.py
Multimodal processing¶
Convert non-text inputs (PDF text + OCR, image OCR, audio
transcription) into the same Document shape the retriever consumes.
ContentType ¶
Bases: str, Enum
Supported content types.
MultimodalProcessor ¶
Unified processor for all content types.
Example
processor = MultimodalProcessor() result = await processor.process(Path("doc.pdf")) print(result.text)
result = await processor.process(image_bytes, content_type=ContentType.IMAGE)
Source code in .sdk/src/tulip/rag/multimodal.py
detect_content_type ¶
Detect content type from content or path.
Source code in .sdk/src/tulip/rag/multimodal.py
process
async
¶
process(content: bytes | str | Path, content_type: ContentType | None = None, **kwargs: Any) -> ProcessedContent
Process content of any supported type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
content
|
bytes | str | Path
|
Content to process (bytes, string, or path) |
required |
content_type
|
ContentType | None
|
Explicit content type (auto-detected if None) |
None
|
**kwargs
|
Any
|
Additional processor options |
{}
|
Returns:
| Type | Description |
|---|---|
ProcessedContent
|
ProcessedContent with extracted text |
Source code in .sdk/src/tulip/rag/multimodal.py
ProcessedContent
dataclass
¶
ProcessedContent(text: str, content_type: ContentType, metadata: dict[str, Any] = dict(), chunks: list[str] | None = None, raw_content: bytes | None = None)
Result of content processing.
Attributes:
| Name | Type | Description |
|---|---|---|
text |
str
|
Extracted/generated text for embedding |
content_type |
ContentType
|
Original content type |
metadata |
dict[str, Any]
|
Additional metadata from processing |
chunks |
list[str] | None
|
If content was chunked, the individual chunks |
raw_content |
bytes | None
|
Original binary content (for storage) |
process_content
async
¶
process_content(content: bytes | str | Path, content_type: ContentType | None = None, **kwargs: Any) -> ProcessedContent
Process any content type and extract text for embedding.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
content
|
bytes | str | Path
|
Content to process |
required |
content_type
|
ContentType | None
|
Optional content type hint |
None
|
Returns:
| Type | Description |
|---|---|
ProcessedContent
|
ProcessedContent with extracted text |
Example
result = await process_content(Path("document.pdf")) embeddings = await embedder.embed(result.text)
Source code in .sdk/src/tulip/rag/multimodal.py
Tool wiring¶
Expose a retriever as an agent tool so the model can call it like any
other function. Use create_rag_tool for a one-shot retrieval call
and create_rag_context_tool when you want the agent to inject
retrieved context into its own response.
RAGToolkit ¶
Collection of RAG tools for comprehensive knowledge access.
Provides multiple tools for different retrieval patterns: - search: Find specific documents with scores - context: Get formatted context for prompts - lookup: Find a specific document by ID
Example
toolkit = RAGToolkit(retriever) agent = Agent( ... model=model, ... tools=toolkit.get_tools(), ... )
Source code in .sdk/src/tulip/rag/tools.py
get_tools ¶
search_tool ¶
context_tool ¶
lookup_tool ¶
Get the lookup tool.
Source code in .sdk/src/tulip/rag/tools.py
create_rag_tool ¶
create_rag_tool(retriever: RAGRetriever, name: str = 'search_knowledge', description: str | None = None, limit: int = 5, threshold: float | None = 0.5) -> Any
Create a RAG search tool for agent use.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
retriever
|
RAGRetriever
|
RAGRetriever instance |
required |
name
|
str
|
Tool name |
'search_knowledge'
|
description
|
str | None
|
Tool description |
None
|
limit
|
int
|
Default number of results |
5
|
threshold
|
float | None
|
Default similarity threshold |
0.5
|
Returns:
| Type | Description |
|---|---|
Any
|
Decorated tool function |
Example
retriever = RAGRetriever(embedder=embedder, store=store) tool = create_rag_tool(retriever)
agent = Agent( ... model=model, ... tools=[tool], ... )
Source code in .sdk/src/tulip/rag/tools.py
create_rag_context_tool ¶
create_rag_context_tool(retriever: RAGRetriever, name: str = 'get_context', description: str | None = None, limit: int = 3) -> Any
Create a RAG tool that returns context as formatted text.
This is useful when you want the agent to receive context directly without processing individual results.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
retriever
|
RAGRetriever
|
RAGRetriever instance |
required |
name
|
str
|
Tool name |
'get_context'
|
description
|
str | None
|
Tool description |
None
|
limit
|
int
|
Number of documents to include |
3
|
Returns:
| Type | Description |
|---|---|
Any
|
Decorated tool function |