mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: DevUI - Internal Refactor, Conversations API support, and per… (#1235)
* Python: DevUI - Internal Refactor, Conversations API support, and performance improvements Comprehensive refactor of DevUI package including samples relocation, frontend reorganization, OpenAI Conversations API support, and critical performance and code quality improvements. Key Changes: Architecture & Organization - Moved DevUI samples to python/samples/getting_started/devui/ - Consolidated with other framework samples for better discoverability - Added .env.example files and comprehensive README - Restructured frontend components into feature-based folders (agent, workflow, gallery, layout) - Created new OpenAI-compliant message renderers (devui should render oai responses types primarily) New Features - Added _conversations.py (467 lines) - Full conversation storage abstraction, replaces the /threads endpoint to better match oai conversations api - Implements OpenAI Conversations API for thread management, Supports in-memory and extensible storage backends API Simplification - Use 'model' field as entity_id (agent/workflow name) instead of extra_body - Use standard OpenAI 'conversation' field for conversation context. Performance & Quality Improvements - Improved context management in MessageMapper with bounded memory (~500KB max) - Implemented hybrid LRU + cleanup approach to prevent unbounded memory growth - General QOL improvement - Eliminated ~150 lines of dead/duplicate code, Consolidated helper functions into _utils.py, Extracted magic numbers to module-level constants, Optimized conversation item lookups with index-based approach Testing - Added test_conversations.py (13 tests) - Added test_performance_fixes.py (9 tests) - Updated existing tests for code consolidation - 53 tests passing Impact: 76 files changed: +4,106 insertions, -2,373 deletions All linting and formatting checks passing. No breaking changes - backward compatible. Migration: Samples moved to python/samples/getting_started/devui/ * readme lint fixes * initial support for function approval and minor ui fixes
This commit is contained in:
committed by
GitHub
Unverified
parent
f5abbc67ae
commit
c341ee7ed2
@@ -4,11 +4,18 @@
|
||||
|
||||
# Import discovery models
|
||||
# Import all OpenAI types directly from the openai package
|
||||
from openai.types.conversations import Conversation, ConversationDeletedResource
|
||||
from openai.types.conversations.conversation_item import ConversationItem
|
||||
from openai.types.responses import (
|
||||
Response,
|
||||
ResponseCompletedEvent,
|
||||
ResponseErrorEvent,
|
||||
ResponseFunctionCallArgumentsDeltaEvent,
|
||||
ResponseFunctionToolCall,
|
||||
ResponseFunctionToolCallOutputItem,
|
||||
ResponseInputParam,
|
||||
ResponseOutputItemAddedEvent,
|
||||
ResponseOutputItemDoneEvent,
|
||||
ResponseOutputMessage,
|
||||
ResponseOutputText,
|
||||
ResponseReasoningTextDeltaEvent,
|
||||
@@ -25,14 +32,9 @@ from ._openai_custom import (
|
||||
AgentFrameworkRequest,
|
||||
OpenAIError,
|
||||
ResponseFunctionResultComplete,
|
||||
ResponseFunctionResultDelta,
|
||||
ResponseTraceEvent,
|
||||
ResponseTraceEventComplete,
|
||||
ResponseTraceEventDelta,
|
||||
ResponseUsageEventComplete,
|
||||
ResponseUsageEventDelta,
|
||||
ResponseWorkflowEventComplete,
|
||||
ResponseWorkflowEventDelta,
|
||||
)
|
||||
|
||||
# Type alias for compatibility
|
||||
@@ -41,6 +43,9 @@ OpenAIResponse = Response
|
||||
# Export all types for easy importing
|
||||
__all__ = [
|
||||
"AgentFrameworkRequest",
|
||||
"Conversation",
|
||||
"ConversationDeletedResource",
|
||||
"ConversationItem",
|
||||
"DiscoveryResponse",
|
||||
"EntityInfo",
|
||||
"InputTokensDetails",
|
||||
@@ -49,11 +54,15 @@ __all__ = [
|
||||
"OpenAIResponse",
|
||||
"OutputTokensDetails",
|
||||
"Response",
|
||||
"ResponseCompletedEvent",
|
||||
"ResponseErrorEvent",
|
||||
"ResponseFunctionCallArgumentsDeltaEvent",
|
||||
"ResponseFunctionResultComplete",
|
||||
"ResponseFunctionResultDelta",
|
||||
"ResponseFunctionToolCall",
|
||||
"ResponseFunctionToolCallOutputItem",
|
||||
"ResponseInputParam",
|
||||
"ResponseOutputItemAddedEvent",
|
||||
"ResponseOutputItemDoneEvent",
|
||||
"ResponseOutputMessage",
|
||||
"ResponseOutputText",
|
||||
"ResponseReasoningTextDeltaEvent",
|
||||
@@ -61,12 +70,8 @@ __all__ = [
|
||||
"ResponseTextDeltaEvent",
|
||||
"ResponseTraceEvent",
|
||||
"ResponseTraceEventComplete",
|
||||
"ResponseTraceEventDelta",
|
||||
"ResponseUsage",
|
||||
"ResponseUsageEventComplete",
|
||||
"ResponseUsageEventDelta",
|
||||
"ResponseWorkflowEventComplete",
|
||||
"ResponseWorkflowEventDelta",
|
||||
"ResponsesModel",
|
||||
"ToolParam",
|
||||
]
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"""Custom OpenAI-compatible event types for Agent Framework extensions.
|
||||
|
||||
These are custom event types that extend beyond the standard OpenAI Responses API
|
||||
to support Agent Framework specific features like workflows, traces, and function results.
|
||||
to support Agent Framework specific features like workflows and traces.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -15,18 +15,6 @@ from pydantic import BaseModel, ConfigDict
|
||||
# Custom Agent Framework OpenAI event types for structured data
|
||||
|
||||
|
||||
class ResponseWorkflowEventDelta(BaseModel):
|
||||
"""Structured workflow event with completion tracking."""
|
||||
|
||||
type: Literal["response.workflow_event.delta"] = "response.workflow_event.delta"
|
||||
delta: dict[str, Any]
|
||||
executor_id: str | None = None
|
||||
is_complete: bool = False # Track if this is the final part
|
||||
item_id: str
|
||||
output_index: int = 0
|
||||
sequence_number: int
|
||||
|
||||
|
||||
class ResponseWorkflowEventComplete(BaseModel):
|
||||
"""Complete workflow event data."""
|
||||
|
||||
@@ -38,41 +26,6 @@ class ResponseWorkflowEventComplete(BaseModel):
|
||||
sequence_number: int
|
||||
|
||||
|
||||
class ResponseFunctionResultDelta(BaseModel):
|
||||
"""Structured function result with completion tracking."""
|
||||
|
||||
type: Literal["response.function_result.delta"] = "response.function_result.delta"
|
||||
delta: dict[str, Any]
|
||||
call_id: str
|
||||
is_complete: bool = False
|
||||
item_id: str
|
||||
output_index: int = 0
|
||||
sequence_number: int
|
||||
|
||||
|
||||
class ResponseFunctionResultComplete(BaseModel):
|
||||
"""Complete function result data."""
|
||||
|
||||
type: Literal["response.function_result.complete"] = "response.function_result.complete"
|
||||
data: dict[str, Any] # Complete function result data, not delta
|
||||
call_id: str
|
||||
item_id: str
|
||||
output_index: int = 0
|
||||
sequence_number: int
|
||||
|
||||
|
||||
class ResponseTraceEventDelta(BaseModel):
|
||||
"""Structured trace event with completion tracking."""
|
||||
|
||||
type: Literal["response.trace.delta"] = "response.trace.delta"
|
||||
delta: dict[str, Any]
|
||||
span_id: str | None = None
|
||||
is_complete: bool = False
|
||||
item_id: str
|
||||
output_index: int = 0
|
||||
sequence_number: int
|
||||
|
||||
|
||||
class ResponseTraceEventComplete(BaseModel):
|
||||
"""Complete trace event data."""
|
||||
|
||||
@@ -84,22 +37,18 @@ class ResponseTraceEventComplete(BaseModel):
|
||||
sequence_number: int
|
||||
|
||||
|
||||
class ResponseUsageEventDelta(BaseModel):
|
||||
"""Structured usage event with completion tracking."""
|
||||
class ResponseFunctionResultComplete(BaseModel):
|
||||
"""Custom DevUI event for function execution results.
|
||||
|
||||
type: Literal["response.usage.delta"] = "response.usage.delta"
|
||||
delta: dict[str, Any]
|
||||
is_complete: bool = False
|
||||
item_id: str
|
||||
output_index: int = 0
|
||||
sequence_number: int
|
||||
This is a DevUI extension - OpenAI doesn't stream function execution results
|
||||
because in their model, the application executes functions, not the API.
|
||||
Agent Framework executes functions, so we emit this event for debugging visibility.
|
||||
"""
|
||||
|
||||
|
||||
class ResponseUsageEventComplete(BaseModel):
|
||||
"""Complete usage event data."""
|
||||
|
||||
type: Literal["response.usage.complete"] = "response.usage.complete"
|
||||
data: dict[str, Any] # Complete usage data, not delta
|
||||
type: Literal["response.function_result.complete"] = "response.function_result.complete"
|
||||
call_id: str
|
||||
output: str
|
||||
status: Literal["in_progress", "completed", "incomplete"]
|
||||
item_id: str
|
||||
output_index: int = 0
|
||||
sequence_number: int
|
||||
@@ -110,7 +59,6 @@ class AgentFrameworkExtraBody(BaseModel):
|
||||
"""Agent Framework specific routing fields for OpenAI requests."""
|
||||
|
||||
entity_id: str
|
||||
thread_id: str | None = None
|
||||
input_data: dict[str, Any] | None = None
|
||||
|
||||
model_config = ConfigDict(extra="allow")
|
||||
@@ -118,17 +66,21 @@ class AgentFrameworkExtraBody(BaseModel):
|
||||
|
||||
# Agent Framework Request Model - Extending real OpenAI types
|
||||
class AgentFrameworkRequest(BaseModel):
|
||||
"""OpenAI ResponseCreateParams with Agent Framework extensions.
|
||||
"""OpenAI ResponseCreateParams with Agent Framework routing.
|
||||
|
||||
This properly extends the real OpenAI API request format while adding
|
||||
our custom routing fields in extra_body.
|
||||
This properly extends the real OpenAI API request format.
|
||||
- Uses 'model' field as entity_id (agent/workflow name)
|
||||
- Uses 'conversation' field for conversation context (OpenAI standard)
|
||||
"""
|
||||
|
||||
# All OpenAI fields from ResponseCreateParams
|
||||
model: str
|
||||
model: str # Used as entity_id in DevUI!
|
||||
input: str | list[Any] # ResponseInputParam
|
||||
stream: bool | None = False
|
||||
|
||||
# OpenAI conversation parameter (standard!)
|
||||
conversation: str | dict[str, Any] | None = None # Union[str, {"id": str}]
|
||||
|
||||
# Common OpenAI optional fields
|
||||
instructions: str | None = None
|
||||
metadata: dict[str, Any] | None = None
|
||||
@@ -136,32 +88,35 @@ class AgentFrameworkRequest(BaseModel):
|
||||
max_output_tokens: int | None = None
|
||||
tools: list[dict[str, Any]] | None = None
|
||||
|
||||
# Agent Framework extension - strongly typed
|
||||
extra_body: AgentFrameworkExtraBody | None = None
|
||||
|
||||
entity_id: str | None = None # Allow entity_id as top-level field
|
||||
# Optional extra_body for advanced use cases
|
||||
extra_body: dict[str, Any] | None = None
|
||||
|
||||
model_config = ConfigDict(extra="allow")
|
||||
|
||||
def get_entity_id(self) -> str | None:
|
||||
"""Get entity_id from either top-level field or extra_body."""
|
||||
# Priority 1: Top-level entity_id field
|
||||
if self.entity_id:
|
||||
return self.entity_id
|
||||
def get_entity_id(self) -> str:
|
||||
"""Get entity_id from model field.
|
||||
|
||||
# Priority 2: entity_id in extra_body
|
||||
if self.extra_body and hasattr(self.extra_body, "entity_id"):
|
||||
return self.extra_body.entity_id
|
||||
In DevUI, model IS the entity_id (agent/workflow name).
|
||||
Simple and clean!
|
||||
"""
|
||||
return self.model
|
||||
|
||||
def get_conversation_id(self) -> str | None:
|
||||
"""Extract conversation_id from conversation parameter.
|
||||
|
||||
Supports both string and object forms:
|
||||
- conversation: "conv_123"
|
||||
- conversation: {"id": "conv_123"}
|
||||
"""
|
||||
if isinstance(self.conversation, str):
|
||||
return self.conversation
|
||||
if isinstance(self.conversation, dict):
|
||||
return self.conversation.get("id")
|
||||
return None
|
||||
|
||||
def to_openai_params(self) -> dict[str, Any]:
|
||||
"""Convert to dict for OpenAI client compatibility."""
|
||||
data = self.model_dump(exclude={"extra_body", "entity_id"}, exclude_none=True)
|
||||
if self.extra_body:
|
||||
# Don't merge extra_body into main params to keep them separate
|
||||
data["extra_body"] = self.extra_body
|
||||
return data
|
||||
return self.model_dump(exclude_none=True)
|
||||
|
||||
|
||||
# Error handling
|
||||
@@ -198,12 +153,7 @@ __all__ = [
|
||||
"AgentFrameworkRequest",
|
||||
"OpenAIError",
|
||||
"ResponseFunctionResultComplete",
|
||||
"ResponseFunctionResultDelta",
|
||||
"ResponseTraceEvent",
|
||||
"ResponseTraceEventComplete",
|
||||
"ResponseTraceEventDelta",
|
||||
"ResponseUsageEventComplete",
|
||||
"ResponseUsageEventDelta",
|
||||
"ResponseWorkflowEventComplete",
|
||||
"ResponseWorkflowEventDelta",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user