Skip to content

Commit 9175eca

Browse files
committed
chore: update python sdk and node errors
1 parent 875707c commit 9175eca

39 files changed

Lines changed: 258 additions & 561 deletions

COMMERCIAL_LICENSE.md

Lines changed: 0 additions & 27 deletions
This file was deleted.

crates/valori-cli/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,4 +385,4 @@ SIFT1M vectors should be placed at `data/sift/sift/sift_base.fvecs`.
385385

386386
## License
387387

388-
AGPLv3 — see the root `LICENSE` file.
388+
MIT OR Apache-2.0 — see `LICENSE-MIT` and `LICENSE-APACHE` at the repo root.

crates/valori-node/src/errors.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ impl IntoResponse for EngineError {
2929
KernelError::InvalidOperation => (StatusCode::BAD_REQUEST, "Invalid operation".to_string()),
3030
KernelError::Overflow => (StatusCode::INTERNAL_SERVER_ERROR, "Numeric overflow".to_string()),
3131
KernelError::InvalidInput => (StatusCode::BAD_REQUEST, "Invalid input".to_string()),
32+
KernelError::MetadataTooLarge => (StatusCode::BAD_REQUEST, "Metadata too large (max 4 KB)".to_string()),
33+
KernelError::DimensionMismatch { expected, found } => (
34+
StatusCode::BAD_REQUEST,
35+
format!(
36+
"Dimension mismatch: node expects {expected}-element vectors, got {found}. \
37+
Use VALORI_DIM={expected} when starting the node, or send {expected}-element vectors."
38+
),
39+
),
3240
_ => (StatusCode::INTERNAL_SERVER_ERROR, "Kernel error".to_string()),
3341
},
3442
EngineError::InvalidInput(msg) => (StatusCode::BAD_REQUEST, msg),

crates/valori-node/tests/graph_cascade.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2025 Varshith Gudur. Licensed under AGPLv3.
1+
// Copyright (c) 2025 Varshith Gudur. Licensed under MIT OR Apache-2.0.
22
//! Graph cascade-delete and reverse-index tests.
33
//!
44
//! These tests verify that:

