Haystack
Give a Haystack pipeline or agent a memory of each of your users. In a pipeline, one small component puts what is known about the user that bears on their message in front of the model; an agent can instead look things up with a recall tool. Either way, each exchange is saved after the reply.
Install
pip install haystack-ai geniffyuv add haystack-ai geniffySet OPENAI_API_KEY, and GENIFFY_API_KEY from API keys in the Geniffy app. Any chat generator
Haystack supports works the same way.
Remember each user
from geniffy import Geniffy
from haystack import Pipeline, component
from haystack.components.builders import ChatPromptBuilder
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage
geniffy = Geniffy() # reads GENIFFY_API_KEY
@component
class GeniffyMemory:
"""What is known about one user that bears on their message."""
@component.output_types(memory=str)
def run(self, user_id: str, query: str) -> dict:
return {"memory": geniffy.space(f"user_{user_id}").context(query)}
pipe = Pipeline()
pipe.add_component("memory", GeniffyMemory())
pipe.add_component("prompt", ChatPromptBuilder(template=[
ChatMessage.from_system("You are a helpful assistant.\n\n<memory>\n{{ memory }}\n</memory>"),
ChatMessage.from_user("{{ query }}"),
]))
pipe.add_component("llm", OpenAIChatGenerator(model="gpt-5.5"))
pipe.connect("memory.memory", "prompt.memory")
pipe.connect("prompt.prompt", "llm.messages")
def chat(user_id: str, message: str) -> str:
out = pipe.run({"memory": {"user_id": user_id, "query": message}, "prompt": {"query": message}})
reply = out["llm"]["replies"][0].text or ""
geniffy.space(f"user_{user_id}").memories.add(messages=[{"role": "user", "content": message},
{"role": "assistant", "content": reply}])
return replyCall it with the user from your own sign-in:
reply = chat(user.id, "Who signs the Lumen renewal?")The model sees what is known about this user, each line with where it came from. When nothing is known, the block says so in one sentence, so the model says it doesn't know instead of guessing.
Let the agent look things up
To let an agent decide when to look something up, give it a recall tool. The tool reads the user from the
agent's state, so the model never sees or chooses whose memory it reads.
from geniffy import Geniffy
from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage
from haystack.tools import Tool
geniffy = Geniffy()
def recall(query: str, user_id: str) -> str:
return geniffy.space(f"user_{user_id}").context(query)
agent = Agent(
chat_generator=OpenAIChatGenerator(model="gpt-5.5"),
tools=[Tool(name="recall", description="Look up what is known about the user, with where it came from.",
parameters={"type": "object", "properties": {"query": {"type": "string", "description": "What to look up"}},
"required": ["query"]},
function=recall, inputs_from_state={"user_id": "user_id"})],
system_prompt="You are a helpful assistant. Use recall before answering anything that depends on what the user said before.",
state_schema={"user_id": {"type": str}},
)
def chat(user_id: str, message: str) -> str:
result = agent.run(messages=[ChatMessage.from_user(message)], user_id=user_id)
reply = result["last_message"].text or ""
geniffy.space(f"user_{user_id}").memories.add(messages=[{"role": "user", "content": message},
{"role": "assistant", "content": reply}])
return reply