# OpenAI

> Give your OpenAI app a memory of each of your users with the Responses API. What is known goes in the instructions, and each exchange is saved after the reply.

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

```bash
pip install openai geniffy
```

```bash
npm install openai geniffy
```

Set `OPENAI_API_KEY`, and `GENIFFY_API_KEY` from **API keys** in the Geniffy app.

## Remember each user

```python
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 reply
```

```ts
import 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()`.

```python
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 reply
```

```ts
import 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.

Source: https://docs.geniffy.com/integrations/openai
