Files

132 lines
4.6 KiB
Python

from __future__ import annotations
import asyncio
import sys
from PyQt5.QtCore import QThread, pyqtSignal
from agent.kernel import EventBus, Event, EventType, AgentRuntime
from agent.llm import LLMClient
from agent.personality import Personality
from agent.conversation import ConversationManager
from agent.interruption import InterruptionEngine
from agent.thinker import Thinker
from agent.orchestrator import Orchestrator
from agent.tools.registry import ToolRegistry
from agent.tools.file_tools import register_file_tools
from agent.tools.bash_tools import register_bash_tools
from agent.tools.ask import register_ask_tools
from config.settings import AppSettings
class AgentThread(QThread):
message_chunk = pyqtSignal(str)
message_done = pyqtSignal(str)
agent_thinking = pyqtSignal()
agent_idle = pyqtSignal()
error_occurred = pyqtSignal(str)
interrupt_signal = pyqtSignal(str, str)
idle_topic_signal = pyqtSignal(str)
def __init__(self, config: AppSettings):
super().__init__()
self.config = config
self._loop: asyncio.AbstractEventLoop = None
self._running = False
self._input_queue: asyncio.Queue[str] = None
def run(self):
self._loop = asyncio.new_event_loop()
asyncio.set_event_loop(self._loop)
self._loop.run_until_complete(self._run_agent())
async def _run_agent(self):
self._input_queue = asyncio.Queue()
self._running = True
runtime = AgentRuntime(self.config)
llm = LLMClient(self.config.llm)
bus = runtime.bus
personality = Personality.from_config({
"name": self.config.personality.name,
"style": self.config.personality.style,
"proactive_chat": self.config.personality.proactive_chat,
"idle_timeout": self.config.personality.idle_timeout,
"humor_enabled": self.config.personality.humor_enabled,
"empathy_enabled": self.config.personality.empathy_enabled,
})
conversation = ConversationManager()
interruption = InterruptionEngine(llm=llm, config=self.config)
thinker = Thinker(bus, llm, conversation, self.config)
orchestrator = Orchestrator(runtime, llm, personality, conversation, interruption, thinker)
conversation.create_topic("general")
await runtime.start()
thinker_task = runtime.create_task(thinker.run())
async def on_thought(event: Event):
data = event.data
if data.get("type") == "idle_chat":
self.idle_topic_signal.emit(data.get("suggestion", ""))
bus.on(EventType.AGENT_THOUGHT, on_thought)
try:
while self._running:
try:
user_text = await asyncio.wait_for(
self._input_queue.get(), timeout=0.5
)
except asyncio.TimeoutError:
continue
thinker.notify_user_activity()
conversation.add_message("user", user_text)
self.agent_thinking.emit()
system_prompt = personality.build_system_prompt()
messages = [{"role": "system", "content": system_prompt}]
messages.extend(conversation.get_context_window(20))
try:
full = ""
async for chunk in llm.chat_stream_iter(messages):
full += chunk
self.message_chunk.emit(chunk)
self.message_done.emit(full)
if full.strip():
conversation.add_message("assistant", full)
except Exception as e:
self.error_occurred.emit(str(e))
self.agent_idle.emit()
finally:
await runtime.stop()
await llm.close()
def send_message(self, text: str):
if self._input_queue and self._running:
asyncio.run_coroutine_threadsafe(
self._input_queue.put(text), self._loop
)
def check_interruption(self, partial_text: str, pause: float):
if not self._running or not self._loop:
return
config = self.config
llm = LLMClient(config.llm)
engine = InterruptionEngine(llm=llm, config=config)
async def _check():
decision = await engine.evaluate(partial_text, pause, False)
if decision.should_interrupt:
self.interrupt_signal.emit(
decision.suggested_response, decision.style
)
asyncio.run_coroutine_threadsafe(_check(), self._loop)
def stop_agent(self):
self._running = False