Remove SPADE dependency #29

Merged
k.marinus merged 28 commits from refactor/remove-spade into dev 2025-11-25 10:26:07 +00:00
7 changed files with 84 additions and 25 deletions
Showing only changes of commit d7353bea8c - Show all commits

View File

@@ -8,7 +8,7 @@ formatters:
# Console output # Console output
colored: colored:
(): "colorlog.ColoredFormatter" (): "colorlog.ColoredFormatter"
format: "{log_color}{asctime} | {levelname:11} | {name:70} | {message}" format: "{log_color}{asctime}.{msecs:03.0f} | {levelname:11} | {name:70} | {message}"
style: "{" style: "{"
datefmt: "%H:%M:%S" datefmt: "%H:%M:%S"

View File

@@ -5,6 +5,7 @@ description = "Add your description here"
readme = "README.md" readme = "README.md"
requires-python = ">=3.13" requires-python = ">=3.13"
dependencies = [ dependencies = [
"agentspeak>=0.2.2",
"colorlog>=6.10.1", "colorlog>=6.10.1",
"fastapi[all]>=0.115.6", "fastapi[all]>=0.115.6",
"mlx-whisper>=0.4.3 ; sys_platform == 'darwin'", "mlx-whisper>=0.4.3 ; sys_platform == 'darwin'",

View File

@@ -13,11 +13,12 @@ from control_backend.schemas.ri_message import SpeechCommand
class BDICoreAgent(BaseAgent): class BDICoreAgent(BaseAgent):
bdi_agent: agentspeak.runtime.Agent
def __init__(self, name: str, asl: str): def __init__(self, name: str, asl: str):
super().__init__(name) super().__init__(name)
self.asl_file = asl self.asl_file = asl
self.env = agentspeak.runtime.Environment() self.env = agentspeak.runtime.Environment()
self.bdi_agent = None
self.actions = agentspeak.stdlib.actions self.actions = agentspeak.stdlib.actions
async def setup(self) -> None: async def setup(self) -> None:
@@ -42,7 +43,6 @@ class BDICoreAgent(BaseAgent):
async def _bdi_loop(self): async def _bdi_loop(self):
"""Runs the AgentSpeak BDI loop.""" """Runs the AgentSpeak BDI loop."""
while self._running: while self._running:
assert self.bdi_agent is not None
self.bdi_agent.step() self.bdi_agent.step()
await asyncio.sleep(0.01) await asyncio.sleep(0.01)
@@ -74,30 +74,72 @@ class BDICoreAgent(BaseAgent):
) )
await self.send(out_msg) await self.send(out_msg)
# TODO: test way of adding beliefs
def _add_beliefs(self, beliefs: dict[str, list[str]]): def _add_beliefs(self, beliefs: dict[str, list[str]]):
if not beliefs: if not beliefs:
return return
for belief_name, args in beliefs.items(): for name, args in beliefs.items():
self._add_belief(belief_name, args) self._add_belief(name, args)
if belief_name == "user_said": def _add_belief(self, name: str, args: Iterable[str] = []):
self._add_belief("new_message") new_args = (agentspeak.Literal(arg) for arg in args)
term = agentspeak.Literal(name, new_args)
def _add_belief(self, belief_name: str, arguments: Iterable[str] = []):
args = (agentspeak.Literal(arg) for arg in arguments)
literal_belief = agentspeak.Literal(belief_name, args)
assert self.bdi_agent is not None assert self.bdi_agent is not None
self.bdi_agent.call( self.bdi_agent.call(
agentspeak.Trigger.addition, agentspeak.Trigger.addition,
agentspeak.GoalType.belief, agentspeak.GoalType.belief,
literal_belief, term,
agentspeak.runtime.Intention(), agentspeak.runtime.Intention(),
) )
self.logger.debug(f"Added belief {belief_name}({','.join(arguments)})") self.logger.debug(f"Added belief {self.format_belief_string(name, args)}")
def _remove_belief(self, name: str, args: Iterable[str]):
"""
Removes a specific belief (with arguments), if it exists.
"""
new_args = (agentspeak.Literal(arg) for arg in args)
term = agentspeak.Literal(name, new_args)
assert self.bdi_agent is not None
result = self.bdi_agent.call(
agentspeak.Trigger.removal,
agentspeak.GoalType.belief,
term,
agentspeak.runtime.Intention(),
)
if result:
self.logger.debug(f"Removed belief {self.format_belief_string(name, args)}")
else:
self.logger.debug("Failed to remove belief (it was not in the belief base).")
# TODO: decide if this is needed
def _remove_all_with_name(self, name: str):
"""
Removes all beliefs that match the given `name`.
"""
assert self.bdi_agent is not None
relevant_groups = []
for key in self.bdi_agent.beliefs:
if key[0] == name:
relevant_groups.append(key)
removed_count = 0
for group in relevant_groups:
for belief in self.bdi_agent.beliefs[group]:
self.bdi_agent.call(
agentspeak.Trigger.removal,
agentspeak.GoalType.belief,
belief,
agentspeak.runtime.Intention(),
)
removed_count += 1
self.logger.debug(f"Removed {removed_count} beliefs.")
def _add_custom_actions(self) -> None: def _add_custom_actions(self) -> None:
"""Add any custom actions here.""" """Add any custom actions here."""
@@ -119,3 +161,10 @@ class BDICoreAgent(BaseAgent):
msg = InternalMessage(to=settings.agent_settings.llm_name, sender=self.name, body=text) msg = InternalMessage(to=settings.agent_settings.llm_name, sender=self.name, body=text)
await self.send(msg) await self.send(msg)
self.logger.info("Message sent to LLM agent: %s", text) self.logger.info("Message sent to LLM agent: %s", text)
@staticmethod
def format_belief_string(name: str, args: Iterable[str] = []):
"""
Given a belief's name and its args, return a string of the form "name(*args)"
"""
return f"{name}{'(' if args else ''}{','.join(args)}{')' if args else ''}"

