Sophons
Sophons is a Python SDK for agent memory, retrieval, and RAG.
The goal is to provide small, reusable building blocks that can be used across different agent SDKs. Sophons should not be tied to one framework, one model provider, or one memory implementation.
Status
Sophons is early-stage. The current repo contains the package skeleton, shared data types, and base interfaces for the first SDK layers.
Implemented so far:
DocumentMessageRetriever/AsyncRetrieverLoader/AsyncLoaderSplitterTool/AsyncToolBM25Retriever,SemanticRetriever,HybridRetriever,MultiQueryRetriever
What Sophons Provides
- Short-term conversation memory
- Long-term memory
- Generic retrievers for RAG, memory, and code/docs search
- RAG workflows built on retrievers
- Agent-callable tools
- Integrations with agent SDKs
Package Shape
sophons/
documents/ shared document schema
models/ messages and model interfaces
loaders/ source -> documents
splitters/ documents -> chunks
retrieval/ generic retrievers: vector, BM25, hybrid, rerank
rag/ RAG workflows built on retrievers
memory/ short-term and long-term memory
tools/ expose retrievers and memory as agent tools
integrations/ adapters for agent SDKs
Install For Local Development
uv sync --group dev
Run tests:
uv run python -m pytest
Install an optional embedding provider when needed:
uv sync --group dev --extra openai
# or
uv sync --group dev --extra sentence-transformers
Core Types
from sophons import Document, Message
document = Document(
id="auth.md#chunk_1",
content="Token refresh happens every 60 minutes.",
metadata={"source": "auth.md"},
)
message = Message(
role="user",
content="How does token refresh work?",
)
Document is for knowledge/context.
Message is for model and agent conversation.
Embeddings
sophons.embeddings is the stable public embedding API. Core protocols are
always available, while provider integrations and their SDKs are loaded only
when requested.
from sophons.embeddings import EmbeddingModel, OpenAIEmbeddings
embedder: EmbeddingModel = OpenAIEmbeddings(
api_key="...",
model="text-embedding-3-small",
)
query_vector = embedder.embed_query("How does token refresh work?")
document_vectors = embedder.embed_documents([
"Token refresh happens every 60 minutes.",
"Sessions expire after 24 hours.",
])
For asynchronous applications:
from sophons.embeddings import AsyncOpenAIEmbeddings
embedder = AsyncOpenAIEmbeddings(api_key="...")
query_vector = await embedder.embed_query("How does token refresh work?")
Applications should import from sophons.embeddings, not from internal
sophons.integrations modules. Provider packages remain optional: importing
the public namespace does not import OpenAI or Sentence Transformers.
Sentence Transformers can return L2-normalized vectors when the consuming application uses cosine similarity or dot product over unit vectors:
from sophons.embeddings import SentenceTransformerEmbeddings
embedder = SentenceTransformerEmbeddings(
model="sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2",
normalize=True,
)
Normalization is explicit and disabled by default so Sophons does not impose a similarity policy on every consumer.
Models
sophons.models is also the stable public API for chat-model contracts,
messages, settings, and provider implementations. Provider SDKs are loaded only
when their model is constructed.
Install DeepSeek support:
uv sync --group dev --extra deepseek
Then create the model through the public API:
from sophons.models import DeepSeekModel, Message
model = DeepSeekModel(
model="deepseek-chat",
api_key="...",
)
response = model.invoke([
Message(role="user", content="Explain Article 43."),
])
The provider-independent ChatModel protocol remains available from the same
namespace:
from sophons.models import ChatModel, DeepSeekModel
model: ChatModel = DeepSeekModel(
model="deepseek-chat",
api_key="...",
)
Applications should prefer sophons.models over internal
sophons.integrations.models imports.
Vector Stores
sophons.stores is the stable public API for vector-store contracts and
implementations. The in-memory store has no optional dependency:
from sophons.stores import InMemoryVectorStore, VectorStore
store: VectorStore = InMemoryVectorStore()
Chroma remains optional and is loaded only when constructed:
uv sync --extra chroma
from sophons.stores import ChromaVectorStore
store = ChromaVectorStore(
collection="katiba",
path="./katiba_index",
)
Applications should prefer sophons.stores over internal
sophons.integrations.vector_stores imports.
For exact cosine search over an existing NumPy matrix:
uv sync --extra numpy
from sophons.stores import NumPyVectorStore
store = NumPyVectorStore()
store.add_matrix(documents, vectors)
results = store.search(query_vector, limit=10)
NumPyVectorStore keeps vectors as a float32 matrix and performs bulk cosine
search without converting the matrix to nested Python lists.
Retriever Pattern
Retrievers follow one simple contract:
query -> documents
That means the same retriever can be used in different places:
- A RAG app can retrieve documents before answering.
- A memory system can retrieve relevant user memories.
- An agent can expose a retriever as a tool.
- A framework adapter can wrap the retriever for OpenAI, Strands, LangChain-style APIs, or another SDK.
The first retriever layer includes the common foundation:
- Vector retrieval
- BM25 lexical retrieval
- Hybrid retrieval
- Multi-query retrieval
More advanced retrievers can wrap or combine those foundations:
- Contextual compression
- Self-query retrieval
- Reranking
- Routing
- Ensemble retrieval
Loader And Splitter Pattern
Loaders ingest external sources:
source -> documents
Splitters prepare documents for indexing and retrieval:
documents -> chunks
Together:
source -> Loader -> Documents -> Splitter -> Chunks -> Retriever
To watch a run as it happens, stream it:
from sophons.agents import Agent
from sophons.models import TextDelta
async for event in Agent(model=model, tools=tools).stream("What is 17 plus 25?"):
if isinstance(event, TextDelta):
print(event.text, end="")
stream() yields model events (text, tool-call fragments, usage), tool start
and finish events, and finally the same result run() returns. A model without
native streaming is delivered as a single piece. Streamed text has not been
through output validation; treat it as provisional until the result arrives.
To read a file as structured elements instead of flat text, parse it:
from sophons.parsers import Blob, default_registry
from sophons.splitters import StructureAwareSplitter
parsed = default_registry().parse(Blob.from_path("contract.docx"))
for element in parsed.leaves():
print(element.kind, parsed.text_of(element)[:60])
chunks = StructureAwareSplitter(max_chunk_size=1000).split_parsed(parsed)
A parser takes bytes and returns one ParsedDocument: the extracted text plus
typed elements (headings, paragraphs, list items, tables with rows and cells)
recorded as offsets into that text. Built-in parsers cover plain text, Markdown,
PDF text layers (sophons[pdf]) and Word. Chunks keep their offsets, pages and
heading path, so a quotation can be traced to its place in the source.
Scanned PDFs and images are read with sophons[ocr] (Tesseract). For layout
analysis that finds unnumbered headings, lists and tables, install
sophons[docling] and use default_registry(layout=True). Either way,
clean(parsed) then removes page numbers and running headers from the content,
separates numbered clauses, sets heading depth from numbering and rejoins
sentences split by a page break, without altering the extracted text.
For structure-aware PDFs, install sophons[pdf-structure] and use:
from sophons.loaders import PDFStructureLoader
from sophons.splitters import StructureAwareSplitter
document = PDFStructureLoader("contract.pdf").load()[0]
chunks = StructureAwareSplitter(max_chunk_size=1000).split(document)
The loader converts PDF layout to Markdown with PyMuPDF4LLM. The splitter preserves detected sections and blocks, with source page metadata. See the walkthrough for inspecting intermediate structure, OCR options, and limitations.
Tool Pattern
Tools follow a simple agent-facing contract:
structured args -> structured result
Tools are how agents will call Sophons capabilities such as memory search, document retrieval, or RAG context retrieval.
Roadmap
Next small implementation steps:
- Text loading and recursive splitting
- BM25 lexical retrieval
- DeepSeek chat integration
- Basic BM25-based RAG pipeline
- Hugging Face embeddings and vector retrieval
Metadata
Release files for sophons 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sophons-0.1.0.tar.gz | 1.2 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sophons-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.4 MB
Release files / sophons-0.1.0.tar.gz
| Download URL | sophons-0.1.0.tar.gz |
|---|---|
| Size | 1.2 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5933996b788318e64706da6517bf29de602458f28bcd55d34cca36c048904608
|
|
BLAKE2b-256 checksum How to use checksums |
06bb2ed5111bcd1a658e8473b3ff17662114dea7b20603bda47243911b8db9d4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.7
|
Release files / sophons-0.1.0-py3-none-any.whl
| Download URL | sophons-0.1.0-py3-none-any.whl |
|---|---|
| Size | 217.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
23ed4972107847f592bcc7b082d7893fd112e2750f468db3c5a9b92140145f6b
|
|
BLAKE2b-256 checksum How to use checksums |
01d4fa2f3fe769a4280f6077073029bb2567cc88b78d456c6a2275ad3ae3602a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.7
|