Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ repos:
- --use-current-year

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.1
rev: v0.14.3
hooks:
- id: ruff-format
stages: [pre-commit]
Expand Down
9 changes: 9 additions & 0 deletions bin/mypy-strict
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ run_mypy() {
main() {
local user_email="none"
local after_date=""
local in_this_branch=""

# Parse arguments
while [[ $# -gt 0 ]]; do
Expand Down Expand Up @@ -74,6 +75,10 @@ main() {
esac
shift 2
;;
--in-this-branch)
in_this_branch=true
shift 1
;;
*)
echo "Error: Unknown argument '$1'" >&2
exit 1
Expand All @@ -92,6 +97,10 @@ main() {
pipeline="$pipeline | ./bin/filter-errors-for-user '$user_email'"
fi

if [[ "$in_this_branch" ]]; then
pipeline="$pipeline | grep -Ff <(git diff --name-only dev..HEAD) -"
fi

eval "$pipeline"
}

Expand Down
3 changes: 1 addition & 2 deletions dimos/agents2/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@
from dimos.agents2.system_prompt import get_system_prompt
from dimos.core import DimosCluster, rpc
from dimos.protocol.skill.coordinator import (
SkillContainer,
SkillCoordinator,
SkillState,
SkillStateDict,
)
from dimos.protocol.skill.skill import SkillContainer
from dimos.protocol.skill.type import Output
from dimos.utils.logging_config import setup_logger

Expand Down Expand Up @@ -270,7 +270,6 @@ def _get_state() -> str:
# we are getting tools from the coordinator on each turn
# since this allows for skillcontainers to dynamically provide new skills
tools = self.get_tools()
print("Available tools:", [tool.name for tool in tools])
self._llm = self._llm.bind_tools(tools)

# publish to /agent topic for observability
Expand Down
38 changes: 38 additions & 0 deletions dimos/agents2/fixtures/test_pounce.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"responses": [
{
"content": "",
"tool_calls": [
{
"name": "execute_sport_command",
"args": {
"args": [
"FrontPounce"
]
},
"id": "call_Ukj6bCAnHQLj28RHRp697blZ",
"type": "tool_call"
}
]
},
{
"content": "",
"tool_calls": [
{
"name": "speak",
"args": {
"args": [
"I have successfully performed a front pounce."
]
},
"id": "call_FR9DtqEvJ9zSY85qVD2UFrll",
"type": "tool_call"
}
]
},
{
"content": "I have successfully performed a front pounce.",
"tool_calls": []
}
]
}
38 changes: 38 additions & 0 deletions dimos/agents2/fixtures/test_show_your_love.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"responses": [
{
"content": "",
"tool_calls": [
{
"name": "execute_sport_command",
"args": {
"args": [
"FingerHeart"
]
},
"id": "call_VFp6x9F00FBmiiUiemFWewop",
"type": "tool_call"
}
]
},
{
"content": "",
"tool_calls": [
{
"name": "speak",
"args": {
"args": [
"Here's a gesture to show you some love!"
]
},
"id": "call_WUUmBJ95s9PtVx8YQsmlJ4EU",
"type": "tool_call"
}
]
},
{
"content": "Just did a finger heart gesture to show my affection!",
"tool_calls": []
}
]
}
61 changes: 27 additions & 34 deletions dimos/agents2/skills/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,13 @@
from functools import partial

import pytest
import reactivex as rx
from reactivex.scheduler import ThreadPoolScheduler

from dimos.agents2.skills.google_maps_skill_container import GoogleMapsSkillContainer
from dimos.agents2.skills.gps_nav_skill import GpsNavSkillContainer
from dimos.agents2.skills.navigation import NavigationSkillContainer
from dimos.agents2.system_prompt import get_system_prompt
from dimos.mapping.types import LatLon
from dimos.msgs.sensor_msgs import Image
from dimos.robot.robot import GpsRobot
from dimos.utils.data import get_data
from dimos.robot.unitree_webrtc.unitree_skill_container import UnitreeSkillContainer

system_prompt = get_system_prompt()

Expand All @@ -45,31 +41,6 @@ def cleanup_threadpool_scheduler(monkeypatch):
threadpool.scheduler = ThreadPoolScheduler(max_workers=threadpool.get_max_workers())


# TODO: Delete
@pytest.fixture
def fake_robot(mocker):
return mocker.MagicMock()


# TODO: Delete
@pytest.fixture
def fake_gps_robot(mocker):
return mocker.Mock(spec=GpsRobot)


@pytest.fixture
def fake_video_stream():
image_path = get_data("chair-image.png")
image = Image.from_file(str(image_path))
return rx.of(image)


# TODO: Delete
@pytest.fixture
def fake_gps_position_stream():
return rx.of(LatLon(lat=37.783, lon=-122.413))


@pytest.fixture
def navigation_skill_container(mocker):
container = NavigationSkillContainer()
Expand All @@ -81,22 +52,35 @@ def navigation_skill_container(mocker):


@pytest.fixture
def gps_nav_skill_container(fake_gps_robot, fake_gps_position_stream):
container = GpsNavSkillContainer(fake_gps_robot, fake_gps_position_stream)
def gps_nav_skill_container(mocker):
container = GpsNavSkillContainer()
container.gps_location.connection = mocker.MagicMock()
container.gps_goal = mocker.MagicMock()
container.start()
yield container
container.stop()


@pytest.fixture
def google_maps_skill_container(fake_gps_robot, fake_gps_position_stream, mocker):
container = GoogleMapsSkillContainer(fake_gps_robot, fake_gps_position_stream)
def google_maps_skill_container(mocker):
container = GoogleMapsSkillContainer()
container.gps_location.connection = mocker.MagicMock()
container.start()
container._client = mocker.MagicMock()
yield container
container.stop()


@pytest.fixture
def unitree_skills(mocker):
container = UnitreeSkillContainer()
container._move = mocker.Mock()
container._publish_request = mocker.Mock()
container.start()
yield container
container.stop()


@pytest.fixture
def create_navigation_agent(navigation_skill_container, create_fake_agent):
return partial(
Expand All @@ -122,3 +106,12 @@ def create_google_maps_agent(
system_prompt=system_prompt,
skill_containers=[gps_nav_skill_container, google_maps_skill_container],
)


@pytest.fixture
def create_unitree_skills_agent(unitree_skills, create_fake_agent):
return partial(
create_fake_agent,
system_prompt=system_prompt,
skill_containers=[unitree_skills],
)
33 changes: 33 additions & 0 deletions dimos/agents2/skills/demo_google_maps_skill.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/env python3
# Copyright 2025 Dimensional Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from dotenv import load_dotenv

from dimos.agents2.agent import llm_agent
from dimos.agents2.cli.human import human_input
from dimos.agents2.skills.demo_robot import demo_robot
from dimos.agents2.skills.google_maps_skill_container import google_maps_skill
from dimos.agents2.system_prompt import get_system_prompt
from dimos.core.blueprints import autoconnect

load_dotenv()


demo_google_maps_skill = autoconnect(
demo_robot(),
google_maps_skill(),
human_input(),
llm_agent(system_prompt=get_system_prompt()),
)
33 changes: 33 additions & 0 deletions dimos/agents2/skills/demo_gps_nav.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/env python3
# Copyright 2025 Dimensional Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from dotenv import load_dotenv

from dimos.agents2.agent import llm_agent
from dimos.agents2.cli.human import human_input
from dimos.agents2.skills.demo_robot import demo_robot
from dimos.agents2.skills.gps_nav_skill import gps_nav_skill
from dimos.agents2.system_prompt import get_system_prompt
from dimos.core.blueprints import autoconnect

load_dotenv()


demo_gps_nav_skill = autoconnect(
demo_robot(),
gps_nav_skill(),
human_input(),
llm_agent(system_prompt=get_system_prompt()),
)
40 changes: 40 additions & 0 deletions dimos/agents2/skills/demo_robot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env python3
# Copyright 2025 Dimensional Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from reactivex import interval

from dimos.core.module import Module
from dimos.core.stream import Out
from dimos.mapping.types import LatLon


class DemoRobot(Module):
gps_location: Out[LatLon] = None

def start(self) -> None:
super().start()
self._disposables.add(interval(1.0).subscribe(lambda _: self._publish_gps_location()))

def stop(self) -> None:
super().stop()

def _publish_gps_location(self) -> None:
self.gps_location.publish(LatLon(lat=37.78092426217621, lon=-122.40682866540769))


demo_robot = DemoRobot.blueprint


__all__ = ["DemoRobot", "demo_robot"]
Loading
Loading