Files
PraisonAIUI/examples/python/26-clean-chat/app.py
T
01e6618833 fix: session memory governance in examples (fixes #67) (#92)
* feat: implement SessionGovernor for bounded session memory management (fixes #67)

- Add SessionGovernor with configurable max_turns/max_sessions limits
- Replace unbounded _contexts dict in example 03 with SessionGovernor
- Fix resource churn in examples 25/26 by reusing AsyncOpenAI client
- Implement documented --datastore json flag in example 04
- Export SessionGovernor in praisonaiui.__init__ for public API
- Enforce context window caps and LRU eviction policies
- Prevent OOM conditions in production deployments

Co-authored-by: Mervin Praison <[email protected]>

* fix: critical bugs in SessionGovernor memory management

Fixes two production-critical bugs in session_governor.py:

1. Line 199: Fixed eviction loop condition (>= → >) to prevent:
   - KeyError crashes when active session gets evicted
   - Unnecessary eviction when at limit
   - Double eviction causing under-capacity

2. Line 89-90: Fixed slicing logic for non-positive keep_count:
   - Added guard to prevent messages[-0:] returning all messages
   - Ensures proper memory governance when system messages exceed turn limits

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
Co-authored-by: Mervin Praison <[email protected]>
2026-06-15 16:34:23 +01:00

94 lines
2.9 KiB
Python

"""Clean Chat — Dashboard chat without the sidebar navigation.
What's New (vs 18-full-chat/):
• aiui.set_dashboard(sidebar=False) — hides the left nav panel
• Uses the dashboard style for the rich chat UI (session list, agents)
• But without the sidebar clutter (Chat, Channels, Agents, Skills, etc.)
• Protocol-driven: all config flows through /ui-config.json
This gives you the dashboard chat experience in a cleaner layout.
Requires: pip install openai (or pip install praisonai)
Set OPENAI_API_KEY before running.
Run:
aiui run app.py
"""
import os
import praisonaiui as aiui
try:
from openai import AsyncOpenAI
except ImportError:
AsyncOpenAI = None
# Reuse client to prevent resource churn
_client = None
def _get_client():
global _client
if _client is None and AsyncOpenAI is not None:
api_key = os.getenv("OPENAI_API_KEY")
if api_key:
_client = AsyncOpenAI(api_key=api_key)
return _client
# ── Dashboard style, but no sidebar navigation ─────────────
aiui.set_style("dashboard")
aiui.set_dashboard(sidebar=False, page_header=False)
aiui.set_branding(title="AI Chat", logo="💬")
aiui.set_theme(preset="blue", dark_mode=True, radius="lg")
aiui.set_pages(["chat"]) # Only show the chat page
@aiui.starters
async def get_starters():
"""Suggest conversation starters."""
return [
{"label": "Explain something", "message": "Explain quantum computing in simple terms", "icon": "🔬"},
{"label": "Write code", "message": "Write a Python function to reverse a linked list", "icon": "💻"},
{"label": "Creative writing", "message": "Write a haiku about coding at midnight", "icon": "✍️"},
{"label": "Brainstorm", "message": "Give me 5 startup ideas using AI agents", "icon": "💡"},
]
@aiui.welcome
async def on_welcome():
"""Greet the user."""
await aiui.say("👋 Hi! Ask me anything — I'm ready to help.")
@aiui.reply
async def on_message(message: str):
"""Stream a response from OpenAI."""
await aiui.think("Thinking...")
client = _get_client()
if client is None:
if AsyncOpenAI is None:
await aiui.say("❌ Please install openai: `pip install openai`")
else:
await aiui.say("❌ Please set OPENAI_API_KEY environment variable.")
return
stream = await client.chat.completions.create(
model=os.getenv("PRAISONAI_MODEL", "gpt-4o-mini"),
messages=[
{"role": "system", "content": "You are a helpful, concise assistant. Use markdown for formatting."},
{"role": "user", "content": str(message)},
],
stream=True,
)
async for chunk in stream:
delta = chunk.choices[0].delta
if delta.content:
await aiui.stream_token(delta.content)
@aiui.cancel
async def on_cancel():
await aiui.say("⏹️ Stopped.")