Mercurial
comparison mrjunejune/inference/copilot_sidecar.py @ 280:49e9e591c9bb
Add persistent dictation, prewarmed WebRTC speech input, Copilot SDK routing, animated conversation lifecycle controls, parking, and architecture coverage.
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Tue, 18 Aug 2026 19:14:53 -0700 |
| parents | 056790c4fb0d |
| children | c57149ad216e |
comparison
equal
deleted
inserted
replaced
| 279:b3b547563ec7 | 280:49e9e591c9bb |
|---|---|
| 1 from __future__ import annotations | 1 from __future__ import annotations |
| 2 | 2 |
| 3 import argparse | 3 import argparse |
| 4 import asyncio | 4 import asyncio |
| 5 import hashlib | |
| 5 import json | 6 import json |
| 6 import os | 7 import os |
| 7 import sys | 8 import sys |
| 8 import time | 9 import time |
| 9 import uuid | 10 import uuid |
| 18 | 19 |
| 19 | 20 |
| 20 JsonObject = dict[str, Any] | 21 JsonObject = dict[str, Any] |
| 21 Emit = Callable[[JsonObject], Awaitable[None]] | 22 Emit = Callable[[JsonObject], Awaitable[None]] |
| 22 | 23 |
| 23 _KNOWN_PROFILES: frozenset = frozenset({"public_visitor", "invited_friend", "june_admin"}) | 24 _KNOWN_PROFILES: frozenset = frozenset({ |
| 25 "public_visitor", | |
| 26 "invited_friend", | |
| 27 "june_admin", | |
| 28 "canvas_orchestrator", | |
| 29 }) | |
| 24 _PROMPT_VERSION: int = 1 | 30 _PROMPT_VERSION: int = 1 |
| 31 _CANVAS_ORCHESTRATOR_PROMPT = """\ | |
| 32 You orchestrate spatial agent sessions on an infinite canvas. | |
| 33 There is one user input scratchpad and any number of conversation entities. | |
| 34 The user message includes only the visible canvas context plus one new thought. | |
| 35 Treat visible conversation entities as the only append candidates. Append when | |
| 36 the new thought clearly continues one of them; otherwise create a new | |
| 37 conversation. Never append to a conversation ID absent from visible context. | |
| 38 Return only one compact JSON object with these fields: | |
| 39 - action: "create" or "append" | |
| 40 - conversation_id: an existing numeric conversation ID for append, otherwise 0 | |
| 41 - title: a short session title | |
| 42 - response: the direct response that should appear on the canvas | |
| 43 Do not use Markdown fences, tools, or text outside the JSON object. | |
| 44 """ | |
| 25 | 45 |
| 26 # Fixed namespace for uuid5 SDK session ID derivation. Must never change. | 46 # Fixed namespace for uuid5 SDK session ID derivation. Must never change. |
| 27 _SDK_SESSION_NAMESPACE = uuid.UUID("3f7e8a1d-9b52-4c6f-a0d3-82e1f5c94b7a") | 47 _SDK_SESSION_NAMESPACE = uuid.UUID("3f7e8a1d-9b52-4c6f-a0d3-82e1f5c94b7a") |
| 28 | 48 |
| 29 _HISTORY_MAX_ENTRIES: int = 20 | 49 _HISTORY_MAX_ENTRIES: int = 20 |
| 252 self._cleanup_task = asyncio.create_task(self._cleanup_loop()) | 272 self._cleanup_task = asyncio.create_task(self._cleanup_loop()) |
| 253 | 273 |
| 254 def _compile_all_profiles(self) -> None: | 274 def _compile_all_profiles(self) -> None: |
| 255 compiled: dict[str, CompiledProfile] = {} | 275 compiled: dict[str, CompiledProfile] = {} |
| 256 for profile in sorted(_KNOWN_PROFILES): | 276 for profile in sorted(_KNOWN_PROFILES): |
| 257 result = self._compile_fn(profile) | 277 if profile == "canvas_orchestrator": |
| 278 content = _CANVAS_ORCHESTRATOR_PROMPT.strip() | |
| 279 result = { | |
| 280 "content": content, | |
| 281 "version": 1, | |
| 282 "hash": hashlib.sha256( | |
| 283 content.encode("utf-8") | |
| 284 ).hexdigest(), | |
| 285 } | |
| 286 else: | |
| 287 result = self._compile_fn(profile) | |
| 258 compiled[profile] = CompiledProfile( | 288 compiled[profile] = CompiledProfile( |
| 259 profile=profile, | 289 profile=profile, |
| 260 content=result["content"], | 290 content=result["content"], |
| 261 prompt_version=_PROMPT_VERSION, | 291 prompt_version=_PROMPT_VERSION, |
| 262 knowledge_version=result["version"], | 292 knowledge_version=result["version"], |
| 325 ) | 355 ) |
| 326 return | 356 return |
| 327 | 357 |
| 328 await self.evict_idle_sessions( | 358 await self.evict_idle_sessions( |
| 329 exclude=conversation_id, | 359 exclude=conversation_id, |
| 330 reserve=1 if command_name == "turn.start" else 0, | 360 reserve=1 |
| 361 if command_name in {"turn.start", "conversation.warm"} | |
| 362 else 0, | |
| 331 ) | 363 ) |
| 332 async with self._conversation_command_lock(conversation_id): | 364 async with self._conversation_command_lock(conversation_id): |
| 333 if command_name == "turn.start": | 365 if command_name == "turn.start": |
| 334 prompt = command.get("prompt") | 366 prompt = command.get("prompt") |
| 335 if not isinstance(prompt, str) or not prompt: | 367 if not isinstance(prompt, str) or not prompt: |
| 363 "invalid_prompt_profile", | 395 "invalid_prompt_profile", |
| 364 "a known prompt profile and current versions are required", | 396 "a known prompt profile and current versions are required", |
| 365 ) | 397 ) |
| 366 return | 398 return |
| 367 await self._start_turn(request_id, conversation_id, prompt, compiled, history) | 399 await self._start_turn(request_id, conversation_id, prompt, compiled, history) |
| 400 elif command_name == "conversation.warm": | |
| 401 compiled = self._validate_profile_fields( | |
| 402 command.get("prompt_profile"), | |
| 403 command.get("prompt_version"), | |
| 404 command.get("knowledge_version"), | |
| 405 ) | |
| 406 if compiled is None: | |
| 407 await self._fail( | |
| 408 request_id, | |
| 409 conversation_id, | |
| 410 "invalid_prompt_profile", | |
| 411 "a known prompt profile and current versions are required", | |
| 412 ) | |
| 413 return | |
| 414 resume_existing = command.get("resume_existing", True) | |
| 415 if not isinstance(resume_existing, bool): | |
| 416 await self._fail( | |
| 417 request_id, | |
| 418 conversation_id, | |
| 419 "invalid_request", | |
| 420 "resume_existing must be a boolean", | |
| 421 ) | |
| 422 return | |
| 423 await self._get_conversation( | |
| 424 conversation_id, | |
| 425 compiled, | |
| 426 [], | |
| 427 resume_existing=resume_existing, | |
| 428 ) | |
| 429 await self._send( | |
| 430 "session.warmed", | |
| 431 request_id, | |
| 432 conversation_id, | |
| 433 ) | |
| 434 await self._send( | |
| 435 "turn.done", | |
| 436 request_id, | |
| 437 conversation_id, | |
| 438 ) | |
| 368 elif command_name == "turn.abort": | 439 elif command_name == "turn.abort": |
| 369 await self._abort_turn(request_id, conversation_id) | 440 await self._abort_turn(request_id, conversation_id) |
| 370 elif command_name == "conversation.delete": | 441 elif command_name == "conversation.delete": |
| 371 await self._delete_conversation(request_id, conversation_id) | 442 await self._delete_conversation(request_id, conversation_id) |
| 372 else: | 443 else: |
| 484 async def _get_conversation( | 555 async def _get_conversation( |
| 485 self, | 556 self, |
| 486 conversation_id: str, | 557 conversation_id: str, |
| 487 compiled: CompiledProfile, | 558 compiled: CompiledProfile, |
| 488 history: list[dict[str, str]], | 559 history: list[dict[str, str]], |
| 560 *, | |
| 561 resume_existing: bool = True, | |
| 489 ) -> Conversation: | 562 ) -> Conversation: |
| 490 derived_id = _derive_sdk_session_id(conversation_id, compiled) | 563 derived_id = _derive_sdk_session_id(conversation_id, compiled) |
| 491 async with self._conversations_lock: | 564 async with self._conversations_lock: |
| 492 existing = self._conversations.get(conversation_id) | 565 existing = self._conversations.get(conversation_id) |
| 493 if existing is not None: | 566 if existing is not None: |
| 506 | 579 |
| 507 if len(self._conversations) >= self._config.max_sessions: | 580 if len(self._conversations) >= self._config.max_sessions: |
| 508 raise RuntimeError("Copilot session capacity exhausted") | 581 raise RuntimeError("Copilot session capacity exhausted") |
| 509 | 582 |
| 510 base_options = self._session_options(compiled) | 583 base_options = self._session_options(compiled) |
| 511 # Try to resume a persisted derived session (no history injection). | |
| 512 session: Any = None | 584 session: Any = None |
| 513 try: | 585 if resume_existing: |
| 514 session = await self._client.resume_session(derived_id, **base_options) | 586 # Resume persisted user-facing sessions, then create if absent. |
| 515 except Exception: | 587 try: |
| 516 pass | 588 session = await self._client.resume_session(derived_id, **base_options) |
| 589 except Exception: | |
| 590 pass | |
| 517 | 591 |
| 518 if session is None: | 592 if session is None: |
| 519 # Fresh create — inject bounded transcript context into system message. | 593 # Fresh create — inject bounded transcript context into system message. |
| 520 create_options = ( | 594 create_options = ( |
| 521 self._session_options_with_history(compiled, history) | 595 self._session_options_with_history(compiled, history) |