mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
a2856d3b92
* restructure: Python samples into progressive 01-05 layout - 01-get-started/: 6 numbered steps (hello agent → hosting) - 02-agents/: all agent concept samples (tools, middleware, providers, etc.) - 03-workflows/: ALL existing workflow samples preserved as-is - 04-hosting/: azure-functions, durabletask, a2a - 05-end-to-end/: demos, evaluation, hosted agents - Old files moved to _to_delete/ for review - Added AGENTS.md with structure documentation - autogen-migration/ and semantic-kernel-migration/ preserved at root * fix: switch to AzureOpenAI Foundry, fix CI failures - Switch all 01-get-started samples to AzureOpenAIResponsesClient with Azure AI Foundry project endpoint (AZURE_AI_PROJECT_ENDPOINT + AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME + AzureCliCredential) - Add _to_delete/ and 05-end-to-end/ to pyrightconfig.samples.json excludes - Fix test paths in packages/ that referenced old getting_started/ dirs: durabletask conftest + streaming test, azurefunctions conftest, devui conftest + capture_messages + openai_sdk_integration - Fix workflow_as_agent_human_in_the_loop.py import (sibling import) - Update hosting READMEs and tool comment paths - Replace root README.md with new structure overview - Update AGENTS.md to document Azure OpenAI Foundry as default provider * cleanup: remove _to_delete folder, copy resource files to active dirs All files in _to_delete/ were either: - Exact duplicates of files in the new structure (240 files) - Same file with only comment path updates (100 files) - One import-fix diff (workflow_as_agent_human_in_the_loop.py) - One superseded minimal_sample.py Resource files (sample.pdf, countries.json, employees.pdf, weather.json) copied to 02-agents/sample_assets/ and 02-agents/resources/ since active samples reference them. * fix: address PR review comments, centralize resources, remove root duplicates - Fix type annotation in 04_memory.py (string union -> proper types) - Fix old sample paths in observability files - Fix grammar/spelling in observability samples - Move sample_assets/ and resources/ to shared/ folder - Remove 8 duplicate observability files from 02-agents root - Update resource path references in multimodal_input and provider samples * fix: update broken links from old getting_started paths to new structure - Update relative paths in READMEs: getting_started/ → 01-get-started/, 02-agents/, 03-workflows/, 04-hosting/, 05-end-to-end/ - Fix absolute GitHub URLs in package READMEs - Fix broken link in ollama package README * fix: convert absolute GitHub URLs to relative paths for link checker Absolute URLs to python/samples/ on main branch 404 until PR merges. Converted to relative paths that linkspector can verify locally. * fix: update link for handoff sample moved to orchestrations/ * fix: update chatkit-integration README path from demos/ to 05-end-to-end/ * fix: update broken links in orchestrations README to match flat directory structure
128 lines
5.3 KiB
Markdown
128 lines
5.3 KiB
Markdown
# Agent Framework and ChatKit Integration
|
|
|
|
This package provides an integration layer between Microsoft Agent Framework
|
|
and [OpenAI ChatKit (Python)](https://github.com/openai/chatkit-python/).
|
|
Specifically, it mirrors the [Agent SDK integration](https://github.com/openai/chatkit-python/blob/main/docs/server.md#agents-sdk-integration), and provides the following helpers:
|
|
|
|
- `stream_agent_response`: A helper to convert a streamed `AgentResponseUpdate`
|
|
from a Microsoft Agent Framework agent that implements `SupportsAgentRun` to ChatKit events.
|
|
- `ThreadItemConverter`: A extendable helper class to convert ChatKit thread items to
|
|
`Message` objects that can be consumed by an Agent Framework agent.
|
|
- `simple_to_agent_input`: A helper function that uses the default implementation
|
|
of `ThreadItemConverter` to convert a ChatKit thread to a list of `Message`,
|
|
useful for getting started quickly.
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
pip install agent-framework-chatkit --pre
|
|
```
|
|
|
|
This will install `agent-framework-core` and `openai-chatkit` as dependencies.
|
|
|
|
## Requirements and Limitations
|
|
|
|
### Frontend Requirements
|
|
|
|
The ChatKit integration requires the OpenAI ChatKit frontend library, which has the following requirements:
|
|
|
|
1. **Internet Connectivity Required**: The ChatKit UI is loaded from OpenAI's CDN (`cdn.platform.openai.com`). This library cannot be self-hosted or bundled locally.
|
|
|
|
2. **External Network Requests**: The ChatKit frontend makes requests to:
|
|
- `cdn.platform.openai.com` - UI library (required)
|
|
- `chatgpt.com/ces/v1/projects/oai/settings` - Configuration
|
|
- `api-js.mixpanel.com` - Telemetry (metadata only, not user messages)
|
|
|
|
3. **Domain Registration for Production**: Production deployments require registering your domain at [platform.openai.com](https://platform.openai.com/settings/organization/security/domain-allowlist) and configuring a domain key.
|
|
|
|
### Air-Gapped / Regulated Environments
|
|
|
|
**The ChatKit frontend is not suitable for air-gapped or highly-regulated environments** where outbound connections to OpenAI domains are restricted.
|
|
|
|
**What IS self-hostable:**
|
|
|
|
- The backend components (`chatkit-python`, `agent-framework-chatkit`) are fully open source and have no external dependencies
|
|
|
|
**What is NOT self-hostable:**
|
|
|
|
- The frontend UI (`chatkit.js`) requires connectivity to OpenAI's CDN
|
|
|
|
For environments with network restrictions, consider building a custom frontend that consumes the ChatKit server protocol, or using alternative UI libraries like `ai-sdk`.
|
|
|
|
See [openai/chatkit-js#57](https://github.com/openai/chatkit-js/issues/57) for tracking self-hosting feature requests.
|
|
|
|
## Example Usage
|
|
|
|
Here's a minimal example showing how to integrate Agent Framework with ChatKit:
|
|
|
|
```python
|
|
from collections.abc import AsyncIterator
|
|
from typing import Any
|
|
|
|
from azure.identity import AzureCliCredential
|
|
from fastapi import FastAPI, Request
|
|
from fastapi.responses import Response, StreamingResponse
|
|
|
|
from agent_framework import Agent
|
|
from agent_framework.azure import AzureOpenAIChatClient
|
|
from agent_framework.chatkit import simple_to_agent_input, stream_agent_response
|
|
|
|
from chatkit.server import ChatKitServer
|
|
from chatkit.types import ThreadMetadata, UserMessageItem, ThreadStreamEvent
|
|
|
|
# You'll need to implement a Store - see the sample for a SQLiteStore implementation
|
|
from your_store import YourStore # type: ignore[import-not-found] # Replace with your Store implementation
|
|
|
|
# Define your agent with tools
|
|
agent = Agent(
|
|
client=AzureOpenAIChatClient(credential=AzureCliCredential()),
|
|
instructions="You are a helpful assistant.",
|
|
tools=[], # Add your tools here
|
|
)
|
|
|
|
# Create a ChatKit server that uses your agent
|
|
class MyChatKitServer(ChatKitServer[dict[str, Any]]):
|
|
async def respond(
|
|
self,
|
|
thread: ThreadMetadata,
|
|
input_user_message: UserMessageItem | None,
|
|
context: dict[str, Any],
|
|
) -> AsyncIterator[ThreadStreamEvent]:
|
|
if input_user_message is None:
|
|
return
|
|
|
|
# Load full thread history to maintain conversation context
|
|
thread_items_page = await self.store.load_thread_items(
|
|
thread_id=thread.id,
|
|
after=None,
|
|
limit=1000,
|
|
order="asc",
|
|
context=context,
|
|
)
|
|
|
|
# Convert all ChatKit messages to Agent Framework format
|
|
agent_messages = await simple_to_agent_input(thread_items_page.data)
|
|
|
|
# Run the agent and stream responses
|
|
response_stream = agent.run(agent_messages, stream=True)
|
|
|
|
# Convert agent responses back to ChatKit events
|
|
async for event in stream_agent_response(response_stream, thread.id):
|
|
yield event
|
|
|
|
# Set up FastAPI endpoint
|
|
app = FastAPI()
|
|
chatkit_server = MyChatKitServer(YourStore()) # type: ignore[misc]
|
|
|
|
@app.post("/chatkit")
|
|
async def chatkit_endpoint(request: Request):
|
|
result = await chatkit_server.process(await request.body(), {"request": request})
|
|
|
|
if hasattr(result, '__aiter__'): # Streaming
|
|
return StreamingResponse(result, media_type="text/event-stream") # type: ignore[arg-type]
|
|
else: # Non-streaming
|
|
return Response(content=result.json, media_type="application/json") # type: ignore[union-attr]
|
|
```
|
|
|
|
For a complete end-to-end example with a full frontend, see the [weather agent sample](../../samples/05-end-to-end/chatkit-integration/README.md).
|