demo/demo_adapters.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2025 Varshith Gudur. Licensed under AGPLv3.
1+
# Copyright (c) 2025 Varshith Gudur. Licensed under MIT OR Apache-2.0.
22
"""
33
Demo for Valori Adapters (LangChain & LlamaIndex)
44
Prerequisites:

examples/langchain_example.py

Lines changed: 92 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1,165 +1,129 @@
11
"""
2-
Complete LangChain + Valori Example
3-
====================================
2+
LangChain + Valori Example
3+
===========================
44
5-
This example shows how to use Valori as a vector store in LangChain applications.
5+
Uses Valori as a LangChain-compatible vector store via SyncRemoteClient.
66
77
Requirements:
8-
pip install langchain langchain-openai openai valori
8+
pip install langchain langchain-openai openai valoricore
9+
10+
Start a Valori node first:
11+
VALORI_DIM=1536 cargo run -p valori-node
912
1013
Usage:
11-
python examples/langchain_example.py
14+
OPENAI_API_KEY=sk-... python examples/langchain_example.py
1215
"""
1316

1417
import os
15-
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
18+
import sys
19+
from typing import List, Optional
20+
21+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "python"))
22+
23+
from valoricore.remote import SyncRemoteClient
24+
25+
# ── Inline LangChain adapter ──────────────────────────────────────────────────
26+
1627
from langchain_core.documents import Document
17-
from langchain.chains import RetrievalQA
18-
from langchain.text_splitter import CharacterTextSplitter
28+
from langchain_core.vectorstores import VectorStore
1929

20-
# Import Valori adapters
21-
import sys
22-
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'python'))
2330

24-
from valori.adapters import ValoriAdapter, LangChainVectorStore
31+
class ValoriVectorStore(VectorStore):
32+
"""Minimal LangChain VectorStore backed by a Valori node."""
2533

34+
def __init__(self, client: SyncRemoteClient, embedding, collection: str = "default"):
35+
self._client = client
36+
self._embedding = embedding
37+
self._collection = collection
38+
39+
def add_texts(self, texts: List[str], metadatas: Optional[List[dict]] = None, **kwargs) -> List[str]:
40+
vectors = self._embedding.embed_documents(texts)
41+
ids = []
42+
for i, (text, vec) in enumerate(zip(texts, vectors)):
43+
meta = str(metadatas[i]) if metadatas else text
44+
rid = self._client.insert(vec, text=meta, collection=self._collection)
45+
ids.append(str(rid))
46+
return ids
47+
48+
def similarity_search(self, query: str, k: int = 4, **kwargs) -> List[Document]:
49+
vec = self._embedding.embed_query(query)
50+
hits = self._client.search(vec, k=k, collection=self._collection)
51+
return [
52+
Document(
53+
page_content=h.get("metadata", ""),
54+
metadata={"id": h["id"], "score": h["score"]},
55+
)
56+
for h in hits
57+
]
58+
59+
@classmethod
60+
def from_texts(cls, texts, embedding, metadatas=None, **kwargs):
61+
client = SyncRemoteClient(kwargs.get("url", "http://localhost:3000"))
62+
store = cls(client, embedding, kwargs.get("collection", "default"))
63+
store.add_texts(texts, metadatas)
64+
return store
65+
66+
67+
# ── Demo ──────────────────────────────────────────────────────────────────────
2668

2769
def main():
2870
print("=" * 60)
2971
print("LangChain + Valori RAG Example")
3072
print("=" * 60)
31-
32-
# Step 1: Setup Valori adapter
33-
print("\n1. Setting up Valori connection...")
34-
adapter = ValoriAdapter(
35-
base_url="http://localhost:3000", # Your Valori node URL
36-
api_key=os.getenv("VALORI_API_KEY"), # Optional
37-
max_retries=3,
38-
)
39-
print("✓ Connected to Valori node")
40-
41-
# Step 2: Setup embeddings
42-
print("\n2. Initializing OpenAI embeddings...")
43-
embeddings = OpenAIEmbeddings(
44-
model="text-embedding-3-small",
45-
openai_api_key=os.getenv("OPENAI_API_KEY")
46-
)
47-
print("✓ Embeddings ready")
48-
49-
# Step 3: Create Valori vector store
50-
print("\n3. Creating Valori vector store...")
51-
vectorstore = LangChainVectorStore(
52-
adapter=adapter,
53-
embedding=embeddings
54-
)
55-
print("✓ Vector store ready")
56-
57-
# Step 4: Add documents
58-
print("\n4. Adding documents to Valori...")
59-
73+
74+
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
75+
from langchain.chains import RetrievalQA
76+
77+
print("\n1. Connecting to Valori node at http://localhost:3000...")
78+
client = SyncRemoteClient("http://localhost:3000")
79+
print(f" health: {client.health()}")
80+
81+
print("\n2. Initializing OpenAI embeddings (text-embedding-3-small, dim=1536)...")
82+
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
83+
84+
print("\n3. Loading documents into Valori...")
6085
documents = [
6186
"Valori is a deterministic vector database built in Rust.",
6287
"It uses Q16.16 fixed-point arithmetic for bit-identical results.",
6388
"Valori works on x86, ARM, and WASM with identical state hashes.",
6489
"The database includes WAL for crash recovery and durability.",
6590
"Perfect for robotics, embedded AI, and safety-critical applications.",
6691
"Valori provides cryptographic proofs of memory state.",
67-
"It's designed for reproducible AI systems.",
68-
]
69-
70-
metadatas = [
71-
{"source": "docs", "topic": "intro"},
72-
{"source": "docs", "topic": "technical"},
73-
{"source": "docs", "topic": "technical"},
74-
{"source": "docs", "topic": "features"},
75-
{"source": "docs", "topic": "use-cases"},
76-
{"source": "docs", "topic": "features"},
77-
{"source": "docs", "topic": "intro"},
92+
"It is designed for reproducible AI systems.",
7893
]
79-
94+
metadatas = [{"source": "docs", "index": i} for i in range(len(documents))]
95+
96+
vectorstore = ValoriVectorStore(client, embeddings)
8097
ids = vectorstore.add_texts(documents, metadatas)
81-
print(f"✓ Added {len(ids)} documents")
82-
83-
# Step 5: Similarity search
84-
print("\n5. Testing similarity search...")
85-
98+
print(f" inserted {len(ids)} records: {ids}")
99+
100+
print("\n4. Similarity search...")
86101
query = "What makes Valori deterministic?"
87102
results = vectorstore.similarity_search(query, k=3)
88-
89-
print(f"\nQuery: '{query}'")
90-
print("\nTop 3 Results:")
103+
print(f"\n Query: '{query}'")
91104
for i, doc in enumerate(results, 1):
92-
print(f"\n{i}. {doc.page_content}")
93-
print(f" Source: {doc.metadata.get('source')}")
94-
print(f" Topic: {doc.metadata.get('topic')}")
95-
print(f" Distance: {doc.metadata.get('distance', 'N/A')}")
96-
97-
# Step 6: RAG with Question Answering
98-
print("\n" + "=" * 60)
99-
print("6. Building RAG Question Answering Chain")
100-
print("=" * 60)
101-
102-
llm = ChatOpenAI(
103-
model="gpt-4",
104-
temperature=0,
105-
openai_api_key=os.getenv("OPENAI_API_KEY")
106-
)
107-
108-
qa_chain = RetrievalQA.from_chain_type(
105+
print(f" {i}. {doc.page_content[:80]}")
106+
print(f" id={doc.metadata['id']} score={doc.metadata['score']:.4f}")
107+
108+
print("\n5. RAG chain (GPT-4o-mini)...")
109+
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
110+
qa = RetrievalQA.from_chain_type(
109111
llm=llm,
110-
chain_type="stuff",
111112
retriever=vectorstore.as_retriever(search_kwargs={"k": 3}),
112-
return_source_documents=True,
113113
)
114-
115-
# Ask questions
116-
questions = [
117-
"What is Valori?",
118-
"What are the main use cases for Valori?",
119-
"How does Valori ensure reproducibility?",
120-
]
121-
122-
for question in questions:
123-
print(f"\n🤔 Question: {question}")
124-
result = qa_chain({"query": question})
125-
print(f"💡 Answer: {result['result']}")
126-
print(f"📚 Sources: {len(result['source_documents'])} documents")
127-
128-
# Step 7: Demonstrate determinism
129-
print("\n" + "=" * 60)
130-
print("7. Valori's Unique Feature: Deterministic Memory")
131-
print("=" * 60)
132-
133-
# Search same query multiple times
134-
print("\nSearching 3 times for same query...")
135-
query = "deterministic"
136-
137-
hashes = []
138-
for i in range(3):
139-
results = vectorstore.similarity_search(query, k=2)
140-
# In production, you'd get state hash from adapter
141-
print(f" Run {i+1}: {len(results)} results")
142-
143-
print("\n✅ Results are IDENTICAL across runs!")
144-
print(" This is guaranteed by Valori's fixed-point arithmetic")
145-
print(" Try this with FAISS or Chroma - you'll see variations!")
114+
for q in ["What is Valori?", "How does Valori ensure reproducibility?"]:
115+
print(f"\n Q: {q}")
116+
print(f" A: {qa.invoke(q)['result']}")
117+
118+
print("\n6. Verifiable state hash (unique to Valori)...")
119+
h = client.get_state_hash()
120+
print(f" {h}")
121+
print(" Any node replaying the same events produces the exact same hash.")
146122

147123

148124
if __name__ == "__main__":
149-
# Check environment
150125
if not os.getenv("OPENAI_API_KEY"):
151-
print("⚠️ Warning: OPENAI_API_KEY not set")
152-
print(" Set it with: export OPENAI_API_KEY=your-key")
153-
print("\n Continuing anyway (some features may not work)...\n")
154-
155-
try:
156-
main()
157-
print("\n" + "=" * 60)
158-
print("✅ Example completed successfully!")
159-
print("=" * 60)
160-
except Exception as e:
161-
print(f"\n❌ Error: {e}")
162-
print("\nMake sure:")
163-
print(" 1. Valori node is running (cargo run --release -p valori-node)")
164-
print(" 2. OPENAI_API_KEY is set")
165-
print(" 3. Dependencies installed (pip install langchain langchain-openai)")
126+
print("Set OPENAI_API_KEY before running this example.")
127+
sys.exit(1)
128+
main()
129+
print("\nDone.")

0 commit comments

Comments
 (0)