{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Memora-OpenAI Tutorial\n", "\n", "**A drop-in replacement for the OpenAI Python client with automatic memory integration**\n", "\n", "## What is Memora-OpenAI?\n", "\n", "`memora-openai` is a transparent wrapper around the official OpenAI Python client that automatically:\n", "\n", "- 🧠 **Injects relevant memories** from your Memora system into conversations\n", "- 💾 **Stores conversation history** to Memora for future retrieval \n", "- 🔄 **Works seamlessly** with existing OpenAI code (just change the import)\n", "- ⚡ **Supports both sync and async** clients\n", "\n", "## Why Use It?\n", "\n", "### The Problem\n", "\n", "AI assistants typically have no memory of previous conversations. Each interaction starts fresh, requiring you to:\n", "- Repeat context manually\n", "- Copy-paste relevant information\n", "- Build custom RAG pipelines\n", "- Manage conversation history yourself\n", "\n", "### The Solution\n", "\n", "Memora-OpenAI gives your AI **automatic long-term memory**:\n", "- Remembers past conversations\n", "- Recalls user preferences and facts\n", "- Maintains context across sessions\n", "- Zero code changes to your existing OpenAI usage\n", "\n", "## Prerequisites\n", "\n", "1. **Memora API server running** (see main Memora README)\n", "2. **OpenAI API key** or compatible API (Groq, OpenRouter, etc.)\n", "3. **Python >= 3.10**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Setup\n", "\n", "First, let's set up our environment and configure Memora integration:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "jupyter": { "is_executing": true } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "✓ Memora configured successfully!\n", "\n", "NOTE: This tutorial uses AsyncOpenAI which works perfectly in Jupyter notebooks.\n", "For regular Python scripts, you can use the sync OpenAI client instead.\n" ] } ], "source": [ "import os\n", "from memora_openai import configure, AsyncOpenAI\n", "\n", "# Set your API keys\n", "# Option 1: Use Groq (fast and free)\n", "GROQ_API_KEY = os.getenv(\"GROQ_API_KEY\", \"your-groq-api-key\")\n", "if not GROQ_API_KEY:\n", " raise (\"GROQ_API_KEY not set\") \n", "\n", "# Option 2: Use OpenAI\n", "# OPENAI_API_KEY = os.getenv(\"OPENAI_API_KEY\", \"sk-...\")\n", "\n", "# Configure Memora integration\n", "configure(\n", " memora_api_url=\"http://localhost:8080\", # Your Memora API server\n", " agent_id=\"tutorial-user\", # Unique ID for this user/agent\n", " store_conversations=True, # Auto-save conversations\n", " inject_memories=True, # Auto-inject relevant context\n", ")\n", "\n", "print(\"✓ Memora configured successfully!\")\n", "print(\"\")\n", "print(\"NOTE: This tutorial uses AsyncOpenAI which works perfectly in Jupyter notebooks.\")\n", "print(\"For regular Python scripts, you can use the sync OpenAI client instead.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Example 1: Basic Usage (No Changes Needed!)\n", "\n", "Use the OpenAI client exactly as you normally would. Memora works transparently in the background." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "=== First Conversation ===\n" ] }, { "ename": "AttributeError", "evalue": "'MemoraInterceptor' object has no attribute 'inject_memories'", "output_type": "error", "traceback": [ "\u001b[31m---------------------------------------------------------------------------\u001b[39m", "\u001b[31mAttributeError\u001b[39m Traceback (most recent call last)", "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[7]\u001b[39m\u001b[32m, line 9\u001b[39m\n\u001b[32m 7\u001b[39m \u001b[38;5;66;03m# First conversation - establish some facts\u001b[39;00m\n\u001b[32m 8\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33m=== First Conversation ===\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m----> \u001b[39m\u001b[32m9\u001b[39m response = \u001b[38;5;28;01mawait\u001b[39;00m client.chat.completions.create(\n\u001b[32m 10\u001b[39m model=\u001b[33m\"\u001b[39m\u001b[33mllama-3.1-8b-instant\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 11\u001b[39m messages=[\n\u001b[32m 12\u001b[39m {\u001b[33m\"\u001b[39m\u001b[33mrole\u001b[39m\u001b[33m\"\u001b[39m: \u001b[33m\"\u001b[39m\u001b[33muser\u001b[39m\u001b[33m\"\u001b[39m, \u001b[33m\"\u001b[39m\u001b[33mcontent\u001b[39m\u001b[33m\"\u001b[39m: \u001b[33m\"\u001b[39m\u001b[33mMy name is Alice and I love Python programming!\u001b[39m\u001b[33m\"\u001b[39m}\n\u001b[32m 13\u001b[39m ],\n\u001b[32m 14\u001b[39m )\n\u001b[32m 16\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mAssistant: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mresponse.choices[\u001b[32m0\u001b[39m].message.content\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m\"\u001b[39m)\n\u001b[32m 17\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33m→ This conversation is now stored in Memora!\u001b[39m\u001b[33m\"\u001b[39m)\n", "\u001b[36mFile \u001b[39m\u001b[32m~/dev/memory-poc/memora-openai/memora_openai/client.py:102\u001b[39m, in \u001b[36m_AsyncCompletionsWrapper.create\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 100\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m config.inject_memories:\n\u001b[32m 101\u001b[39m interceptor = get_interceptor()\n\u001b[32m--> \u001b[39m\u001b[32m102\u001b[39m modified_messages = \u001b[38;5;28;01mawait\u001b[39;00m \u001b[43minterceptor\u001b[49m\u001b[43m.\u001b[49m\u001b[43minject_memories\u001b[49m(messages, config)\n\u001b[32m 103\u001b[39m kwargs[\u001b[33m\"\u001b[39m\u001b[33mmessages\u001b[39m\u001b[33m\"\u001b[39m] = modified_messages\n\u001b[32m 105\u001b[39m \u001b[38;5;66;03m# Call original OpenAI API\u001b[39;00m\n", "\u001b[31mAttributeError\u001b[39m: 'MemoraInterceptor' object has no attribute 'inject_memories'" ] } ], "source": [ "# Create client (using Groq's OpenAI-compatible API)\n", "client = AsyncOpenAI(\n", " api_key=GROQ_API_KEY,\n", " base_url=\"https://api.groq.com/openai/v1\",\n", ")\n", "\n", "# First conversation - establish some facts\n", "print(\"=== First Conversation ===\")\n", "response = await client.chat.completions.create(\n", " model=\"llama-3.1-8b-instant\",\n", " messages=[\n", " {\"role\": \"user\", \"content\": \"My name is Alice and I love Python programming!\"}\n", " ],\n", ")\n", "\n", "print(f\"Assistant: {response.choices[0].message.content}\\n\")\n", "print(\"→ This conversation is now stored in Memora!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Example 2: Memory Injection in Action\n", "\n", "Now ask a question that requires remembering the previous conversation:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"=== Second Conversation (with memory) ===\")\n", "response = await client.chat.completions.create(\n", " model=\"llama-3.1-8b-instant\",\n", " messages=[\n", " {\"role\": \"user\", \"content\": \"What's my name and what do I like?\"}\n", " ],\n", ")\n", "\n", "print(f\"Assistant: {response.choices[0].message.content}\\n\")\n", "print(\"→ Memora automatically injected relevant memories before this request!\")\n", "print(\"→ The AI knew your name and preferences without you repeating them.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## How It Works\n", "\n", "Behind the scenes, Memora-OpenAI:\n", "\n", "### 1. **Memory Storage**\n", "After each API call:\n", "- Captures the full conversation context\n", "- Stores it in Memora's semantic memory system\n", "- Indexes it for fast retrieval\n", "\n", "### 2. **Memory Injection**\n", "Before each API call:\n", "- Extracts the user's query\n", "- Searches Memora for relevant past conversations\n", "- Injects top memories as a system message\n", "\n", "### What Gets Sent to OpenAI\n", "\n", "Without Memora:\n", "```python\n", "messages = [\n", " {\"role\": \"user\", \"content\": \"What's my name?\"}\n", "]\n", "```\n", "\n", "With Memora (automatic):\n", "```python\n", "messages = [\n", " {\n", " \"role\": \"system\",\n", " \"content\": \"Relevant context from your memory:\\n\\n1. User's name is Alice\\n (Date: 2024-11-18)\\n (Type: world)\"\n", " },\n", " {\"role\": \"user\", \"content\": \"What's my name?\"}\n", "]\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Example 3: Multi-Turn Conversations\n", "\n", "Build up context over multiple interactions:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Conversation 1: Share a preference\n", "print(\"=== Conversation 1: Sharing preferences ===\")\n", "response = await client.chat.completions.create(\n", " model=\"llama-3.1-8b-instant\",\n", " messages=[\n", " {\"role\": \"user\", \"content\": \"I'm working on a machine learning project using PyTorch.\"}\n", " ],\n", ")\n", "print(f\"Assistant: {response.choices[0].message.content}\\n\")\n", "\n", "# Conversation 2: Different topic\n", "print(\"=== Conversation 2: Different topic ===\")\n", "response = await client.chat.completions.create(\n", " model=\"llama-3.1-8b-instant\",\n", " messages=[\n", " {\"role\": \"user\", \"content\": \"I prefer functional programming over OOP.\"}\n", " ],\n", ")\n", "print(f\"Assistant: {response.choices[0].message.content}\\n\")\n", "\n", "# Conversation 3: Ask for recommendations\n", "print(\"=== Conversation 3: Getting personalized advice ===\")\n", "response = await client.chat.completions.create(\n", " model=\"llama-3.1-8b-instant\",\n", " messages=[\n", " {\"role\": \"user\", \"content\": \"Can you recommend a good book for me based on what you know?\"}\n", " ],\n", ")\n", "print(f\"Assistant: {response.choices[0].message.content}\\n\")\n", "print(\"→ The AI used your programming interests and preferences to make recommendations!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Example 4: Document Grouping\n", "\n", "Group related conversations using `document_id`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from memora_openai import configure\n", "\n", "# Configure with document ID for a specific project\n", "configure(\n", " memora_api_url=\"http://localhost:8000\",\n", " agent_id=\"tutorial-user\",\n", " document_id=\"ml-project-2024\", # All conversations tagged with this ID\n", ")\n", "\n", "# All these conversations will be grouped together\n", "conversations = [\n", " \"I'm using ResNet for image classification\",\n", " \"My dataset has 10,000 images\",\n", " \"Training accuracy is stuck at 65%\",\n", "]\n", "\n", "for msg in conversations:\n", " response = await client.chat.completions.create(\n", " model=\"llama-3.1-8b-instant\",\n", " messages=[{\"role\": \"user\", \"content\": msg}],\n", " )\n", " print(f\"User: {msg}\")\n", " print(f\"Assistant: {response.choices[0].message.content}\\n\")\n", "\n", "print(\"→ All these conversations are grouped under document 'ml-project-2024'\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Example 5: Async Support\n", "\n", "Works perfectly with AsyncOpenAI for high-throughput applications:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from memora_openai import AsyncOpenAI\n", "import asyncio\n", "\n", "async def async_example():\n", " # Create async client\n", " async_client = AsyncOpenAI(\n", " api_key=GROQ_API_KEY,\n", " base_url=\"https://api.groq.com/openai/v1\",\n", " )\n", " \n", " # Store a fact\n", " print(\"=== Storing fact ===\")\n", " response = await async_client.chat.completions.create(\n", " model=\"llama-3.1-8b-instant\",\n", " messages=[{\"role\": \"user\", \"content\": \"My favorite color is blue.\"}],\n", " )\n", " print(f\"Assistant: {response.choices[0].message.content}\\n\")\n", " \n", " # Query with memory\n", " print(\"=== Querying with memory ===\")\n", " response = await async_client.chat.completions.create(\n", " model=\"llama-3.1-8b-instant\",\n", " messages=[{\"role\": \"user\", \"content\": \"What's my favorite color?\"}],\n", " )\n", " print(f\"Assistant: {response.choices[0].message.content}\\n\")\n", "\n", "# Run async example\n", "await async_example()\n", "print(\"→ Async operations work seamlessly with Memora!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Configuration Options\n", "\n", "Fine-tune Memora's behavior:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from memora_openai import configure\n", "\n", "# Full configuration example\n", "configure(\n", " memora_api_url=\"http://localhost:8000\", # Memora API URL\n", " agent_id=\"my-agent\", # Agent identifier (required)\n", " api_key=None, # Optional Memora API key\n", " \n", " # Features\n", " store_conversations=True, # Store conversations automatically\n", " inject_memories=True, # Inject memories automatically\n", " \n", " # Organization\n", " document_id=\"session-123\", # Optional document grouping\n", " \n", " # Control\n", " enabled=True, # Master on/off switch\n", ")\n", "\n", "print(\"Configuration options explained:\")\n", "print(\"- store_conversations: Automatically save conversations to Memora\")\n", "print(\"- inject_memories: Automatically retrieve and inject relevant context\")\n", "print(\"- document_id: Group related conversations together\")\n", "print(\"- enabled=False: Disable Memora without changing code\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Use Cases\n", "\n", "### 1. **Personal AI Assistant**\n", "- Remembers your preferences, work history, and interests\n", "- Provides personalized recommendations\n", "- Maintains context across days/weeks\n", "\n", "### 2. **Customer Support Chatbot**\n", "- Recalls previous support tickets\n", "- Knows customer preferences and history\n", "- Provides consistent, context-aware responses\n", "\n", "### 3. **Research Assistant**\n", "- Remembers documents you've discussed\n", "- Connects related topics from different sessions\n", "- Builds knowledge over time\n", "\n", "### 4. **Code Review Tool**\n", "- Remembers project architecture decisions\n", "- Recalls past code review comments\n", "- Maintains consistency across reviews\n", "\n", "## Benefits Summary\n", "\n", "✅ **Zero Code Changes** - Drop-in replacement for OpenAI client \n", "✅ **Automatic Context** - No manual RAG pipeline needed \n", "✅ **Long-term Memory** - Conversations persist across sessions \n", "✅ **Smart Retrieval** - Semantic search finds relevant context \n", "✅ **Both Sync/Async** - Works with any OpenAI client pattern \n", "✅ **Configurable** - Fine-tune behavior to your needs \n", "✅ **Transparent** - Original OpenAI responses unchanged \n", "\n", "## Next Steps\n", "\n", "- **Explore Memora API**: Check out `memora/README.md` for advanced features\n", "- **Customize Search**: Tune `memory_search_budget` for your use case\n", "- **Use Document IDs**: Organize conversations by project/session\n", "- **Try Different Models**: Works with OpenAI, Groq, Ollama, and more\n", "\n", "## Resources\n", "\n", "- [Memora Main README](../README.md) - Core memory system docs\n", "- [Memora-OpenAI README](README.md) - Package documentation\n", "- [OpenAI API Docs](https://platform.openai.com/docs/api-reference) - Original API reference\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.10" } }, "nbformat": 4, "nbformat_minor": 4 }