Skip to content
Back to skills

997 Agent Streaming Plugin Fix 7495bbef

ASecurity

**Version:** 0.233.281 **Fixed in:** December 19, 2025 **Issue Type:** Bug Fix **Severity:** High

  • 9 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 11, 2026
businesspythongotestingfrontendbackendperformancedocumentation

Security analysis

A100/100

Scanned October 11, 2026

npx -y skills add tools-only/X-Skills --skill 997-agent_streaming_plugin_fix_7495bbef --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 997 Agent Streaming Plugin Fix 7495bbef?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 997 Agent Streaming Plugin Fix 7495bbef
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-997-agent-streaming-plugin-fix-7495bbef/badge)](https://www.skillsdirectory.com/skills/tools-only-997-agent-streaming-plugin-fix-7495bbef)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
# Agent Streaming Plugin Execution Fix

**Version:** 0.233.281  
**Fixed in:** December 19, 2025  
**Issue Type:** Bug Fix  
**Severity:** High  

## Problem

Agent streaming was failing when agents attempted to execute plugins (tools/functions) during streaming. The SmartHttpPlugin and other async plugins would work correctly in non-streaming mode but failed in streaming mode due to improper async event loop management.

### Symptoms
- Agent streaming worked for simple responses (no plugin calls)
- When agent tried to use plugins (e.g., SmartHttpPlugin.get_web_content_async), streaming would fail
- Non-streaming mode worked perfectly with the same plugins
- No error displayed to user, stream would just stop

### Example from Logs
```
DEBUG: [Log] [Plugin SUCCESS] SmartHttpPlugin.get_web_content_async (10535.3ms)
```
This shows the plugin worked in non-streaming mode, taking 10.5 seconds to download and process a PDF.

## Root Cause

The initial streaming implementation used `loop.run_until_complete(async_gen.__anext__())` in a while loop, attempting to iterate an async generator one item at a time. This approach:

1. **Created event loop conflicts** - New event loop per stream interfered with plugin async execution
2. **Broke async generator protocol** - Calling `__anext__()` directly bypassed proper async context
3. **Prevented plugin execution** - Plugins couldn't properly execute their async operations within the fragmented event loop
4. **Closed loop prematurely** - `loop.close()` in finally block prevented cleanup

### Original Problematic Code
```python
# ❌ BROKEN - tried to iterate async generator manually
async def stream_agent():
    async for response in selected_agent.invoke_stream(...):
        yield response.content

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)

try:
    async_gen = stream_agent()
    while True:
        try:
            chunk_content = loop.run_until_complete(async_gen.__anext__())
            yield f"data: {json.dumps({'content': chunk_content})}\n\n"
        except StopAsyncIteration:
            break
finally:
    loop.close()  # ❌ Closes loop too early
```

## Solution

Changed to collect all streaming chunks within a single async context, then yield them to the SSE stream. This allows:

1. **Proper async execution** - Plugins run in a stable event loop
2. **Complete agent lifecycle** - Agent can execute all plugins before streaming to frontend
3. **Cleaner error handling** - Errors captured and reported properly
4. **Event loop reuse** - Attempts to use existing loop before creating new one

### Fixed Code
```python
# ✅ FIXED - collect chunks in single async context
async def stream_agent_async():
    """Collect all streaming chunks from agent"""
    chunks = []
    async for response in selected_agent.invoke_stream(messages=agent_message_history, thread=thread):
        if hasattr(response, 'content') and response.content:
            chunks.append(response.content)
    return chunks

# Execute async streaming with proper loop management
import asyncio
try:
    # Try to get existing event loop
    loop = asyncio.get_event_loop()
    if loop.is_closed():
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
except RuntimeError:
    # No event loop in current thread
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)

try:
    # Run streaming and collect chunks
    chunks = loop.run_until_complete(stream_agent_async())
    
    # Yield chunks to frontend
    for chunk_content in chunks:
        accumulated_content += chunk_content
        yield f"data: {json.dumps({'content': chunk_content})}\n\n"
except Exception as stream_error:
    print(f"❌ Agent streaming error: {stream_error}")
    import traceback
    traceback.print_exc()
    yield f"data: {json.dumps({'error': f'Agent streaming failed: {str(stream_error)}'})}\n\n"
    return
```

## Technical Details

### Why This Works

1. **Single Async Context**: All async operations (agent streaming, plugin execution) happen in one `run_until_complete` call
2. **Plugin-Friendly**: Plugins can execute their async operations without loop conflicts
3. **Event Loop Reuse**: Attempts to use existing event loop before creating new one
4. **Error Isolation**: Exceptions during plugin execution are caught and reported
5. **No Premature Cleanup**: Event loop not closed, allowing proper async cleanup

### Trade-offs

**Before Fix:**
- ✅ Attempted true streaming (chunk-by-chunk)
- ❌ Broke plugin execution
- ❌ Complex event loop management
- ❌ Poor error handling

**After Fix:**
- ✅ Plugins work correctly
- ✅ Simpler event loop management  
- ✅ Better error handling
- ⚠️ Collects chunks first, then streams (small delay before first chunk)

### Performance Impact

- **Latency**: Slight increase in time-to-first-token (waits for agent to complete all plugin calls)
- **Throughput**: No change - same total processing time
- **Memory**: Negligible - chunks accumulated in memory briefly
- **Reliability**: Significantly improved - plugins now work

## Affected Components

### Backend
- `route_backend_chats.py` - Agent streaming logic (lines ~3055-3095)

### User Experience
- ✅ Agents with plugins now work in streaming mode
- ✅ Long-running plugin calls (10+ seconds) complete successfully
- ✅ Error messages displayed if streaming fails
- ⚠️ Slight delay before first chunk appears (waits for plugins to complete)

## Testing Results

### Test Case: PDF Download and Summary
**Agent:** Default  
**Plugin:** SmartHttpPlugin.get_web_content_async  
**Action:** Download and extract 7-page PDF from whitehouse.gov  
**Plugin Duration:** 10.5 seconds  

**Before Fix:**
- ❌ Non-streaming: Worked perfectly
- ❌ Streaming: Failed silently

**After Fix:**
- ✅ Non-streaming: Still works  
- ✅ Streaming: Now works correctly

### Validation
From logs showing successful execution:
```
[DEBUG] [INFO]: [Plugin SUCCESS] SmartHttpPlugin.get_web_content_async (10535.3ms)
DEBUG: [Log] [Enhanced Agent Citations] Extracted 1 detailed plugin invocations
[DEBUG] [INFO]: Service aoai-chat-Default prompt_tokens: 8000, completion_tokens: 2016, total_tokens: 10016
```

## Future Improvements

1. **True Streaming**: Implement real chunk-by-chunk streaming without breaking plugins
   - Requires deeper integration with Semantic Kernel's async architecture
   - May need custom async generator wrapper

2. **Progress Indicators**: Show plugin execution status during the "waiting" period
   - "Agent is downloading PDF..."
   - "Agent is processing document (10.5s)..."

3. **Incremental Streaming**: Stream agent reasoning/thoughts while plugins execute
   - Show thinking process in real-time
   - Stream final response after plugins complete

4. **Event Loop Pooling**: Reuse event loops across requests for better performance

## Related Documentation

- Initial feature: `docs/features/AGENT_STREAMING_SUPPORT.md` (v0.233.280)
- This fix: `docs/fixes/AGENT_STREAMING_PLUGIN_FIX.md` (v0.233.281)

## Version History

- **v0.233.280** - Initial agent streaming implementation (broken with plugins)
- **v0.233.281** - Fixed plugin execution in streaming mode

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.