OpenAI
Give your OpenAI app a memory of each of your users. Before the model answers, what is known about the user goes in the instructions; after it answers, the exchange is saved, so the next conversation starts knowing it.
Install
pip install openai geniffynpm install openai geniffySet OPENAI_API_KEY, and GENIFFY_API_KEY from API keys in the Geniffy app.
Remember each user
from geniffy import Geniffy
from openai import OpenAI
client = OpenAI() # reads OPENAI_API_KEY
geniffy = Geniffy() # reads GENIFFY_API_KEY
def chat(user_id: str, message: str, history: list) -> str:
"""history is the conversation so far, as {"role", "content"} messages."""
mem = geniffy.space(f"user_{user_id}")
turn = {"role": "user", "content": message}
context = mem.context(message) # what is known that bears on the message
response = client.responses.create(
model="gpt-5.5",
instructions=f"You are a helpful assistant.\n\n<memory>\n{context}\n</memory>",
input=[*history, turn],
)
reply = response.output_text
mem.memories.add(messages=[turn, {"role": "assistant", "content": reply}])
return replyimport OpenAI from "openai";
import { Geniffy } from "geniffy";
const client = new OpenAI(); // reads OPENAI_API_KEY
const geniffy = new Geniffy(); // reads GENIFFY_API_KEY
type Turn = { role: "user" | "assistant"; content: string };
// history is the conversation so far.
export async function chat(userId: string, message: string, history: Turn[]) {
const mem = geniffy.space(`user_${userId}`);
const turn: Turn = { role: "user", content: message };
const context = await mem.context(message); // what is known that bears on the message
const response = await client.responses.create({
model: "gpt-5.5",
instructions: `You are a helpful assistant.\n\n<memory>\n${context}\n</memory>`,
input: [...history, turn],
});
const reply = response.output_text;
await mem.memories.add({ messages: [turn, { role: "assistant", content: reply }] });
return reply;
}The model now sees what is known about this user before every reply, 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.
On Chat Completions, put the same block in a developer message at the start of messages, and save the
exchange the same way.
Let the model look things up
To let the model decide when to look something up, give it a recall function, and answer each call with
context().
import json
from geniffy import Geniffy
from openai import OpenAI
client = OpenAI()
geniffy = Geniffy()
INSTRUCTIONS = ("You are a helpful assistant. Use recall before answering anything "
"that depends on what the user said before.")
RECALL = {
"type": "function",
"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"],
"additionalProperties": False,
},
"strict": True,
}
def chat(user_id: str, message: str) -> str:
mem = geniffy.space(f"user_{user_id}")
response = client.responses.create(
model="gpt-5.5", instructions=INSTRUCTIONS, tools=[RECALL], input=message,
)
while calls := [item for item in response.output if item.type == "function_call"]:
outputs = [
{"type": "function_call_output", "call_id": call.call_id,
"output": mem.context(json.loads(call.arguments)["query"])}
for call in calls
]
response = client.responses.create(
model="gpt-5.5", instructions=INSTRUCTIONS, tools=[RECALL],
previous_response_id=response.id, input=outputs,
)
reply = response.output_text
mem.memories.add(messages=[{"role": "user", "content": message},
{"role": "assistant", "content": reply}])
return replyimport OpenAI from "openai";
import { Geniffy } from "geniffy";
const client = new OpenAI();
const geniffy = new Geniffy();
const instructions =
"You are a helpful assistant. Use recall before answering anything " +
"that depends on what the user said before.";
const recall: OpenAI.Responses.FunctionTool = {
type: "function",
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"],
additionalProperties: false,
},
strict: true,
};
export async function chat(userId: string, message: string) {
const mem = geniffy.space(`user_${userId}`);
let response = await client.responses.create({
model: "gpt-5.5",
instructions,
tools: [recall],
input: message,
});
for (;;) {
const calls = response.output.filter((item) => item.type === "function_call");
if (calls.length === 0) break;
const outputs = await Promise.all(
calls.map(async (call) => ({
type: "function_call_output" as const,
call_id: call.call_id,
output: await mem.context(JSON.parse(call.arguments).query),
})),
);
response = await client.responses.create({
model: "gpt-5.5",
instructions,
tools: [recall],
previous_response_id: response.id,
input: outputs,
});
}
const reply = response.output_text;
await mem.memories.add({
messages: [
{ role: "user", content: message },
{ role: "assistant", content: reply },
],
});
return reply;
}Instructions are not carried over from the previous response, so each follow-up sends them again. The
tool answers with context(), so the model reads the same lines, with their sources, that the instructions
would hold.