experiment: reduce keepalive to 5s and add progress bar
EXPERIMENT (NOT FOR PRODUCTION YET)
Changes:
- KEEPALIVE_INTERVAL_SECONDS reduced from 20s to 5s
- Keepalive messages now show progress bar with dots: ●●●●○○○○○○
- Show elapsed time, total wait, and remaining seconds
- Example: ⏳ Waiting for instructions... ●●●●○○○○○○ 20s / 50s (agent=copilot, 30s remaining)
Goal: Test if more frequent progress updates provide better UX and prevent
perceived freezing during the 50s wait. No functional change - the
60s client timeout limit remains the binding constraint.
This commit is contained in:
@@ -33,6 +33,14 @@ mcp_asgi_app = mcp.streamable_http_app()
|
||||
# asyncio.Event wakeup, so there is no practical danger in long waits.
|
||||
_MAX_WAIT_SECONDS = 86400 # 24 hours
|
||||
|
||||
# Default wait time when no instruction is available (seconds)
|
||||
# Set to 50s to stay safely under the 60s client timeout while allowing
|
||||
# multiple keepalive progress updates.
|
||||
DEFAULT_WAIT_SECONDS = 50
|
||||
|
||||
# Default response when queue is empty after waiting
|
||||
DEFAULT_EMPTY_RESPONSE = "call this tool `get_user_request` again to fetch latest user input..."
|
||||
|
||||
# Per-agent generation counter — incremented on every new call.
|
||||
# The wait loop only consumes an instruction when it holds the latest generation,
|
||||
# preventing abandoned (timed-out) coroutines from silently consuming queue items.
|
||||
@@ -50,7 +58,8 @@ _agent_generations: dict[str, int] = {}
|
||||
# start and are unaffected by intermediate SSE events.
|
||||
#
|
||||
# Set to 0 to disable keepalives entirely.
|
||||
KEEPALIVE_INTERVAL_SECONDS: float = 20.0
|
||||
# EXPERIMENT: Reduced from 20.0 to 5.0 for more frequent progress updates
|
||||
KEEPALIVE_INTERVAL_SECONDS: float = 5.0
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
@@ -74,10 +83,8 @@ async def get_user_request(
|
||||
A dict with keys: status, result_type, instruction, response,
|
||||
remaining_pending, waited_seconds.
|
||||
"""
|
||||
cfg = config_service.get_config()
|
||||
|
||||
# Wait time is entirely server-controlled — the user sets it via the web UI.
|
||||
actual_wait = min(cfg.default_wait_seconds, _MAX_WAIT_SECONDS)
|
||||
# Wait time is hardcoded to stay safely under the 60s client timeout
|
||||
actual_wait = min(DEFAULT_WAIT_SECONDS, _MAX_WAIT_SECONDS)
|
||||
|
||||
# Register this call as the newest for this agent.
|
||||
my_gen = _agent_generations.get(agent_id, 0) + 1
|
||||
@@ -171,14 +178,19 @@ async def get_user_request(
|
||||
if KEEPALIVE_INTERVAL_SECONDS > 0 and ctx is not None:
|
||||
if now - last_keepalive >= KEEPALIVE_INTERVAL_SECONDS:
|
||||
waited_so_far = int(now - start)
|
||||
remaining_sec = max(0, actual_wait - waited_so_far)
|
||||
# Progress bar: filled dots proportional to elapsed time
|
||||
progress_pct = min(100, int((waited_so_far / actual_wait) * 100))
|
||||
filled = int(progress_pct / 10)
|
||||
bar = "●" * filled + "○" * (10 - filled)
|
||||
try:
|
||||
await ctx.info(
|
||||
f"keepalive: waiting for instructions "
|
||||
f"(agent={agent_id}, waited={waited_so_far}s)"
|
||||
f"⏳ Waiting for instructions... {bar} "
|
||||
f"{waited_so_far}s / {actual_wait}s (agent={agent_id}, {remaining_sec}s remaining)"
|
||||
)
|
||||
logger.debug(
|
||||
"get_user_request: keepalive sent agent=%s waited=%ds",
|
||||
agent_id, waited_so_far,
|
||||
"get_user_request: keepalive sent agent=%s waited=%ds progress=%d%%",
|
||||
agent_id, waited_so_far, progress_pct,
|
||||
)
|
||||
except Exception as exc:
|
||||
# Client disconnected — no point continuing
|
||||
@@ -213,7 +225,7 @@ async def get_user_request(
|
||||
empty_response = (
|
||||
default_response_override
|
||||
if default_response_override is not None
|
||||
else cfg.default_empty_response
|
||||
else DEFAULT_EMPTY_RESPONSE
|
||||
)
|
||||
result_type = "default_response" if empty_response else "empty"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user