Move conversational fast path into runtime launcher
- SHA
5b9c14f85ebea90049931c329e1b983ab91d2c38- Parents
-
c61058b - Tree
b7a78b3
5b9c14f
5b9c14f85ebea90049931c329e1b983ab91d2c38c61058b
b7a78b3| Status | File | + | - |
|---|---|---|---|
| M |
src/loader/agent/loop.py
|
2 | 48 |
| A |
src/loader/runtime/chat_lane.py
|
55 | 0 |
| M |
src/loader/runtime/launcher.py
|
11 | 0 |
src/loader/agent/loop.pymodified@@ -375,53 +375,6 @@ class Agent: | ||
| 375 | 375 | ) |
| 376 | 376 | return parse_decomposition(response.content, task) |
| 377 | 377 | |
| 378 | - async def _handle_conversational( | |
| 379 | - self, | |
| 380 | - user_message: str, | |
| 381 | - emit: Callable[[AgentEvent], Awaitable[None]], | |
| 382 | - ) -> str: | |
| 383 | - """Fast path for conversational messages - no tools, quick response.""" | |
| 384 | - await emit(AgentEvent(type="thinking")) | |
| 385 | - | |
| 386 | - # Add to history | |
| 387 | - self.session.append(Message(role=Role.USER, content=user_message)) | |
| 388 | - | |
| 389 | - # Simple system prompt for chat (no tools) | |
| 390 | - chat_system = Message( | |
| 391 | - role=Role.SYSTEM, | |
| 392 | - content=( | |
| 393 | - "You are Loader, a friendly local coding assistant. " | |
| 394 | - "Respond naturally and briefly to conversational messages. " | |
| 395 | - "If the user wants to do a coding task, tell them to describe it. " | |
| 396 | - "Keep responses short (1-3 sentences)." | |
| 397 | - ), | |
| 398 | - ) | |
| 399 | - | |
| 400 | - # Use only recent context for speed | |
| 401 | - recent_messages = self.messages[-4:] if len(self.messages) > 4 else self.messages | |
| 402 | - | |
| 403 | - # Stream the response | |
| 404 | - full_content = "" | |
| 405 | - async for chunk in self.backend.stream( | |
| 406 | - messages=[chat_system] + recent_messages, | |
| 407 | - tools=None, # No tools for chat | |
| 408 | - temperature=0.7, # More natural | |
| 409 | - max_tokens=256, # Short response | |
| 410 | - ): | |
| 411 | - if chunk.content: | |
| 412 | - await emit(AgentEvent( | |
| 413 | - type="stream", | |
| 414 | - content=chunk.content, | |
| 415 | - is_stream_end=chunk.is_done, | |
| 416 | - )) | |
| 417 | - full_content += chunk.content | |
| 418 | - | |
| 419 | - # Add to history | |
| 420 | - self.session.append(Message(role=Role.ASSISTANT, content=full_content)) | |
| 421 | - | |
| 422 | - await emit(AgentEvent(type="response", content=full_content)) | |
| 423 | - return full_content | |
| 424 | - | |
| 425 | 378 | async def run( |
| 426 | 379 | self, |
| 427 | 380 | user_message: str, |
@@ -474,10 +427,11 @@ class Agent: | ||
| 474 | 427 | ) -> str: |
| 475 | 428 | """Internal run method that supports steering.""" |
| 476 | 429 | cfg = self.config.reasoning |
| 430 | + launcher = build_runtime_launcher(self) | |
| 477 | 431 | |
| 478 | 432 | # Fast path: conversational messages don't need tools |
| 479 | 433 | if is_conversational(user_message): |
| 480 | - return await self._handle_conversational(user_message, emit) | |
| 434 | + return await launcher.run_conversational(user_message, emit) | |
| 481 | 435 | |
| 482 | 436 | # Track original task for multi-turn conversations |
| 483 | 437 | # Only set on first non-conversational message |
src/loader/runtime/chat_lane.pyadded@@ -0,0 +1,55 @@ | ||
| 1 | +"""Runtime-owned conversational fast path for non-tool chat turns.""" | |
| 2 | + | |
| 3 | +from __future__ import annotations | |
| 4 | + | |
| 5 | +from ..llm.base import Message, Role | |
| 6 | +from .bootstrap import RuntimeBootstrapSource | |
| 7 | +from .events import AgentEvent | |
| 8 | + | |
| 9 | +CHAT_SYSTEM_PROMPT = ( | |
| 10 | + "You are Loader, a friendly local coding assistant. " | |
| 11 | + "Respond naturally and briefly to conversational messages. " | |
| 12 | + "If the user wants to do a coding task, tell them to describe it. " | |
| 13 | + "Keep responses short (1-3 sentences)." | |
| 14 | +) | |
| 15 | + | |
| 16 | + | |
| 17 | +class ConversationalTurnRunner: | |
| 18 | + """Own the non-tool conversational fast path outside the agent shell.""" | |
| 19 | + | |
| 20 | + def __init__(self, source: RuntimeBootstrapSource) -> None: | |
| 21 | + self.source = source | |
| 22 | + | |
| 23 | + async def run(self, user_message: str, emit) -> str: | |
| 24 | + """Stream one short conversational reply and persist the transcript.""" | |
| 25 | + | |
| 26 | + await emit(AgentEvent(type="thinking")) | |
| 27 | + self.source.session.append(Message(role=Role.USER, content=user_message)) | |
| 28 | + | |
| 29 | + recent_messages = ( | |
| 30 | + self.source.session.messages[-4:] | |
| 31 | + if len(self.source.session.messages) > 4 | |
| 32 | + else self.source.session.messages | |
| 33 | + ) | |
| 34 | + chat_system = Message(role=Role.SYSTEM, content=CHAT_SYSTEM_PROMPT) | |
| 35 | + | |
| 36 | + full_content = "" | |
| 37 | + async for chunk in self.source.backend.stream( | |
| 38 | + messages=[chat_system] + recent_messages, | |
| 39 | + tools=None, | |
| 40 | + temperature=0.7, | |
| 41 | + max_tokens=256, | |
| 42 | + ): | |
| 43 | + if chunk.content: | |
| 44 | + await emit( | |
| 45 | + AgentEvent( | |
| 46 | + type="stream", | |
| 47 | + content=chunk.content, | |
| 48 | + is_stream_end=chunk.is_done, | |
| 49 | + ) | |
| 50 | + ) | |
| 51 | + full_content += chunk.content | |
| 52 | + | |
| 53 | + self.source.session.append(Message(role=Role.ASSISTANT, content=full_content)) | |
| 54 | + await emit(AgentEvent(type="response", content=full_content)) | |
| 55 | + return full_content | |
src/loader/runtime/launcher.pymodified@@ -3,6 +3,7 @@ | ||
| 3 | 3 | from __future__ import annotations |
| 4 | 4 | |
| 5 | 5 | from .bootstrap import RuntimeBootstrapSource |
| 6 | +from .chat_lane import ConversationalTurnRunner | |
| 6 | 7 | from .conversation import ConfirmationHandler, ConversationRuntime, EventSink, UserQuestionHandler |
| 7 | 8 | from .events import TurnSummary |
| 8 | 9 | from .explore import ExploreRuntime |
@@ -14,6 +15,16 @@ class RuntimeLauncher: | ||
| 14 | 15 | def __init__(self, source: RuntimeBootstrapSource) -> None: |
| 15 | 16 | self.source = source |
| 16 | 17 | |
| 18 | + async def run_conversational( | |
| 19 | + self, | |
| 20 | + user_message: str, | |
| 21 | + emit: EventSink, | |
| 22 | + ) -> str: | |
| 23 | + """Run the runtime-owned conversational fast path.""" | |
| 24 | + | |
| 25 | + runner = ConversationalTurnRunner(self.source) | |
| 26 | + return await runner.run(user_message, emit) | |
| 27 | + | |
| 17 | 28 | async def run_turn( |
| 18 | 29 | self, |
| 19 | 30 | task: str, |