Skip to main content

Personalization memory connector for LangChain and LangGraph applications

Project description

personiq

Personalization memory connector for LangChain and LangGraph.

One adapter. Any LLM. Works alongside any other memory system.

personiq automatically extracts what matters about each user from their conversations, stores it with hybrid search (BM25 + semantic + recency), and injects it into future prompts — so your chatbot, recommendation engine, or AI application feels like it genuinely knows the user.

from langchain_groq import ChatGroq
from personiq import PersoniqAdapter

piq = PersoniqAdapter(llm=ChatGroq(model="llama-3.1-8b-instant"))

# LangGraph — two lines:
builder.add_node("personiq_load", piq.load_node)
builder.add_node("personiq_save", piq.save_node)

# Direct — three lines:
ctx    = piq.persona("alice", user_message)
system = f"{ctx}\n{base_system}" if ctx else base_system
await piq.learn("alice", messages)

Installation

pip install personiq

# Then install your LLM provider:
pip install personiq[groq]        # Groq  (llama, gemma, mixtral)
pip install personiq[openai]      # OpenAI
pip install personiq[anthropic]   # Anthropic (Claude)
pip install personiq[ollama]      # Ollama — local, no API key needed
pip install personiq[mistral]     # Mistral

What personiq does

  1. Extracts durable facts from conversations using your LLM (skills, goals, preferences, context, style, personal info)
  2. Stores them in a local SQLite database with vector embeddings and BM25 keyword indexes
  3. Retrieves the most relevant memories per query using hybrid search (BM25 + cosine similarity + recency decay) fused with Reciprocal Rank Fusion
  4. Injects them into your system prompt — either as a structured bullet list or a natural-language persona paragraph

Integration styles

Style 1 — LangGraph nodes (recommended)

from langchain_groq import ChatGroq
from langchain_core.messages import HumanMessage, SystemMessage
from langgraph.graph import END, StateGraph
from personiq import PersoniqAdapter, PersoniqState

piq = PersoniqAdapter(llm=ChatGroq(model="llama-3.1-8b-instant"), mode="persona")

async def chat(state: PersoniqState) -> dict:
    mem_ctx  = state.get("memory_context", "")
    system   = f"{mem_ctx}\nYou are a helpful assistant." if mem_ctx else "You are a helpful assistant."
    messages = [SystemMessage(content=system)] + list(state["messages"])
    response = await llm.ainvoke(messages)
    return {"messages": [response]}

builder = StateGraph(PersoniqState)
builder.add_node("personiq_load", piq.load_node)   # retrieves memories
builder.add_node("chat",          chat)
builder.add_node("personiq_save", piq.save_node)   # saves memories

builder.set_entry_point("personiq_load")
builder.add_edge("personiq_load", "chat")
builder.add_edge("chat",          "personiq_save")
builder.add_edge("personiq_save", END)
graph = builder.compile()

result = await graph.ainvoke({
    "user_id":  "alice",
    "messages": [HumanMessage(content="I'm a backend engineer using Go and Postgres.")],
})

Style 2 — Direct usage (any framework)

piq = PersoniqAdapter(llm=llm)

# Before your LLM call — get personalization context:
ctx    = piq.context("alice", user_message)   # structured bullets
# or:
ctx    = piq.persona("alice", user_message)   # natural-language paragraph (better UX)

system = f"{ctx}\n{base_system}" if ctx else base_system

# After your LLM call — save new memories (fire-and-forget):
await piq.learn("alice", messages)

Style 3 — Inject into messages list

messages = piq.inject(messages, user_id="alice", query=user_message)
response = await llm.ainvoke(messages)

Two context modes

mode="context" (default) — structured bullets

[personiq: what I know about this user]

Technical background:
  • User is an experienced Go developer
  • User works with PostgreSQL and Redis

Goals & current work:
  • User is building a distributed payment service

[end of personiq context]

mode="persona" — natural language

You already know this user. Here is what you know about them:
They work with Go and PostgreSQL. They're currently focused on: building
a distributed payment service. They prefer concise, direct technical answers.
Use this naturally — don't recite it back, just let it shape how you respond.