View File

@@ -1,3 +1,3 @@
+new_message : user_said(Message) <- +user_said(NewMessage) <-
-new_message; -user_said(NewMessage);
.reply(Message). .reply(NewMessage).

View File

@@ -67,7 +67,7 @@ class VADAgent(BaseAgent):
self.model = None self.model = None
async def setup(self): async def setup(self):
self.logger.info("Setting up %s", self.jid) self.logger.info("Setting up %s", self.name)
self._connect_audio_in_socket() self._connect_audio_in_socket()
@@ -99,7 +99,7 @@ class VADAgent(BaseAgent):
transcriber = TranscriptionAgent(audio_out_address) transcriber = TranscriptionAgent(audio_out_address)
await transcriber.start() await transcriber.start()
self.logger.info("Finished setting up %s", self.jid) self.logger.info("Finished setting up %s", self.name)
async def stop(self): async def stop(self):
""" """

View File

@@ -1,8 +1,7 @@
import asyncio import asyncio
import logging import logging
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from dataclasses import dataclass, field from dataclasses import dataclass
from typing import Any
# Central directory to resolve agent names to instances # Central directory to resolve agent names to instances
_agent_directory: dict[str, "BaseAgent"] = {} _agent_directory: dict[str, "BaseAgent"] = {}
@@ -14,7 +13,6 @@ class InternalMessage:
sender: str sender: str
body: str body: str
thread: str | None = None thread: str | None = None
metadata: dict[str, Any] = field(default_factory=dict)
class AgentDirectory: class AgentDirectory:
@@ -32,7 +30,6 @@ class BaseAgent(ABC):
def __init__(self, name: str): def __init__(self, name: str):
self.name = name self.name = name
self.jid = name # present for backwards compatibility
self.inbox: asyncio.Queue[InternalMessage] = asyncio.Queue() self.inbox: asyncio.Queue[InternalMessage] = asyncio.Queue()
self._tasks: set[asyncio.Task] = set() self._tasks: set[asyncio.Task] = set()
self._running = False self._running = False
@@ -83,5 +80,3 @@ class BaseAgent(ABC):
task = asyncio.create_task(coro) task = asyncio.create_task(coro)
self._tasks.add(task) self._tasks.add(task)
task.add_done_callback(self._tasks.discard) task.add_done_callback(self._tasks.discard)
# await asyncio.sleep(1)

14
uv.lock generated
View File

@@ -6,6 +6,18 @@ resolution-markers = [
"python_full_version < '3.14'", "python_full_version < '3.14'",
] ]
[[package]]
name = "agentspeak"
version = "0.2.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d0/a3/f8e9292cfd47aa5558f4578c498ca12c068a3a1d60ddfd0af13a87c1e47a/agentspeak-0.2.2.tar.gz", hash = "sha256:7c7fcf689fd54460597be1798ce11535f42a60c3d79af59381af3e13ef7a41bb", size = 59628, upload-time = "2024-03-21T11:55:39.026Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/03/b5/e95cbd9d9e999ac8dc4e0bb7a940112a2751cf98880b4ff0626e53d14249/agentspeak-0.2.2-py3-none-any.whl", hash = "sha256:9b454bc0adf63cb0d73fb4a3a9a489e7d892d5fbf17f750de532670736c0c4dd", size = 61628, upload-time = "2024-03-21T11:55:36.741Z" },
]
[[package]] [[package]]
name = "alabaster" name = "alabaster"
version = "0.7.16" version = "0.7.16"
@@ -975,6 +987,7 @@ name = "pepperplus-cb"
version = "0.1.0" version = "0.1.0"
source = { virtual = "." } source = { virtual = "." }
dependencies = [ dependencies = [
{ name = "agentspeak" },
{ name = "colorlog" }, { name = "colorlog" },
{ name = "fastapi", extra = ["all"] }, { name = "fastapi", extra = ["all"] },
{ name = "mlx-whisper", marker = "sys_platform == 'darwin'" }, { name = "mlx-whisper", marker = "sys_platform == 'darwin'" },
@@ -1016,6 +1029,7 @@ test = [
[package.metadata] [package.metadata]
requires-dist = [ requires-dist = [
{ name = "agentspeak", specifier = ">=0.2.2" },
{ name = "colorlog", specifier = ">=6.10.1" }, { name = "colorlog", specifier = ">=6.10.1" },
{ name = "fastapi", extras = ["all"], specifier = ">=0.115.6" }, { name = "fastapi", extras = ["all"], specifier = ">=0.115.6" },
{ name = "mlx-whisper", marker = "sys_platform == 'darwin'", specifier = ">=0.4.3" }, { name = "mlx-whisper", marker = "sys_platform == 'darwin'", specifier = ">=0.4.3" },