Files
agent-framework/python/samples/05-end-to-end/chatkit-integration
T
Eduard van Valkenburg 1e350ea22f Python: [BREAKING] PR2 — Wire context provider pipeline, remove old types, update all consumers (#3850)
* PR2: Wire context provider pipeline and update all internal consumers

- Replace AgentThread with AgentSession across all packages
- Replace ContextProvider with BaseContextProvider across all packages
- Replace context_provider param with context_providers (Sequence)
- Replace thread= with session= in run() signatures
- Replace get_new_thread() with create_session()
- Add get_session(service_session_id) to agent interface
- DurableAgentThread -> DurableAgentSession
- Remove _notify_thread_of_new_messages from WorkflowAgent
- Wire before_run/after_run context provider pipeline in RawAgent
- Auto-inject InMemoryHistoryProvider when no providers configured

* fix: update all tests for context provider pipeline, fix lazy-loaders, remove old test files

* refactor: update all sample files for context provider pipeline (AgentThread→AgentSession, ContextProvider→BaseContextProvider)

* fix: update remaining ag-ui references (client docstring, getting_started sample)

* fix: make get_session service_session_id keyword-only to avoid confusion with session_id

* refactor: rename _RunContext.thread_messages to session_messages

* refactor: remove _threads.py, _memory.py, and old provider files; migrate devui to use plain message lists

* rename: remove _new_ prefix from test files

* refactor: rewrite SlidingWindowChatMessageStore as SlidingWindowHistoryProvider(InMemoryHistoryProvider)

* fix: read full history from session state directly instead of reaching into provider internals

* fix: update stale .pyi stubs, sample imports, and README references for new provider types

* fix: remove stale message_store, _notify_thread_of_new_messages, and session_id.key references in samples

* refactor: merge context_providers and sessions sample folders into sessions, remove aggregate_context_provider

* refactor: UserInfoMemory stores state in session.state instead of instance attributes

* feat: add Pydantic BaseModel support to session state serialization

Pydantic models stored in session.state are now automatically serialized
via model_dump() and restored via model_validate() during to_dict()/from_dict()
round-trips. Models are auto-registered on first serialization; use
register_state_type() for cold-start deserialization.

Also export register_state_type as a public API.

* fix mem0

* Update sample README links and descriptions for session terminology

- Replace 'thread' with 'session' in sample descriptions across all READMEs
- Update file links for renamed samples (mem0_sessions, redis_sessions, etc.)
- Fix Threads section → Sessions section in main samples/README.md
- Update tools, middleware, workflows, durabletask, azure_functions READMEs
- Update architecture diagrams in concepts/tools/README.md
- Update migration guides (autogen, semantic-kernel)

* Fix broken Redis README link to renamed sample

* Fix Mem0 OSS client search: pass scoping params as direct kwargs

AsyncMemory (OSS) expects user_id/agent_id/run_id as direct kwargs,
while AsyncMemoryClient (Platform) expects them in a filters dict.
Adds tests for both client types.

Port of fix from #3844 to new Mem0ContextProvider.

* Fix rebase issues: restore missing _conversation_state.py and checkpoint decode logic

- Add back _conversation_state.py (encode/decode_chat_messages) lost in rebase
- Fix on_checkpoint_restore to decode cache/conversation with decode_chat_messages
- Fix on_checkpoint_restore to use decode_checkpoint_value for pending requests
- Add tests/workflow/__init__.py for relative import support
- Fix test_agent_executor checkpoint selection (checkpoints[1] not superstep)

* Add STORES_BY_DEFAULT ClassVar to skip redundant InMemoryHistoryProvider injection

Chat clients that store history server-side by default (OpenAI Responses API,
Azure AI Agent) now declare STORES_BY_DEFAULT = True. The agent checks this
during auto-injection and skips InMemoryHistoryProvider unless the user
explicitly sets store=False.

* Fix broken markdown links in azure_ai and redis READMEs

* Fix getting-started samples to use session API instead of removed thread/ContextProvider API

* updates to workflow as agent

* fix group chat import

* Rename Thread→Session throughout, fix service_session_id propagation, remove stale AGUIThread

- Fix: Propagate conversation_id from ChatResponse back to session.service_session_id
  in both streaming and non-streaming paths in _agents.py
- Rename AgentThreadException → AgentSessionException
- Remove stale AGUIThread from ag_ui lazy-loader
- Rename use_service_thread → use_service_session in ag-ui package
- Rename test functions from *_thread_* to *_session_*
- Rename sample files from *_thread* to *_session*
- Update docstrings and comments: thread → session
- Update _mcp.py kwargs filter: add 'session' alongside 'thread'
- Fix ContinuationToken docstring example: thread=thread → session=session
- Fix _clients.py docstring: 'Agent threads' → 'Agent sessions'

* Fix broken markdown links after thread→session file renames

* fix azure ai test
1e350ea22f · 2026-02-12 21:00:32 +00:00
History
..

ChatKit Integration Sample with Weather Agent and Image Analysis

This sample demonstrates how to integrate Microsoft Agent Framework with OpenAI ChatKit. It provides a complete implementation of a weather assistant with interactive widget visualization, image analysis, and file upload support.

Features:

  • Weather information with interactive widgets
  • Image analysis using vision models
  • Current time queries
  • File upload with attachment storage
  • Chat interface with streaming responses
  • City selector widget with one-click weather

Architecture

graph TB
    subgraph Frontend["React Frontend (ChatKit UI)"]
        UI[ChatKit Components]
        Upload[File Upload]
    end

    subgraph Backend["FastAPI Server"]
        FastAPI[FastAPI Endpoints]

        subgraph ChatKit["WeatherChatKitServer"]
            Respond[respond method]
            Action[action method]
        end

        subgraph Stores["Data & Storage Layer"]
            SQLite[SQLiteStore<br/>Store Protocol]
            AttStore[FileBasedAttachmentStore<br/>AttachmentStore Protocol]
            DB[(SQLite DB<br/>chatkit_demo.db)]
            Files[/uploads directory/]
        end

        subgraph Integration["Agent Framework Integration"]
            Converter[ThreadItemConverter]
            Streamer[stream_agent_response]
            Agent[Agent]
        end

        Widgets[Widget Rendering<br/>render_weather_widget<br/>render_city_selector_widget]
    end

    subgraph Azure["Azure AI"]
        Foundry[GPT-5<br/>with Vision]
    end

    UI -->|HTTP POST /chatkit| FastAPI
    Upload -->|HTTP POST /upload/id| FastAPI

    FastAPI --> ChatKit

    ChatKit -->|save/load threads| SQLite
    ChatKit -->|save/load attachments| AttStore
    ChatKit -->|convert messages| Converter

    SQLite -.->|persist| DB
    AttStore -.->|save files| Files
    AttStore -.->|save metadata| SQLite

    Converter -->|Message array| Agent
    Agent -->|AgentResponseUpdate| Streamer
    Streamer -->|ThreadStreamEvent| ChatKit

    ChatKit --> Widgets
    Widgets -->|WidgetItem| ChatKit

    Agent <-->|Chat Completions API| Foundry

    ChatKit -->|ThreadStreamEvent| FastAPI
    FastAPI -->|SSE Stream| UI

    style ChatKit fill:#e1f5ff
    style Stores fill:#fff4e1
    style Integration fill:#f0e1ff
    style Azure fill:#e1ffe1

Server Implementation

The sample implements a ChatKit server using the ChatKitServer base class from the chatkit package:

Core Components:

  • WeatherChatKitServer: Custom ChatKit server implementation that:

    • Extends ChatKitServer[dict[str, Any]]
    • Uses Agent Framework's Agent with Azure OpenAI
    • Converts ChatKit messages to Agent Framework format using ThreadItemConverter
    • Streams responses back to ChatKit using stream_agent_response
    • Creates and streams interactive widgets after agent responses
  • SQLiteStore: Data persistence layer that:

    • Implements the Store[dict[str, Any]] protocol from ChatKit
    • Persists threads, messages, and attachment metadata in SQLite
    • Provides thread management and item history
    • Stores attachment metadata for the upload lifecycle
  • FileBasedAttachmentStore: File storage implementation that:

    • Implements the AttachmentStore[dict[str, Any]] protocol from ChatKit
    • Stores uploaded files on the local filesystem (in ./uploads directory)
    • Generates upload URLs for two-phase file upload
    • Saves attachment metadata to the data store for upload tracking
    • Provides preview URLs for images

Key Integration Points:

# Converting ChatKit messages to Agent Framework
converter = ThreadItemConverter(
    attachment_data_fetcher=self._fetch_attachment_data
)
agent_messages = await converter.to_agent_input(user_message_item)

# Running agent and streaming back to ChatKit
async for event in stream_agent_response(
    self.weather_agent.run(agent_messages, stream=True),
    thread_id=thread.id,
):
    yield event

# Streaming widgets
widget = render_weather_widget(weather_data)
async for event in stream_widget(thread_id=thread.id, widget=widget):
    yield event

Installation and Setup

Prerequisites

  • Python 3.10+
  • Node.js 18.18+ and npm 9+
  • Azure OpenAI service configured
  • Azure CLI for authentication (az login)

Network Requirements

Important: This sample uses the OpenAI ChatKit frontend, which requires internet connectivity to OpenAI services.

The frontend makes outbound requests to:

  • cdn.platform.openai.com - ChatKit UI library (required)
  • chatgpt.com - Configuration endpoint
  • api-js.mixpanel.com - Telemetry

This sample is not suitable for air-gapped or network-restricted environments. The ChatKit frontend library cannot be self-hosted. See Limitations for details.

Domain Key Configuration

For local development, the sample uses a default domain key (domain_pk_localhost_dev).

For production deployment:

  1. Register your domain at platform.openai.com

  2. Create a .env file in the frontend directory:

    VITE_CHATKIT_API_DOMAIN_KEY=your_domain_key_here
    

Backend Setup

  1. Install Python packages:
cd python/samples/05-end-to-end/chatkit-integration
pip install agent-framework-chatkit fastapi uvicorn azure-identity
  1. Configure Azure OpenAI:
export AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com/"
export AZURE_OPENAI_API_VERSION="2024-06-01"
export AZURE_OPENAI_CHAT_DEPLOYMENT_NAME="gpt-4o"
  1. Authenticate with Azure:
az login

Frontend Setup

Install the Node.js dependencies:

cd frontend
npm install

How to Run

Start the Backend Server

From the chatkit-integration directory:

python app.py

Or with auto-reload for development:

uvicorn app:app --host 127.0.0.1 --port 8001 --reload

The backend will start on http://localhost:8001

Start the Frontend Development Server

In a new terminal, from the frontend directory:

npm run dev

The frontend will start on http://localhost:5171

Access the Application

Open your browser and navigate to:

http://localhost:5171

You can now:

  • Ask about weather in any location (weather widgets display automatically)
  • Upload images for analysis using the attachment button
  • Get the current time
  • Ask to see available cities and click city buttons for instant weather

Project Structure

chatkit-integration/
├── app.py                    # FastAPI backend with ChatKitServer implementation
├── store.py                  # SQLiteStore implementation
├── attachment_store.py       # FileBasedAttachmentStore implementation
├── weather_widget.py         # Widget rendering functions
├── chatkit_demo.db          # SQLite database (auto-created)
├── uploads/                  # Uploaded files directory (auto-created)
└── frontend/
    ├── package.json
    ├── vite.config.ts
    ├── index.html
    └── src/
        ├── main.tsx
        └── App.tsx           # ChatKit UI integration

Configuration

You can customize the application by editing constants at the top of app.py:

# Server configuration
SERVER_HOST = "127.0.0.1"  # Bind to localhost only for security (local dev)
SERVER_PORT = 8001
SERVER_BASE_URL = f"http://localhost:{SERVER_PORT}"

# Database configuration
DATABASE_PATH = "chatkit_demo.db"

# File storage configuration
UPLOADS_DIRECTORY = "./uploads"

# User context
DEFAULT_USER_ID = "demo_user"

Sample Conversations

Try these example queries:

  • "What's the weather like in Tokyo?"
  • "Show me available cities" (displays interactive city selector)
  • "What's the current time?"
  • Upload an image and ask "What do you see in this image?"

Limitations

Air-Gapped / Regulated Environments

The ChatKit frontend (chatkit.js) is loaded from OpenAI's CDN and cannot be self-hosted. This means:

  • Not suitable for air-gapped environments where *.openai.com is blocked
  • Not suitable for regulated environments that prohibit external telemetry
  • Requires domain registration with OpenAI for production use

What you CAN self-host:

  • The Python backend (FastAPI server, ChatKitServer, stores)
  • The agent-framework-chatkit integration layer
  • Your LLM infrastructure (Azure OpenAI, local models, etc.)

What you CANNOT self-host:

  • The ChatKit frontend UI library

For more details, see:

Learn More