Works alongside other memory systems

personiq is fully independent. It stores memories in its own SQLite database and does not interfere with Mem0, Zep, LangMem, or any other memory setup.

piq  = PersoniqAdapter(llm=llm)   # personiq — personalization memory
mem0 = MemoryClient(...)          # Mem0 — conversation history

# Both work independently on the same conversation
ctx  = piq.persona("alice", message)
hist = mem0.get_history("alice")

Supported LLM providers

Provider Install Import
Groq pip install personiq[groq] from langchain_groq import ChatGroq
OpenAI pip install personiq[openai] from langchain_openai import ChatOpenAI
Anthropic pip install personiq[anthropic] from langchain_anthropic import ChatAnthropic
Ollama (local) pip install personiq[ollama] from langchain_ollama import ChatOllama
Mistral pip install personiq[mistral] from langchain_mistralai import ChatMistralAI

Any BaseChatModel from LangChain works.


Memory categories

Category What gets stored
technical Languages, frameworks, tools, platforms
goal Objectives, projects, purchase intent, problems to solve
preference Likes, dislikes, favourites — products, approaches, content
context Occupation, location, education, life stage
style Communication style, verbosity, tone preference
personal Name, relationships, values, life events

Configuration

from personiq import PersoniqAdapter, PersoniqConfig

config = PersoniqConfig(
    db_path              = "./myapp.db",
    embedding_backend    = "local",      # "local" (default) or "openai"
    top_k                = 5,
    similarity_threshold = 0.30,
    semantic_weight      = 0.60,
    bm25_weight          = 0.30,
    recency_weight       = 0.10,
    async_extraction     = True,         # fire-and-forget (default)
    mode                 = "persona",    # "context" or "persona"
    debug                = False,
)

piq = PersoniqAdapter(llm=llm, config=config)

All settings are also configurable via environment variables (prefix PERSONIQ_).


Memory management

# Inspect stored memories
for m in piq.memories("alice"):
    print(f"[{m.category:12}] {m.content}  (importance: {m.importance_score:.2f})")

# Filter by category
goals = piq.memories("alice", category="goal")
tech  = piq.memories("alice", category="technical")

# Count
print(piq.count("alice"))

# Delete all memories (GDPR / account deletion)
piq.forget("alice")

Running tests

pip install -e ".[dev]"
pytest tests/ -v

No API key required — all LLM calls are mocked in tests.


Use cases

personiq is designed for any application where knowing the user improves outcomes:

  • Personalised chatbots — assistant remembers user's name, job, preferences across sessions
  • Recommendation engines — surface products/content matching stored preferences and goals
  • AI sales agents — know the prospect's industry, pain points, and purchase intent
  • Content personalisation — adapt tone, complexity, and topics to each user's profile
  • Ad targeting — build rich user profiles from natural conversation signals
  • Customer support — remember past issues, preferences, and technical environment

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

personiq-0.2.0.tar.gz (35.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

personiq-0.2.0-py3-none-any.whl (28.4 kB view details)

Uploaded Python 3

File details

Details for the file personiq-0.2.0.tar.gz.

File metadata

  • Download URL: personiq-0.2.0.tar.gz
  • Upload date:
  • Size: 35.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.11

File hashes

Hashes for personiq-0.2.0.tar.gz
Algorithm Hash digest
SHA256 53866a9926e17484e32cdb9cc2bd5e09f12307292ec6b1d55db71c80d711f2e6
MD5 0bb1b95932e04dd308b06ed81edfa6af
BLAKE2b-256 ecb5fe0047e2daabf4a30904eec9dd9cc42aadf0183720d6a0968da1fbdc096a

See more details on using hashes here.

File details

Details for the file personiq-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: personiq-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 28.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.11

File hashes

Hashes for personiq-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 642100165cd66cad88940e90f3178a69c47d886021e4742dbd1fdad0dd505639
MD5 f88297f92d79466f90c68813784b7e43
BLAKE2b-256 ba4356a0ac9bc35b164c84a66cab30771af39bafff469d93535e9057b487096d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page