{"id":50554001,"url":"https://github.com/softchris/ecommerce-agent-memory","last_synced_at":"2026-06-04T05:05:07.526Z","repository":{"id":356600900,"uuid":"1233268836","full_name":"softchris/ecommerce-agent-memory","owner":"softchris","description":"Shopping assistant with SQL Server chat memory, built on Microsoft Agent Framework + Ollama","archived":false,"fork":false,"pushed_at":"2026-05-20T13:07:18.000Z","size":10946,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-05-20T17:23:06.207Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/softchris.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-08T19:21:14.000Z","updated_at":"2026-05-20T13:07:30.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/softchris/ecommerce-agent-memory","commit_stats":null,"previous_names":["softchris/ecommerce-agent-memory"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/softchris/ecommerce-agent-memory","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/softchris%2Fecommerce-agent-memory","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/softchris%2Fecommerce-agent-memory/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/softchris%2Fecommerce-agent-memory/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/softchris%2Fecommerce-agent-memory/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/softchris","download_url":"https://codeload.github.com/softchris/ecommerce-agent-memory/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/softchris%2Fecommerce-agent-memory/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33890054,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-04T02:00:06.755Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-04T05:05:07.005Z","updated_at":"2026-06-04T05:05:07.519Z","avatar_url":"https://github.com/softchris.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🛍️ Commerce Agent\n\nA personalized shopping assistant web app powered by [Microsoft Agent Framework](https://learn.microsoft.com/en-us/agent-framework/) with [Foundry Local](https://learn.microsoft.com/en-us/azure/ai-foundry/foundry-local/get-started) as the default LLM provider (and optional Ollama support).\n\n## 🎬 Demo\n\n![Commerce Agent Demo](./assets/demo.gif)\n\nLog in as **Marla** or **Steve**, chat with your AI shopping assistant about your preferences, and get personalized product recommendations — all backed by persistent SQL Server conversation history.\n\n### 👀 What you'll get\n\n![Steve chatting with Foundry provider](./assets/steve-foundry.png)\n\n![Marla chatting with Foundry provider](./assets/marla-foundry.png)\n\n## ✨ Features\n\n- 🔐 **User login** — switch between users, each with their own conversation history\n- 💬 **AI chat** — conversational shopping assistant powered by a local LLM provider\n- 🧠 **Memory** — conversations persist in SQL Server across sessions\n- 🎯 **Smart recommendations** — LLM analyzes your preferences with deterministic fallback\n- ⚡ **Model warmup** — starts/warms the selected local provider at startup for fast responses\n- 🏗️ **Microsoft Agent Framework** — built on `BaseHistoryProvider` for proper session management\n\n## 🏗️ Architecture\n\n```mermaid\ngraph TB\n    subgraph Frontend\n        UI[\"🌐 Browser SPA\u003cbr/\u003eHTML / CSS / JS\"]\n    end\n\n    subgraph Backend[\"⚙️ FastAPI Backend\"]\n        API[\"📡 REST API\u003cbr/\u003e/login /chat /recommendations\"]\n        CA[\"🤖 Chat Agent\u003cbr/\u003eagent_framework.Agent\"]\n        RA[\"🎯 Recommendation Agent\u003cbr/\u003eagent_framework.Agent\"]\n        HP[\"💾 CommerceHistoryProvider\u003cbr/\u003eBaseHistoryProvider\"]\n    end\n\n    subgraph AI[\"🧠 Local LLM Providers\"]\n        FL[\"🧩 Foundry Local\u003cbr/\u003eDefault\"]\n        OL[\"🦙 Ollama\u003cbr/\u003eOptional\"]\n    end\n\n    subgraph Data[\"🗄️ SQL Server (Docker)\"]\n        UDB[(\"👤 Users\")]\n        SDB[(\"📋 Sessions\")]\n        HDB[(\"💬 ChatHistory\")]\n    end\n\n    UI --\u003e|\"HTTP\"| API\n    API --\u003e CA\n    API --\u003e RA\n    CA --\u003e HP\n    CA --\u003e|\"OpenAI API\"| FL\n    CA -. \"OpenAI API (optional)\" .-\u003e OL\n    RA --\u003e|\"OpenAI API\"| FL\n    RA -. \"OpenAI API (optional)\" .-\u003e OL\n    HP --\u003e|\"mssql_python\"| HDB\n    API --\u003e|\"login/session\"| UDB\n    API --\u003e|\"session mgmt\"| SDB\n    SDB -.-\u003e|\"FK\"| UDB\n    HDB -.-\u003e|\"FK\"| SDB\n```\n\n## 📋 Prerequisites\n\n| Requirement | Version | Purpose |\n|---|---|---|\n| 🐍 **Python** | 3.12+ | Runtime |\n| 📦 **uv** | 0.8+ | Package manager |\n| 🐳 **Docker** | Latest | SQL Server container |\n| 🧩 **Foundry Local** | Latest | Default local LLM provider |\n| 🦙 **Ollama** | Latest | Optional local LLM provider |\n\n### Install local LLM runtimes\n\nBefore running setup, install the local runtime(s) you want to use. Foundry Local is the default in this project, and Ollama is available as an optional provider.\n\n1. **Install Foundry Local (default)**\n\nUse the official Microsoft quickstart to install and validate Foundry Local:\nhttps://learn.microsoft.com/en-us/azure/ai-foundry/foundry-local/get-started\n\n2. **Install Ollama (optional)**\n\nDownload and install Ollama from:\nhttps://ollama.com/download\n\nThen verify it is available:\n\n```powershell\nollama --version\n```\n\n## 🚀 Setup\n\nIn the setup steps below we cover:\n\n- Pulling the SQL Server Docker image.\n- Starting the SQL Server container.\n- Running with Foundry Local (default) or Ollama (optional).\n\n### 0. Pull the SQL Server Docker image\n\nPulls down the SQL Server 2022 image from Microsoft's container registry, i.e ready to run locally.\n\n```powershell\ndocker run -d `\n  --name sql `\n  -e \"ACCEPT_EULA=Y\" `\n  -e \"MSSQL_SA_PASSWORD=YourStrong!Passw0rd\" `\n  -p 1433:1433 `\n  -v sqlvolume:/var/opt/mssql `\n  mcr.microsoft.com/mssql/server:2022-latest\n```\n\n### 1. Connect to SQL Server with sqlcmd\n\nOpens a SQL shell inside the running container so you can initialize or inspect the database.\n\n```powershell\ndocker exec -it sql /opt/mssql-tools18/bin/sqlcmd `\n    -S localhost -U sa -P \"YourStrong!Passw0rd\" -C\n```\n\n### 2. Download models\n\nChoose the provider you want to use and download (or pre-load) models.\n\n**Foundry Local (default)**\n\nDownload your model explicitly:\n\n```powershell\nfoundry download qwen2.5-0.5b\n```\n\nThen run the app with that model selected:\n\n```powershell\n$env:LLM_PROVIDER=\"foundry\"\n$env:FOUNDRY_LOCAL_MODEL=\"qwen2.5-0.5b\"\nuv run uvicorn app:app --reload --port 8000\n```\n\n**Ollama (optional)**\n\nPull the models explicitly with `ollama pull`:\n\n```powershell\nollama pull llama3.1\nollama pull phi3:mini\n```\n\n### 3. Install dependencies\n\n```bash\ncd commerce-agent\nuv sync\nuv pip install fastapi uvicorn httpx\n```\n\n## 🚀 Run the app\n\n```bash\nuv run uvicorn app:app --reload --port 8000\n```\n\nYou'll see the models warming up:\n\n```text\nDatabase initialized ✅\nStarting selected LLM provider...\nProvider ready ✅\nApplication startup complete.\n```\n\n## 🧠 Provider Modes (Ollama vs Foundry)\n\nThe app supports two LLM providers: `ollama` and `foundry`.\n\nImportant behavior:\n\n- **Chat provider is app-level** and is controlled by `LLM_PROVIDER` at startup.\n- **Recommendation provider is request-level** and is selected in the UI (`KeyMatch`, `Ollama`, `Foundry`).\n- **KeyMatch** is deterministic and does not call an LLM.\n\n### Use Foundry for chat (default)\n\n```powershell\n# Optional because Foundry is the default when LLM_PROVIDER is not set\n$env:LLM_PROVIDER=\"foundry\"\n# Optional: point to an existing Foundry Local OpenAI-compatible endpoint\n# $env:FOUNDRY_LOCAL_BASE_URL=\"http://localhost:5273/v1\"\n# Optional model override (default: qwen2.5-0.5b)\n# $env:FOUNDRY_LOCAL_MODEL=\"qwen2.5-0.5b\"\nuv run uvicorn app:app --reload --port 8000\n```\n\nWhen `FOUNDRY_LOCAL_BASE_URL` is not set, the app uses Foundry Local SDK to start a local service automatically.\n\nIf `LLM_PROVIDER` is not set, the app defaults to `foundry`.\n\n### Use Ollama for chat\n\n```powershell\n$env:LLM_PROVIDER=\"ollama\"\n$env:OLLAMA_BASE_URL=\"http://localhost:11434/v1\"\n$env:OLLAMA_MODEL=\"llama3.1:latest\"\nuv run uvicorn app:app --reload --port 8000\n```\n\n### Switching recommendation provider in the UI\n\nIn the app header, use **Recs** to switch between:\n\n- **KeyMatch**: keyword scoring only (no LLM call)\n- **Ollama**: LLM-based ranking via Ollama\n- **Foundry**: LLM-based ranking via Foundry\n\nIf an LLM recommendation call fails, the backend falls back to KeyMatch automatically.\n\n### 5. Open the app\n\nNavigate to **http://localhost:8000** 🎉\n\n## 🗂️ Project Structure\n\n```text\ncommerce-agent/\n├── app.py              # FastAPI backend — routes, agents, warmup\n├── db.py               # Database init, user/session queries, history provider\n├── products.py         # Product catalog, keyword scoring, recommendations\n└── static/\n    └── index.html      # Single-page app (login, chat, recommendations)\n```\n\n## 🎮 How It Works\n\n1. **Log in** as Marla or Steve\n2. **Chat** with the assistant — tell it what you like and don't like\n3. **Click \"Show me recommendations\"** — the app analyzes your conversation and surfaces products that match your stated preferences\n4. **Log out and back in** — your conversation history is preserved in SQL Server\n\n## 🧩 Key Design Decisions\n\n| Decision | Why |\n|---|---|\n| **Foundry Local as default provider** | Works out of the box with the project's default runtime settings |\n| **Ollama as optional provider** | Easy alternative when you want to run specific local models directly |\n| **LLM + deterministic fallback** | LLM picks products first; keyword scorer catches failures |\n| **Model warmup on startup** | Prevents cold-start latency on first request |\n| **`BaseHistoryProvider`** | Plugs into Agent Framework's session lifecycle properly |\n| **Users + Sessions tables** | Supports multiple sessions per user |\n\n## 🔧 How the History Provider Works\n\nThe `CommerceHistoryProvider` extends Agent Framework's `BaseHistoryProvider` to persist conversation history in SQL Server, scoped per session.\n\nThe framework calls `get_messages()` **before** each agent run to load context, and `save_messages()` **after** to persist new messages. Each session ID maps to a user, so different users get isolated conversation histories.\n\n### Loading messages for a session\n\n```python\nasync def get_messages(\n    self, session_id: str | None, *, state: dict[str, Any] | None = None, **kwargs: Any\n) -\u003e list[Message]:\n    conn = get_conn()\n    cursor = conn.cursor()\n    cursor.execute(\"\"\"\n        SELECT Role, Content FROM ChatHistory\n        WHERE SessionId = ?\n        ORDER BY CreatedAt\n    \"\"\", (session_id,))\n    rows = cursor.fetchall()\n    conn.close()\n    return [Message(role=role, text=content) for role, content in rows]\n```\n\n### Saving messages after a run\n\n```python\nasync def save_messages(\n    self,\n    session_id: str | None,\n    messages: Sequence[Message],\n    *,\n    state: dict[str, Any] | None = None,\n    **kwargs: Any,\n) -\u003e None:\n    conn = get_conn()\n    cursor = conn.cursor()\n    for msg in messages:\n        text = msg.text or \"\"\n        if not text and msg.contents:\n            text = \"\".join(c.text for c in msg.contents if hasattr(c, \"text\"))\n        cursor.execute(\n            \"INSERT INTO ChatHistory (SessionId, Role, Content) VALUES (?, ?, ?)\",\n            (session_id, msg.role, text)\n        )\n    conn.commit()\n    conn.close()\n```\n\n### Wiring it up\n\nThe provider is passed to the agent via `context_providers`. The framework handles the lifecycle automatically — no manual load/save calls needed:\n\n```python\nhistory_provider = CommerceHistoryProvider()\n\nagent = Agent(\n    client=chat_client,\n    instructions=\"You are a friendly shopping assistant.\",\n    context_providers=[history_provider]  # framework calls get/save_messages automatically\n)\n\n# Each user gets their own session, so history is isolated\nsession_id = get_or_create_session(user[\"id\"])\nsession = agent.create_session(session_id=session_id)\nresponse = await agent.run(\"I like jackets!\", session=session)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoftchris%2Fecommerce-agent-memory","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsoftchris%2Fecommerce-agent-memory","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoftchris%2Fecommerce-agent-memory/lists"}