mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-17 21:25:46 +02:00
- Initializing the store with an 'embedding config' -> this contains the
'dims' (used to create the table) and the encoder object (rn langchain
embeddings object, though that is ......)
- Call setup() -> creates the vector table.
Each document has 1 or more vectors associated with it for each json
path in the embedding config.
Would welcome critique and requests!
Leaving the params as the defaults for pgvector but open to feedback if
you think it's important to be able to more transparently configure that
in setup()
```python
from typing import TypedDict, List, Dict, Any, Optional
from langchain_openai import OpenAIEmbeddings
from langgraph.graph import StateGraph
from langgraph.store.postgres import PostgresStore
emb_config = {
"dims": 1536, # OpenAI embedding dimensions
"embed": OpenAIEmbeddings(model="text-embedding-3-small"),
"distance_type": "cosine",
}
with PostgresStore.from_conn_string(
"postgres://postgres:postgres@localhost:5441",
embedding=emb_config,
) as store:
store.setup()
# Define the state type for our graph
class State(TypedDict):
query: str
results: Optional[List[Dict[str, Any]]]
def put_stuff(state: State) -> State:
docs = [
("doc1", {"text": "red apple in kitchen"}),
("doc2", {"text": "blue car in garage"}),
("doc3", {"text": "green apple on table"}),
]
for key, value in docs:
store.put(("docs",), key, value)
def search_stuff(state: State) -> State:
"""Search for documents using vector similarity."""
results = store.search(("docs",), query=state["query"])
return {"results": results}
builder = StateGraph(State)
builder.add_node(put_stuff)
builder.add_node(search_stuff)
builder.add_edge("__start__", "put_stuff")
builder.add_edge("put_stuff", "search_stuff")
# Compile
with PostgresStore.from_conn_string(
"postgres://postgres:postgres@localhost:5441",
embedding=emb_config,
) as store:
chain = builder.compile(store=store)
result = chain.invoke({"query": "sour apple"})
# Print results
for doc in result["results"]:
print(doc.key)
print(doc.value)
print(doc.response_metadata)
```
56 lines
1.8 KiB
Python
56 lines
1.8 KiB
Python
"""Embedding utilities for testing."""
|
|
|
|
import math
|
|
import random
|
|
from collections import Counter, defaultdict
|
|
from typing import Any
|
|
|
|
from langchain_core.embeddings import Embeddings
|
|
|
|
|
|
class CharacterEmbeddings(Embeddings):
|
|
"""Simple character-frequency based embeddings using random projections."""
|
|
|
|
def __init__(self, dims: int = 50, seed: int = 42):
|
|
"""Initialize with embedding dimensions and random seed."""
|
|
self._rng = random.Random(seed)
|
|
self.dims = dims
|
|
# Create projection vector for each character lazily
|
|
self._char_projections: defaultdict[str, list[float]] = defaultdict(
|
|
lambda: [
|
|
self._rng.gauss(0, 1 / math.sqrt(self.dims)) for _ in range(self.dims)
|
|
]
|
|
)
|
|
|
|
def _embed_one(self, text: str) -> list[float]:
|
|
"""Embed a single text."""
|
|
counts = Counter(text)
|
|
total = sum(counts.values())
|
|
|
|
if total == 0:
|
|
return [0.0] * self.dims
|
|
|
|
embedding = [0.0] * self.dims
|
|
for char, count in counts.items():
|
|
weight = count / total
|
|
char_proj = self._char_projections[char]
|
|
for i, proj in enumerate(char_proj):
|
|
embedding[i] += weight * proj
|
|
|
|
norm = math.sqrt(sum(x * x for x in embedding))
|
|
if norm > 0:
|
|
embedding = [x / norm for x in embedding]
|
|
|
|
return embedding
|
|
|
|
def embed_documents(self, texts: list[str]) -> list[list[float]]:
|
|
"""Embed a list of documents."""
|
|
return [self._embed_one(text) for text in texts]
|
|
|
|
def embed_query(self, text: str) -> list[float]:
|
|
"""Embed a query string."""
|
|
return self._embed_one(text)
|
|
|
|
def __eq__(self, other: Any) -> bool:
|
|
return isinstance(other, CharacterEmbeddings) and self.dims == other.dims
|