From 748cf156f50d617ad5f2aadd234b6907ddd04219 Mon Sep 17 00:00:00 2001 From: Paul Nechifor Date: Tue, 25 Nov 2025 05:14:35 +0200 Subject: [PATCH 1/3] add `type: ignore` --- bin/mypy-strict | 1 - dimos/agents/agent.py | 158 ++++++------ dimos/agents/agent_message.py | 2 +- dimos/agents/agent_types.py | 6 +- dimos/agents/claude_agent.py | 44 ++-- dimos/agents/memory/base.py | 20 +- dimos/agents/memory/chroma_impl.py | 40 +-- dimos/agents/memory/image_embedding.py | 20 +- dimos/agents/memory/spatial_vector_db.py | 24 +- dimos/agents/memory/visual_memory.py | 6 +- dimos/agents/modules/base.py | 28 +-- dimos/agents/modules/base_agent.py | 22 +- dimos/agents/modules/gateway/client.py | 20 +- .../modules/gateway/tensorzero_embedded.py | 30 +-- dimos/agents/prompt_builder/impl.py | 8 +- dimos/agents/tokenizer/base.py | 8 +- .../agents/tokenizer/huggingface_tokenizer.py | 12 +- dimos/agents/tokenizer/openai_tokenizer.py | 10 +- dimos/agents2/agent.py | 80 +++--- dimos/agents2/cli/human.py | 14 +- dimos/agents2/skills/demo_robot.py | 4 +- .../skills/google_maps_skill_container.py | 4 +- dimos/agents2/skills/gps_nav_skill.py | 12 +- dimos/agents2/skills/navigation.py | 10 +- dimos/agents2/skills/osm.py | 10 +- dimos/agents2/skills/ros_navigation.py | 4 +- dimos/agents2/spec.py | 24 +- dimos/agents2/temp/webcam_agent.py | 24 +- dimos/agents2/testing.py | 8 +- dimos/core/__init__.py | 40 +-- dimos/core/blueprints.py | 24 +- dimos/core/module.py | 58 ++--- dimos/core/module_coordinator.py | 10 +- dimos/core/o3dpickle.py | 6 +- dimos/core/rpc_client.py | 24 +- dimos/core/skill_module.py | 4 +- dimos/core/stream.py | 76 +++--- dimos/core/testing.py | 22 +- dimos/core/transport.py | 76 +++--- dimos/environment/environment.py | 22 +- dimos/exceptions/agent_memory_exceptions.py | 6 +- dimos/hardware/camera/module.py | 48 ++-- dimos/hardware/camera/spec.py | 4 +- dimos/hardware/camera/webcam.py | 26 +- dimos/hardware/camera/zed/__init__.py | 10 +- dimos/hardware/camera/zed/camera.py | 50 ++-- dimos/hardware/end_effector.py | 4 +- dimos/hardware/fake_zed_module.py | 38 +-- dimos/hardware/gstreamer_camera.py | 38 +-- .../hardware/gstreamer_camera_test_script.py | 6 +- dimos/hardware/gstreamer_sender.py | 62 ++--- dimos/hardware/piper_arm.py | 52 ++-- dimos/hardware/sensor.py | 8 +- dimos/manipulation/manip_aio_pipeline.py | 70 +++--- dimos/manipulation/manip_aio_processer.py | 48 ++-- dimos/manipulation/manipulation_history.py | 22 +- dimos/manipulation/manipulation_interface.py | 16 +- .../visual_servoing/detection3d.py | 12 +- .../visual_servoing/manipulation_module.py | 76 +++--- dimos/manipulation/visual_servoing/pbvs.py | 24 +- dimos/manipulation/visual_servoing/utils.py | 46 ++-- dimos/mapping/google_maps/google_maps.py | 2 +- dimos/mapping/osm/current_location_map.py | 12 +- dimos/models/depth/metric3d.py | 38 +-- dimos/models/embedding/base.py | 6 +- dimos/models/embedding/clip.py | 2 +- .../embedding_models_disabled_tests.py | 38 +-- dimos/models/embedding/mobileclip.py | 4 +- dimos/models/embedding/treid.py | 4 +- .../contact_graspnet_pytorch/inference.py | 10 +- dimos/models/pointcloud/pointcloud_utils.py | 28 +-- dimos/models/qwen/video_query.py | 16 +- dimos/models/segmentation/segment_utils.py | 12 +- dimos/models/vl/base.py | 18 +- dimos/models/vl/moondream.py | 8 +- dimos/models/vl/moondream_hosted.py | 14 +- dimos/models/vl/qwen.py | 4 +- dimos/msgs/foxglove_msgs/Color.py | 4 +- dimos/msgs/foxglove_msgs/ImageAnnotations.py | 8 +- dimos/msgs/geometry_msgs/Pose.py | 36 +-- dimos/msgs/geometry_msgs/PoseStamped.py | 20 +- .../msgs/geometry_msgs/PoseWithCovariance.py | 62 ++--- .../PoseWithCovarianceStamped.py | 40 +-- dimos/msgs/geometry_msgs/Quaternion.py | 26 +- dimos/msgs/geometry_msgs/Transform.py | 30 +-- dimos/msgs/geometry_msgs/Twist.py | 28 +-- dimos/msgs/geometry_msgs/TwistStamped.py | 20 +- .../msgs/geometry_msgs/TwistWithCovariance.py | 62 ++--- .../TwistWithCovarianceStamped.py | 40 +-- dimos/msgs/geometry_msgs/Vector3.py | 74 +++--- dimos/msgs/nav_msgs/OccupancyGrid.py | 28 +-- dimos/msgs/nav_msgs/Odometry.py | 38 +-- dimos/msgs/nav_msgs/Path.py | 24 +- dimos/msgs/nav_msgs/__init__.py | 2 +- dimos/msgs/sensor_msgs/CameraInfo.py | 46 ++-- dimos/msgs/sensor_msgs/Image.py | 100 ++++---- dimos/msgs/sensor_msgs/Joy.py | 24 +- dimos/msgs/sensor_msgs/PointCloud2.py | 44 ++-- .../sensor_msgs/image_impls/AbstractImage.py | 50 ++-- .../msgs/sensor_msgs/image_impls/CudaImage.py | 178 ++++++------- .../sensor_msgs/image_impls/NumpyImage.py | 36 +-- dimos/msgs/std_msgs/Bool.py | 10 +- dimos/msgs/std_msgs/Header.py | 20 +- dimos/msgs/std_msgs/Int32.py | 4 +- dimos/msgs/std_msgs/Int8.py | 10 +- dimos/msgs/tf2_msgs/TFMessage.py | 16 +- dimos/msgs/vision_msgs/BoundingBox2DArray.py | 4 +- dimos/msgs/vision_msgs/BoundingBox3DArray.py | 4 +- dimos/msgs/vision_msgs/Detection2DArray.py | 4 +- dimos/msgs/vision_msgs/Detection3DArray.py | 4 +- dimos/navigation/bbox_navigation.py | 8 +- .../navigation/bt_navigator/goal_validator.py | 4 +- dimos/navigation/bt_navigator/navigator.py | 34 +-- .../bt_navigator/recovery_server.py | 6 +- dimos/navigation/demo_ros_navigation.py | 6 +- .../wavefront_frontier_goal_selector.py | 30 +-- dimos/navigation/global_planner/algo.py | 12 +- dimos/navigation/global_planner/planner.py | 12 +- .../local_planner/holonomic_local_planner.py | 30 +-- .../navigation/local_planner/local_planner.py | 16 +- dimos/navigation/rosnav.py | 56 ++--- .../perception/common/detection2d_tracker.py | 24 +- dimos/perception/common/export_tensorrt.py | 4 +- dimos/perception/common/ibvs.py | 12 +- dimos/perception/common/utils.py | 144 +++++------ dimos/perception/detection/detectors/detic.py | 42 ++-- .../detection/detectors/person/yolo.py | 2 +- dimos/perception/detection/detectors/yolo.py | 2 +- dimos/perception/detection/module2D.py | 26 +- dimos/perception/detection/module3D.py | 24 +- dimos/perception/detection/moduleDB.py | 34 +-- .../detection/reid/embedding_id_system.py | 4 +- dimos/perception/detection/reid/module.py | 14 +- dimos/perception/detection/type/__init__.py | 2 +- .../detection/type/detection2d/base.py | 4 +- .../detection/type/detection2d/bbox.py | 16 +- .../type/detection2d/imageDetections2D.py | 6 +- .../detection/type/detection2d/person.py | 18 +- .../detection/type/detection3d/base.py | 2 +- .../type/detection3d/imageDetections3DPC.py | 2 +- .../detection/type/detection3d/pointcloud.py | 14 +- .../type/detection3d/pointcloud_filters.py | 2 +- .../detection/type/imageDetections.py | 12 +- dimos/perception/detection/type/utils.py | 12 +- dimos/perception/detection2d/utils.py | 22 +- .../grasp_generation/grasp_generation.py | 20 +- dimos/perception/grasp_generation/utils.py | 66 ++--- dimos/perception/object_detection_stream.py | 56 ++--- dimos/perception/object_tracker.py | 66 ++--- dimos/perception/object_tracker_2d.py | 22 +- dimos/perception/object_tracker_3d.py | 48 ++-- dimos/perception/person_tracker.py | 26 +- dimos/perception/pointcloud/cuboid_fit.py | 40 +-- .../pointcloud/pointcloud_filtering.py | 24 +- dimos/perception/pointcloud/utils.py | 90 +++---- .../perception/segmentation/image_analyzer.py | 8 +- dimos/perception/segmentation/sam_2d_seg.py | 26 +- dimos/perception/segmentation/utils.py | 32 +-- dimos/perception/spatial_perception.py | 26 +- dimos/protocol/encode/__init__.py | 6 +- dimos/protocol/pubsub/jpeg_shm.py | 2 +- dimos/protocol/pubsub/lcmpubsub.py | 14 +- dimos/protocol/pubsub/memory.py | 2 +- dimos/protocol/pubsub/redispubsub.py | 18 +- dimos/protocol/pubsub/shm/ipc_factory.py | 54 ++-- dimos/protocol/pubsub/shmpubsub.py | 6 +- dimos/protocol/pubsub/spec.py | 18 +- dimos/protocol/rpc/lcmrpc.py | 2 +- dimos/protocol/rpc/off_test_pubsubrpc.py | 28 +-- dimos/protocol/rpc/pubsubrpc.py | 18 +- dimos/protocol/rpc/redisrpc.py | 2 +- dimos/protocol/rpc/spec.py | 18 +- dimos/protocol/service/lcmservice.py | 8 +- dimos/protocol/service/spec.py | 6 +- dimos/protocol/skill/comms.py | 20 +- dimos/protocol/skill/coordinator.py | 50 ++-- dimos/protocol/skill/schema.py | 4 +- dimos/protocol/skill/skill.py | 14 +- dimos/protocol/skill/type.py | 26 +- dimos/protocol/tf/tf.py | 22 +- dimos/protocol/tf/tflcmcpp.py | 8 +- dimos/robot/agilex/piper_arm.py | 42 ++-- dimos/robot/agilex/run.py | 18 +- dimos/robot/all_blueprints.py | 4 +- dimos/robot/cli/dimos.py | 16 +- dimos/robot/connection_interface.py | 2 +- dimos/robot/foxglove_bridge.py | 14 +- dimos/robot/position_stream.py | 16 +- dimos/robot/robot.py | 2 +- dimos/robot/ros_bridge.py | 30 +-- dimos/robot/ros_command_queue.py | 30 +-- dimos/robot/ros_control.py | 106 ++++---- dimos/robot/ros_observable_topic.py | 54 ++-- dimos/robot/ros_transform.py | 62 ++--- dimos/robot/unitree/connection/connection.py | 38 +-- dimos/robot/unitree/connection/g1.py | 10 +- dimos/robot/unitree/connection/go2.py | 54 ++-- dimos/robot/unitree/g1/g1agent.py | 6 +- dimos/robot/unitree/g1/g1detector.py | 2 +- dimos/robot/unitree/g1/g1zed.py | 6 +- dimos/robot/unitree/go2/go2.py | 2 +- dimos/robot/unitree/run.py | 2 +- dimos/robot/unitree_webrtc/connection.py | 66 ++--- .../demo_error_on_name_conflicts.py | 4 +- dimos/robot/unitree_webrtc/depth_module.py | 18 +- dimos/robot/unitree_webrtc/g1_run.py | 18 +- dimos/robot/unitree_webrtc/keyboard_teleop.py | 8 +- .../modular/connection_module.py | 94 +++---- dimos/robot/unitree_webrtc/modular/detect.py | 30 +-- .../unitree_webrtc/modular/ivan_unitree.py | 16 +- dimos/robot/unitree_webrtc/rosnav.py | 20 +- dimos/robot/unitree_webrtc/testing/helpers.py | 12 +- dimos/robot/unitree_webrtc/testing/mock.py | 12 +- dimos/robot/unitree_webrtc/type/lidar.py | 18 +- dimos/robot/unitree_webrtc/type/map.py | 32 +-- dimos/robot/unitree_webrtc/type/odometry.py | 6 +- dimos/robot/unitree_webrtc/type/timeseries.py | 12 +- dimos/robot/unitree_webrtc/type/vector.py | 4 +- .../unitree_webrtc/unitree_b1/b1_command.py | 2 +- .../unitree_webrtc/unitree_b1/connection.py | 26 +- .../unitree_b1/joystick_module.py | 24 +- .../unitree_webrtc/unitree_b1/unitree_b1.py | 44 ++-- dimos/robot/unitree_webrtc/unitree_g1.py | 118 ++++----- .../unitree_webrtc/unitree_g1_blueprints.py | 2 +- dimos/robot/unitree_webrtc/unitree_go2.py | 238 +++++++++--------- .../unitree_webrtc/unitree_go2_blueprints.py | 2 +- .../unitree_webrtc/unitree_skill_container.py | 10 +- dimos/robot/unitree_webrtc/unitree_skills.py | 26 +- dimos/robot/utils/robot_debugger.py | 12 +- dimos/simulation/base/simulator_base.py | 6 +- dimos/simulation/base/stream_base.py | 14 +- dimos/simulation/genesis/simulator.py | 12 +- dimos/simulation/genesis/stream.py | 10 +- dimos/simulation/isaac/simulator.py | 8 +- dimos/simulation/isaac/stream.py | 20 +- dimos/skills/kill_skill.py | 8 +- .../abstract_manipulation_skill.py | 6 +- .../manipulation/force_constraint_skill.py | 6 +- dimos/skills/manipulation/manipulate_skill.py | 8 +- dimos/skills/manipulation/pick_and_place.py | 28 +-- .../manipulation/rotation_constraint_skill.py | 10 +- .../translation_constraint_skill.py | 14 +- dimos/skills/skills.py | 44 ++-- dimos/skills/speak.py | 18 +- dimos/skills/unitree/unitree_speak.py | 26 +- dimos/skills/visual_navigation_skills.py | 16 +- dimos/stream/audio/base.py | 6 +- dimos/stream/audio/node_key_recorder.py | 30 +-- dimos/stream/audio/node_microphone.py | 14 +- dimos/stream/audio/node_normalizer.py | 10 +- dimos/stream/audio/node_output.py | 20 +- dimos/stream/audio/node_simulated.py | 20 +- dimos/stream/audio/node_volume_monitor.py | 16 +- dimos/stream/audio/pipelines.py | 4 +- dimos/stream/audio/stt/node_whisper.py | 8 +- dimos/stream/audio/text/base.py | 4 +- dimos/stream/audio/text/node_stdout.py | 6 +- dimos/stream/audio/tts/node_openai.py | 22 +- dimos/stream/audio/tts/node_pytts.py | 16 +- dimos/stream/audio/volume.py | 12 +- dimos/stream/data_provider.py | 12 +- dimos/stream/frame_processor.py | 54 ++-- dimos/stream/ros_video_provider.py | 8 +- dimos/stream/rtsp_video_provider.py | 24 +- dimos/stream/video_operators.py | 110 ++++---- dimos/stream/video_provider.py | 18 +- dimos/types/manipulation.py | 14 +- dimos/types/robot_location.py | 4 +- dimos/types/ros_polyfill.py | 16 +- dimos/types/sample.py | 78 +++--- dimos/types/timestamped.py | 38 +-- dimos/types/vector.py | 42 ++-- dimos/types/weaklist.py | 4 +- dimos/utils/actor_registry.py | 6 +- dimos/utils/cli/agentspy/agentspy.py | 14 +- dimos/utils/cli/agentspy/demo_agentspy.py | 2 +- dimos/utils/cli/boxglove/boxglove.py | 28 +-- dimos/utils/cli/boxglove/connection.py | 10 +- .../foxglove_bridge/run_foxglove_bridge.py | 4 +- dimos/utils/cli/human/humancli.py | 40 +-- dimos/utils/cli/human/humanclianim.py | 12 +- dimos/utils/cli/lcmspy/lcmspy.py | 44 ++-- dimos/utils/cli/lcmspy/run_lcmspy.py | 16 +- dimos/utils/cli/skillspy/skillspy.py | 14 +- dimos/utils/decorators/accumulators.py | 16 +- dimos/utils/decorators/decorators.py | 20 +- dimos/utils/demo_image_encoding.py | 16 +- dimos/utils/extract_frames.py | 2 +- dimos/utils/generic.py | 4 +- dimos/utils/gpu_utils.py | 4 +- dimos/utils/llm_utils.py | 6 +- dimos/utils/monitoring.py | 24 +- dimos/utils/reactive.py | 38 +-- dimos/utils/simple_controller.py | 20 +- dimos/utils/testing.py | 24 +- dimos/utils/transform_utils.py | 20 +- dimos/web/dimos_interface/api/server.py | 54 ++-- dimos/web/fastapi_server.py | 36 +-- dimos/web/flask_server.py | 20 +- dimos/web/robot_web_interface.py | 2 +- dimos/web/websocket_vis/costmap_viz.py | 4 +- dimos/web/websocket_vis/optimized_costmap.py | 10 +- dimos/web/websocket_vis/path_history.py | 4 +- .../web/websocket_vis/websocket_vis_module.py | 82 +++--- mypy_strict.ini | 30 --- pyproject.toml | 11 +- 306 files changed, 3702 insertions(+), 3736 deletions(-) delete mode 100644 mypy_strict.ini diff --git a/bin/mypy-strict b/bin/mypy-strict index 660faa1a14..33e1f0c798 100755 --- a/bin/mypy-strict +++ b/bin/mypy-strict @@ -22,7 +22,6 @@ run_mypy() { export MYPYPATH=/opt/ros/jazzy/lib/python3.12/site-packages mypy_args=( - --config-file mypy_strict.ini --show-error-codes --hide-error-context --no-pretty diff --git a/dimos/agents/agent.py b/dimos/agents/agent.py index 62765ef706..f5bb6d49ee 100644 --- a/dimos/agents/agent.py +++ b/dimos/agents/agent.py @@ -154,9 +154,9 @@ def __init__( system_query: str | None = None, max_output_tokens_per_request: int = 16384, max_input_tokens_per_request: int = 128000, - input_query_stream: Observable | None = None, - input_data_stream: Observable | None = None, - input_video_stream: Observable | None = None, + input_query_stream: Observable | None = None, # type: ignore[type-arg] + input_data_stream: Observable | None = None, # type: ignore[type-arg] + input_video_stream: Observable | None = None, # type: ignore[type-arg] ) -> None: """ Initializes a new instance of the LLMAgent. @@ -189,10 +189,10 @@ def __init__( os.makedirs(self.output_dir, exist_ok=True) # Subject for emitting responses - self.response_subject = Subject() + self.response_subject = Subject() # type: ignore[var-annotated] # Conversation history for maintaining context between calls - self.conversation_history = [] + self.conversation_history = [] # type: ignore[var-annotated] # Initialize input streams self.input_video_stream = input_video_stream @@ -200,22 +200,22 @@ def __init__( input_query_stream if (input_data_stream is None) else ( - input_query_stream.pipe( + input_query_stream.pipe( # type: ignore[misc, union-attr] RxOps.with_latest_from(input_data_stream), RxOps.map( lambda combined: { - "query": combined[0], - "objects": combined[1] - if len(combined) > 1 + "query": combined[0], # type: ignore[index] + "objects": combined[1] # type: ignore[index] + if len(combined) > 1 # type: ignore[arg-type] else "No object data available", } ), RxOps.map( - lambda data: f"{data['query']}\n\nCurrent objects detected:\n{data['objects']}" + lambda data: f"{data['query']}\n\nCurrent objects detected:\n{data['objects']}" # type: ignore[index] ), RxOps.do_action( - lambda x: print(f"\033[34mEnriched query: {x.split(chr(10))[0]}\033[0m") - or [print(f"\033[34m{line}\033[0m") for line in x.split(chr(10))[1:]] + lambda x: print(f"\033[34mEnriched query: {x.split(chr(10))[0]}\033[0m") # type: ignore[arg-type] + or [print(f"\033[34m{line}\033[0m") for line in x.split(chr(10))[1:]] # type: ignore[var-annotated] ), ) ) @@ -230,7 +230,7 @@ def __init__( logger.info("Subscribing to merged input stream...") # Define a query extractor for the merged stream - def query_extractor(emission): + def query_extractor(emission): # type: ignore[no-untyped-def] return (emission[0], emission[1][0]) self.disposables.add( @@ -283,7 +283,7 @@ def _build_prompt( dimensions: tuple[int, int] | None, override_token_limit: bool, condensed_results: str, - ) -> list: + ) -> list: # type: ignore[type-arg] """Builds a prompt message using the prompt builder. Args: @@ -311,7 +311,7 @@ def _build_prompt( "rag": "truncate_end", } - return self.prompt_builder.build( + return self.prompt_builder.build( # type: ignore[no-any-return, union-attr] user_query=self.query, override_token_limit=override_token_limit, base64_image=base64_image, @@ -324,7 +324,7 @@ def _build_prompt( policies=policies, ) - def _handle_tooling(self, response_message, messages): + def _handle_tooling(self, response_message, messages): # type: ignore[no-untyped-def] """Handles tooling callbacks in the response message. If tool calls are present, the corresponding functions are executed and @@ -340,7 +340,7 @@ def _handle_tooling(self, response_message, messages): # TODO: Make this more generic or move implementation to OpenAIAgent. # This is presently OpenAI-specific. - def _tooling_callback(message, messages, response_message, skill_library: SkillLibrary): + def _tooling_callback(message, messages, response_message, skill_library: SkillLibrary): # type: ignore[no-untyped-def] has_called_tools = False new_messages = [] for tool_call in message.tool_calls: @@ -369,13 +369,13 @@ def _tooling_callback(message, messages, response_message, skill_library: SkillL if response_message.tool_calls is not None: return _tooling_callback( - response_message, messages, response_message, self.skill_library + response_message, messages, response_message, self.skill_library # type: ignore[attr-defined] ) return None - def _observable_query( + def _observable_query( # type: ignore[no-untyped-def] self, - observer: Observer, + observer: Observer, # type: ignore[type-arg] base64_image: str | None = None, dimensions: tuple[int, int] | None = None, override_token_limit: bool = False, @@ -409,8 +409,8 @@ def _observable_query( # TODO: Make this more generic. The parsed tag and tooling handling may be OpenAI-specific. # If no skill library is provided or there are no tool calls, emit the response directly. if ( - self.skill_library is None - or self.skill_library.get_tools() in (None, NOT_GIVEN) + self.skill_library is None # type: ignore[attr-defined] + or self.skill_library.get_tools() in (None, NOT_GIVEN) # type: ignore[attr-defined] or response_message.tool_calls is None ): final_msg = ( @@ -425,12 +425,12 @@ def _observable_query( observer.on_next(final_msg) self.response_subject.on_next(final_msg) else: - response_message_2 = self._handle_tooling(response_message, messages) + response_message_2 = self._handle_tooling(response_message, messages) # type: ignore[no-untyped-call] final_msg = ( response_message_2 if response_message_2 is not None else response_message ) if isinstance(final_msg, BaseModel): # TODO: Test - final_msg = str(final_msg.content) + final_msg = str(final_msg.content) # type: ignore[attr-defined] observer.on_next(final_msg) self.response_subject.on_next(final_msg) observer.on_completed() @@ -439,7 +439,7 @@ def _observable_query( observer.on_error(e) self.response_subject.on_error(e) - def _send_query(self, messages: list) -> Any: + def _send_query(self, messages: list) -> Any: # type: ignore[type-arg] """Sends the query to the LLM API. This method must be implemented by subclasses with specifics of the LLM API. @@ -455,7 +455,7 @@ def _send_query(self, messages: list) -> Any: """ raise NotImplementedError("Subclasses must implement _send_query method.") - def _log_response_to_file(self, response, output_dir: str | None = None) -> None: + def _log_response_to_file(self, response, output_dir: str | None = None) -> None: # type: ignore[no-untyped-def] """Logs the LLM response to a file. Args: @@ -471,8 +471,8 @@ def _log_response_to_file(self, response, output_dir: str | None = None) -> None file.write(f"{self.dev_name}: {response}\n") logger.info(f"LLM Response [{self.dev_name}]: {response}") - def subscribe_to_image_processing( - self, frame_observable: Observable, query_extractor=None + def subscribe_to_image_processing( # type: ignore[no-untyped-def] + self, frame_observable: Observable, query_extractor=None # type: ignore[type-arg] ) -> Disposable: """Subscribes to a stream of video frames for processing. @@ -496,7 +496,7 @@ def subscribe_to_image_processing( print_emission_args = {"enabled": True, "dev_name": self.dev_name, "counts": {}} - def _process_frame(emission) -> Observable: + def _process_frame(emission) -> Observable: # type: ignore[no-untyped-def, type-arg] """ Processes a frame or (query, frame) tuple. """ @@ -506,65 +506,65 @@ def _process_frame(emission) -> Observable: else: query = self.system_query frame = emission - return just(frame).pipe( - MyOps.print_emission(id="B", **print_emission_args), + return just(frame).pipe( # type: ignore[call-overload, no-any-return] + MyOps.print_emission(id="B", **print_emission_args), # type: ignore[arg-type] RxOps.observe_on(self.pool_scheduler), - MyOps.print_emission(id="C", **print_emission_args), + MyOps.print_emission(id="C", **print_emission_args), # type: ignore[arg-type] RxOps.subscribe_on(self.pool_scheduler), - MyOps.print_emission(id="D", **print_emission_args), + MyOps.print_emission(id="D", **print_emission_args), # type: ignore[arg-type] MyVidOps.with_jpeg_export( - self.frame_processor, + self.frame_processor, # type: ignore[arg-type] suffix=f"{self.dev_name}_frame_", save_limit=_MAX_SAVED_FRAMES, ), - MyOps.print_emission(id="E", **print_emission_args), + MyOps.print_emission(id="E", **print_emission_args), # type: ignore[arg-type] MyVidOps.encode_image(), - MyOps.print_emission(id="F", **print_emission_args), + MyOps.print_emission(id="F", **print_emission_args), # type: ignore[arg-type] RxOps.filter( lambda base64_and_dims: base64_and_dims is not None - and base64_and_dims[0] is not None - and base64_and_dims[1] is not None + and base64_and_dims[0] is not None # type: ignore[index] + and base64_and_dims[1] is not None # type: ignore[index] ), - MyOps.print_emission(id="G", **print_emission_args), + MyOps.print_emission(id="G", **print_emission_args), # type: ignore[arg-type] RxOps.flat_map( - lambda base64_and_dims: create( + lambda base64_and_dims: create( # type: ignore[arg-type, return-value] lambda observer, _: self._observable_query( - observer, + observer, # type: ignore[arg-type] base64_image=base64_and_dims[0], dimensions=base64_and_dims[1], incoming_query=query, ) ) ), # Use the extracted query - MyOps.print_emission(id="H", **print_emission_args), + MyOps.print_emission(id="H", **print_emission_args), # type: ignore[arg-type] ) # Use a mutable flag to ensure only one frame is processed at a time. is_processing = [False] - def process_if_free(emission): + def process_if_free(emission): # type: ignore[no-untyped-def] if not self.process_all_inputs and is_processing[0]: # Drop frame if a request is in progress and process_all_inputs is False return empty() else: is_processing[0] = True return _process_frame(emission).pipe( - MyOps.print_emission(id="I", **print_emission_args), + MyOps.print_emission(id="I", **print_emission_args), # type: ignore[arg-type] RxOps.observe_on(self.pool_scheduler), - MyOps.print_emission(id="J", **print_emission_args), + MyOps.print_emission(id="J", **print_emission_args), # type: ignore[arg-type] RxOps.subscribe_on(self.pool_scheduler), - MyOps.print_emission(id="K", **print_emission_args), + MyOps.print_emission(id="K", **print_emission_args), # type: ignore[arg-type] RxOps.do_action( on_completed=lambda: is_processing.__setitem__(0, False), on_error=lambda e: is_processing.__setitem__(0, False), ), - MyOps.print_emission(id="L", **print_emission_args), + MyOps.print_emission(id="L", **print_emission_args), # type: ignore[arg-type] ) observable = frame_observable.pipe( - MyOps.print_emission(id="A", **print_emission_args), + MyOps.print_emission(id="A", **print_emission_args), # type: ignore[arg-type] RxOps.flat_map(process_if_free), - MyOps.print_emission(id="M", **print_emission_args), + MyOps.print_emission(id="M", **print_emission_args), # type: ignore[arg-type] ) disposable = observable.subscribe( @@ -573,9 +573,9 @@ def process_if_free(emission): on_completed=lambda: logger.info(f"Stream processing completed for {self.dev_name}"), ) self.disposables.add(disposable) - return disposable + return disposable # type: ignore[no-any-return] - def subscribe_to_query_processing(self, query_observable: Observable) -> Disposable: + def subscribe_to_query_processing(self, query_observable: Observable) -> Disposable: # type: ignore[type-arg] """Subscribes to a stream of queries for processing. This method sets up a subscription to process incoming queries by directly @@ -589,25 +589,25 @@ def subscribe_to_query_processing(self, query_observable: Observable) -> Disposa """ print_emission_args = {"enabled": False, "dev_name": self.dev_name, "counts": {}} - def _process_query(query) -> Observable: + def _process_query(query) -> Observable: # type: ignore[no-untyped-def, type-arg] """ Processes a single query by logging it and passing it to _observable_query. Returns an observable that emits the LLM response. """ return just(query).pipe( - MyOps.print_emission(id="Pr A", **print_emission_args), + MyOps.print_emission(id="Pr A", **print_emission_args), # type: ignore[arg-type] RxOps.flat_map( - lambda query: create( - lambda observer, _: self._observable_query(observer, incoming_query=query) + lambda query: create( # type: ignore[arg-type, return-value] + lambda observer, _: self._observable_query(observer, incoming_query=query) # type: ignore[arg-type] ) ), - MyOps.print_emission(id="Pr B", **print_emission_args), + MyOps.print_emission(id="Pr B", **print_emission_args), # type: ignore[arg-type] ) # A mutable flag indicating whether a query is currently being processed. is_processing = [False] - def process_if_free(query): + def process_if_free(query): # type: ignore[no-untyped-def] logger.info(f"Processing Query: {query}") if not self.process_all_inputs and is_processing[0]: # Drop query if a request is already in progress and process_all_inputs is False @@ -616,22 +616,22 @@ def process_if_free(query): is_processing[0] = True logger.info("Processing Query.") return _process_query(query).pipe( - MyOps.print_emission(id="B", **print_emission_args), + MyOps.print_emission(id="B", **print_emission_args), # type: ignore[arg-type] RxOps.observe_on(self.pool_scheduler), - MyOps.print_emission(id="C", **print_emission_args), + MyOps.print_emission(id="C", **print_emission_args), # type: ignore[arg-type] RxOps.subscribe_on(self.pool_scheduler), - MyOps.print_emission(id="D", **print_emission_args), + MyOps.print_emission(id="D", **print_emission_args), # type: ignore[arg-type] RxOps.do_action( on_completed=lambda: is_processing.__setitem__(0, False), on_error=lambda e: is_processing.__setitem__(0, False), ), - MyOps.print_emission(id="E", **print_emission_args), + MyOps.print_emission(id="E", **print_emission_args), # type: ignore[arg-type] ) observable = query_observable.pipe( - MyOps.print_emission(id="A", **print_emission_args), - RxOps.flat_map(lambda query: process_if_free(query)), - MyOps.print_emission(id="F", **print_emission_args), + MyOps.print_emission(id="A", **print_emission_args), # type: ignore[arg-type] + RxOps.flat_map(lambda query: process_if_free(query)), # type: ignore[no-untyped-call] + MyOps.print_emission(id="F", **print_emission_args), # type: ignore[arg-type] ) disposable = observable.subscribe( @@ -640,9 +640,9 @@ def process_if_free(query): on_completed=lambda: logger.info(f"Stream processing completed for {self.dev_name}"), ) self.disposables.add(disposable) - return disposable + return disposable # type: ignore[no-any-return] - def get_response_observable(self) -> Observable: + def get_response_observable(self) -> Observable: # type: ignore[type-arg] """Gets an observable that emits responses from this agent. Returns: @@ -654,7 +654,7 @@ def get_response_observable(self) -> Observable: RxOps.share(), ) - def run_observable_query(self, query_text: str, **kwargs) -> Observable: + def run_observable_query(self, query_text: str, **kwargs) -> Observable: # type: ignore[no-untyped-def, type-arg] """Creates an observable that processes a one-off text query to Agent and emits the response. This method provides a simple way to send a text query and get an observable @@ -672,7 +672,7 @@ def run_observable_query(self, query_text: str, **kwargs) -> Observable: """ return create( lambda observer, _: self._observable_query( - observer, incoming_query=query_text, **kwargs + observer, incoming_query=query_text, **kwargs # type: ignore[arg-type] ) ) @@ -701,9 +701,9 @@ def __init__( dev_name: str, agent_type: str = "Vision", query: str = "What do you see?", - input_query_stream: Observable | None = None, - input_data_stream: Observable | None = None, - input_video_stream: Observable | None = None, + input_query_stream: Observable | None = None, # type: ignore[type-arg] + input_data_stream: Observable | None = None, # type: ignore[type-arg] + input_video_stream: Observable | None = None, # type: ignore[type-arg] output_dir: str = os.path.join(os.getcwd(), "assets", "agent"), agent_memory: AbstractAgentSemanticMemory | None = None, system_query: str | None = None, @@ -831,9 +831,9 @@ def _add_context_to_memory(self) -> None: ), ] for doc_id, text in context_data: - self.agent_memory.add_vector(doc_id, text) + self.agent_memory.add_vector(doc_id, text) # type: ignore[no-untyped-call] - def _send_query(self, messages: list) -> Any: + def _send_query(self, messages: list) -> Any: # type: ignore[type-arg] """Sends the query to OpenAI's API. Depending on whether a response model is provided, the appropriate API @@ -855,21 +855,21 @@ def _send_query(self, messages: list) -> Any: response = self.client.beta.chat.completions.parse( model=self.model_name, messages=messages, - response_format=self.response_model, + response_format=self.response_model, # type: ignore[arg-type] tools=( - self.skill_library.get_tools() + self.skill_library.get_tools() # type: ignore[arg-type] if self.skill_library is not None else NOT_GIVEN ), max_tokens=self.max_output_tokens_per_request, ) else: - response = self.client.chat.completions.create( + response = self.client.chat.completions.create( # type: ignore[assignment] model=self.model_name, messages=messages, max_tokens=self.max_output_tokens_per_request, tools=( - self.skill_library.get_tools() + self.skill_library.get_tools() # type: ignore[arg-type] if self.skill_library is not None else NOT_GIVEN ), @@ -889,7 +889,7 @@ def _send_query(self, messages: list) -> Any: logger.error(f"Unexpected error in API call: {e}") raise - def stream_query(self, query_text: str) -> Observable: + def stream_query(self, query_text: str) -> Observable: # type: ignore[type-arg] """Creates an observable that processes a text query and emits the response. This method provides a simple way to send a text query and get an observable @@ -903,7 +903,7 @@ def stream_query(self, query_text: str) -> Observable: Observable: An observable that emits the response as a string. """ return create( - lambda observer, _: self._observable_query(observer, incoming_query=query_text) + lambda observer, _: self._observable_query(observer, incoming_query=query_text) # type: ignore[arg-type] ) diff --git a/dimos/agents/agent_message.py b/dimos/agents/agent_message.py index cecd8092c1..7be0ad84f2 100644 --- a/dimos/agents/agent_message.py +++ b/dimos/agents/agent_message.py @@ -47,7 +47,7 @@ def add_image(self, image: Image | AgentImage) -> None: if isinstance(image, Image): # Convert to AgentImage agent_image = AgentImage( - base64_jpeg=image.agent_encode(), + base64_jpeg=image.agent_encode(), # type: ignore[arg-type] width=image.width, height=image.height, metadata={"format": image.format.value, "frame_id": image.frame_id}, diff --git a/dimos/agents/agent_types.py b/dimos/agents/agent_types.py index db41acbafb..aece190ff7 100644 --- a/dimos/agents/agent_types.py +++ b/dimos/agents/agent_types.py @@ -95,15 +95,15 @@ def to_openai_format(self) -> dict[str, Any]: msg["content"] = self.content else: # Content is already a list of content blocks - msg["content"] = self.content + msg["content"] = self.content # type: ignore[assignment] # Add tool calls if present if self.tool_calls: # Handle both ToolCall objects and dicts if isinstance(self.tool_calls[0], dict): - msg["tool_calls"] = self.tool_calls + msg["tool_calls"] = self.tool_calls # type: ignore[assignment] else: - msg["tool_calls"] = [ + msg["tool_calls"] = [ # type: ignore[assignment] { "id": tc.id, "type": "function", diff --git a/dimos/agents/claude_agent.py b/dimos/agents/claude_agent.py index c8163de162..a6c81fc4c7 100644 --- a/dimos/agents/claude_agent.py +++ b/dimos/agents/claude_agent.py @@ -51,7 +51,7 @@ # Response object compatible with LLMAgent class ResponseMessage: - def __init__(self, content: str = "", tool_calls=None, thinking_blocks=None) -> None: + def __init__(self, content: str = "", tool_calls=None, thinking_blocks=None) -> None: # type: ignore[no-untyped-def] self.content = content self.tool_calls = tool_calls or [] self.thinking_blocks = thinking_blocks or [] @@ -85,9 +85,9 @@ def __init__( dev_name: str, agent_type: str = "Vision", query: str = "What do you see?", - input_query_stream: Observable | None = None, - input_video_stream: Observable | None = None, - input_data_stream: Observable | None = None, + input_query_stream: Observable | None = None, # type: ignore[type-arg] + input_video_stream: Observable | None = None, # type: ignore[type-arg] + input_data_stream: Observable | None = None, # type: ignore[type-arg] output_dir: str = os.path.join(os.getcwd(), "assets", "agent"), agent_memory: AbstractAgentSemanticMemory | None = None, system_query: str | None = None, @@ -158,7 +158,7 @@ def __init__( # Claude-specific parameters self.thinking_budget_tokens = thinking_budget_tokens - self.claude_api_params = {} # Will store params for Claude API calls + self.claude_api_params = {} # type: ignore[var-annotated] # Will store params for Claude API calls # Configure skills self.skills = skills @@ -217,7 +217,7 @@ def _add_context_to_memory(self) -> None: ), ] for doc_id, text in context_data: - self.agent_memory.add_vector(doc_id, text) + self.agent_memory.add_vector(doc_id, text) # type: ignore[no-untyped-call] def _convert_tools_to_claude_format(self, tools: list[dict[str, Any]]) -> list[dict[str, Any]]: """ @@ -258,15 +258,15 @@ def _convert_tools_to_claude_format(self, tools: list[dict[str, Any]]) -> list[d return claude_tools - def _build_prompt( + def _build_prompt( # type: ignore[override] self, - messages: list, + messages: list, # type: ignore[type-arg] base64_image: str | list[str] | None = None, dimensions: tuple[int, int] | None = None, override_token_limit: bool = False, rag_results: str = "", thinking_budget_tokens: int | None = None, - ) -> list: + ) -> list: # type: ignore[type-arg] """Builds a prompt message specifically for Claude API, using local messages copy.""" """Builds a prompt message specifically for Claude API. @@ -347,9 +347,9 @@ def _build_prompt( # Store the parameters for use in _send_query and return them self.claude_api_params = claude_params.copy() - return messages, claude_params + return messages, claude_params # type: ignore[return-value] - def _send_query(self, messages: list, claude_params: dict) -> Any: + def _send_query(self, messages: list, claude_params: dict) -> Any: # type: ignore[override, type-arg] """Sends the query to Anthropic's API using streaming for better thinking visualization. Args: @@ -397,7 +397,7 @@ def _send_query(self, messages: list, claude_params: dict) -> Any: block_type = event.content_block.type current_block = { "type": block_type, - "id": event.index, + "id": event.index, # type: ignore[dict-item] "content": "", "signature": None, } @@ -413,7 +413,7 @@ def _send_query(self, messages: list, claude_params: dict) -> Any: elif event.delta.type == "text_delta": # Accumulate text content text_content += event.delta.text - current_block["content"] += event.delta.text + current_block["content"] += event.delta.text # type: ignore[operator] memory_file.write(f"{event.delta.text}") memory_file.flush() @@ -463,9 +463,9 @@ def _send_query(self, messages: list, claude_params: dict) -> Any: # Process tool use blocks when they're complete if hasattr(event, "content_block"): tool_block = event.content_block - tool_id = tool_block.id - tool_name = tool_block.name - tool_input = tool_block.input + tool_id = tool_block.id # type: ignore[union-attr] + tool_name = tool_block.name # type: ignore[union-attr] + tool_input = tool_block.input # type: ignore[union-attr] # Create a tool call object for LLMAgent compatibility tool_call_obj = type( @@ -537,7 +537,7 @@ def _send_query(self, messages: list, claude_params: dict) -> Any: def _observable_query( self, - observer: Observer, + observer: Observer, # type: ignore[name-defined] base64_image: str | None = None, dimensions: tuple[int, int] | None = None, override_token_limit: bool = False, @@ -605,7 +605,7 @@ def _observable_query( # Handle tool calls if present if response_message.tool_calls: - self._handle_tooling(response_message, messages) + self._handle_tooling(response_message, messages) # type: ignore[no-untyped-call] # At the end, append only new messages (including tool-use/results) to the global conversation history under a lock import threading @@ -633,7 +633,7 @@ def _observable_query( self.response_subject.on_next(error_message) observer.on_completed() - def _handle_tooling(self, response_message, messages): + def _handle_tooling(self, response_message, messages): # type: ignore[no-untyped-def] """Executes tools and appends tool-use/result blocks to messages.""" if not hasattr(response_message, "tool_calls") or not response_message.tool_calls: logger.info("No tool calls found in response message") @@ -658,7 +658,7 @@ def _handle_tooling(self, response_message, messages): try: # Execute the tool args = json.loads(tool_call.function.arguments) - tool_result = self.skills.call(tool_call.function.name, **args) + tool_result = self.skills.call(tool_call.function.name, **args) # type: ignore[union-attr] # Check if the result is an error message if isinstance(tool_result, str) and ( @@ -698,7 +698,7 @@ def _handle_tooling(self, response_message, messages): } ) - def _tooling_callback(self, response_message) -> None: + def _tooling_callback(self, response_message) -> None: # type: ignore[no-untyped-def] """Runs the observable query for each tool call in the current response_message""" if not hasattr(response_message, "tool_calls") or not response_message.tool_calls: return @@ -716,7 +716,7 @@ def _tooling_callback(self, response_message) -> None: # Continue processing even if the callback fails pass - def _debug_api_call(self, claude_params: dict): + def _debug_api_call(self, claude_params: dict): # type: ignore[no-untyped-def, type-arg] """Debugging function to log API calls with truncated base64 data.""" # Remove tools to reduce verbosity import copy diff --git a/dimos/agents/memory/base.py b/dimos/agents/memory/base.py index eb48dcca44..3431f3c83f 100644 --- a/dimos/agents/memory/base.py +++ b/dimos/agents/memory/base.py @@ -28,7 +28,7 @@ class AbstractAgentSemanticMemory: # AbstractAgentMemory): - def __init__(self, connection_type: str = "local", **kwargs) -> None: + def __init__(self, connection_type: str = "local", **kwargs) -> None: # type: ignore[no-untyped-def] """ Initialize with dynamic connection parameters. Args: @@ -53,9 +53,9 @@ def __init__(self, connection_type: str = "local", **kwargs) -> None: try: if connection_type == "remote": - self.connect() + self.connect() # type: ignore[no-untyped-call] elif connection_type == "local": - self.create() + self.create() # type: ignore[no-untyped-call] except Exception as e: self.logger.error("Failed to initialize database connection: %s", str(e), exc_info=True) raise AgentMemoryConnectionError( @@ -63,16 +63,16 @@ def __init__(self, connection_type: str = "local", **kwargs) -> None: ) from e @abstractmethod - def connect(self): + def connect(self): # type: ignore[no-untyped-def] """Establish a connection to the data store using dynamic parameters specified during initialization.""" @abstractmethod - def create(self): + def create(self): # type: ignore[no-untyped-def] """Create a local instance of the data store tailored to specific requirements.""" ## Create ## @abstractmethod - def add_vector(self, vector_id, vector_data): + def add_vector(self, vector_id, vector_data): # type: ignore[no-untyped-def] """Add a vector to the database. Args: vector_id (any): Unique identifier for the vector. @@ -81,14 +81,14 @@ def add_vector(self, vector_id, vector_data): ## Read ## @abstractmethod - def get_vector(self, vector_id): + def get_vector(self, vector_id): # type: ignore[no-untyped-def] """Retrieve a vector from the database by its identifier. Args: vector_id (any): The identifier of the vector to retrieve. """ @abstractmethod - def query(self, query_texts, n_results: int = 4, similarity_threshold=None): + def query(self, query_texts, n_results: int = 4, similarity_threshold=None): # type: ignore[no-untyped-def] """Performs a semantic search in the vector database. Args: @@ -109,7 +109,7 @@ def query(self, query_texts, n_results: int = 4, similarity_threshold=None): ## Update ## @abstractmethod - def update_vector(self, vector_id, new_vector_data): + def update_vector(self, vector_id, new_vector_data): # type: ignore[no-untyped-def] """Update an existing vector in the database. Args: vector_id (any): The identifier of the vector to update. @@ -118,7 +118,7 @@ def update_vector(self, vector_id, new_vector_data): ## Delete ## @abstractmethod - def delete_vector(self, vector_id): + def delete_vector(self, vector_id): # type: ignore[no-untyped-def] """Delete a vector from the database using its identifier. Args: vector_id (any): The identifier of the vector to delete. diff --git a/dimos/agents/memory/chroma_impl.py b/dimos/agents/memory/chroma_impl.py index b238b616d8..d09d515292 100644 --- a/dimos/agents/memory/chroma_impl.py +++ b/dimos/agents/memory/chroma_impl.py @@ -32,16 +32,16 @@ def __init__(self, collection_name: str = "my_collection") -> None: self.embeddings = None super().__init__(connection_type="local") - def connect(self): + def connect(self): # type: ignore[no-untyped-def] # Stub - return super().connect() + return super().connect() # type: ignore[no-untyped-call, safe-super] - def create(self): + def create(self): # type: ignore[no-untyped-def] """Create the embedding function and initialize the Chroma database. This method must be implemented by child classes.""" raise NotImplementedError("Child classes must implement this method") - def add_vector(self, vector_id, vector_data): + def add_vector(self, vector_id, vector_data): # type: ignore[no-untyped-def] """Add a vector to the ChromaDB collection.""" if not self.db_connection: raise Exception("Collection not initialized. Call connect() first.") @@ -51,12 +51,12 @@ def add_vector(self, vector_id, vector_data): metadatas=[{"name": vector_id}], ) - def get_vector(self, vector_id): + def get_vector(self, vector_id): # type: ignore[no-untyped-def] """Retrieve a vector from the ChromaDB by its identifier.""" - result = self.db_connection.get(include=["embeddings"], ids=[vector_id]) + result = self.db_connection.get(include=["embeddings"], ids=[vector_id]) # type: ignore[attr-defined] return result - def query(self, query_texts, n_results: int = 4, similarity_threshold=None): + def query(self, query_texts, n_results: int = 4, similarity_threshold=None): # type: ignore[no-untyped-def] """Query the collection with a specific text and return up to n results.""" if not self.db_connection: raise Exception("Collection not initialized. Call connect() first.") @@ -71,11 +71,11 @@ def query(self, query_texts, n_results: int = 4, similarity_threshold=None): documents = self.db_connection.similarity_search(query=query_texts, k=n_results) return [(doc, None) for doc in documents] - def update_vector(self, vector_id, new_vector_data): + def update_vector(self, vector_id, new_vector_data): # type: ignore[no-untyped-def] # TODO - return super().connect() + return super().connect() # type: ignore[no-untyped-call, safe-super] - def delete_vector(self, vector_id): + def delete_vector(self, vector_id): # type: ignore[no-untyped-def] """Delete a vector from the ChromaDB using its identifier.""" if not self.db_connection: raise Exception("Collection not initialized. Call connect() first.") @@ -102,7 +102,7 @@ def __init__( self.dimensions = dimensions super().__init__(collection_name=collection_name) - def create(self): + def create(self): # type: ignore[no-untyped-def] """Connect to OpenAI API and create the ChromaDB client.""" # Get OpenAI key self.OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") @@ -110,14 +110,14 @@ def create(self): raise Exception("OpenAI key was not specified.") # Set embeddings - self.embeddings = OpenAIEmbeddings( + self.embeddings = OpenAIEmbeddings( # type: ignore[assignment] model=self.model, dimensions=self.dimensions, - api_key=self.OPENAI_API_KEY, + api_key=self.OPENAI_API_KEY, # type: ignore[arg-type] ) # Create the database - self.db_connection = Chroma( + self.db_connection = Chroma( # type: ignore[assignment] collection_name=self.collection_name, embedding_function=self.embeddings, collection_metadata={"hnsw:space": "cosine"}, @@ -148,26 +148,26 @@ def create(self) -> None: # Use CUDA if available, otherwise fall back to CPU device = "cuda" if torch.cuda.is_available() else "cpu" print(f"Using device: {device}") - self.model = SentenceTransformer(self.model_name, device=device) + self.model = SentenceTransformer(self.model_name, device=device) # type: ignore[name-defined] # Create a custom embedding class that implements the embed_query method class SentenceTransformerEmbeddings: - def __init__(self, model) -> None: + def __init__(self, model) -> None: # type: ignore[no-untyped-def] self.model = model - def embed_query(self, text: str): + def embed_query(self, text: str): # type: ignore[no-untyped-def] """Embed a single query text.""" return self.model.encode(text, normalize_embeddings=True).tolist() - def embed_documents(self, texts: Sequence[str]): + def embed_documents(self, texts: Sequence[str]): # type: ignore[no-untyped-def] """Embed multiple documents/texts.""" return self.model.encode(texts, normalize_embeddings=True).tolist() # Create an instance of our custom embeddings class - self.embeddings = SentenceTransformerEmbeddings(self.model) + self.embeddings = SentenceTransformerEmbeddings(self.model) # type: ignore[assignment] # Create the database - self.db_connection = Chroma( + self.db_connection = Chroma( # type: ignore[assignment] collection_name=self.collection_name, embedding_function=self.embeddings, collection_metadata={"hnsw:space": "cosine"}, diff --git a/dimos/agents/memory/image_embedding.py b/dimos/agents/memory/image_embedding.py index 7b6dd88515..c06bb30989 100644 --- a/dimos/agents/memory/image_embedding.py +++ b/dimos/agents/memory/image_embedding.py @@ -55,31 +55,31 @@ def __init__(self, model_name: str = "clip", dimensions: int = 512) -> None: self.processor = None self.model_path = None - self._initialize_model() + self._initialize_model() # type: ignore[no-untyped-call] logger.info(f"ImageEmbeddingProvider initialized with model {model_name}") - def _initialize_model(self): + def _initialize_model(self): # type: ignore[no-untyped-def] """Initialize the specified embedding model.""" try: - import onnxruntime as ort + import onnxruntime as ort # type: ignore[import-untyped] import torch - from transformers import AutoFeatureExtractor, AutoModel, CLIPProcessor + from transformers import AutoFeatureExtractor, AutoModel, CLIPProcessor # type: ignore[import-untyped] if self.model_name == "clip": model_id = get_data("models_clip") / "model.onnx" - self.model_path = str(model_id) # Store for pickling + self.model_path = str(model_id) # type: ignore[assignment] # Store for pickling processor_id = "openai/clip-vit-base-patch32" providers = ["CUDAExecutionProvider", "CPUExecutionProvider"] self.model = ort.InferenceSession(str(model_id), providers=providers) - actual_providers = self.model.get_providers() + actual_providers = self.model.get_providers() # type: ignore[attr-defined] self.processor = CLIPProcessor.from_pretrained(processor_id) logger.info(f"Loaded CLIP model: {model_id} with providers: {actual_providers}") elif self.model_name == "resnet": - model_id = "microsoft/resnet-50" + model_id = "microsoft/resnet-50" # type: ignore[assignment] self.model = AutoModel.from_pretrained(model_id) self.processor = AutoFeatureExtractor.from_pretrained(model_id) logger.info(f"Loaded ResNet model: {model_id}") @@ -93,7 +93,7 @@ def _initialize_model(self): self.processor = None raise - def get_embedding(self, image: np.ndarray | str | bytes) -> np.ndarray: + def get_embedding(self, image: np.ndarray | str | bytes) -> np.ndarray: # type: ignore[type-arg] """ Generate an embedding vector for the provided image. @@ -166,7 +166,7 @@ def get_embedding(self, image: np.ndarray | str | bytes) -> np.ndarray: logger.error(f"Error generating embedding: {e}") return np.random.randn(self.dimensions).astype(np.float32) - def get_text_embedding(self, text: str) -> np.ndarray: + def get_text_embedding(self, text: str) -> np.ndarray: # type: ignore[type-arg] """ Generate an embedding vector for the provided text. @@ -233,7 +233,7 @@ def get_text_embedding(self, text: str) -> np.ndarray: logger.error(f"Error generating text embedding: {e}") return np.random.randn(self.dimensions).astype(np.float32) - def _prepare_image(self, image: np.ndarray | str | bytes) -> Image.Image: + def _prepare_image(self, image: np.ndarray | str | bytes) -> Image.Image: # type: ignore[type-arg] """ Convert the input image to PIL format required by the models. diff --git a/dimos/agents/memory/spatial_vector_db.py b/dimos/agents/memory/spatial_vector_db.py index ac5dcc026a..46c1f04464 100644 --- a/dimos/agents/memory/spatial_vector_db.py +++ b/dimos/agents/memory/spatial_vector_db.py @@ -39,7 +39,7 @@ class SpatialVectorDB: their absolute locations and querying by location, text, or image cosine semantic similarity. """ - def __init__( + def __init__( # type: ignore[no-untyped-def] self, collection_name: str = "spatial_memory", chroma_client=None, @@ -109,7 +109,7 @@ def __init__( ) def add_image_vector( - self, vector_id: str, image: np.ndarray, embedding: np.ndarray, metadata: dict[str, Any] + self, vector_id: str, image: np.ndarray, embedding: np.ndarray, metadata: dict[str, Any] # type: ignore[type-arg] ) -> None: """ Add an image with its embedding and metadata to the vector database. @@ -130,7 +130,7 @@ def add_image_vector( logger.info(f"Added image vector {vector_id} with metadata: {metadata}") - def query_by_embedding(self, embedding: np.ndarray, limit: int = 5) -> list[dict]: + def query_by_embedding(self, embedding: np.ndarray, limit: int = 5) -> list[dict]: # type: ignore[type-arg] """ Query the vector database for images similar to the provided embedding. @@ -150,7 +150,7 @@ def query_by_embedding(self, embedding: np.ndarray, limit: int = 5) -> list[dict # TODO: implement efficient nearest neighbor search def query_by_location( self, x: float, y: float, radius: float = 2.0, limit: int = 5 - ) -> list[dict]: + ) -> list[dict]: # type: ignore[type-arg] """ Query the vector database for images near the specified location. @@ -168,9 +168,9 @@ def query_by_location( if not results or not results["ids"]: return [] - filtered_results = {"ids": [], "metadatas": [], "distances": []} + filtered_results = {"ids": [], "metadatas": [], "distances": []} # type: ignore[var-annotated] - for i, metadata in enumerate(results["metadatas"]): + for i, metadata in enumerate(results["metadatas"]): # type: ignore[arg-type] item_x = metadata.get("x") item_y = metadata.get("y") @@ -193,7 +193,7 @@ def query_by_location( return self._process_query_results(filtered_results) - def _process_query_results(self, results) -> list[dict]: + def _process_query_results(self, results) -> list[dict]: # type: ignore[no-untyped-def, type-arg] """Process query results to include decoded images.""" if not results or not results["ids"]: return [] @@ -228,7 +228,7 @@ def _process_query_results(self, results) -> list[dict]: return processed_results - def query_by_text(self, text: str, limit: int = 5) -> list[dict]: + def query_by_text(self, text: str, limit: int = 5) -> list[dict]: # type: ignore[type-arg] """ Query the vector database for images matching the provided text description. @@ -283,7 +283,7 @@ def get_all_locations(self) -> list[tuple[float, float, float]]: return locations @property - def image_storage(self): + def image_storage(self): # type: ignore[no-untyped-def] """Legacy accessor for compatibility with existing code.""" return self.visual_memory.images @@ -320,10 +320,10 @@ def query_tagged_location(self, query: str) -> tuple[RobotLocation | None, float if not (results and results["ids"] and len(results["ids"][0]) > 0): return None, 0 - best_match_metadata = results["metadatas"][0][0] - distance = float(results["distances"][0][0] if "distances" in results else 0.0) + best_match_metadata = results["metadatas"][0][0] # type: ignore[index] + distance = float(results["distances"][0][0] if "distances" in results else 0.0) # type: ignore[index] - location = RobotLocation.from_vector_metadata(best_match_metadata) + location = RobotLocation.from_vector_metadata(best_match_metadata) # type: ignore[arg-type] logger.info( f"Found location '{location.name}' for query '{query}' (distance: {distance:.3f})" diff --git a/dimos/agents/memory/visual_memory.py b/dimos/agents/memory/visual_memory.py index 90f1272fef..955c67a749 100644 --- a/dimos/agents/memory/visual_memory.py +++ b/dimos/agents/memory/visual_memory.py @@ -44,7 +44,7 @@ def __init__(self, output_dir: str | None = None) -> None: Args: output_dir: Directory to store the serialized image data """ - self.images = {} # Maps IDs to encoded images + self.images = {} # type: ignore[var-annotated] # Maps IDs to encoded images self.output_dir = output_dir if output_dir: @@ -53,7 +53,7 @@ def __init__(self, output_dir: str | None = None) -> None: else: logger.info("VisualMemory initialized with no persistence directory") - def add(self, image_id: str, image: np.ndarray) -> None: + def add(self, image_id: str, image: np.ndarray) -> None: # type: ignore[type-arg] """ Add an image to visual memory. @@ -74,7 +74,7 @@ def add(self, image_id: str, image: np.ndarray) -> None: self.images[image_id] = b64_encoded logger.debug(f"Added image {image_id} to visual memory") - def get(self, image_id: str) -> np.ndarray | None: + def get(self, image_id: str) -> np.ndarray | None: # type: ignore[type-arg] """ Retrieve an image from visual memory. diff --git a/dimos/agents/modules/base.py b/dimos/agents/modules/base.py index 9caaac49cc..7abd5ac513 100644 --- a/dimos/agents/modules/base.py +++ b/dimos/agents/modules/base.py @@ -63,7 +63,7 @@ class BaseAgent: - Model capability detection """ - def __init__( + def __init__( # type: ignore[no-untyped-def] self, model: str = "openai::gpt-4o-mini", system_prompt: str | None = None, @@ -126,10 +126,10 @@ def __init__( self.skills = SkillLibrary() # Initialize memory - allow None for testing - if memory is False: # Explicit False means no memory + if memory is False: # type: ignore[comparison-overlap] # Explicit False means no memory self.memory = None else: - self.memory = memory or OpenAISemanticMemory() + self.memory = memory or OpenAISemanticMemory() # type: ignore[has-type] # Initialize gateway self.gateway = UnifiedGatewayClient() @@ -141,7 +141,7 @@ def __init__( self._executor = ThreadPoolExecutor(max_workers=2) # Response subject for emitting responses - self.response_subject = Subject() + self.response_subject = Subject() # type: ignore[var-annotated] # Check model capabilities self._supports_vision = self._check_vision_support() @@ -183,9 +183,9 @@ def _initialize_memory(self) -> None: else "I cannot process visual content.", ), ] - if self.memory: + if self.memory: # type: ignore[has-type] for doc_id, text in contexts: - self.memory.add_vector(doc_id, text) + self.memory.add_vector(doc_id, text) # type: ignore[has-type] except Exception as e: logger.warning(f"Failed to initialize memory: {e}") @@ -241,10 +241,10 @@ async def _process_query_async(self, agent_msg: AgentMessage) -> AgentResponse: inference_params["seed"] = self.seed # Make inference call - response = await self.gateway.ainference(**inference_params) + response = await self.gateway.ainference(**inference_params) # type: ignore[arg-type] # Extract response - message = response["choices"][0]["message"] + message = response["choices"][0]["message"] # type: ignore[index] content = message.get("content", "") # Don't update history yet - wait until we have the complete interaction @@ -301,11 +301,11 @@ async def _process_query_async(self, agent_msg: AgentMessage) -> AgentResponse: def _get_rag_context(self, query: str) -> str: """Get relevant context from memory.""" - if not self.memory: + if not self.memory: # type: ignore[has-type] return "" try: - results = self.memory.query( + results = self.memory.query( # type: ignore[has-type] query_texts=query, n_results=self.rag_n, similarity_threshold=self.rag_threshold ) @@ -367,7 +367,7 @@ def _build_messages( ) logger.debug(f"Building message with {len(content)} content items (vision enabled)") - messages.append({"role": "user", "content": content}) + messages.append({"role": "user", "content": content}) # type: ignore[dict-item] else: # Text-only message messages.append({"role": "user", "content": user_content}) @@ -445,10 +445,10 @@ async def _handle_tool_calls( followup_params["seed"] = self.seed # Get follow-up response - response = await self.gateway.ainference(**followup_params) + response = await self.gateway.ainference(**followup_params) # type: ignore[arg-type] # Extract final response - final_message = response["choices"][0]["message"] + final_message = response["choices"][0]["message"] # type: ignore[index] # Now add all messages to history in order (like Claude does) # Add user message @@ -468,7 +468,7 @@ async def _handle_tool_calls( final_content = final_message.get("content", "") self.conversation.add_assistant_message(final_content) - return final_message.get("content", "") + return final_message.get("content", "") # type: ignore[no-any-return] except Exception as e: logger.error(f"Error handling tool calls: {e}") diff --git a/dimos/agents/modules/base_agent.py b/dimos/agents/modules/base_agent.py index 0bceb1112e..aeab9c7eb2 100644 --- a/dimos/agents/modules/base_agent.py +++ b/dimos/agents/modules/base_agent.py @@ -32,7 +32,7 @@ logger = setup_logger("dimos.agents.modules.base_agent") -class BaseAgentModule(BaseAgent, Module): +class BaseAgentModule(BaseAgent, Module): # type: ignore[misc] """Agent module that inherits from BaseAgent and adds DimOS module interface. This provides a thin wrapper around BaseAgent functionality, exposing it @@ -40,10 +40,10 @@ class BaseAgentModule(BaseAgent, Module): """ # Module I/O - AgentMessage based communication - message_in: In[AgentMessage] = None # Primary input for AgentMessage - response_out: Out[AgentResponse] = None # Output AgentResponse objects + message_in: In[AgentMessage] = None # type: ignore[assignment] # Primary input for AgentMessage + response_out: Out[AgentResponse] = None # type: ignore[assignment] # Output AgentResponse objects - def __init__( + def __init__( # type: ignore[no-untyped-def] self, model: str = "openai::gpt-4o-mini", system_prompt: str | None = None, @@ -98,7 +98,7 @@ def __init__( ) # Track module-specific subscriptions - self._module_disposables = [] + self._module_disposables = [] # type: ignore[var-annotated] # For legacy stream support self._latest_image = None @@ -115,7 +115,7 @@ def start(self) -> None: # Primary AgentMessage input if self.message_in and self.message_in.connection is not None: try: - disposable = self.message_in.observable().subscribe( + disposable = self.message_in.observable().subscribe( # type: ignore[no-untyped-call] lambda msg: self._handle_agent_message(msg) ) self._module_disposables.append(disposable) @@ -125,7 +125,7 @@ def start(self) -> None: # Connect response output if self.response_out: disposable = self.response_subject.subscribe( - lambda response: self.response_out.publish(response) + lambda response: self.response_out.publish(response) # type: ignore[no-untyped-call] ) self._module_disposables.append(disposable) @@ -150,8 +150,8 @@ def stop(self) -> None: @rpc def clear_history(self) -> None: """Clear conversation history.""" - with self._history_lock: - self.history = [] + with self._history_lock: # type: ignore[attr-defined] + self.history = [] # type: ignore[var-annotated] logger.info("Conversation history cleared") @rpc @@ -169,7 +169,7 @@ def set_system_prompt(self, prompt: str) -> None: @rpc def get_conversation_history(self) -> list[dict[str, Any]]: """Get current conversation history.""" - with self._history_lock: + with self._history_lock: # type: ignore[attr-defined] return self.history.copy() def _handle_agent_message(self, message: AgentMessage) -> None: @@ -195,7 +195,7 @@ def _handle_module_query(self, query: str) -> None: def _update_latest_data(self, data: dict[str, Any]) -> None: """Update latest data context.""" with self._data_lock: - self._latest_data = data + self._latest_data = data # type: ignore[assignment] def _update_latest_image(self, img: Any) -> None: """Update latest image.""" diff --git a/dimos/agents/modules/gateway/client.py b/dimos/agents/modules/gateway/client.py index 6d8abf5e14..9978d3f4c5 100644 --- a/dimos/agents/modules/gateway/client.py +++ b/dimos/agents/modules/gateway/client.py @@ -64,25 +64,25 @@ def __init__( def _get_client(self) -> httpx.Client: """Get or create sync HTTP client.""" if self._client is None: - self._client = httpx.Client( - base_url=self.gateway_url, + self._client = httpx.Client( # type: ignore[assignment] + base_url=self.gateway_url, # type: ignore[arg-type] timeout=self.timeout, headers={"Content-Type": "application/json"}, ) - return self._client + return self._client # type: ignore[return-value] def _get_async_client(self) -> httpx.AsyncClient: """Get or create async HTTP client.""" if self._async_client is None: - self._async_client = httpx.AsyncClient( - base_url=self.gateway_url, + self._async_client = httpx.AsyncClient( # type: ignore[assignment] + base_url=self.gateway_url, # type: ignore[arg-type] timeout=self.timeout, headers={"Content-Type": "application/json"}, ) - return self._async_client + return self._async_client # type: ignore[return-value] @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=4, max=10)) - def inference( + def inference( # type: ignore[no-untyped-def] self, model: str, messages: list[dict[str, Any]], @@ -117,7 +117,7 @@ def inference( ) @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=4, max=10)) - async def ainference( + async def ainference( # type: ignore[no-untyped-def] self, model: str, messages: list[dict[str, Any]], @@ -184,7 +184,7 @@ def __del__(self) -> None: # No event loop, just let it be garbage collected pass - def __enter__(self): + def __enter__(self): # type: ignore[no-untyped-def] """Context manager entry.""" return self @@ -197,7 +197,7 @@ def __exit__( """Context manager exit.""" self.close() - async def __aenter__(self): + async def __aenter__(self): # type: ignore[no-untyped-def] """Async context manager entry.""" return self diff --git a/dimos/agents/modules/gateway/tensorzero_embedded.py b/dimos/agents/modules/gateway/tensorzero_embedded.py index 90d30fe82d..2350f8535d 100644 --- a/dimos/agents/modules/gateway/tensorzero_embedded.py +++ b/dimos/agents/modules/gateway/tensorzero_embedded.py @@ -30,13 +30,13 @@ def __init__(self) -> None: self._client = None self._config_path = None self._setup_config() - self._initialize_client() + self._initialize_client() # type: ignore[no-untyped-call] def _setup_config(self) -> None: """Create TensorZero configuration with correct format.""" config_dir = Path("/tmp/tensorzero_embedded") config_dir.mkdir(exist_ok=True) - self._config_path = config_dir / "tensorzero.toml" + self._config_path = config_dir / "tensorzero.toml" # type: ignore[assignment] # Create config using the correct format from working example config_content = """ @@ -143,18 +143,18 @@ def _setup_config(self) -> None: weight = 0.4 """ - with open(self._config_path, "w") as f: + with open(self._config_path, "w") as f: # type: ignore[call-overload] f.write(config_content) logger.info(f"Created TensorZero config at {self._config_path}") - def _initialize_client(self): + def _initialize_client(self): # type: ignore[no-untyped-def] """Initialize OpenAI client with TensorZero patch.""" try: from openai import OpenAI from tensorzero import patch_openai_client - self._client = OpenAI() + self._client = OpenAI() # type: ignore[assignment] # Patch with TensorZero embedded gateway patch_openai_client( @@ -176,7 +176,7 @@ def _map_model_to_tensorzero(self, model: str) -> str: # based on input type and model capabilities automatically return "tensorzero::function_name::chat" - def inference( + def inference( # type: ignore[no-untyped-def] self, model: str, messages: list[dict[str, Any]], @@ -214,22 +214,22 @@ def inference( # Make the call through patched client if stream: # Return streaming iterator - stream_response = self._client.chat.completions.create(**params) + stream_response = self._client.chat.completions.create(**params) # type: ignore[attr-defined] - def stream_generator(): + def stream_generator(): # type: ignore[no-untyped-def] for chunk in stream_response: yield chunk.model_dump() - return stream_generator() + return stream_generator() # type: ignore[no-any-return, no-untyped-call] else: - response = self._client.chat.completions.create(**params) - return response.model_dump() + response = self._client.chat.completions.create(**params) # type: ignore[attr-defined] + return response.model_dump() # type: ignore[no-any-return] except Exception as e: logger.error(f"TensorZero inference failed: {e}") raise - async def ainference( + async def ainference( # type: ignore[no-untyped-def] self, model: str, messages: list[dict[str, Any]], @@ -246,7 +246,7 @@ async def ainference( if stream: # Create async generator from sync streaming - async def stream_generator(): + async def stream_generator(): # type: ignore[no-untyped-def] # Run sync streaming in executor sync_stream = await loop.run_in_executor( None, @@ -259,7 +259,7 @@ async def stream_generator(): for chunk in sync_stream: yield chunk - return stream_generator() + return stream_generator() # type: ignore[no-any-return, no-untyped-call] else: result = await loop.run_in_executor( None, @@ -267,7 +267,7 @@ async def stream_generator(): model, messages, tools, temperature, max_tokens, stream, **kwargs ), ) - return result + return result # type: ignore[return-value] def close(self) -> None: """Close the client.""" diff --git a/dimos/agents/prompt_builder/impl.py b/dimos/agents/prompt_builder/impl.py index 9cd532fea9..42deea09dd 100644 --- a/dimos/agents/prompt_builder/impl.py +++ b/dimos/agents/prompt_builder/impl.py @@ -55,7 +55,7 @@ def __init__( self.max_tokens = max_tokens self.tokenizer: AbstractTokenizer = tokenizer or OpenAITokenizer(model_name=self.model_name) - def truncate_tokens(self, text: str, max_tokens, strategy): + def truncate_tokens(self, text: str, max_tokens, strategy): # type: ignore[no-untyped-def] """ Truncate text to fit within max_tokens using a specified strategy. Args: @@ -82,9 +82,9 @@ def truncate_tokens(self, text: str, max_tokens, strategy): else: raise ValueError(f"Unknown truncation strategy: {strategy}") - return self.tokenizer.detokenize_text(truncated) + return self.tokenizer.detokenize_text(truncated) # type: ignore[no-untyped-call] - def build( + def build( # type: ignore[no-untyped-def] self, system_prompt=None, user_query=None, @@ -207,7 +207,7 @@ def build( user_content.append( { "type": "image_url", - "image_url": { + "image_url": { # type: ignore[dict-item] "url": f"data:image/jpeg;base64,{base64_image}", "detail": image_detail, }, diff --git a/dimos/agents/tokenizer/base.py b/dimos/agents/tokenizer/base.py index 7957c896fa..16f35cf7c7 100644 --- a/dimos/agents/tokenizer/base.py +++ b/dimos/agents/tokenizer/base.py @@ -21,17 +21,17 @@ class AbstractTokenizer(ABC): @abstractmethod - def tokenize_text(self, text: str): + def tokenize_text(self, text: str): # type: ignore[no-untyped-def] pass @abstractmethod - def detokenize_text(self, tokenized_text): + def detokenize_text(self, tokenized_text): # type: ignore[no-untyped-def] pass @abstractmethod - def token_count(self, text: str): + def token_count(self, text: str): # type: ignore[no-untyped-def] pass @abstractmethod - def image_token_count(self, image_width, image_height, image_detail: str = "low"): + def image_token_count(self, image_width, image_height, image_detail: str = "low"): # type: ignore[no-untyped-def] pass diff --git a/dimos/agents/tokenizer/huggingface_tokenizer.py b/dimos/agents/tokenizer/huggingface_tokenizer.py index 34ace64fb0..e259b5810a 100644 --- a/dimos/agents/tokenizer/huggingface_tokenizer.py +++ b/dimos/agents/tokenizer/huggingface_tokenizer.py @@ -12,14 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -from transformers import AutoTokenizer +from transformers import AutoTokenizer # type: ignore[import-untyped] from dimos.agents.tokenizer.base import AbstractTokenizer from dimos.utils.logging_config import setup_logger class HuggingFaceTokenizer(AbstractTokenizer): - def __init__(self, model_name: str = "Qwen/Qwen2.5-0.5B", **kwargs) -> None: + def __init__(self, model_name: str = "Qwen/Qwen2.5-0.5B", **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(**kwargs) # Initilize the tokenizer for the huggingface models @@ -31,13 +31,13 @@ def __init__(self, model_name: str = "Qwen/Qwen2.5-0.5B", **kwargs) -> None: f"Failed to initialize tokenizer for model {self.model_name}. Error: {e!s}" ) - def tokenize_text(self, text: str): + def tokenize_text(self, text: str): # type: ignore[no-untyped-def] """ Tokenize a text string using the openai tokenizer. """ return self.tokenizer.encode(text) - def detokenize_text(self, tokenized_text): + def detokenize_text(self, tokenized_text): # type: ignore[no-untyped-def] """ Detokenize a text string using the openai tokenizer. """ @@ -46,14 +46,14 @@ def detokenize_text(self, tokenized_text): except Exception as e: raise ValueError(f"Failed to detokenize text. Error: {e!s}") - def token_count(self, text: str): + def token_count(self, text: str): # type: ignore[no-untyped-def] """ Gets the token count of a text string using the openai tokenizer. """ return len(self.tokenize_text(text)) if text else 0 @staticmethod - def image_token_count(image_width, image_height, image_detail: str = "high"): + def image_token_count(image_width, image_height, image_detail: str = "high"): # type: ignore[no-untyped-def] """ Calculate the number of tokens in an image. Low detail is 85 tokens, high detail is 170 tokens per 512x512 square. """ diff --git a/dimos/agents/tokenizer/openai_tokenizer.py b/dimos/agents/tokenizer/openai_tokenizer.py index 7fe5017241..0f09c8ea9b 100644 --- a/dimos/agents/tokenizer/openai_tokenizer.py +++ b/dimos/agents/tokenizer/openai_tokenizer.py @@ -19,7 +19,7 @@ class OpenAITokenizer(AbstractTokenizer): - def __init__(self, model_name: str = "gpt-4o", **kwargs) -> None: + def __init__(self, model_name: str = "gpt-4o", **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(**kwargs) # Initilize the tokenizer for the openai set of models @@ -31,13 +31,13 @@ def __init__(self, model_name: str = "gpt-4o", **kwargs) -> None: f"Failed to initialize tokenizer for model {self.model_name}. Error: {e!s}" ) - def tokenize_text(self, text: str): + def tokenize_text(self, text: str): # type: ignore[no-untyped-def] """ Tokenize a text string using the openai tokenizer. """ return self.tokenizer.encode(text) - def detokenize_text(self, tokenized_text): + def detokenize_text(self, tokenized_text): # type: ignore[no-untyped-def] """ Detokenize a text string using the openai tokenizer. """ @@ -46,14 +46,14 @@ def detokenize_text(self, tokenized_text): except Exception as e: raise ValueError(f"Failed to detokenize text. Error: {e!s}") - def token_count(self, text: str): + def token_count(self, text: str): # type: ignore[no-untyped-def] """ Gets the token count of a text string using the openai tokenizer. """ return len(self.tokenize_text(text)) if text else 0 @staticmethod - def image_token_count(image_width, image_height, image_detail: str = "high"): + def image_token_count(image_width, image_height, image_detail: str = "high"): # type: ignore[no-untyped-def] """ Calculate the number of tokens in an image. Low detail is 85 tokens, high detail is 170 tokens per 512x512 square. """ diff --git a/dimos/agents2/agent.py b/dimos/agents2/agent.py index e58e1aa9a3..840a220ef7 100644 --- a/dimos/agents2/agent.py +++ b/dimos/agents2/agent.py @@ -50,7 +50,7 @@ def toolmsg_from_state(state: SkillState) -> ToolMessage: if state.skill_config.output != Output.standard: content = "output attached in separate messages" else: - content = state.content() + content = state.content() # type: ignore[assignment] return ToolMessage( # if agent call has been triggered by another skill, @@ -86,7 +86,7 @@ def summary_from_state(state: SkillState, special_data: bool = False) -> SkillSt } -def _custom_json_serializers(obj): +def _custom_json_serializers(obj): # type: ignore[no-untyped-def] if isinstance(obj, datetime.date | datetime.datetime): return obj.isoformat() raise TypeError(f"Type {type(obj)} not serializable") @@ -131,7 +131,7 @@ def snapshot_to_messages( content = skill_state.content() if not content: continue - history_msgs.append(HumanMessage(content=content)) + history_msgs.append(HumanMessage(content=content)) # type: ignore[arg-type] continue special_data = skill_state.skill_config.output == Output.image @@ -139,12 +139,12 @@ def snapshot_to_messages( content = skill_state.content() if not content: continue - special_msgs.append(HumanMessage(content=content)) + special_msgs.append(HumanMessage(content=content)) # type: ignore[arg-type] if skill_state.call_id in tool_call_ids: continue - state_overview.append(summary_from_state(skill_state, special_data)) + state_overview.append(summary_from_state(skill_state, special_data)) # type: ignore[arg-type] if state_overview: state_overview_str = "\n".join( @@ -152,7 +152,7 @@ def snapshot_to_messages( ) state_msg = AIMessage("State Overview:\n" + state_overview_str) - return { + return { # type: ignore[return-value] "tool_msgs": tool_msgs, "history_msgs": history_msgs, "state_msgs": ([state_msg] if state_msg else []) + special_msgs, @@ -164,7 +164,7 @@ class Agent(AgentSpec): system_message: SystemMessage state_messages: list[AIMessage | HumanMessage] - def __init__( + def __init__( # type: ignore[no-untyped-def] self, *args, **kwargs, @@ -173,7 +173,7 @@ def __init__( self.state_messages = [] self.coordinator = SkillCoordinator() - self._history = [] + self._history = [] # type: ignore[var-annotated] self._agent_id = str(uuid.uuid4()) self._agent_stopped = False @@ -181,7 +181,7 @@ def __init__( if isinstance(self.config.system_prompt, str): self.system_message = SystemMessage(self.config.system_prompt + SYSTEM_MSG_APPEND) else: - self.config.system_prompt.content += SYSTEM_MSG_APPEND + self.config.system_prompt.content += SYSTEM_MSG_APPEND # type: ignore[operator] self.system_message = self.config.system_prompt else: self.system_message = SystemMessage(get_system_prompt() + SYSTEM_MSG_APPEND) @@ -192,7 +192,7 @@ def __init__( if self.config.model_instance: self._llm = self.config.model_instance else: - self._llm = init_chat_model( + self._llm = init_chat_model( # type: ignore[call-overload] model_provider=self.config.provider, model=self.config.model ) @@ -216,11 +216,11 @@ def clear_history(self) -> None: def append_history(self, *msgs: list[AIMessage | HumanMessage]) -> None: for msg in msgs: - self.publish(msg) + self.publish(msg) # type: ignore[arg-type] self._history.extend(msgs) - def history(self): + def history(self): # type: ignore[no-untyped-def] return [self.system_message, *self._history, *self.state_messages] # Used by agent to execute tool calls @@ -232,19 +232,19 @@ def execute_tool_calls(self, tool_calls: list[ToolCall]) -> None: for tool_call in tool_calls: logger.info(f"executing skill call {tool_call}") self.coordinator.call_skill( - tool_call.get("id"), - tool_call.get("name"), - tool_call.get("args"), + tool_call.get("id"), # type: ignore[arg-type] + tool_call.get("name"), # type: ignore[arg-type] + tool_call.get("args"), # type: ignore[arg-type] ) # used to inject skill calls into the agent loop without agent asking for it - def run_implicit_skill(self, skill_name: str, **kwargs) -> None: + def run_implicit_skill(self, skill_name: str, **kwargs) -> None: # type: ignore[no-untyped-def] if self._agent_stopped: logger.warning("Agent is stopped, cannot execute implicit skill calls.") return self.coordinator.call_skill(False, skill_name, {"args": kwargs}) - async def agent_loop(self, first_query: str = ""): + async def agent_loop(self, first_query: str = ""): # type: ignore[no-untyped-def] # TODO: Should I add a lock here to prevent concurrent calls to agent_loop? if self._agent_stopped: @@ -257,20 +257,20 @@ async def agent_loop(self, first_query: str = ""): self.state_messages = [] if first_query: - self.append_history(HumanMessage(first_query)) + self.append_history(HumanMessage(first_query)) # type: ignore[arg-type] def _get_state() -> str: # TODO: FIX THIS EXTREME HACK update = self.coordinator.generate_snapshot(clear=False) - snapshot_msgs = snapshot_to_messages(update, msg.tool_calls) + snapshot_msgs = snapshot_to_messages(update, msg.tool_calls) # type: ignore[attr-defined] return json.dumps(snapshot_msgs, sort_keys=True, default=lambda o: repr(o)) try: while True: # we are getting tools from the coordinator on each turn # since this allows for skillcontainers to dynamically provide new skills - tools = self.get_tools() - self._llm = self._llm.bind_tools(tools) + tools = self.get_tools() # type: ignore[no-untyped-call] + self._llm = self._llm.bind_tools(tools) # type: ignore[assignment] # publish to /agent topic for observability for state_msg in self.state_messages: @@ -278,15 +278,15 @@ def _get_state() -> str: # history() builds our message history dynamically # ensures we include latest system state, but not old ones. - msg = self._llm.invoke(self.history()) - self.append_history(msg) + msg = self._llm.invoke(self.history()) # type: ignore[no-untyped-call] + self.append_history(msg) # type: ignore[arg-type] logger.info(f"Agent response: {msg.content}") state = _get_state() - if msg.tool_calls: - self.execute_tool_calls(msg.tool_calls) + if msg.tool_calls: # type: ignore[attr-defined] + self.execute_tool_calls(msg.tool_calls) # type: ignore[attr-defined] # print(self) # print(self.coordinator) @@ -310,11 +310,11 @@ def _get_state() -> str: # generate tool_msgs and general state update message, # depending on a skill having associated tool call from previous interaction # we will return a tool message, and not a general state message - snapshot_msgs = snapshot_to_messages(update, msg.tool_calls) + snapshot_msgs = snapshot_to_messages(update, msg.tool_calls) # type: ignore[attr-defined] - self.state_messages = snapshot_msgs.get("state_msgs", []) + self.state_messages = snapshot_msgs.get("state_msgs", []) # type: ignore[attr-defined] self.append_history( - *snapshot_msgs.get("tool_msgs", []), *snapshot_msgs.get("history_msgs", []) + *snapshot_msgs.get("tool_msgs", []), *snapshot_msgs.get("history_msgs", []) # type: ignore[attr-defined] ) except Exception as e: @@ -325,29 +325,29 @@ def _get_state() -> str: @rpc def loop_thread(self) -> bool: - asyncio.run_coroutine_threadsafe(self.agent_loop(), self._loop) + asyncio.run_coroutine_threadsafe(self.agent_loop(), self._loop) # type: ignore[arg-type] return True @rpc - def query(self, query: str): + def query(self, query: str): # type: ignore[no-untyped-def] # TODO: could this be # from distributed.utils import sync # return sync(self._loop, self.agent_loop, query) - return asyncio.run_coroutine_threadsafe(self.agent_loop(query), self._loop).result() + return asyncio.run_coroutine_threadsafe(self.agent_loop(query), self._loop).result() # type: ignore[arg-type] - async def query_async(self, query: str): + async def query_async(self, query: str): # type: ignore[no-untyped-def] return await self.agent_loop(query) @rpc - def register_skills(self, container, run_implicit_name: str | None = None): - ret = self.coordinator.register_skills(container) + def register_skills(self, container, run_implicit_name: str | None = None): # type: ignore[no-untyped-def] + ret = self.coordinator.register_skills(container) # type: ignore[func-returns-value] if run_implicit_name: self.run_implicit_skill(run_implicit_name) return ret - def get_tools(self): + def get_tools(self): # type: ignore[no-untyped-def] return self.coordinator.get_tools() def _write_debug_history_file(self) -> None: @@ -355,7 +355,7 @@ def _write_debug_history_file(self) -> None: if not file_path: return - history = [x.__dict__ for x in self.history()] + history = [x.__dict__ for x in self.history()] # type: ignore[no-untyped-call] with open(file_path, "w") as f: json.dump(history, f, default=lambda x: repr(x), indent=2) @@ -379,21 +379,21 @@ def deploy( dimos: DimosCluster, system_prompt: str = "You are a helpful assistant for controlling a Unitree Go2 robot.", model: Model = Model.GPT_4O, - provider: Provider = Provider.OPENAI, + provider: Provider = Provider.OPENAI, # type: ignore[attr-defined] skill_containers: list[SkillContainer] | None = None, ) -> Agent: from dimos.agents2.cli.human import HumanInput if skill_containers is None: skill_containers = [] - agent = dimos.deploy( + agent = dimos.deploy( # type: ignore[attr-defined] Agent, system_prompt=system_prompt, model=model, provider=provider, ) - human_input = dimos.deploy(HumanInput) + human_input = dimos.deploy(HumanInput) # type: ignore[attr-defined] human_input.start() agent.register_skills(human_input) @@ -406,7 +406,7 @@ def deploy( agent.start() agent.loop_thread() - return agent + return agent # type: ignore[no-any-return] __all__ = ["Agent", "deploy", "llm_agent"] diff --git a/dimos/agents2/cli/human.py b/dimos/agents2/cli/human.py index 15727d87b8..bbeee4961f 100644 --- a/dimos/agents2/cli/human.py +++ b/dimos/agents2/cli/human.py @@ -16,7 +16,7 @@ from reactivex.disposable import Disposable -from dimos.agents2 import Output, Reducer, Stream, skill +from dimos.agents2 import Output, Reducer, Stream, skill # type: ignore[attr-defined] from dimos.core import pLCMTransport, rpc from dimos.core.module import Module from dimos.core.rpc_client import RpcCall @@ -25,16 +25,16 @@ class HumanInput(Module): running: bool = False - @skill(stream=Stream.call_agent, reducer=Reducer.string, output=Output.human, hide_skill=True) - def human(self): + @skill(stream=Stream.call_agent, reducer=Reducer.string, output=Output.human, hide_skill=True) # type: ignore[arg-type] + def human(self): # type: ignore[no-untyped-def] """receives human input, no need to run this, it's running implicitly""" if self.running: return "already running" self.running = True - transport = pLCMTransport("/human_input") + transport = pLCMTransport("/human_input") # type: ignore[var-annotated] - msg_queue = queue.Queue() - unsub = transport.subscribe(msg_queue.put) + msg_queue = queue.Queue() # type: ignore[var-annotated] + unsub = transport.subscribe(msg_queue.put) # type: ignore[func-returns-value] self._disposables.add(Disposable(unsub)) yield from iter(msg_queue.get, None) @@ -48,7 +48,7 @@ def stop(self) -> None: @rpc def set_LlmAgent_register_skills(self, callable: RpcCall) -> None: - callable.set_rpc(self.rpc) + callable.set_rpc(self.rpc) # type: ignore[arg-type] callable(self, run_implicit_name="human") diff --git a/dimos/agents2/skills/demo_robot.py b/dimos/agents2/skills/demo_robot.py index 74b5c47bd3..2f2502849a 100644 --- a/dimos/agents2/skills/demo_robot.py +++ b/dimos/agents2/skills/demo_robot.py @@ -21,7 +21,7 @@ class DemoRobot(Module): - gps_location: Out[LatLon] = None + gps_location: Out[LatLon] = None # type: ignore[assignment] def start(self) -> None: super().start() @@ -31,7 +31,7 @@ def stop(self) -> None: super().stop() def _publish_gps_location(self) -> None: - self.gps_location.publish(LatLon(lat=37.78092426217621, lon=-122.40682866540769)) + self.gps_location.publish(LatLon(lat=37.78092426217621, lon=-122.40682866540769)) # type: ignore[no-untyped-call] demo_robot = DemoRobot.blueprint diff --git a/dimos/agents2/skills/google_maps_skill_container.py b/dimos/agents2/skills/google_maps_skill_container.py index f5c1af428e..91a9621fd6 100644 --- a/dimos/agents2/skills/google_maps_skill_container.py +++ b/dimos/agents2/skills/google_maps_skill_container.py @@ -30,7 +30,7 @@ class GoogleMapsSkillContainer(SkillModule): _latest_location: LatLon | None = None _client: GoogleMaps - gps_location: In[LatLon] = None + gps_location: In[LatLon] = None # type: ignore[assignment] def __init__(self) -> None: super().__init__() @@ -39,7 +39,7 @@ def __init__(self) -> None: @rpc def start(self) -> None: super().start() - self._disposables.add(self.gps_location.subscribe(self._on_gps_location)) + self._disposables.add(self.gps_location.subscribe(self._on_gps_location)) # type: ignore[arg-type] @rpc def stop(self) -> None: diff --git a/dimos/agents2/skills/gps_nav_skill.py b/dimos/agents2/skills/gps_nav_skill.py index fa68b32800..413053d2d7 100644 --- a/dimos/agents2/skills/gps_nav_skill.py +++ b/dimos/agents2/skills/gps_nav_skill.py @@ -31,8 +31,8 @@ class GpsNavSkillContainer(SkillModule): _max_valid_distance: int = 50000 _set_gps_travel_goal_points: RpcCall | None = None - gps_location: In[LatLon] = None - gps_goal: Out[LatLon] = None + gps_location: In[LatLon] = None # type: ignore[assignment] + gps_goal: Out[LatLon] = None # type: ignore[assignment] def __init__(self) -> None: super().__init__() @@ -40,7 +40,7 @@ def __init__(self) -> None: @rpc def start(self) -> None: super().start() - self._disposables.add(self.gps_location.subscribe(self._on_gps_location)) + self._disposables.add(self.gps_location.subscribe(self._on_gps_location)) # type: ignore[arg-type] @rpc def stop(self) -> None: @@ -49,7 +49,7 @@ def stop(self) -> None: @rpc def set_WebsocketVisModule_set_gps_travel_goal_points(self, callable: RpcCall) -> None: self._set_gps_travel_goal_points = callable - self._set_gps_travel_goal_points.set_rpc(self.rpc) + self._set_gps_travel_goal_points.set_rpc(self.rpc) # type: ignore[arg-type] def _on_gps_location(self, location: LatLon) -> None: self._latest_location = location @@ -77,14 +77,14 @@ def set_gps_travel_points(self, *points: dict[str, float]) -> str: return f"Not all points were valid. I parsed this: {parsed}" for new_point in new_points: - distance = distance_in_meters(self._get_latest_location(), new_point) + distance = distance_in_meters(self._get_latest_location(), new_point) # type: ignore[arg-type] if distance > self._max_valid_distance: return f"Point {new_point} is too far ({int(distance)} meters away)." logger.info(f"Set travel points: {new_points}") if self.gps_goal._transport is not None: - self.gps_goal.publish(new_points) + self.gps_goal.publish(new_points) # type: ignore[no-untyped-call] if self._set_gps_travel_goal_points: self._set_gps_travel_goal_points(new_points) diff --git a/dimos/agents2/skills/navigation.py b/dimos/agents2/skills/navigation.py index cf3411d497..1b03736073 100644 --- a/dimos/agents2/skills/navigation.py +++ b/dimos/agents2/skills/navigation.py @@ -54,8 +54,8 @@ class NavigationSkillContainer(SkillModule): "WavefrontFrontierExplorer.is_exploration_active", ] - color_image: In[Image] = None - odom: In[PoseStamped] = None + color_image: In[Image] = None # type: ignore[assignment] + odom: In[PoseStamped] = None # type: ignore[assignment] def __init__(self) -> None: super().__init__() @@ -64,8 +64,8 @@ def __init__(self) -> None: @rpc def start(self) -> None: - self._disposables.add(self.color_image.subscribe(self._on_color_image)) - self._disposables.add(self.odom.subscribe(self._on_odom)) + self._disposables.add(self.color_image.subscribe(self._on_color_image)) # type: ignore[arg-type] + self._disposables.add(self.odom.subscribe(self._on_odom)) # type: ignore[arg-type] self._skill_started = True @rpc @@ -327,7 +327,7 @@ def _cancel_goal_and_stop(self) -> None: return cancel_goal_rpc() - return stop_exploration_rpc() + return stop_exploration_rpc() # type: ignore[no-any-return] @skill() def start_exploration(self, timeout: float = 240.0) -> str: diff --git a/dimos/agents2/skills/osm.py b/dimos/agents2/skills/osm.py index d4455f14bd..f2a088a2ab 100644 --- a/dimos/agents2/skills/osm.py +++ b/dimos/agents2/skills/osm.py @@ -29,7 +29,7 @@ class OsmSkill(SkillModule): _latest_location: LatLon | None _current_location_map: CurrentLocationMap - gps_location: In[LatLon] = None + gps_location: In[LatLon] = None # type: ignore[assignment] def __init__(self) -> None: super().__init__() @@ -38,7 +38,7 @@ def __init__(self) -> None: def start(self) -> None: super().start() - self._disposables.add(self.gps_location.subscribe(self._on_gps_location)) + self._disposables.add(self.gps_location.subscribe(self._on_gps_location)) # type: ignore[arg-type] def stop(self) -> None: super().stop() @@ -60,16 +60,16 @@ def street_map_query(self, query_sentence: str) -> str: query_sentence (str): The query sentence. """ - self._current_location_map.update_position(self._latest_location) + self._current_location_map.update_position(self._latest_location) # type: ignore[arg-type] location = self._current_location_map.query_for_one_position_and_context( - query_sentence, self._latest_location + query_sentence, self._latest_location # type: ignore[arg-type] ) if not location: return "Could not find anything." latlon, context = location - distance = int(distance_in_meters(latlon, self._latest_location)) + distance = int(distance_in_meters(latlon, self._latest_location)) # type: ignore[arg-type] return f"{context}. It's at position latitude={latlon.lat}, longitude={latlon.lon}. It is {distance} meters away." diff --git a/dimos/agents2/skills/ros_navigation.py b/dimos/agents2/skills/ros_navigation.py index f6c257a941..d21b80dce1 100644 --- a/dimos/agents2/skills/ros_navigation.py +++ b/dimos/agents2/skills/ros_navigation.py @@ -65,7 +65,7 @@ def navigate_with_text(self, query: str) -> str: return "Failed to navigate." def _navigate_using_semantic_map(self, query: str) -> str: - results = self._robot.spatial_memory.query_by_text(query) + results = self._robot.spatial_memory.query_by_text(query) # type: ignore[union-attr] if not results: return f"No matching location found in semantic map for '{query}'" @@ -91,7 +91,7 @@ def stop_movement(self) -> str: if not self._started: raise ValueError(f"{self} has not been started.") - self._robot.cancel_navigation() + self._robot.cancel_navigation() # type: ignore[attr-defined] return "Stopped" diff --git a/dimos/agents2/spec.py b/dimos/agents2/spec.py index 9973b05356..b604c7c366 100644 --- a/dimos/agents2/spec.py +++ b/dimos/agents2/spec.py @@ -33,8 +33,8 @@ from dimos.core import Module, rpc from dimos.core.module import ModuleConfig -from dimos.protocol.pubsub import PubSub, lcm -from dimos.protocol.service import Service +from dimos.protocol.pubsub import PubSub, lcm # type: ignore[attr-defined] +from dimos.protocol.service import Service # type: ignore[attr-defined] from dimos.protocol.skill.skill import SkillContainer from dimos.utils.generic import truncate_display_string from dimos.utils.logging_config import setup_logger @@ -44,7 +44,7 @@ # Dynamically create ModelProvider enum from LangChain's supported providers _providers = {provider.upper(): provider for provider in _SUPPORTED_PROVIDERS} -Provider = Enum("Provider", _providers, type=str) +Provider = Enum("Provider", _providers, type=str) # type: ignore[misc] class Model(str, Enum): @@ -134,10 +134,10 @@ class AgentConfig(ModuleConfig): # we can provide model/provvider enums or instantiated model_instance model: Model = Model.GPT_4O - provider: Provider = Provider.OPENAI + provider: Provider = Provider.OPENAI # type: ignore[attr-defined] model_instance: BaseChatModel | None = None - agent_transport: type[PubSub] = lcm.PickleLCM + agent_transport: type[PubSub] = lcm.PickleLCM # type: ignore[type-arg] agent_topic: Any = field(default_factory=lambda: lcm.Topic("/agent")) @@ -147,7 +147,7 @@ class AgentConfig(ModuleConfig): class AgentSpec(Service[AgentConfig], Module, ABC): default_config: type[AgentConfig] = AgentConfig - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def] Service.__init__(self, *args, **kwargs) Module.__init__(self, *args, **kwargs) @@ -166,17 +166,17 @@ def stop(self) -> None: @rpc @abstractmethod - def clear_history(self): ... + def clear_history(self): ... # type: ignore[no-untyped-def] @abstractmethod - def append_history(self, *msgs: list[AIMessage | HumanMessage]): ... + def append_history(self, *msgs: list[AIMessage | HumanMessage]): ... # type: ignore[no-untyped-def] @abstractmethod def history(self) -> list[AnyMessage]: ... @rpc @abstractmethod - def query(self, query: str): ... + def query(self, query: str): ... # type: ignore[no-untyped-def] def __str__(self) -> str: console = Console(force_terminal=True, legacy_windows=False) @@ -196,11 +196,11 @@ def __str__(self) -> str: if hasattr(message, "metadata") and message.metadata.get("state"): table.add_row( Text("State Summary", style="blue"), - Text(message.content, style="blue"), + Text(message.content, style="blue"), # type: ignore[arg-type] ) else: table.add_row( - Text("Agent", style="magenta"), Text(message.content, style="magenta") + Text("Agent", style="magenta"), Text(message.content, style="magenta") # type: ignore[arg-type] ) for tool_call in message.tool_calls: @@ -224,6 +224,6 @@ def __str__(self) -> str: # Render to string with title above with console.capture() as capture: - console.print(Text(f" Agent ({self._agent_id})", style="bold blue")) + console.print(Text(f" Agent ({self._agent_id})", style="bold blue")) # type: ignore[attr-defined] console.print(table) return capture.get().strip() diff --git a/dimos/agents2/temp/webcam_agent.py b/dimos/agents2/temp/webcam_agent.py index 485684d9e0..2a70770e14 100644 --- a/dimos/agents2/temp/webcam_agent.py +++ b/dimos/agents2/temp/webcam_agent.py @@ -24,7 +24,7 @@ import reactivex as rx import reactivex.operators as ops -from dimos.agents2 import Agent, Output, Reducer, Stream, skill +from dimos.agents2 import Agent, Output, Reducer, Stream, skill # type: ignore[attr-defined] from dimos.agents2.cli.human import HumanInput from dimos.agents2.spec import Model, Provider from dimos.core import LCMTransport, Module, rpc, start @@ -38,11 +38,11 @@ class WebModule(Module): - web_interface: RobotWebInterface = None - human_query: rx.subject.Subject = None - agent_response: rx.subject.Subject = None + web_interface: RobotWebInterface = None # type: ignore[assignment] + human_query: rx.subject.Subject = None # type: ignore[assignment, type-arg] + agent_response: rx.subject.Subject = None # type: ignore[assignment, type-arg] - thread: Thread = None + thread: Thread = None # type: ignore[assignment] _human_messages_running = False @@ -74,15 +74,15 @@ def start(self) -> None: @rpc def stop(self) -> None: if self.web_interface: - self.web_interface.stop() + self.web_interface.stop() # type: ignore[attr-defined] if self.thread: # TODO, you can't just wait for a server to close, you have to signal it to end. self.thread.join(timeout=1.0) super().stop() - @skill(stream=Stream.call_agent, reducer=Reducer.all, output=Output.human) - def human_messages(self): + @skill(stream=Stream.call_agent, reducer=Reducer.all, output=Output.human) # type: ignore[arg-type] + def human_messages(self): # type: ignore[no-untyped-def] """Provide human messages from web interface. Don't use this tool, it's running implicitly already""" if self._human_messages_running: print("human_messages already running, not starting another") @@ -101,11 +101,11 @@ def main() -> None: agent = Agent( system_prompt="You are a helpful assistant for controlling a Unitree Go2 robot. ", model=Model.GPT_4O, # Could add CLAUDE models to enum - provider=Provider.OPENAI, # Would need ANTHROPIC provider + provider=Provider.OPENAI, # type: ignore[attr-defined] # Would need ANTHROPIC provider ) - testcontainer = dimos.deploy(SkillContainerTest) - webcam = dimos.deploy( + testcontainer = dimos.deploy(SkillContainerTest) # type: ignore[attr-defined] + webcam = dimos.deploy( # type: ignore[attr-defined] CameraModule, transform=Transform( translation=Vector3(0.0, 0.0, 0.0), @@ -127,7 +127,7 @@ def main() -> None: webcam.start() - human_input = dimos.deploy(HumanInput) + human_input = dimos.deploy(HumanInput) # type: ignore[attr-defined] time.sleep(1) diff --git a/dimos/agents2/testing.py b/dimos/agents2/testing.py index b729c13d50..c338b8632b 100644 --- a/dimos/agents2/testing.py +++ b/dimos/agents2/testing.py @@ -47,7 +47,7 @@ class MockModel(SimpleChatModel): real_model: Any | None = None recorded_messages: list[dict[str, Any]] = [] - def __init__(self, **kwargs) -> None: + def __init__(self, **kwargs) -> None: # type: ignore[no-untyped-def] # Extract custom parameters before calling super().__init__ responses = kwargs.pop("responses", []) json_path = kwargs.pop("json_path", None) @@ -67,7 +67,7 @@ def __init__(self, **kwargs) -> None: self.real_model = init_chat_model(model_provider=model_provider, model=model_name) self.responses = [] # Initialize empty for record mode elif self.json_path: - self.responses = self._load_responses_from_json() + self.responses = self._load_responses_from_json() # type: ignore[assignment] elif responses: self.responses = responses else: @@ -78,7 +78,7 @@ def _llm_type(self) -> str: return "tool-call-fake-chat-model" def _load_responses_from_json(self) -> list[AIMessage]: - with open(self.json_path) as f: + with open(self.json_path) as f: # type: ignore[arg-type] data = json.load(f) responses = [] @@ -183,7 +183,7 @@ def bind_tools( *, tool_choice: str | None = None, **kwargs: Any, - ) -> Runnable: + ) -> Runnable: # type: ignore[type-arg] """Store tools and return self.""" self._bound_tools = tools if self.record and self.real_model: diff --git a/dimos/core/__init__.py b/dimos/core/__init__.py index cf040f77eb..14f3ff27b6 100644 --- a/dimos/core/__init__.py +++ b/dimos/core/__init__.py @@ -54,17 +54,17 @@ class CudaCleanupPlugin: """Dask worker plugin to cleanup CUDA resources on shutdown.""" - def setup(self, worker) -> None: + def setup(self, worker) -> None: # type: ignore[no-untyped-def] """Called when worker starts.""" pass - def teardown(self, worker) -> None: + def teardown(self, worker) -> None: # type: ignore[no-untyped-def] """Clean up CUDA resources when worker shuts down.""" try: import sys if "cupy" in sys.modules: - import cupy as cp + import cupy as cp # type: ignore[import-not-found] # Clear memory pools mempool = cp.get_default_memory_pool() @@ -78,21 +78,21 @@ def teardown(self, worker) -> None: pass -def patch_actor(actor, cls) -> None: ... +def patch_actor(actor, cls) -> None: ... # type: ignore[no-untyped-def] DimosCluster = Client def patchdask(dask_client: Client, local_cluster: LocalCluster) -> DimosCluster: - def deploy( + def deploy( # type: ignore[no-untyped-def] actor_class, *args, **kwargs, ): console = Console() with console.status(f"deploying [green]{actor_class.__name__}", spinner="arc"): - actor = dask_client.submit( + actor = dask_client.submit( # type: ignore[no-untyped-call] actor_class, *args, **kwargs, @@ -164,7 +164,7 @@ def close_all() -> None: # Prevents multiple calls to close_all if hasattr(dask_client, "_closed") and dask_client._closed: return - dask_client._closed = True + dask_client._closed = True # type: ignore[attr-defined] # Stop all SharedMemory transports before closing Dask # This prevents the "leaked shared_memory objects" warning and hangs @@ -196,7 +196,7 @@ def close_all() -> None: pass try: - dask_client.close(timeout=5) + dask_client.close(timeout=5) # type: ignore[no-untyped-call] except Exception: pass @@ -217,11 +217,11 @@ def close_all() -> None: # This is needed, solves race condition in CI thread check time.sleep(0.1) - dask_client.deploy = deploy - dask_client.check_worker_memory = check_worker_memory - dask_client.stop = lambda: dask_client.close() - dask_client.close_all = close_all - return dask_client # type: ignore[return-value] + dask_client.deploy = deploy # type: ignore[attr-defined] + dask_client.check_worker_memory = check_worker_memory # type: ignore[attr-defined] + dask_client.stop = lambda: dask_client.close() # type: ignore[attr-defined, no-untyped-call] + dask_client.close_all = close_all # type: ignore[attr-defined] + return dask_client def start(n: int | None = None, memory_limit: str = "auto") -> DimosCluster: @@ -241,35 +241,35 @@ def start(n: int | None = None, memory_limit: str = "auto") -> DimosCluster: with console.status( f"[green]Initializing dimos local cluster with [bright_blue]{n} workers", spinner="arc" ): - cluster = LocalCluster( + cluster = LocalCluster( # type: ignore[no-untyped-call] n_workers=n, threads_per_worker=4, memory_limit=memory_limit, plugins=[CudaCleanupPlugin()], # Register CUDA cleanup plugin ) - client = Client(cluster) + client = Client(cluster) # type: ignore[no-untyped-call] console.print( f"[green]Initialized dimos local cluster with [bright_blue]{n} workers, memory limit: {memory_limit}" ) patched_client = patchdask(client, cluster) - patched_client._shutting_down = False + patched_client._shutting_down = False # type: ignore[attr-defined] # Signal handler with proper exit handling - def signal_handler(sig, frame) -> None: + def signal_handler(sig, frame) -> None: # type: ignore[no-untyped-def] # If already shutting down, force exit - if patched_client._shutting_down: + if patched_client._shutting_down: # type: ignore[attr-defined] import os console.print("[red]Force exit!") os._exit(1) - patched_client._shutting_down = True + patched_client._shutting_down = True # type: ignore[attr-defined] console.print(f"[yellow]Shutting down (signal {sig})...") try: - patched_client.close_all() + patched_client.close_all() # type: ignore[attr-defined] except Exception: pass diff --git a/dimos/core/blueprints.py b/dimos/core/blueprints.py index 1954f45721..2512da4a9d 100644 --- a/dimos/core/blueprints.py +++ b/dimos/core/blueprints.py @@ -159,7 +159,7 @@ def _connect_transports(self, module_coordinator: ModuleCoordinator) -> None: # Gather all the In/Out connections with remapping applied. connections = defaultdict(list) # Track original name -> remapped name for each module - module_conn_mapping = defaultdict(dict) + module_conn_mapping = defaultdict(dict) # type: ignore[var-annotated] for blueprint in self.blueprints: for conn in blueprint.connections: @@ -175,7 +175,7 @@ def _connect_transports(self, module_coordinator: ModuleCoordinator) -> None: transport = self._get_transport_for(remapped_name, type) for module, original_name in connections[(remapped_name, type)]: instance = module_coordinator.get_instance(module) - instance.set_transport(original_name, transport) + instance.set_transport(original_name, transport) # type: ignore[union-attr] def _connect_rpc_methods(self, module_coordinator: ModuleCoordinator) -> None: # Gather all RPC methods. @@ -185,7 +185,7 @@ def _connect_rpc_methods(self, module_coordinator: ModuleCoordinator) -> None: interface_methods = defaultdict(list) # interface_name.method -> [(module_class, method)] for blueprint in self.blueprints: - for method_name in blueprint.module.rpcs.keys(): + for method_name in blueprint.module.rpcs.keys(): # type: ignore[attr-defined] method = getattr(module_coordinator.get_instance(blueprint.module), method_name) # Register under concrete class name (backward compatibility) rpc_methods[f"{blueprint.module.__name__}_{method_name}"] = method @@ -211,14 +211,14 @@ def _connect_rpc_methods(self, module_coordinator: ModuleCoordinator) -> None: # Fulfil method requests (so modules can call each other). for blueprint in self.blueprints: instance = module_coordinator.get_instance(blueprint.module) - for method_name in blueprint.module.rpcs.keys(): + for method_name in blueprint.module.rpcs.keys(): # type: ignore[attr-defined] if not method_name.startswith("set_"): continue linked_name = method_name.removeprefix("set_") if linked_name not in rpc_methods: continue getattr(instance, method_name)(rpc_methods[linked_name]) - for requested_method_name in instance.get_rpc_method_names(): + for requested_method_name in instance.get_rpc_method_names(): # type: ignore[union-attr] # Check if this is an ambiguous interface method if ( requested_method_name in interface_methods @@ -235,7 +235,7 @@ def _connect_rpc_methods(self, module_coordinator: ModuleCoordinator) -> None: if requested_method_name not in rpc_methods_dot: continue - instance.set_rpc_method( + instance.set_rpc_method( # type: ignore[union-attr] requested_method_name, rpc_methods_dot[requested_method_name] ) @@ -270,11 +270,11 @@ def _make_module_blueprint( for name, annotation in all_annotations.items(): origin = get_origin(annotation) - if origin not in (In, Out): + if origin not in (In, Out): # type: ignore[comparison-overlap] continue - direction = "in" if origin == In else "out" + direction = "in" if origin == In else "out" # type: ignore[comparison-overlap] type_ = get_args(annotation)[0] - connections.append(ModuleConnection(name=name, type=type_, direction=direction)) + connections.append(ModuleConnection(name=name, type=type_, direction=direction)) # type: ignore[arg-type] return ModuleBlueprint(module=module, connections=tuple(connections), args=args, kwargs=kwargs) @@ -286,13 +286,13 @@ def create_module_blueprint(module: type[Module], *args: Any, **kwargs: Any) -> def autoconnect(*blueprints: ModuleBlueprintSet) -> ModuleBlueprintSet: all_blueprints = tuple(_eliminate_duplicates([bp for bs in blueprints for bp in bs.blueprints])) - all_transports = dict( + all_transports = dict( # type: ignore[var-annotated] reduce(operator.iadd, [list(x.transport_map.items()) for x in blueprints], []) ) - all_config_overrides = dict( + all_config_overrides = dict( # type: ignore[var-annotated] reduce(operator.iadd, [list(x.global_config_overrides.items()) for x in blueprints], []) ) - all_remappings = dict( + all_remappings = dict( # type: ignore[var-annotated] reduce(operator.iadd, [list(x.remapping_map.items()) for x in blueprints], []) ) diff --git a/dimos/core/module.py b/dimos/core/module.py index 25a39b37ac..aa2753db81 100644 --- a/dimos/core/module.py +++ b/dimos/core/module.py @@ -33,8 +33,8 @@ from dimos.core.resource import Resource from dimos.core.rpc_client import RpcCall from dimos.core.stream import In, Out, RemoteIn, RemoteOut, Transport -from dimos.protocol.rpc import LCMRPC, RPCSpec -from dimos.protocol.service import Configurable +from dimos.protocol.rpc import LCMRPC, RPCSpec # type: ignore[attr-defined] +from dimos.protocol.service import Configurable # type: ignore[attr-defined] from dimos.protocol.skill.skill import SkillContainer from dimos.protocol.tf import LCMTF, TFSpec from dimos.utils.generic import classproperty @@ -86,7 +86,7 @@ class ModuleBase(Configurable[ModuleConfig], SkillContainer, Resource): default_config = ModuleConfig - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(*args, **kwargs) self._loop, self._loop_thread = get_loop() self._disposables = CompositeDisposable() @@ -97,7 +97,7 @@ def __init__(self, *args, **kwargs) -> None: # and we register our RPC server self.rpc = self.config.rpc_transport() self.rpc.serve_module_rpc(self) - self.rpc.start() + self.rpc.start() # type: ignore[attr-defined] except ValueError: ... @@ -114,7 +114,7 @@ def _close_module(self) -> None: self._close_rpc() if hasattr(self, "_loop") and self._loop_thread: if self._loop_thread.is_alive(): - self._loop.call_soon_threadsafe(self._loop.stop) + self._loop.call_soon_threadsafe(self._loop.stop) # type: ignore[union-attr] self._loop_thread.join(timeout=2) self._loop = None self._loop_thread = None @@ -127,10 +127,10 @@ def _close_module(self) -> None: def _close_rpc(self) -> None: # Using hasattr is needed because SkillCoordinator skips ModuleBase.__init__ and self.rpc is never set. if hasattr(self, "rpc") and self.rpc: - self.rpc.stop() - self.rpc = None + self.rpc.stop() # type: ignore[attr-defined] + self.rpc = None # type: ignore[assignment] - def __getstate__(self): + def __getstate__(self): # type: ignore[no-untyped-def] """Exclude unpicklable runtime attributes when serializing.""" state = self.__dict__.copy() # Remove unpicklable attributes @@ -141,7 +141,7 @@ def __getstate__(self): state.pop("_tf", None) return state - def __setstate__(self, state) -> None: + def __setstate__(self, state) -> None: # type: ignore[no-untyped-def] """Restore object from pickled state.""" self.__dict__.update(state) # Reinitialize runtime attributes @@ -152,14 +152,14 @@ def __setstate__(self, state) -> None: self._tf = None @property - def tf(self): + def tf(self): # type: ignore[no-untyped-def] if self._tf is None: # self._tf = self.config.tf_transport() self._tf = LCMTF() return self._tf @tf.setter - def tf(self, value) -> None: + def tf(self, value) -> None: # type: ignore[no-untyped-def] import warnings warnings.warn( @@ -169,7 +169,7 @@ def tf(self, value) -> None: ) @property - def outputs(self) -> dict[str, Out]: + def outputs(self) -> dict[str, Out]: # type: ignore[type-arg] return { name: s for name, s in self.__dict__.items() @@ -177,16 +177,16 @@ def outputs(self) -> dict[str, Out]: } @property - def inputs(self) -> dict[str, In]: + def inputs(self) -> dict[str, In]: # type: ignore[type-arg] return { name: s for name, s in self.__dict__.items() if isinstance(s, In) and not name.startswith("_") } - @classmethod + @classmethod # type: ignore[misc] @property - def rpcs(cls) -> dict[str, Callable]: + def rpcs(cls) -> dict[str, Callable]: # type: ignore[type-arg] return { name: getattr(cls, name) for name in dir(cls) @@ -199,7 +199,7 @@ def rpcs(cls) -> dict[str, Callable]: @rpc def io(self) -> str: def _box(name: str) -> str: - return [ + return [ # type: ignore[return-value] "┌┴" + "─" * (len(name) + 1) + "┐", f"│ {name} │", "└┬" + "─" * (len(name) + 1) + "┘", @@ -207,7 +207,7 @@ def _box(name: str) -> str: # can't modify __str__ on a function like we are doing for I/O # so we have a separate repr function here - def repr_rpc(fn: Callable) -> str: + def repr_rpc(fn: Callable) -> str: # type: ignore[type-arg] sig = inspect.signature(fn) # Remove 'self' parameter params = [p for name, p in sig.parameters.items() if name != "self"] @@ -244,11 +244,11 @@ def repr_rpc(fn: Callable) -> str: return "\n".join(ret) @classproperty - def blueprint(self): + def blueprint(self): # type: ignore[no-untyped-def] # Here to prevent circular imports. from dimos.core.blueprints import create_module_blueprint - return partial(create_module_blueprint, self) + return partial(create_module_blueprint, self) # type: ignore[arg-type] @rpc def get_rpc_method_names(self) -> list[str]: @@ -256,7 +256,7 @@ def get_rpc_method_names(self) -> list[str]: @rpc def set_rpc_method(self, method: str, callable: RpcCall) -> None: - callable.set_rpc(self.rpc) + callable.set_rpc(self.rpc) # type: ignore[arg-type] self._bound_rpc_calls[method] = callable @overload @@ -265,7 +265,7 @@ def get_rpc_calls(self, method: str) -> RpcCall: ... @overload def get_rpc_calls(self, method1: str, method2: str, *methods: str) -> tuple[RpcCall, ...]: ... - def get_rpc_calls(self, *methods: str) -> RpcCall | tuple[RpcCall, ...]: + def get_rpc_calls(self, *methods: str) -> RpcCall | tuple[RpcCall, ...]: # type: ignore[misc] missing = [m for m in methods if m not in self._bound_rpc_calls] if missing: raise ValueError( @@ -279,8 +279,8 @@ class DaskModule(ModuleBase): ref: Actor worker: int - def __init__(self, *args, **kwargs) -> None: - self.ref = None + def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def] + self.ref = None # type: ignore[assignment] # Get type hints with proper namespace resolution for subclasses # Collect namespaces from all classes in the MRO chain @@ -302,25 +302,25 @@ def __init__(self, *args, **kwargs) -> None: origin = get_origin(ann) if origin is Out: inner, *_ = get_args(ann) or (Any,) - stream = Out(inner, name, self) + stream = Out(inner, name, self) # type: ignore[var-annotated] setattr(self, name, stream) elif origin is In: inner, *_ = get_args(ann) or (Any,) - stream = In(inner, name, self) + stream = In(inner, name, self) # type: ignore[assignment] setattr(self, name, stream) super().__init__(*args, **kwargs) - def set_ref(self, ref) -> int: + def set_ref(self, ref) -> int: # type: ignore[no-untyped-def] worker = get_worker() self.ref = ref self.worker = worker.name - return worker.name + return worker.name # type: ignore[no-any-return] def __str__(self) -> str: return f"{self.__class__.__name__}" # called from remote - def set_transport(self, stream_name: str, transport: Transport) -> bool: + def set_transport(self, stream_name: str, transport: Transport) -> bool: # type: ignore[type-arg] stream = getattr(self, stream_name, None) if not stream: raise ValueError(f"{stream_name} not found in {self.__class__.__name__}") @@ -332,7 +332,7 @@ def set_transport(self, stream_name: str, transport: Transport) -> bool: return True # called from remote - def connect_stream(self, input_name: str, remote_stream: RemoteOut[T]): + def connect_stream(self, input_name: str, remote_stream: RemoteOut[T]): # type: ignore[no-untyped-def] input_stream = getattr(self, input_name, None) if not input_stream: raise ValueError(f"{input_name} not found in {self.__class__.__name__}") diff --git a/dimos/core/module_coordinator.py b/dimos/core/module_coordinator.py index 88dfe3b129..10eb425d35 100644 --- a/dimos/core/module_coordinator.py +++ b/dimos/core/module_coordinator.py @@ -46,22 +46,22 @@ def stop(self) -> None: for module in reversed(self._deployed_modules.values()): module.stop() - self._client.close_all() + self._client.close_all() # type: ignore[union-attr] - def deploy(self, module_class: type[T], *args, **kwargs) -> T: + def deploy(self, module_class: type[T], *args, **kwargs) -> T: # type: ignore[no-untyped-def] if not self._client: raise ValueError("Not started") - module = self._client.deploy(module_class, *args, **kwargs) + module = self._client.deploy(module_class, *args, **kwargs) # type: ignore[attr-defined] self._deployed_modules[module_class] = module - return module + return module # type: ignore[no-any-return] def start_all_modules(self) -> None: for module in self._deployed_modules.values(): module.start() def get_instance(self, module: type[T]) -> T | None: - return self._deployed_modules.get(module) + return self._deployed_modules.get(module) # type: ignore[return-value] def loop(self) -> None: try: diff --git a/dimos/core/o3dpickle.py b/dimos/core/o3dpickle.py index 8e0f13dbf0..122070bc79 100644 --- a/dimos/core/o3dpickle.py +++ b/dimos/core/o3dpickle.py @@ -15,16 +15,16 @@ import copyreg import numpy as np -import open3d as o3d +import open3d as o3d # type: ignore[import-untyped] -def reduce_external(obj): +def reduce_external(obj): # type: ignore[no-untyped-def] # Convert Vector3dVector to numpy array for pickling points_array = np.asarray(obj.points) return (reconstruct_pointcloud, (points_array,)) -def reconstruct_pointcloud(points_array): +def reconstruct_pointcloud(points_array): # type: ignore[no-untyped-def] # Create new PointCloud and assign the points pc = o3d.geometry.PointCloud() pc.points = o3d.utility.Vector3dVector(points_array) diff --git a/dimos/core/rpc_client.py b/dimos/core/rpc_client.py index bfcec5bb71..9744620615 100644 --- a/dimos/core/rpc_client.py +++ b/dimos/core/rpc_client.py @@ -26,7 +26,7 @@ class RpcCall: _rpc: LCMRPC | None _name: str _remote_name: str - _unsub_fns: list + _unsub_fns: list # type: ignore[type-arg] _stop_rpc_client: Callable[[], None] | None = None def __init__( @@ -35,7 +35,7 @@ def __init__( rpc: LCMRPC, name: str, remote_name: str, - unsub_fns: list, + unsub_fns: list, # type: ignore[type-arg] stop_client: Callable[[], None] | None = None, ) -> None: self._original_method = original_method @@ -53,7 +53,7 @@ def __init__( def set_rpc(self, rpc: LCMRPC) -> None: self._rpc = rpc - def __call__(self, *args, **kwargs): + def __call__(self, *args, **kwargs): # type: ignore[no-untyped-def] if not self._rpc: logger.warning("RPC client not initialized") return None @@ -61,19 +61,19 @@ def __call__(self, *args, **kwargs): # For stop, use call_nowait to avoid deadlock # (the remote side stops its RPC service before responding) if self._name == "stop": - self._rpc.call_nowait(f"{self._remote_name}/{self._name}", (args, kwargs)) + self._rpc.call_nowait(f"{self._remote_name}/{self._name}", (args, kwargs)) # type: ignore[arg-type] if self._stop_rpc_client: self._stop_rpc_client() return None - result, unsub_fn = self._rpc.call_sync(f"{self._remote_name}/{self._name}", (args, kwargs)) + result, unsub_fn = self._rpc.call_sync(f"{self._remote_name}/{self._name}", (args, kwargs)) # type: ignore[arg-type] self._unsub_fns.append(unsub_fn) return result - def __getstate__(self): + def __getstate__(self): # type: ignore[no-untyped-def] return (self._original_method, self._name, self._remote_name) - def __setstate__(self, state) -> None: + def __setstate__(self, state) -> None: # type: ignore[no-untyped-def] self._original_method, self._name, self._remote_name = state self._unsub_fns = [] self._rpc = None @@ -81,14 +81,14 @@ def __setstate__(self, state) -> None: class RPCClient: - def __init__(self, actor_instance, actor_class) -> None: + def __init__(self, actor_instance, actor_class) -> None: # type: ignore[no-untyped-def] self.rpc = LCMRPC() self.actor_class = actor_class self.remote_name = actor_class.__name__ self.actor_instance = actor_instance self.rpcs = actor_class.rpcs.keys() self.rpc.start() - self._unsub_fns = [] + self._unsub_fns = [] # type: ignore[var-annotated] def stop_rpc_client(self) -> None: for unsub in self._unsub_fns: @@ -101,9 +101,9 @@ def stop_rpc_client(self) -> None: if self.rpc: self.rpc.stop() - self.rpc = None + self.rpc = None # type: ignore[assignment] - def __reduce__(self): + def __reduce__(self): # type: ignore[no-untyped-def] # Return the class and the arguments needed to reconstruct the object return ( self.__class__, @@ -111,7 +111,7 @@ def __reduce__(self): ) # passthrough - def __getattr__(self, name: str): + def __getattr__(self, name: str): # type: ignore[no-untyped-def] # Check if accessing a known safe attribute to avoid recursion if name in { "__class__", diff --git a/dimos/core/skill_module.py b/dimos/core/skill_module.py index 4c6a42fa5b..dcf8c06326 100644 --- a/dimos/core/skill_module.py +++ b/dimos/core/skill_module.py @@ -22,11 +22,11 @@ class SkillModule(Module): @rpc def set_LlmAgent_register_skills(self, callable: RpcCall) -> None: - callable.set_rpc(self.rpc) + callable.set_rpc(self.rpc) # type: ignore[arg-type] callable(RPCClient(self, self.__class__)) def __getstate__(self) -> None: pass - def __setstate__(self, _state) -> None: + def __setstate__(self, _state) -> None: # type: ignore[no-untyped-def] pass diff --git a/dimos/core/stream.py b/dimos/core/stream.py index a8843b0989..2556aa5f03 100644 --- a/dimos/core/stream.py +++ b/dimos/core/stream.py @@ -42,8 +42,8 @@ class ObservableMixin(Generic[T]): # might be nicer to write without rxpy but had this snippet ready def get_next(self, timeout: float = 10.0) -> T: try: - return ( - self.observable() + return ( # type: ignore[no-any-return] + self.observable() # type: ignore[no-untyped-call] .pipe(ops.first(), *([ops.timeout(timeout)] if timeout is not None else [])) .run() ) @@ -51,19 +51,19 @@ def get_next(self, timeout: float = 10.0) -> T: raise Exception(f"No value received after {timeout} seconds") from e def hot_latest(self) -> Callable[[], T]: - return reactive.getter_streaming(self.observable()) + return reactive.getter_streaming(self.observable()) # type: ignore[no-untyped-call] - def pure_observable(self): - def _subscribe(observer, scheduler=None): - unsubscribe = self.subscribe(observer.on_next) + def pure_observable(self): # type: ignore[no-untyped-def] + def _subscribe(observer, scheduler=None): # type: ignore[no-untyped-def] + unsubscribe = self.subscribe(observer.on_next) # type: ignore[attr-defined] return Disposable(unsubscribe) return rx.create(_subscribe) # default return is backpressured because most # use cases will want this by default - def observable(self): - return backpressure(self.pure_observable()) + def observable(self): # type: ignore[no-untyped-def] + return backpressure(self.pure_observable()) # type: ignore[no-untyped-call] class State(enum.Enum): @@ -78,21 +78,21 @@ class Transport(ObservableMixin[T]): def broadcast(self, selfstream: Out[T], value: T) -> None: ... def publish(self, msg: T) -> None: - self.broadcast(None, msg) + self.broadcast(None, msg) # type: ignore[arg-type] # used by local Input - def subscribe(self, selfstream: In[T], callback: Callable[[T], any]) -> None: ... + def subscribe(self, selfstream: In[T], callback: Callable[[T], any]) -> None: ... # type: ignore[valid-type] class Stream(Generic[T]): - _transport: Transport | None + _transport: Transport | None # type: ignore[type-arg] def __init__( self, type: type[T], name: str, owner: Any | None = None, - transport: Transport | None = None, + transport: Transport | None = None, # type: ignore[type-arg] ) -> None: self.name = name self.owner = owner @@ -107,11 +107,11 @@ def type_name(self) -> str: return getattr(self.type, "__name__", repr(self.type)) def _color_fn(self) -> Callable[[str], str]: - if self.state == State.UNBOUND: + if self.state == State.UNBOUND: # type: ignore[attr-defined] return colors.orange - if self.state == State.READY: + if self.state == State.READY: # type: ignore[attr-defined] return colors.blue - if self.state == State.CONNECTED: + if self.state == State.CONNECTED: # type: ignore[attr-defined] return colors.green return lambda s: s @@ -122,18 +122,18 @@ def __str__(self) -> str: + self._color_fn()(f"{self.name}[{self.type_name}]") + " @ " + ( - colors.orange(self.owner) + colors.orange(self.owner) # type: ignore[arg-type] if isinstance(self.owner, Actor) - else colors.green(self.owner) + else colors.green(self.owner) # type: ignore[arg-type] ) + ("" if not self._transport else " via " + str(self._transport)) ) class Out(Stream[T]): - _transport: Transport + _transport: Transport # type: ignore[type-arg] - def __init__(self, *argv, **kwargs) -> None: + def __init__(self, *argv, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(*argv, **kwargs) @property @@ -149,7 +149,7 @@ def transport(self, value: Transport[T]) -> None: def state(self) -> State: return State.UNBOUND if self.owner is None else State.READY - def __reduce__(self): + def __reduce__(self): # type: ignore[no-untyped-def] if self.owner is None or not hasattr(self.owner, "ref"): raise ValueError("Cannot serialise Out without an owner ref") return ( @@ -162,7 +162,7 @@ def __reduce__(self): ), ) - def publish(self, msg): + def publish(self, msg): # type: ignore[no-untyped-def] if not hasattr(self, "_transport") or self._transport is None: raise Exception(f"{self} transport for stream is not specified,") self._transport.broadcast(self, msg) @@ -176,27 +176,27 @@ def state(self) -> State: # this won't work but nvm @property def transport(self) -> Transport[T]: - return self._transport + return self._transport # type: ignore[return-value] @transport.setter def transport(self, value: Transport[T]) -> None: - self.owner.set_transport(self.name, value).result() + self.owner.set_transport(self.name, value).result() # type: ignore[union-attr] self._transport = value class RemoteOut(RemoteStream[T]): - def connect(self, other: RemoteIn[T]): + def connect(self, other: RemoteIn[T]): # type: ignore[no-untyped-def] return other.connect(self) - def subscribe(self, cb) -> Callable[[], None]: - return self.transport.subscribe(cb, self) + def subscribe(self, cb) -> Callable[[], None]: # type: ignore[no-untyped-def] + return self.transport.subscribe(cb, self) # type: ignore[arg-type, func-returns-value, no-any-return] # representation of Input # as views from inside of the module class In(Stream[T], ObservableMixin[T]): connection: RemoteOut[T] | None = None - _transport: Transport + _transport: Transport # type: ignore[type-arg] def __str__(self) -> str: mystr = super().__str__() @@ -206,7 +206,7 @@ def __str__(self) -> str: return (mystr + " ◀─").ljust(60, "─") + f" {self.connection}" - def __reduce__(self): + def __reduce__(self): # type: ignore[no-untyped-def] if self.owner is None or not hasattr(self.owner, "ref"): raise ValueError("Cannot serialise Out without an owner ref") return (RemoteIn, (self.type, self.name, self.owner.ref, self._transport)) @@ -214,7 +214,7 @@ def __reduce__(self): @property def transport(self) -> Transport[T]: if not self._transport: - self._transport = self.connection.transport + self._transport = self.connection.transport # type: ignore[union-attr] return self._transport @transport.setter @@ -231,25 +231,25 @@ def state(self) -> State: return State.UNBOUND if self.owner is None else State.READY # returns unsubscribe function - def subscribe(self, cb) -> Callable[[], None]: - return self.transport.subscribe(cb, self) + def subscribe(self, cb) -> Callable[[], None]: # type: ignore[no-untyped-def] + return self.transport.subscribe(cb, self) # type: ignore[arg-type, func-returns-value, no-any-return] # representation of input outside of module # used for configuring connections, setting a transport class RemoteIn(RemoteStream[T]): def connect(self, other: RemoteOut[T]) -> None: - return self.owner.connect_stream(self.name, other).result() + return self.owner.connect_stream(self.name, other).result() # type: ignore[no-any-return, union-attr] # this won't work but that's ok - @property + @property # type: ignore[misc] def transport(self) -> Transport[T]: - return self._transport + return self._transport # type: ignore[return-value] - def publish(self, msg) -> None: - self.transport.broadcast(self, msg) + def publish(self, msg) -> None: # type: ignore[no-untyped-def] + self.transport.broadcast(self, msg) # type: ignore[arg-type] - @transport.setter + @transport.setter # type: ignore[attr-defined, misc, no-redef] def transport(self, value: Transport[T]) -> None: - self.owner.set_transport(self.name, value).result() + self.owner.set_transport(self.name, value).result() # type: ignore[union-attr] self._transport = value diff --git a/dimos/core/testing.py b/dimos/core/testing.py index 92f6d6b497..30f45383d8 100644 --- a/dimos/core/testing.py +++ b/dimos/core/testing.py @@ -25,21 +25,21 @@ @pytest.fixture -def dimos(): +def dimos(): # type: ignore[no-untyped-def] """Fixture to create a Dimos client for testing.""" client = start(2) yield client - client.stop() + client.stop() # type: ignore[attr-defined] class MockRobotClient(Module): - odometry: Out[Odometry] = None - lidar: Out[LidarMessage] = None - mov: In[Vector3] = None + odometry: Out[Odometry] = None # type: ignore[assignment] + lidar: Out[LidarMessage] = None # type: ignore[assignment] + mov: In[Vector3] = None # type: ignore[assignment] mov_msg_count = 0 - def mov_callback(self, msg) -> None: + def mov_callback(self, msg) -> None: # type: ignore[no-untyped-def] self.mov_msg_count += 1 def __init__(self) -> None: @@ -51,8 +51,8 @@ def __init__(self) -> None: def start(self) -> None: super().start() - self._thread = Thread(target=self.odomloop) - self._thread.start() + self._thread = Thread(target=self.odomloop) # type: ignore[assignment] + self._thread.start() # type: ignore[attr-defined] self.mov.subscribe(self.mov_callback) @rpc @@ -75,9 +75,9 @@ def odomloop(self) -> None: return print(odom) odom.pubtime = time.perf_counter() - self.odometry.publish(odom) + self.odometry.publish(odom) # type: ignore[no-untyped-call] lidarmsg = next(lidariter) - lidarmsg.pubtime = time.perf_counter() - self.lidar.publish(lidarmsg) + lidarmsg.pubtime = time.perf_counter() # type: ignore[union-attr] + self.lidar.publish(lidarmsg) # type: ignore[no-untyped-call] time.sleep(0.1) diff --git a/dimos/core/transport.py b/dimos/core/transport.py index 5cd05460f0..b64645001b 100644 --- a/dimos/core/transport.py +++ b/dimos/core/transport.py @@ -34,7 +34,7 @@ if TYPE_CHECKING: from collections.abc import Callable -T = TypeVar("T") +T = TypeVar("T") # type: ignore[misc] class PubSubTransport(Transport[T]): @@ -54,132 +54,132 @@ def __str__(self) -> str: class pLCMTransport(PubSubTransport[T]): _started: bool = False - def __init__(self, topic: str, **kwargs) -> None: + def __init__(self, topic: str, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(topic) self.lcm = PickleLCM(**kwargs) - def __reduce__(self): + def __reduce__(self): # type: ignore[no-untyped-def] return (pLCMTransport, (self.topic,)) - def broadcast(self, _, msg) -> None: + def broadcast(self, _, msg) -> None: # type: ignore[no-untyped-def] if not self._started: self.lcm.start() self._started = True self.lcm.publish(self.topic, msg) - def subscribe(self, callback: Callable[[T], None], selfstream: In[T] = None) -> None: + def subscribe(self, callback: Callable[[T], None], selfstream: In[T] = None) -> None: # type: ignore[assignment, override] if not self._started: self.lcm.start() self._started = True - return self.lcm.subscribe(self.topic, lambda msg, topic: callback(msg)) + return self.lcm.subscribe(self.topic, lambda msg, topic: callback(msg)) # type: ignore[return-value] class LCMTransport(PubSubTransport[T]): _started: bool = False - def __init__(self, topic: str, type: type, **kwargs) -> None: + def __init__(self, topic: str, type: type, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(LCMTopic(topic, type)) if not hasattr(self, "lcm"): self.lcm = LCM(**kwargs) - def __reduce__(self): + def __reduce__(self): # type: ignore[no-untyped-def] return (LCMTransport, (self.topic.topic, self.topic.lcm_type)) - def broadcast(self, _, msg) -> None: + def broadcast(self, _, msg) -> None: # type: ignore[no-untyped-def] if not self._started: self.lcm.start() self._started = True self.lcm.publish(self.topic, msg) - def subscribe(self, callback: Callable[[T], None], selfstream: In[T] = None) -> None: + def subscribe(self, callback: Callable[[T], None], selfstream: In[T] = None) -> None: # type: ignore[assignment, override] if not self._started: self.lcm.start() self._started = True - return self.lcm.subscribe(self.topic, lambda msg, topic: callback(msg)) + return self.lcm.subscribe(self.topic, lambda msg, topic: callback(msg)) # type: ignore[return-value] -class JpegLcmTransport(LCMTransport): - def __init__(self, topic: str, type: type, **kwargs) -> None: - self.lcm = JpegLCM(**kwargs) +class JpegLcmTransport(LCMTransport): # type: ignore[type-arg] + def __init__(self, topic: str, type: type, **kwargs) -> None: # type: ignore[no-untyped-def] + self.lcm = JpegLCM(**kwargs) # type: ignore[assignment] super().__init__(topic, type) - def __reduce__(self): + def __reduce__(self): # type: ignore[no-untyped-def] return (JpegLcmTransport, (self.topic.topic, self.topic.lcm_type)) class pSHMTransport(PubSubTransport[T]): _started: bool = False - def __init__(self, topic: str, **kwargs) -> None: + def __init__(self, topic: str, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(topic) self.shm = PickleSharedMemory(**kwargs) - def __reduce__(self): + def __reduce__(self): # type: ignore[no-untyped-def] return (pSHMTransport, (self.topic,)) - def broadcast(self, _, msg) -> None: + def broadcast(self, _, msg) -> None: # type: ignore[no-untyped-def] if not self._started: self.shm.start() self._started = True self.shm.publish(self.topic, msg) - def subscribe(self, callback: Callable[[T], None], selfstream: In[T] = None) -> None: + def subscribe(self, callback: Callable[[T], None], selfstream: In[T] = None) -> None: # type: ignore[assignment, override] if not self._started: self.shm.start() self._started = True - return self.shm.subscribe(self.topic, lambda msg, topic: callback(msg)) + return self.shm.subscribe(self.topic, lambda msg, topic: callback(msg)) # type: ignore[return-value] class SHMTransport(PubSubTransport[T]): _started: bool = False - def __init__(self, topic: str, **kwargs) -> None: + def __init__(self, topic: str, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(topic) self.shm = SharedMemory(**kwargs) - def __reduce__(self): + def __reduce__(self): # type: ignore[no-untyped-def] return (SHMTransport, (self.topic,)) - def broadcast(self, _, msg) -> None: + def broadcast(self, _, msg) -> None: # type: ignore[no-untyped-def] if not self._started: self.shm.start() self._started = True self.shm.publish(self.topic, msg) - def subscribe(self, callback: Callable[[T], None], selfstream: In[T] = None) -> None: + def subscribe(self, callback: Callable[[T], None], selfstream: In[T] = None) -> None: # type: ignore[assignment, override] if not self._started: self.shm.start() self._started = True - return self.shm.subscribe(self.topic, lambda msg, topic: callback(msg)) + return self.shm.subscribe(self.topic, lambda msg, topic: callback(msg)) # type: ignore[arg-type, return-value] class JpegShmTransport(PubSubTransport[T]): _started: bool = False - def __init__(self, topic: str, quality: int = 75, **kwargs) -> None: + def __init__(self, topic: str, quality: int = 75, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(topic) self.shm = JpegSharedMemory(quality=quality, **kwargs) self.quality = quality - def __reduce__(self): + def __reduce__(self): # type: ignore[no-untyped-def] return (JpegShmTransport, (self.topic, self.quality)) - def broadcast(self, _, msg) -> None: + def broadcast(self, _, msg) -> None: # type: ignore[no-untyped-def] if not self._started: self.shm.start() self._started = True self.shm.publish(self.topic, msg) - def subscribe(self, callback: Callable[[T], None], selfstream: In[T] = None) -> None: + def subscribe(self, callback: Callable[[T], None], selfstream: In[T] = None) -> None: # type: ignore[assignment, override] if not self._started: self.shm.start() self._started = True - return self.shm.subscribe(self.topic, lambda msg, topic: callback(msg)) + return self.shm.subscribe(self.topic, lambda msg, topic: callback(msg)) # type: ignore[arg-type, return-value] class DaskTransport(Transport[T]): @@ -192,19 +192,19 @@ def __init__(self) -> None: def __str__(self) -> str: return colors.yellow("DaskTransport") - def __reduce__(self): + def __reduce__(self): # type: ignore[no-untyped-def] return (DaskTransport, ()) - def broadcast(self, selfstream: RemoteIn[T], msg: T) -> None: + def broadcast(self, selfstream: RemoteIn[T], msg: T) -> None: # type: ignore[override] for subscriber in self.subscribers: # there is some sort of a bug here with losing worker loop # print(subscriber.owner, subscriber.owner._worker, subscriber.owner._client) # subscriber.owner._try_bind_worker_client() # print(subscriber.owner, subscriber.owner._worker, subscriber.owner._client) - subscriber.owner.dask_receive_msg(subscriber.name, msg).result() + subscriber.owner.dask_receive_msg(subscriber.name, msg).result() # type: ignore[attr-defined] - def dask_receive_msg(self, msg) -> None: + def dask_receive_msg(self, msg) -> None: # type: ignore[no-untyped-def] for subscriber in self.subscribers: try: subscriber(msg) @@ -217,13 +217,13 @@ def dask_receive_msg(self, msg) -> None: # for outputs def dask_register_subscriber(self, remoteInput: RemoteIn[T]) -> None: - self.subscribers.append(remoteInput) + self.subscribers.append(remoteInput) # type: ignore[arg-type] # for inputs - def subscribe(self, callback: Callable[[T], None], selfstream: In[T]) -> None: + def subscribe(self, callback: Callable[[T], None], selfstream: In[T]) -> None: # type: ignore[override] if not self._started: - selfstream.connection.owner.dask_register_subscriber( - selfstream.connection.name, selfstream + selfstream.connection.owner.dask_register_subscriber( # type: ignore[union-attr] + selfstream.connection.name, selfstream # type: ignore[union-attr] ).result() self._started = True self.subscribers.append(callback) diff --git a/dimos/environment/environment.py b/dimos/environment/environment.py index 8b0068cbae..9eb01bae7d 100644 --- a/dimos/environment/environment.py +++ b/dimos/environment/environment.py @@ -33,14 +33,14 @@ def label_objects(self) -> list[str]: pass @abstractmethod - def get_visualization(self, format_type): + def get_visualization(self, format_type): # type: ignore[no-untyped-def] """Return different visualization formats like images, NERFs, or other 3D file types.""" pass @abstractmethod - def generate_segmentations( + def generate_segmentations( # type: ignore[no-untyped-def] self, model: str | None = None, objects: list[str] | None = None, *args, **kwargs - ) -> list[np.ndarray]: + ) -> list[np.ndarray]: # type: ignore[type-arg] """ Generate object segmentations of objects[] using neural methods. @@ -60,7 +60,7 @@ def generate_segmentations( pass @abstractmethod - def get_segmentations(self) -> list[np.ndarray]: + def get_segmentations(self) -> list[np.ndarray]: # type: ignore[type-arg] """ Get segmentations using a method like 'segment anything'. @@ -71,7 +71,7 @@ def get_segmentations(self) -> list[np.ndarray]: pass @abstractmethod - def generate_point_cloud(self, object: str | None = None, *args, **kwargs) -> np.ndarray: + def generate_point_cloud(self, object: str | None = None, *args, **kwargs) -> np.ndarray: # type: ignore[no-untyped-def, type-arg] """ Generate a point cloud for the entire environment or a specific object. @@ -91,7 +91,7 @@ def generate_point_cloud(self, object: str | None = None, *args, **kwargs) -> np pass @abstractmethod - def get_point_cloud(self, object: str | None = None) -> np.ndarray: + def get_point_cloud(self, object: str | None = None) -> np.ndarray: # type: ignore[type-arg] """ Return point clouds of the entire environment or a specific object. @@ -105,14 +105,14 @@ def get_point_cloud(self, object: str | None = None) -> np.ndarray: pass @abstractmethod - def generate_depth_map( + def generate_depth_map( # type: ignore[no-untyped-def] self, stereo: bool | None = None, monocular: bool | None = None, model: str | None = None, *args, **kwargs, - ) -> np.ndarray: + ) -> np.ndarray: # type: ignore[type-arg] """ Generate a depth map using monocular or stereo camera methods. @@ -134,7 +134,7 @@ def generate_depth_map( pass @abstractmethod - def get_depth_map(self) -> np.ndarray: + def get_depth_map(self) -> np.ndarray: # type: ignore[type-arg] """ Return a depth map of the environment. @@ -150,11 +150,11 @@ def get_depth_map(self) -> np.ndarray: """ pass - def initialize_from_images(self, images): + def initialize_from_images(self, images): # type: ignore[no-untyped-def] """Initialize the environment from a set of image frames or video.""" raise NotImplementedError("This method is not implemented for this environment type.") - def initialize_from_file(self, file_path): + def initialize_from_file(self, file_path): # type: ignore[no-untyped-def] """Initialize the environment from a spatial file type. Supported file types include: diff --git a/dimos/exceptions/agent_memory_exceptions.py b/dimos/exceptions/agent_memory_exceptions.py index 073e56c643..bbdca2ad38 100644 --- a/dimos/exceptions/agent_memory_exceptions.py +++ b/dimos/exceptions/agent_memory_exceptions.py @@ -38,14 +38,14 @@ class AgentMemoryConnectionError(AgentMemoryError): cause (Exception, optional): Original exception, if any, that led to this error. """ - def __init__(self, message: str = "Failed to connect to the database", cause=None) -> None: + def __init__(self, message: str = "Failed to connect to the database", cause=None) -> None: # type: ignore[no-untyped-def] super().__init__(message) if cause: self.cause = cause self.traceback = traceback.format_exc() if cause else None def __str__(self) -> str: - return f"{self.message}\nCaused by: {self.cause!r}" if self.cause else self.message + return f"{self.message}\nCaused by: {self.cause!r}" if self.cause else self.message # type: ignore[attr-defined] class UnknownConnectionTypeError(AgentMemoryConnectionError): @@ -87,7 +87,7 @@ class DataNotFoundError(DataRetrievalError): message (str, optional): Human-readable message providing more detail. If not provided, a default message is generated. """ - def __init__(self, vector_id, message=None) -> None: + def __init__(self, vector_id, message=None) -> None: # type: ignore[no-untyped-def] message = message or f"Requested data for vector ID {vector_id} was not found." super().__init__(message) self.vector_id = vector_id diff --git a/dimos/hardware/camera/module.py b/dimos/hardware/camera/module.py index f5ef549bac..43e08864a7 100644 --- a/dimos/hardware/camera/module.py +++ b/dimos/hardware/camera/module.py @@ -17,13 +17,13 @@ import queue import time -from dimos_lcm.sensor_msgs import CameraInfo +from dimos_lcm.sensor_msgs import CameraInfo # type: ignore[import-untyped] import reactivex as rx from reactivex import operators as ops from reactivex.observable import Observable from dimos import spec -from dimos.agents2 import Output, Reducer, Stream, skill +from dimos.agents2 import Output, Reducer, Stream, skill # type: ignore[attr-defined] from dimos.core import Module, ModuleConfig, Out, rpc from dimos.hardware.camera.spec import CameraHardware from dimos.hardware.camera.webcam import Webcam @@ -32,7 +32,7 @@ from dimos.msgs.sensor_msgs.Image import Image, sharpness_barrier -def default_transform(): +def default_transform(): # type: ignore[no-untyped-def] return Transform( translation=Vector3(0.0, 0.0, 0.0), rotation=Quaternion(0.0, 0.0, 0.0, 1.0), @@ -45,36 +45,36 @@ def default_transform(): class CameraModuleConfig(ModuleConfig): frame_id: str = "camera_link" transform: Transform | None = field(default_factory=default_transform) - hardware: Callable[[], CameraHardware] | CameraHardware = Webcam + hardware: Callable[[], CameraHardware] | CameraHardware = Webcam # type: ignore[type-arg] frequency: float = 5.0 class CameraModule(Module, spec.Camera): - image: Out[Image] = None - camera_info: Out[CameraInfo] = None + image: Out[Image] = None # type: ignore[assignment] + camera_info: Out[CameraInfo] = None # type: ignore[assignment] - hardware: Callable[[], CameraHardware] | CameraHardware = None + hardware: Callable[[], CameraHardware] | CameraHardware = None # type: ignore[assignment, type-arg] _skill_stream: Observable[Image] | None = None default_config = CameraModuleConfig - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(*args, **kwargs) @property def hardware_camera_info(self) -> CameraInfo: - return self.hardware.camera_info + return self.hardware.camera_info # type: ignore[union-attr] @rpc - def start(self) -> str: - if callable(self.config.hardware): - self.hardware = self.config.hardware() + def start(self) -> str: # type: ignore[return] + if callable(self.config.hardware): # type: ignore[attr-defined] + self.hardware = self.config.hardware() # type: ignore[attr-defined] else: - self.hardware = self.config.hardware + self.hardware = self.config.hardware # type: ignore[attr-defined] self._disposables.add(self.camera_info_stream().subscribe(self.publish_info)) - stream = self.hardware.image_stream().pipe(sharpness_barrier(self.config.frequency)) + stream = self.hardware.image_stream().pipe(sharpness_barrier(self.config.frequency)) # type: ignore[attr-defined, union-attr] self._disposables.add(stream.subscribe(self.image.publish)) @rpc @@ -83,21 +83,21 @@ def stop(self) -> None: self.hardware.stop() super().stop() - @skill(stream=Stream.passive, output=Output.image, reducer=Reducer.latest) - def video_stream(self) -> Image: + @skill(stream=Stream.passive, output=Output.image, reducer=Reducer.latest) # type: ignore[arg-type] + def video_stream(self) -> Image: # type: ignore[misc] """implicit video stream skill""" - _queue = queue.Queue(maxsize=1) - self.hardware.image_stream().subscribe(_queue.put) + _queue = queue.Queue(maxsize=1) # type: ignore[var-annotated] + self.hardware.image_stream().subscribe(_queue.put) # type: ignore[union-attr] yield from iter(_queue.get, None) def publish_info(self, camera_info: CameraInfo) -> None: - self.camera_info.publish(camera_info) + self.camera_info.publish(camera_info) # type: ignore[no-untyped-call] - if self.config.transform is None: + if self.config.transform is None: # type: ignore[attr-defined] return - camera_link = self.config.transform + camera_link = self.config.transform # type: ignore[attr-defined] camera_link.ts = camera_info.ts camera_optical = Transform( translation=Vector3(0.0, 0.0, 0.0), @@ -110,9 +110,9 @@ def publish_info(self, camera_info: CameraInfo) -> None: self.tf.publish(camera_link, camera_optical) def camera_info_stream(self, frequency: float = 1.0) -> Observable[CameraInfo]: - def camera_info(_) -> CameraInfo: - self.hardware.camera_info.ts = time.time() - return self.hardware.camera_info + def camera_info(_) -> CameraInfo: # type: ignore[no-untyped-def] + self.hardware.camera_info.ts = time.time() # type: ignore[union-attr] + return self.hardware.camera_info # type: ignore[union-attr] return rx.interval(1.0 / frequency).pipe(ops.map(camera_info)) diff --git a/dimos/hardware/camera/spec.py b/dimos/hardware/camera/spec.py index b9722d6cd2..59334c6f82 100644 --- a/dimos/hardware/camera/spec.py +++ b/dimos/hardware/camera/spec.py @@ -15,11 +15,11 @@ from abc import ABC, abstractmethod, abstractproperty from typing import Generic, Protocol, TypeVar -from dimos_lcm.sensor_msgs import CameraInfo +from dimos_lcm.sensor_msgs import CameraInfo # type: ignore[import-untyped] from reactivex.observable import Observable from dimos.msgs.sensor_msgs import Image -from dimos.protocol.service import Configurable +from dimos.protocol.service import Configurable # type: ignore[attr-defined] class CameraConfig(Protocol): diff --git a/dimos/hardware/camera/webcam.py b/dimos/hardware/camera/webcam.py index 0f68989002..6c9abb566d 100644 --- a/dimos/hardware/camera/webcam.py +++ b/dimos/hardware/camera/webcam.py @@ -19,7 +19,7 @@ from typing import Literal import cv2 -from dimos_lcm.sensor_msgs import CameraInfo +from dimos_lcm.sensor_msgs import CameraInfo # type: ignore[import-untyped] from reactivex import create from reactivex.observable import Observable @@ -43,7 +43,7 @@ class WebcamConfig(CameraConfig): class Webcam(CameraHardware[WebcamConfig]): default_config = WebcamConfig - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(*args, **kwargs) self._capture = None self._capture_thread = None @@ -54,13 +54,13 @@ def __init__(self, *args, **kwargs) -> None: def image_stream(self) -> Observable[Image]: """Create an observable that starts/stops camera on subscription""" - def subscribe(observer, scheduler=None): + def subscribe(observer, scheduler=None): # type: ignore[no-untyped-def] # Store the observer so emit() can use it self._observer = observer # Start the camera when someone subscribes try: - self.start() + self.start() # type: ignore[no-untyped-call] except Exception as e: observer.on_error(e) return @@ -74,23 +74,23 @@ def dispose() -> None: return backpressure(create(subscribe)) - def start(self): + def start(self): # type: ignore[no-untyped-def] if self._capture_thread and self._capture_thread.is_alive(): return # Open the video capture - self._capture = cv2.VideoCapture(self.config.camera_index) - if not self._capture.isOpened(): + self._capture = cv2.VideoCapture(self.config.camera_index) # type: ignore[assignment] + if not self._capture.isOpened(): # type: ignore[attr-defined] raise RuntimeError(f"Failed to open camera {self.config.camera_index}") # Set camera properties - self._capture.set(cv2.CAP_PROP_FRAME_WIDTH, self.config.frame_width) - self._capture.set(cv2.CAP_PROP_FRAME_HEIGHT, self.config.frame_height) + self._capture.set(cv2.CAP_PROP_FRAME_WIDTH, self.config.frame_width) # type: ignore[attr-defined] + self._capture.set(cv2.CAP_PROP_FRAME_HEIGHT, self.config.frame_height) # type: ignore[attr-defined] # Clear stop event and start the capture thread self._stop_event.clear() - self._capture_thread = threading.Thread(target=self._capture_loop, daemon=True) - self._capture_thread.start() + self._capture_thread = threading.Thread(target=self._capture_loop, daemon=True) # type: ignore[assignment] + self._capture_thread.start() # type: ignore[attr-defined] def stop(self) -> None: """Stop capturing frames""" @@ -106,7 +106,7 @@ def stop(self) -> None: self._capture.release() self._capture = None - def _frame(self, frame: str): + def _frame(self, frame: str): # type: ignore[no-untyped-def] if not self.config.frame_id_prefix: return frame else: @@ -114,7 +114,7 @@ def _frame(self, frame: str): def capture_frame(self) -> Image: # Read frame - ret, frame = self._capture.read() + ret, frame = self._capture.read() # type: ignore[attr-defined] if not ret: raise RuntimeError(f"Failed to read frame from camera {self.config.camera_index}") diff --git a/dimos/hardware/camera/zed/__init__.py b/dimos/hardware/camera/zed/__init__.py index d7b70a1319..6321ded4bd 100644 --- a/dimos/hardware/camera/zed/__init__.py +++ b/dimos/hardware/camera/zed/__init__.py @@ -20,7 +20,7 @@ # Check if ZED SDK is available try: - import pyzed.sl as sl + import pyzed.sl as sl # type: ignore[import-not-found] HAS_ZED_SDK = True except ImportError: @@ -31,14 +31,14 @@ from dimos.hardware.camera.zed.camera import ZEDCamera, ZEDModule else: # Provide stub classes when SDK is not available - class ZEDCamera: - def __init__(self, *args, **kwargs) -> None: + class ZEDCamera: # type: ignore[no-redef] + def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def] raise ImportError( "ZED SDK not installed. Please install pyzed package to use ZED camera functionality." ) - class ZEDModule: - def __init__(self, *args, **kwargs) -> None: + class ZEDModule: # type: ignore[no-redef] + def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def] raise ImportError( "ZED SDK not installed. Please install pyzed package to use ZED camera functionality." ) diff --git a/dimos/hardware/camera/zed/camera.py b/dimos/hardware/camera/zed/camera.py index fdcd93f731..bfaee3884c 100644 --- a/dimos/hardware/camera/zed/camera.py +++ b/dimos/hardware/camera/zed/camera.py @@ -16,10 +16,10 @@ from typing import Any import cv2 -from dimos_lcm.sensor_msgs import CameraInfo +from dimos_lcm.sensor_msgs import CameraInfo # type: ignore[import-untyped] import numpy as np -import open3d as o3d -import pyzed.sl as sl +import open3d as o3d # type: ignore[import-untyped] +import pyzed.sl as sl # type: ignore[import-not-found] from reactivex import interval from dimos.core import Module, Out, rpc @@ -37,7 +37,7 @@ class ZEDCamera: """ZED Camera capture node with neural depth processing.""" - def __init__( + def __init__( # type: ignore[no-untyped-def] self, camera_id: int = 0, resolution: sl.RESOLUTION = sl.RESOLUTION.HD720, @@ -278,7 +278,7 @@ def get_imu_data(self) -> dict[str, Any] | None: def capture_frame( self, - ) -> tuple[np.ndarray | None, np.ndarray | None, np.ndarray | None]: + ) -> tuple[np.ndarray | None, np.ndarray | None, np.ndarray | None]: # type: ignore[type-arg] """ Capture a frame from ZED camera. @@ -373,7 +373,7 @@ def capture_pointcloud(self) -> o3d.geometry.PointCloud | None: def capture_frame_with_pose( self, - ) -> tuple[np.ndarray | None, np.ndarray | None, np.ndarray | None, dict[str, Any] | None]: + ) -> tuple[np.ndarray | None, np.ndarray | None, np.ndarray | None, dict[str, Any] | None]: # type: ignore[type-arg] """ Capture a frame with synchronized pose data. @@ -485,11 +485,11 @@ def get_camera_info(self) -> dict[str, Any]: logger.error(f"Error getting camera info: {e}") return {} - def calculate_intrinsics(self): + def calculate_intrinsics(self): # type: ignore[no-untyped-def] """Calculate camera intrinsics from ZED calibration.""" info = self.get_camera_info() if not info: - return super().calculate_intrinsics() + return super().calculate_intrinsics() # type: ignore[misc] left_cam = info.get("left_cam", {}) resolution = info.get("resolution", {}) @@ -504,7 +504,7 @@ def calculate_intrinsics(self): "resolution_height": resolution.get("height", 0), } - def __enter__(self): + def __enter__(self): # type: ignore[no-untyped-def] """Context manager entry.""" if not self.open(): raise RuntimeError("Failed to open ZED camera") @@ -532,12 +532,12 @@ class ZEDModule(Module): """ # Define LCM outputs - color_image: Out[Image] = None - depth_image: Out[Image] = None - camera_info: Out[CameraInfo] = None - pose: Out[PoseStamped] = None + color_image: Out[Image] = None # type: ignore[assignment] + depth_image: Out[Image] = None # type: ignore[assignment] + camera_info: Out[CameraInfo] = None # type: ignore[assignment] + pose: Out[PoseStamped] = None # type: ignore[assignment] - def __init__( + def __init__( # type: ignore[no-untyped-def] self, camera_id: int = 0, resolution: str = "HD720", @@ -616,7 +616,7 @@ def start(self) -> None: try: # Initialize ZED camera - self.zed_camera = ZEDCamera( + self.zed_camera = ZEDCamera( # type: ignore[assignment] camera_id=self.camera_id, resolution=self.resolution, depth_mode=self.depth_mode, @@ -624,13 +624,13 @@ def start(self) -> None: ) # Open camera - if not self.zed_camera.open(): + if not self.zed_camera.open(): # type: ignore[attr-defined] logger.error("Failed to open ZED camera") return # Enable tracking if requested if self.enable_tracking: - success = self.zed_camera.enable_positional_tracking( + success = self.zed_camera.enable_positional_tracking( # type: ignore[attr-defined] enable_imu_fusion=self.enable_imu_fusion, set_floor_as_origin=self.set_floor_as_origin, enable_pose_smoothing=True, @@ -647,7 +647,7 @@ def start(self) -> None: self._running = True publish_interval = 1.0 / self.publish_rate - self._subscription = interval(publish_interval).subscribe( + self._subscription = interval(publish_interval).subscribe( # type: ignore[assignment] lambda _: self._capture_and_publish() ) @@ -721,7 +721,7 @@ def _capture_and_publish(self) -> None: except Exception as e: logger.error(f"Error in capture and publish: {e}") - def _publish_color_image(self, image: np.ndarray, header: Header) -> None: + def _publish_color_image(self, image: np.ndarray, header: Header) -> None: # type: ignore[type-arg] """Publish color image as LCM message.""" try: # Convert BGR to RGB if needed @@ -738,12 +738,12 @@ def _publish_color_image(self, image: np.ndarray, header: Header) -> None: ts=header.ts, ) - self.color_image.publish(msg) + self.color_image.publish(msg) # type: ignore[no-untyped-call] except Exception as e: logger.error(f"Error publishing color image: {e}") - def _publish_depth_image(self, depth: np.ndarray, header: Header) -> None: + def _publish_depth_image(self, depth: np.ndarray, header: Header) -> None: # type: ignore[type-arg] """Publish depth image as LCM message.""" try: # Depth is float32 in meters @@ -753,7 +753,7 @@ def _publish_depth_image(self, depth: np.ndarray, header: Header) -> None: frame_id=header.frame_id, ts=header.ts, ) - self.depth_image.publish(msg) + self.depth_image.publish(msg) # type: ignore[no-untyped-call] except Exception as e: logger.error(f"Error publishing depth image: {e}") @@ -761,7 +761,7 @@ def _publish_depth_image(self, depth: np.ndarray, header: Header) -> None: def _publish_camera_info(self) -> None: """Publish camera calibration information.""" try: - info = self.zed_camera.get_camera_info() + info = self.zed_camera.get_camera_info() # type: ignore[attr-defined] if not info: return @@ -831,7 +831,7 @@ def _publish_camera_info(self) -> None: binning_y=0, ) - self.camera_info.publish(msg) + self.camera_info.publish(msg) # type: ignore[no-untyped-call] except Exception as e: logger.error(f"Error publishing camera info: {e}") @@ -844,7 +844,7 @@ def _publish_pose(self, pose_data: dict[str, Any], header: Header) -> None: # Create PoseStamped message msg = PoseStamped(ts=header.ts, position=position, orientation=rotation) - self.pose.publish(msg) + self.pose.publish(msg) # type: ignore[no-untyped-call] # Publish TF transform camera_tf = Transform( diff --git a/dimos/hardware/end_effector.py b/dimos/hardware/end_effector.py index 1c5eb08281..aaa2ff21d8 100644 --- a/dimos/hardware/end_effector.py +++ b/dimos/hardware/end_effector.py @@ -14,8 +14,8 @@ class EndEffector: - def __init__(self, effector_type=None) -> None: + def __init__(self, effector_type=None) -> None: # type: ignore[no-untyped-def] self.effector_type = effector_type - def get_effector_type(self): + def get_effector_type(self): # type: ignore[no-untyped-def] return self.effector_type diff --git a/dimos/hardware/fake_zed_module.py b/dimos/hardware/fake_zed_module.py index c4c46c33b3..9421d94478 100644 --- a/dimos/hardware/fake_zed_module.py +++ b/dimos/hardware/fake_zed_module.py @@ -20,7 +20,7 @@ import functools import logging -from dimos_lcm.sensor_msgs import CameraInfo +from dimos_lcm.sensor_msgs import CameraInfo # type: ignore[import-untyped] import numpy as np from dimos.core import Module, Out, rpc @@ -40,12 +40,12 @@ class FakeZEDModule(Module): """ # Define LCM outputs (same as ZEDModule) - color_image: Out[Image] = None - depth_image: Out[Image] = None - camera_info: Out[CameraInfo] = None - pose: Out[PoseStamped] = None + color_image: Out[Image] = None # type: ignore[assignment] + depth_image: Out[Image] = None # type: ignore[assignment] + camera_info: Out[CameraInfo] = None # type: ignore[assignment] + pose: Out[PoseStamped] = None # type: ignore[assignment] - def __init__(self, recording_path: str, frame_id: str = "zed_camera", **kwargs) -> None: + def __init__(self, recording_path: str, frame_id: str = "zed_camera", **kwargs) -> None: # type: ignore[no-untyped-def] """ Initialize FakeZEDModule with recording path. @@ -65,11 +65,11 @@ def __init__(self, recording_path: str, frame_id: str = "zed_camera", **kwargs) logger.info(f"FakeZEDModule initialized with recording: {self.recording_path}") @functools.cache - def _get_color_stream(self): + def _get_color_stream(self): # type: ignore[no-untyped-def] """Get cached color image stream.""" logger.info(f"Loading color image stream from {self.recording_path}/color") - def image_autocast(x): + def image_autocast(x): # type: ignore[no-untyped-def] """Convert raw numpy array to Image.""" if isinstance(x, np.ndarray): return Image(data=x, format=ImageFormat.RGB) @@ -81,11 +81,11 @@ def image_autocast(x): return color_replay.stream() @functools.cache - def _get_depth_stream(self): + def _get_depth_stream(self): # type: ignore[no-untyped-def] """Get cached depth image stream.""" logger.info(f"Loading depth image stream from {self.recording_path}/depth") - def depth_autocast(x): + def depth_autocast(x): # type: ignore[no-untyped-def] """Convert raw numpy array to depth Image.""" if isinstance(x, np.ndarray): # Depth images are float32 @@ -98,11 +98,11 @@ def depth_autocast(x): return depth_replay.stream() @functools.cache - def _get_pose_stream(self): + def _get_pose_stream(self): # type: ignore[no-untyped-def] """Get cached pose stream.""" logger.info(f"Loading pose stream from {self.recording_path}/pose") - def pose_autocast(x): + def pose_autocast(x): # type: ignore[no-untyped-def] """Convert raw pose dict to PoseStamped.""" if isinstance(x, dict): import time @@ -120,11 +120,11 @@ def pose_autocast(x): return pose_replay.stream() @functools.cache - def _get_camera_info_stream(self): + def _get_camera_info_stream(self): # type: ignore[no-untyped-def] """Get cached camera info stream.""" logger.info(f"Loading camera info stream from {self.recording_path}/camera_info") - def camera_info_autocast(x): + def camera_info_autocast(x): # type: ignore[no-untyped-def] """Convert raw camera info dict to CameraInfo message.""" if isinstance(x, dict): # Extract calibration parameters @@ -214,7 +214,7 @@ def start(self) -> None: try: # Color image stream unsub = self._get_color_stream().subscribe( - lambda msg: self.color_image.publish(msg) if self._running else None + lambda msg: self.color_image.publish(msg) if self._running else None # type: ignore[no-untyped-call] ) self._disposables.add(unsub) logger.info("Started color image replay stream") @@ -224,7 +224,7 @@ def start(self) -> None: try: # Depth image stream unsub = self._get_depth_stream().subscribe( - lambda msg: self.depth_image.publish(msg) if self._running else None + lambda msg: self.depth_image.publish(msg) if self._running else None # type: ignore[no-untyped-call] ) self._disposables.add(unsub) logger.info("Started depth image replay stream") @@ -244,7 +244,7 @@ def start(self) -> None: try: # Camera info stream unsub = self._get_camera_info_stream().subscribe( - lambda msg: self.camera_info.publish(msg) if self._running else None + lambda msg: self.camera_info.publish(msg) if self._running else None # type: ignore[no-untyped-call] ) self._disposables.add(unsub) logger.info("Started camera info replay stream") @@ -262,10 +262,10 @@ def stop(self) -> None: super().stop() - def _publish_pose(self, msg) -> None: + def _publish_pose(self, msg) -> None: # type: ignore[no-untyped-def] """Publish pose and TF transform.""" if msg: - self.pose.publish(msg) + self.pose.publish(msg) # type: ignore[no-untyped-call] # Publish TF transform from world to camera import time diff --git a/dimos/hardware/gstreamer_camera.py b/dimos/hardware/gstreamer_camera.py index 38ede23ee1..0c0da5e464 100644 --- a/dimos/hardware/gstreamer_camera.py +++ b/dimos/hardware/gstreamer_camera.py @@ -29,11 +29,11 @@ if "/usr/lib/python3/dist-packages" not in sys.path: sys.path.insert(0, "/usr/lib/python3/dist-packages") -import gi +import gi # type: ignore[import-not-found] gi.require_version("Gst", "1.0") gi.require_version("GstApp", "1.0") -from gi.repository import GLib, Gst +from gi.repository import GLib, Gst # type: ignore[import-not-found] logger = setup_logger("dimos.hardware.gstreamer_camera", level=logging.INFO) @@ -43,9 +43,9 @@ class GstreamerCameraModule(Module): """Module that captures frames from a remote camera using GStreamer TCP with absolute timestamps.""" - video: Out[Image] = None + video: Out[Image] = None # type: ignore[assignment] - def __init__( + def __init__( # type: ignore[no-untyped-def] self, host: str = "localhost", port: int = 5000, @@ -120,8 +120,8 @@ def _connect(self) -> None: return try: - self._create_pipeline() - self._start_pipeline() + self._create_pipeline() # type: ignore[no-untyped-call] + self._start_pipeline() # type: ignore[no-untyped-call] self.running = True logger.info(f"GStreamer TCP camera module connected to {self.host}:{self.port}") except Exception as e: @@ -165,7 +165,7 @@ def _handle_disconnect(self) -> None: logger.warning(f"Disconnected from {self.host}:{self.port}") self._schedule_reconnect() - def _create_pipeline(self): + def _create_pipeline(self): # type: ignore[no-untyped-def] # TCP client source with Matroska demuxer to extract absolute timestamps pipeline_str = f""" tcpclientsrc host={self.host} port={self.port} ! @@ -179,39 +179,39 @@ def _create_pipeline(self): try: self.pipeline = Gst.parse_launch(pipeline_str) - self.appsink = self.pipeline.get_by_name("sink") - self.appsink.connect("new-sample", self._on_new_sample) + self.appsink = self.pipeline.get_by_name("sink") # type: ignore[attr-defined] + self.appsink.connect("new-sample", self._on_new_sample) # type: ignore[attr-defined] except Exception as e: logger.error(f"Failed to create GStreamer pipeline: {e}") raise - def _start_pipeline(self): + def _start_pipeline(self): # type: ignore[no-untyped-def] """Start the GStreamer pipeline and main loop.""" self.main_loop = GLib.MainLoop() # Start the pipeline - ret = self.pipeline.set_state(Gst.State.PLAYING) + ret = self.pipeline.set_state(Gst.State.PLAYING) # type: ignore[attr-defined] if ret == Gst.StateChangeReturn.FAILURE: logger.error("Unable to set the pipeline to playing state") raise RuntimeError("Failed to start GStreamer pipeline") # Run the main loop in a separate thread - self.main_loop_thread = threading.Thread(target=self._run_main_loop) - self.main_loop_thread.daemon = True - self.main_loop_thread.start() + self.main_loop_thread = threading.Thread(target=self._run_main_loop) # type: ignore[assignment] + self.main_loop_thread.daemon = True # type: ignore[attr-defined] + self.main_loop_thread.start() # type: ignore[attr-defined] # Set up bus message handling - bus = self.pipeline.get_bus() + bus = self.pipeline.get_bus() # type: ignore[attr-defined] bus.add_signal_watch() bus.connect("message", self._on_bus_message) def _run_main_loop(self) -> None: try: - self.main_loop.run() + self.main_loop.run() # type: ignore[attr-defined] except Exception as e: logger.error(f"Main loop error: {e}") - def _on_bus_message(self, bus, message) -> None: + def _on_bus_message(self, bus, message) -> None: # type: ignore[no-untyped-def] t = message.type if t == Gst.MessageType.EOS: @@ -230,7 +230,7 @@ def _on_bus_message(self, bus, message) -> None: if new_state == Gst.State.PLAYING: logger.info("Pipeline is now playing - connected to TCP server") - def _on_new_sample(self, appsink): + def _on_new_sample(self, appsink): # type: ignore[no-untyped-def] """Handle new video samples from the appsink.""" sample = appsink.emit("pull-sample") if sample is None: @@ -286,7 +286,7 @@ def _on_new_sample(self, appsink): # Publish the image if self.video and self.running: - self.video.publish(image_msg) + self.video.publish(image_msg) # type: ignore[no-untyped-call] # Log statistics periodically self.frame_count += 1 diff --git a/dimos/hardware/gstreamer_camera_test_script.py b/dimos/hardware/gstreamer_camera_test_script.py index f815579c0d..e71123e229 100755 --- a/dimos/hardware/gstreamer_camera_test_script.py +++ b/dimos/hardware/gstreamer_camera_test_script.py @@ -58,7 +58,7 @@ def main() -> None: logging.getLogger().setLevel(logging.DEBUG) # Initialize LCM - pubsub.lcm.autoconf() + pubsub.lcm.autoconf() # type: ignore[attr-defined] # Start dimos logger.info("Starting dimos...") @@ -66,7 +66,7 @@ def main() -> None: # Deploy the GStreamer camera module logger.info(f"Deploying GStreamer TCP camera module (connecting to {args.host}:{args.port})...") - camera = dimos.deploy( + camera = dimos.deploy( # type: ignore[attr-defined] GstreamerCameraModule, host=args.host, port=args.port, @@ -82,7 +82,7 @@ def main() -> None: last_log_time = [time.time()] first_timestamp = [None] - def on_frame(msg) -> None: + def on_frame(msg) -> None: # type: ignore[no-untyped-def] frame_count[0] += 1 current_time = time.time() diff --git a/dimos/hardware/gstreamer_sender.py b/dimos/hardware/gstreamer_sender.py index ce7c1d6145..4f10c8eb76 100755 --- a/dimos/hardware/gstreamer_sender.py +++ b/dimos/hardware/gstreamer_sender.py @@ -24,11 +24,11 @@ if "/usr/lib/python3/dist-packages" not in sys.path: sys.path.insert(0, "/usr/lib/python3/dist-packages") -import gi +import gi # type: ignore[import-not-found] gi.require_version("Gst", "1.0") gi.require_version("GstVideo", "1.0") -from gi.repository import GLib, Gst +from gi.repository import GLib, Gst # type: ignore[import-not-found] # Initialize GStreamer Gst.init(None) @@ -85,7 +85,7 @@ def __init__( self.start_time = None self.frame_count = 0 - def create_pipeline(self): + def create_pipeline(self): # type: ignore[no-untyped-def] """Create the GStreamer pipeline with TCP server sink.""" # Create pipeline @@ -93,8 +93,8 @@ def create_pipeline(self): # Create elements self.videosrc = Gst.ElementFactory.make("v4l2src", "source") - self.videosrc.set_property("device", self.device) - self.videosrc.set_property("do-timestamp", True) + self.videosrc.set_property("device", self.device) # type: ignore[attr-defined] + self.videosrc.set_property("do-timestamp", True) # type: ignore[attr-defined] logger.info(f"Using camera device: {self.device}") # Create caps filter for video format @@ -120,17 +120,17 @@ def create_pipeline(self): # H264 encoder self.encoder = Gst.ElementFactory.make("x264enc", "encoder") - self.encoder.set_property("tune", "zerolatency") - self.encoder.set_property("bitrate", self.bitrate) - self.encoder.set_property("key-int-max", 30) + self.encoder.set_property("tune", "zerolatency") # type: ignore[attr-defined] + self.encoder.set_property("bitrate", self.bitrate) # type: ignore[attr-defined] + self.encoder.set_property("key-int-max", 30) # type: ignore[attr-defined] # H264 parser h264parse = Gst.ElementFactory.make("h264parse", "parser") # Use matroskamux which preserves timestamps better self.mux = Gst.ElementFactory.make("matroskamux", "mux") - self.mux.set_property("streamable", True) - self.mux.set_property("writing-app", "gstreamer-tcp-sender") + self.mux.set_property("streamable", True) # type: ignore[attr-defined] + self.mux.set_property("writing-app", "gstreamer-tcp-sender") # type: ignore[attr-defined] # TCP server sink tcpserversink = Gst.ElementFactory.make("tcpserversink", "sink") @@ -139,18 +139,18 @@ def create_pipeline(self): tcpserversink.set_property("sync", False) # Add elements to pipeline - self.pipeline.add(self.videosrc) - self.pipeline.add(capsfilter) - self.pipeline.add(videoconvert) + self.pipeline.add(self.videosrc) # type: ignore[attr-defined] + self.pipeline.add(capsfilter) # type: ignore[attr-defined] + self.pipeline.add(videoconvert) # type: ignore[attr-defined] if videocrop: - self.pipeline.add(videocrop) - self.pipeline.add(self.encoder) - self.pipeline.add(h264parse) - self.pipeline.add(self.mux) - self.pipeline.add(tcpserversink) + self.pipeline.add(videocrop) # type: ignore[attr-defined] + self.pipeline.add(self.encoder) # type: ignore[attr-defined] + self.pipeline.add(h264parse) # type: ignore[attr-defined] + self.pipeline.add(self.mux) # type: ignore[attr-defined] + self.pipeline.add(tcpserversink) # type: ignore[attr-defined] # Link elements - if not self.videosrc.link(capsfilter): + if not self.videosrc.link(capsfilter): # type: ignore[attr-defined] raise RuntimeError("Failed to link source to capsfilter") if not capsfilter.link(videoconvert): raise RuntimeError("Failed to link capsfilter to videoconvert") @@ -165,11 +165,11 @@ def create_pipeline(self): if not videoconvert.link(self.encoder): raise RuntimeError("Failed to link videoconvert to encoder") - if not self.encoder.link(h264parse): + if not self.encoder.link(h264parse): # type: ignore[attr-defined] raise RuntimeError("Failed to link encoder to h264parse") if not h264parse.link(self.mux): raise RuntimeError("Failed to link h264parse to mux") - if not self.mux.link(tcpserversink): + if not self.mux.link(tcpserversink): # type: ignore[attr-defined] raise RuntimeError("Failed to link mux to tcpserversink") # Add probe to inject absolute timestamps @@ -182,11 +182,11 @@ def create_pipeline(self): probe_pad.add_probe(Gst.PadProbeType.BUFFER, self._inject_absolute_timestamp, None) # Set up bus message handling - bus = self.pipeline.get_bus() + bus = self.pipeline.get_bus() # type: ignore[attr-defined] bus.add_signal_watch() bus.connect("message", self._on_bus_message) - def _inject_absolute_timestamp(self, pad, info, user_data): + def _inject_absolute_timestamp(self, pad, info, user_data): # type: ignore[no-untyped-def] buffer = info.get_buffer() if buffer: absolute_time = time.time() @@ -200,7 +200,7 @@ def _inject_absolute_timestamp(self, pad, info, user_data): self.frame_count += 1 return Gst.PadProbeReturn.OK - def _on_bus_message(self, bus, message) -> None: + def _on_bus_message(self, bus, message) -> None: # type: ignore[no-untyped-def] t = message.type if t == Gst.MessageType.EOS: @@ -220,22 +220,22 @@ def _on_bus_message(self, bus, message) -> None: f"Pipeline state changed: {old_state.value_nick} -> {new_state.value_nick}" ) - def start(self): + def start(self): # type: ignore[no-untyped-def] if self.running: logger.warning("Sender is already running") return logger.info("Creating TCP pipeline with absolute timestamps...") - self.create_pipeline() + self.create_pipeline() # type: ignore[no-untyped-call] logger.info("Starting pipeline...") - ret = self.pipeline.set_state(Gst.State.PLAYING) + ret = self.pipeline.set_state(Gst.State.PLAYING) # type: ignore[attr-defined] if ret == Gst.StateChangeReturn.FAILURE: logger.error("Failed to start pipeline") raise RuntimeError("Failed to start GStreamer pipeline") self.running = True - self.start_time = time.time() + self.start_time = time.time() # type: ignore[assignment] self.frame_count = 0 logger.info("TCP video sender started:") @@ -255,7 +255,7 @@ def start(self): self.main_loop = GLib.MainLoop() try: - self.main_loop.run() + self.main_loop.run() # type: ignore[attr-defined] except KeyboardInterrupt: logger.info("Interrupted by user") finally: @@ -340,7 +340,7 @@ def main() -> None: ) # Handle signals gracefully - def signal_handler(sig, frame) -> None: + def signal_handler(sig, frame) -> None: # type: ignore[no-untyped-def] logger.info(f"Received signal {sig}, shutting down...") sender.stop() sys.exit(0) @@ -349,7 +349,7 @@ def signal_handler(sig, frame) -> None: signal.signal(signal.SIGTERM, signal_handler) try: - sender.start() + sender.start() # type: ignore[no-untyped-call] except Exception as e: logger.error(f"Failed to start sender: {e}") sys.exit(1) diff --git a/dimos/hardware/piper_arm.py b/dimos/hardware/piper_arm.py index e6e9da693d..5d8a41545f 100644 --- a/dimos/hardware/piper_arm.py +++ b/dimos/hardware/piper_arm.py @@ -21,10 +21,10 @@ import time import tty -from dimos_lcm.geometry_msgs import Pose, Twist, Vector3 -import kinpy as kp +from dimos_lcm.geometry_msgs import Pose, Twist, Vector3 # type: ignore[import-untyped] +import kinpy as kp # type: ignore[import-not-found] import numpy as np -from piper_sdk import * # from the official Piper SDK +from piper_sdk import * # type: ignore[import-not-found] # from the official Piper SDK import pytest from reactivex.disposable import Disposable from scipy.spatial.transform import Rotation as R @@ -40,7 +40,7 @@ class PiperArm: def __init__(self, arm_name: str = "arm") -> None: - self.arm = C_PiperInterface_V2() # noqa: F405 + self.arm = C_PiperInterface_V2() # type: ignore[name-defined] # noqa: F405 self.arm.ConnectPort() self.resetArm() time.sleep(0.5) @@ -105,7 +105,7 @@ def softStop(self) -> None: self.arm.MotionCtrl_1(0x01, 0, 0) time.sleep(3) - def cmd_ee_pose_values(self, x, y, z, r, p, y_, line_mode: bool = False) -> None: + def cmd_ee_pose_values(self, x, y, z, r, p, y_, line_mode: bool = False) -> None: # type: ignore[no-untyped-def] """Command end-effector to target pose in space (position + Euler angles)""" factor = 1000 pose = [ @@ -137,7 +137,7 @@ def cmd_ee_pose(self, pose: Pose, line_mode: bool = False) -> None: line_mode, ) - def get_ee_pose(self): + def get_ee_pose(self): # type: ignore[no-untyped-def] """Return the current end-effector pose as Pose message with position in meters and quaternion orientation""" pose = self.arm.GetArmEndPoseMsgs() factor = 1000.0 @@ -158,7 +158,7 @@ def get_ee_pose(self): return Pose(position, orientation) - def cmd_gripper_ctrl(self, position, effort: float = 0.25) -> None: + def cmd_gripper_ctrl(self, position, effort: float = 0.25) -> None: # type: ignore[no-untyped-def] """Command end-effector gripper""" factor = 1000 position = position * factor * factor # meters @@ -245,7 +245,7 @@ def init_vel_controller(self) -> None: self.J_pinv = np.linalg.pinv(self.J) self.dt = 0.01 - def cmd_vel(self, x_dot, y_dot, z_dot, R_dot, P_dot, Y_dot) -> None: + def cmd_vel(self, x_dot, y_dot, z_dot, R_dot, P_dot, Y_dot) -> None: # type: ignore[no-untyped-def] joint_state = self.arm.GetArmJointMsgs().joint_state # print(f"[PiperArm] Current Joints (direct): {joint_state}", type(joint_state)) joint_angles = np.array( @@ -291,7 +291,7 @@ def cmd_vel(self, x_dot, y_dot, z_dot, R_dot, P_dot, Y_dot) -> None: time.sleep(self.dt) # print(f"[PiperArm] Moving to Joints to : {newq}") - def cmd_vel_ee(self, x_dot, y_dot, z_dot, RX_dot, PY_dot, YZ_dot) -> None: + def cmd_vel_ee(self, x_dot, y_dot, z_dot, RX_dot, PY_dot, YZ_dot) -> None: # type: ignore[no-untyped-def] factor = 1000 x_dot = x_dot * factor y_dot = y_dot * factor @@ -300,7 +300,7 @@ def cmd_vel_ee(self, x_dot, y_dot, z_dot, RX_dot, PY_dot, YZ_dot) -> None: PY_dot = PY_dot * factor YZ_dot = YZ_dot * factor - current_pose_msg = self.get_ee_pose() + current_pose_msg = self.get_ee_pose() # type: ignore[no-untyped-call] # Convert quaternion to euler angles quat = [ @@ -349,9 +349,9 @@ def disable(self) -> None: class VelocityController(Module): - cmd_vel: In[Twist] = None + cmd_vel: In[Twist] = None # type: ignore[assignment] - def __init__(self, arm, period: float = 0.01, *args, **kwargs) -> None: + def __init__(self, arm, period: float = 0.01, *args, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(*args, **kwargs) self.arm = arm self.period = period @@ -402,21 +402,21 @@ def control_loop() -> None: ] ) - J = self.chain.jacobian(q) + J = self.chain.jacobian(q) # type: ignore[attr-defined] self.J_pinv = np.linalg.pinv(J) dq = ( self.J_pinv @ np.array( [ - cmd_vel.linear.X, - cmd_vel.linear.y, - cmd_vel.linear.z, - cmd_vel.angular.x, - cmd_vel.angular.y, - cmd_vel.angular.z, + cmd_vel.linear.X, # type: ignore[attr-defined] + cmd_vel.linear.y, # type: ignore[attr-defined] + cmd_vel.linear.z, # type: ignore[attr-defined] + cmd_vel.angular.x, # type: ignore[attr-defined] + cmd_vel.angular.y, # type: ignore[attr-defined] + cmd_vel.angular.z, # type: ignore[attr-defined] ] ) - * self.dt + * self.dt # type: ignore[attr-defined] ) newq = q + dq @@ -433,8 +433,8 @@ def control_loop() -> None: ) time.sleep(self.period) - self._thread = threading.Thread(target=control_loop, daemon=True) - self._thread.start() + self._thread = threading.Thread(target=control_loop, daemon=True) # type: ignore[assignment] + self._thread.start() # type: ignore[attr-defined] @rpc def stop(self) -> None: @@ -445,7 +445,7 @@ def stop(self) -> None: def handle_cmd_vel(self, cmd_vel: Twist) -> None: self.latest_cmd = cmd_vel - self.last_cmd_time = time.time() + self.last_cmd_time = time.time() # type: ignore[assignment] @pytest.mark.tool @@ -453,7 +453,7 @@ def run_velocity_controller() -> None: lcmservice.autoconf() dimos = core.start(2) - velocity_controller = dimos.deploy(VelocityController, arm=arm, period=0.01) + velocity_controller = dimos.deploy(VelocityController, arm=arm, period=0.01) # type: ignore[attr-defined] velocity_controller.cmd_vel.transport = core.LCMTransport("/cmd_vel", Twist) velocity_controller.start() @@ -468,7 +468,7 @@ def run_velocity_controller() -> None: if __name__ == "__main__": arm = PiperArm() - def get_key(timeout: float = 0.1): + def get_key(timeout: float = 0.1): # type: ignore[no-untyped-def] """Non-blocking key reader for arrow keys.""" fd = sys.stdin.fileno() old_settings = termios.tcgetattr(fd) @@ -488,7 +488,7 @@ def get_key(timeout: float = 0.1): finally: termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) - def teleop_linear_vel(arm) -> None: + def teleop_linear_vel(arm) -> None: # type: ignore[no-untyped-def] print("Use arrow keys to control linear velocity (x/y/z). Press 'q' to quit.") print("Up/Down: +x/-x, Left/Right: +y/-y, 'w'/'s': +z/-z") x_dot, y_dot, z_dot = 0.0, 0.0, 0.0 diff --git a/dimos/hardware/sensor.py b/dimos/hardware/sensor.py index aa39f25ec6..1003981f2e 100644 --- a/dimos/hardware/sensor.py +++ b/dimos/hardware/sensor.py @@ -16,20 +16,20 @@ class AbstractSensor(ABC): - def __init__(self, sensor_type=None) -> None: + def __init__(self, sensor_type=None) -> None: # type: ignore[no-untyped-def] self.sensor_type = sensor_type @abstractmethod - def get_sensor_type(self): + def get_sensor_type(self): # type: ignore[no-untyped-def] """Return the type of sensor.""" pass @abstractmethod - def calculate_intrinsics(self): + def calculate_intrinsics(self): # type: ignore[no-untyped-def] """Calculate the sensor's intrinsics.""" pass @abstractmethod - def get_intrinsics(self): + def get_intrinsics(self): # type: ignore[no-untyped-def] """Return the sensor's intrinsics.""" pass diff --git a/dimos/manipulation/manip_aio_pipeline.py b/dimos/manipulation/manip_aio_pipeline.py index 164c7b1774..4a10147fba 100644 --- a/dimos/manipulation/manip_aio_pipeline.py +++ b/dimos/manipulation/manip_aio_pipeline.py @@ -28,7 +28,7 @@ import websockets from dimos.perception.common.utils import colorize_depth -from dimos.perception.detection2d.detic_2d_det import Detic2DDetector +from dimos.perception.detection2d.detic_2d_det import Detic2DDetector # type: ignore[import-untyped] from dimos.perception.grasp_generation.utils import draw_grasps_on_image from dimos.perception.object_detection_stream import ObjectDetectionStream from dimos.perception.pointcloud.pointcloud_filtering import PointcloudFiltering @@ -79,19 +79,19 @@ def __init__( self.grasp_loop_thread = None # Storage for grasp results and filtered objects - self.latest_grasps: list[dict] = [] # Simplified: just a list of grasps + self.latest_grasps: list[dict] = [] # type: ignore[type-arg] # Simplified: just a list of grasps self.grasps_consumed = False - self.latest_filtered_objects = [] + self.latest_filtered_objects = [] # type: ignore[var-annotated] self.latest_rgb_for_grasps = None # Store RGB image for grasp overlay self.grasp_lock = threading.Lock() # Track pending requests - simplified to single task - self.grasp_task: asyncio.Task | None = None + self.grasp_task: asyncio.Task | None = None # type: ignore[type-arg] # Reactive subjects for streaming filtered objects and grasps - self.filtered_objects_subject = rx.subject.Subject() - self.grasps_subject = rx.subject.Subject() - self.grasp_overlay_subject = rx.subject.Subject() # Add grasp overlay subject + self.filtered_objects_subject = rx.subject.Subject() # type: ignore[var-annotated] + self.grasps_subject = rx.subject.Subject() # type: ignore[var-annotated] + self.grasp_overlay_subject = rx.subject.Subject() # type: ignore[var-annotated] # Add grasp overlay subject # Initialize grasp client if enabled if self.enable_grasp_generation and self.grasp_server_url: @@ -109,7 +109,7 @@ def __init__( logger.info(f"Initialized ManipulationPipeline with confidence={min_confidence}") - def create_streams(self, zed_stream: rx.Observable) -> dict[str, rx.Observable]: + def create_streams(self, zed_stream: rx.Observable) -> dict[str, rx.Observable]: # type: ignore[type-arg] """ Create streams using exact old main logic. """ @@ -118,7 +118,7 @@ def create_streams(self, zed_stream: rx.Observable) -> dict[str, rx.Observable]: # RGB stream for object detection (from old main) video_stream = zed_frame_stream.pipe( - ops.map(lambda x: x.get("rgb") if x is not None else None), + ops.map(lambda x: x.get("rgb") if x is not None else None), # type: ignore[attr-defined] ops.filter(lambda x: x is not None), ops.share(), ) @@ -138,7 +138,7 @@ def create_streams(self, zed_stream: rx.Observable) -> dict[str, rx.Observable]: frame_lock = threading.Lock() # Subscribe to combined ZED frames (from old main) - def on_zed_frame(zed_data) -> None: + def on_zed_frame(zed_data) -> None: # type: ignore[no-untyped-def] nonlocal latest_rgb, latest_depth if zed_data is not None: with frame_lock: @@ -146,7 +146,7 @@ def on_zed_frame(zed_data) -> None: latest_depth = zed_data.get("depth") # Depth stream for point cloud filtering (from old main) - def get_depth_or_overlay(zed_data): + def get_depth_or_overlay(zed_data): # type: ignore[no-untyped-def] if zed_data is None: return None @@ -165,7 +165,7 @@ def get_depth_or_overlay(zed_data): ) # Process object detection results with point cloud filtering (from old main) - def on_detection_next(result) -> None: + def on_detection_next(result) -> None: # type: ignore[no-untyped-def] nonlocal latest_point_cloud_overlay if result.get("objects"): # Get latest RGB and depth frames @@ -190,9 +190,9 @@ def on_detection_next(result) -> None: # Create point cloud overlay visualization overlay_viz = create_point_cloud_overlay_visualization( - base_image=base_image, - objects=filtered_objects, - intrinsics=self.camera_intrinsics, + base_image=base_image, # type: ignore[arg-type] + objects=filtered_objects, # type: ignore[arg-type] + intrinsics=self.camera_intrinsics, # type: ignore[arg-type] ) # Store the overlay for the stream @@ -205,7 +205,7 @@ def on_detection_next(result) -> None: with frame_lock: self.latest_rgb_for_grasps = rgb.copy() - task = self.request_scene_grasps(filtered_objects) + task = self.request_scene_grasps(filtered_objects) # type: ignore[arg-type] if task: # Check for results after a delay def check_grasps_later() -> None: @@ -213,7 +213,7 @@ def check_grasps_later() -> None: # Wait for task to complete if hasattr(self, "grasp_task") and self.grasp_task: try: - self.grasp_task.result( + self.grasp_task.result( # type: ignore[call-arg] timeout=3.0 ) # Get result with timeout except Exception as e: @@ -226,7 +226,7 @@ def check_grasps_later() -> None: if grasps and hasattr(self, "latest_rgb_for_grasps"): # Create grasp overlay on the saved RGB image try: - bgr_image = cv2.cvtColor( + bgr_image = cv2.cvtColor( # type: ignore[call-overload] self.latest_rgb_for_grasps, cv2.COLOR_RGB2BGR ) result_bgr = draw_grasps_on_image( @@ -256,7 +256,7 @@ def check_grasps_later() -> None: with frame_lock: latest_point_cloud_overlay = None - def on_error(error) -> None: + def on_error(error) -> None: # type: ignore[no-untyped-def] logger.error(f"Error in stream: {error}") def on_completed() -> None: @@ -273,13 +273,13 @@ def start_subscriptions() -> None: time.sleep(2) # Give subscriptions time to start # Subscribe to object detection stream (from old main) - object_detector.get_stream().subscribe( + object_detector.get_stream().subscribe( # type: ignore[no-untyped-call] on_next=on_detection_next, on_error=on_error, on_completed=on_completed ) # Create visualization stream for web interface (from old main) - viz_stream = object_detector.get_stream().pipe( - ops.map(lambda x: x["viz_frame"] if x is not None else None), + viz_stream = object_detector.get_stream().pipe( # type: ignore[no-untyped-call] + ops.map(lambda x: x["viz_frame"] if x is not None else None), # type: ignore[index] ops.filter(lambda x: x is not None), ) @@ -295,7 +295,7 @@ def start_subscriptions() -> None: return { "detection_viz": viz_stream, "pointcloud_viz": depth_stream, - "objects": object_detector.get_stream().pipe(ops.map(lambda x: x.get("objects", []))), + "objects": object_detector.get_stream().pipe(ops.map(lambda x: x.get("objects", []))), # type: ignore[attr-defined, no-untyped-call] "filtered_objects": filtered_objects_stream, "grasps": grasps_stream, "grasp_overlay": grasp_overlay_stream, @@ -305,20 +305,20 @@ def _start_grasp_loop(self) -> None: """Start asyncio event loop in a background thread for WebSocket communication.""" def run_loop() -> None: - self.grasp_loop = asyncio.new_event_loop() + self.grasp_loop = asyncio.new_event_loop() # type: ignore[assignment] asyncio.set_event_loop(self.grasp_loop) - self.grasp_loop.run_forever() + self.grasp_loop.run_forever() # type: ignore[attr-defined] - self.grasp_loop_thread = threading.Thread(target=run_loop, daemon=True) - self.grasp_loop_thread.start() + self.grasp_loop_thread = threading.Thread(target=run_loop, daemon=True) # type: ignore[assignment] + self.grasp_loop_thread.start() # type: ignore[attr-defined] # Wait for loop to start while self.grasp_loop is None: time.sleep(0.01) async def _send_grasp_request( - self, points: np.ndarray, colors: np.ndarray | None - ) -> list[dict] | None: + self, points: np.ndarray, colors: np.ndarray | None # type: ignore[type-arg] + ) -> list[dict] | None: # type: ignore[type-arg] """Send grasp request to Dimensional Grasp server.""" try: # Comprehensive client-side validation to prevent server errors @@ -371,7 +371,7 @@ async def _send_grasp_request( # Clamp color values to valid range [0, 1] colors = np.clip(colors, 0.0, 1.0) - async with websockets.connect(self.grasp_server_url) as websocket: + async with websockets.connect(self.grasp_server_url) as websocket: # type: ignore[arg-type] request = { "points": points.tolist(), "colors": colors.tolist(), # Always send colors array @@ -417,7 +417,7 @@ async def _send_grasp_request( return None - def request_scene_grasps(self, objects: list[dict]) -> asyncio.Task | None: + def request_scene_grasps(self, objects: list[dict]) -> asyncio.Task | None: # type: ignore[type-arg] """Request grasps for entire scene by combining all object point clouds.""" if not self.grasp_loop or not objects: return None @@ -496,7 +496,7 @@ def request_scene_grasps(self, objects: list[dict]) -> asyncio.Task | None: logger.warning("Failed to create grasp task") return None - def get_latest_grasps(self, timeout: float = 5.0) -> list[dict] | None: + def get_latest_grasps(self, timeout: float = 5.0) -> list[dict] | None: # type: ignore[type-arg] """Get latest grasp results, waiting for new ones if current ones have been consumed.""" # Mark current grasps as consumed and get a reference with self.grasp_lock: @@ -523,7 +523,7 @@ def clear_grasps(self) -> None: with self.grasp_lock: self.latest_grasps = [] - def _prepare_colors(self, colors: np.ndarray | None) -> np.ndarray | None: + def _prepare_colors(self, colors: np.ndarray | None) -> np.ndarray | None: # type: ignore[type-arg] """Prepare colors array, converting from various formats if needed.""" if colors is None: return None @@ -533,7 +533,7 @@ def _prepare_colors(self, colors: np.ndarray | None) -> np.ndarray | None: return colors - def _convert_grasp_format(self, grasps: list[dict]) -> list[dict]: + def _convert_grasp_format(self, grasps: list[dict]) -> list[dict]: # type: ignore[type-arg] """Convert Grasp format to our visualization format.""" converted = [] @@ -557,7 +557,7 @@ def _convert_grasp_format(self, grasps: list[dict]) -> list[dict]: return converted - def _rotation_matrix_to_euler(self, rotation_matrix: np.ndarray) -> dict[str, float]: + def _rotation_matrix_to_euler(self, rotation_matrix: np.ndarray) -> dict[str, float]: # type: ignore[type-arg] """Convert rotation matrix to Euler angles (in radians).""" sy = np.sqrt(rotation_matrix[0, 0] ** 2 + rotation_matrix[1, 0] ** 2) diff --git a/dimos/manipulation/manip_aio_processer.py b/dimos/manipulation/manip_aio_processer.py index e0bfc73256..4b39d87233 100644 --- a/dimos/manipulation/manip_aio_processer.py +++ b/dimos/manipulation/manip_aio_processer.py @@ -27,7 +27,7 @@ combine_object_data, detection_results_to_object_data, ) -from dimos.perception.detection2d.detic_2d_det import Detic2DDetector +from dimos.perception.detection2d.detic_2d_det import Detic2DDetector # type: ignore[import-untyped] from dimos.perception.grasp_generation.grasp_generation import HostedGraspGenerator from dimos.perception.grasp_generation.utils import create_grasp_overlay from dimos.perception.pointcloud.pointcloud_filtering import PointcloudFiltering @@ -106,7 +106,7 @@ def __init__( self.grasp_generator = None if self.enable_grasp_generation: try: - self.grasp_generator = HostedGraspGenerator(server_url=grasp_server_url) + self.grasp_generator = HostedGraspGenerator(server_url=grasp_server_url) # type: ignore[arg-type] logger.info("Hosted grasp generator initialized successfully") except Exception as e: logger.error(f"Failed to initialize hosted grasp generator: {e}") @@ -119,7 +119,7 @@ def __init__( ) def process_frame( - self, rgb_image: np.ndarray, depth_image: np.ndarray, generate_grasps: bool | None = None + self, rgb_image: np.ndarray, depth_image: np.ndarray, generate_grasps: bool | None = None # type: ignore[type-arg] ) -> dict[str, Any]: """ Process a single RGB-D frame through the complete pipeline. @@ -164,7 +164,7 @@ def process_frame( segmentation_results = self.run_segmentation(rgb_image) results["segmentation2d_objects"] = segmentation_results.get("objects", []) results["segmentation_viz"] = segmentation_results.get("viz_frame") - segmentation_time = time.time() - step_start + segmentation_time = time.time() - step_start # type: ignore[assignment] # Step 3: Point Cloud Processing pointcloud_time = 0 @@ -178,7 +178,7 @@ def process_frame( detected_objects = self.run_pointcloud_filtering( rgb_image, depth_image, detection2d_objects ) - pointcloud_time += time.time() - step_start + pointcloud_time += time.time() - step_start # type: ignore[assignment] # Process segmentation objects if available segmentation_filtered_objects = [] @@ -187,11 +187,11 @@ def process_frame( segmentation_filtered_objects = self.run_pointcloud_filtering( rgb_image, depth_image, segmentation2d_objects ) - pointcloud_time += time.time() - step_start + pointcloud_time += time.time() - step_start # type: ignore[assignment] # Combine all objects using intelligent duplicate removal all_objects = combine_object_data( - detected_objects, segmentation_filtered_objects, overlap_threshold=0.8 + detected_objects, segmentation_filtered_objects, overlap_threshold=0.8 # type: ignore[arg-type] ) # Get full point cloud @@ -201,7 +201,7 @@ def process_frame( misc_start = time.time() misc_clusters, misc_voxel_grid = extract_and_cluster_misc_points( full_pcd, - all_objects, + all_objects, # type: ignore[arg-type] eps=0.03, min_points=100, enable_filtering=True, @@ -226,9 +226,9 @@ def process_frame( # Create visualizations results["pointcloud_viz"] = ( create_point_cloud_overlay_visualization( - base_image=base_image, - objects=all_objects, - intrinsics=self.camera_intrinsics, + base_image=base_image, # type: ignore[arg-type] + objects=all_objects, # type: ignore[arg-type] + intrinsics=self.camera_intrinsics, # type: ignore[arg-type] ) if all_objects else base_image @@ -236,9 +236,9 @@ def process_frame( results["detected_pointcloud_viz"] = ( create_point_cloud_overlay_visualization( - base_image=base_image, + base_image=base_image, # type: ignore[arg-type] objects=detected_objects, - intrinsics=self.camera_intrinsics, + intrinsics=self.camera_intrinsics, # type: ignore[arg-type] ) if detected_objects else base_image @@ -251,7 +251,7 @@ def process_frame( for i in range(len(misc_clusters)) ] results["misc_pointcloud_viz"] = overlay_point_clouds_on_image( - base_image=base_image, + base_image=base_image, # type: ignore[arg-type] point_clouds=misc_clusters, camera_intrinsics=self.camera_intrinsics, colors=cluster_colors, @@ -267,7 +267,7 @@ def process_frame( ) if should_generate_grasps and all_objects and full_pcd: - grasps = self.run_grasp_generation(all_objects, full_pcd) + grasps = self.run_grasp_generation(all_objects, full_pcd) # type: ignore[arg-type] results["grasps"] = grasps if grasps: results["grasp_overlay"] = create_grasp_overlay( @@ -295,7 +295,7 @@ def process_frame( return results - def run_object_detection(self, rgb_image: np.ndarray) -> dict[str, Any]: + def run_object_detection(self, rgb_image: np.ndarray) -> dict[str, Any]: # type: ignore[type-arg] """Run object detection on RGB image.""" try: # Convert RGB to BGR for Detic detector @@ -329,19 +329,19 @@ def run_object_detection(self, rgb_image: np.ndarray) -> dict[str, Any]: return {"objects": [], "viz_frame": rgb_image.copy()} def run_pointcloud_filtering( - self, rgb_image: np.ndarray, depth_image: np.ndarray, objects: list[dict] - ) -> list[dict]: + self, rgb_image: np.ndarray, depth_image: np.ndarray, objects: list[dict] # type: ignore[type-arg] + ) -> list[dict]: # type: ignore[type-arg] """Run point cloud filtering on detected objects.""" try: filtered_objects = self.pointcloud_filter.process_images( - rgb_image, depth_image, objects + rgb_image, depth_image, objects # type: ignore[arg-type] ) - return filtered_objects if filtered_objects else [] + return filtered_objects if filtered_objects else [] # type: ignore[return-value] except Exception as e: logger.error(f"Point cloud filtering failed: {e}") return [] - def run_segmentation(self, rgb_image: np.ndarray) -> dict[str, Any]: + def run_segmentation(self, rgb_image: np.ndarray) -> dict[str, Any]: # type: ignore[type-arg] """Run semantic segmentation on RGB image.""" if not self.segmenter: return {"objects": [], "viz_frame": rgb_image.copy()} @@ -351,7 +351,7 @@ def run_segmentation(self, rgb_image: np.ndarray) -> dict[str, Any]: bgr_image = cv2.cvtColor(rgb_image, cv2.COLOR_RGB2BGR) # Get segmentation results - masks, bboxes, track_ids, probs, names = self.segmenter.process_image(bgr_image) + masks, bboxes, track_ids, probs, names = self.segmenter.process_image(bgr_image) # type: ignore[no-untyped-call] # Convert to ObjectData format using utility function objects = detection_results_to_object_data( @@ -380,7 +380,7 @@ def run_segmentation(self, rgb_image: np.ndarray) -> dict[str, Any]: logger.error(f"Segmentation failed: {e}") return {"objects": [], "viz_frame": rgb_image.copy()} - def run_grasp_generation(self, filtered_objects: list[dict], full_pcd) -> list[dict] | None: + def run_grasp_generation(self, filtered_objects: list[dict], full_pcd) -> list[dict] | None: # type: ignore[no-untyped-def, type-arg] """Run grasp generation using the configured generator.""" if not self.grasp_generator: logger.warning("Grasp generation requested but no generator available") @@ -388,7 +388,7 @@ def run_grasp_generation(self, filtered_objects: list[dict], full_pcd) -> list[d try: # Generate grasps using the configured generator - grasps = self.grasp_generator.generate_grasps_from_objects(filtered_objects, full_pcd) + grasps = self.grasp_generator.generate_grasps_from_objects(filtered_objects, full_pcd) # type: ignore[arg-type] # Return parsed results directly (list of grasp dictionaries) return grasps diff --git a/dimos/manipulation/manipulation_history.py b/dimos/manipulation/manipulation_history.py index a77900ba30..4b82f06a74 100644 --- a/dimos/manipulation/manipulation_history.py +++ b/dimos/manipulation/manipulation_history.py @@ -256,7 +256,7 @@ def create_task_entry( self.add_entry(entry) return entry - def search(self, **kwargs) -> list[ManipulationHistoryEntry]: + def search(self, **kwargs) -> list[ManipulationHistoryEntry]: # type: ignore[no-untyped-def] """Flexible search method that can search by any field in ManipulationHistoryEntry using dot notation. This method supports dot notation to access nested fields. String values automatically use @@ -294,7 +294,7 @@ def search(self, **kwargs) -> list[ManipulationHistoryEntry]: return results - def _check_field_match(self, entry, field_path, value) -> bool: + def _check_field_match(self, entry, field_path, value) -> bool: # type: ignore[no-untyped-def] """Check if a field matches the value, with special handling for strings, collections and comparisons. For string values, we automatically use substring matching (contains). @@ -315,19 +315,19 @@ def _check_field_match(self, entry, field_path, value) -> bool: True if the field matches the value, False otherwise """ try: - field_value = self._get_value_by_path(entry, field_path) + field_value = self._get_value_by_path(entry, field_path) # type: ignore[no-untyped-call] # Handle comparison operators for timestamps and numbers if isinstance(value, tuple) and len(value) == 2: op, compare_value = value if op == ">": - return field_value > compare_value + return field_value > compare_value # type: ignore[no-any-return] elif op == "<": - return field_value < compare_value + return field_value < compare_value # type: ignore[no-any-return] elif op == ">=": - return field_value >= compare_value + return field_value >= compare_value # type: ignore[no-any-return] elif op == "<=": - return field_value <= compare_value + return field_value <= compare_value # type: ignore[no-any-return] # Handle lists (from collection searches) if isinstance(field_value, list): @@ -346,12 +346,12 @@ def _check_field_match(self, entry, field_path, value) -> bool: return value in field_value # All other types use exact matching else: - return field_value == value + return field_value == value # type: ignore[no-any-return] except (AttributeError, KeyError): return False - def _get_value_by_path(self, obj, path): + def _get_value_by_path(self, obj, path): # type: ignore[no-untyped-def] """Get a value from an object using a dot-separated path. This method handles three special cases: @@ -385,7 +385,7 @@ def _get_value_by_path(self, obj, path): if not remaining_path: # If * is the last part, return all values return list(items) elif isinstance(current, list): - items = current + items = current # type: ignore[assignment] if not remaining_path: # If * is the last part, return all items return items else: # Not a collection @@ -398,7 +398,7 @@ def _get_value_by_path(self, obj, path): for item in items: try: # Recursively get values from each item - value = self._get_value_by_path(item, remaining_path) + value = self._get_value_by_path(item, remaining_path) # type: ignore[no-untyped-call] if isinstance(value, list): # Flatten nested lists results.extend(value) else: diff --git a/dimos/manipulation/manipulation_interface.py b/dimos/manipulation/manipulation_interface.py index ae63eb79ed..07cffe3861 100644 --- a/dimos/manipulation/manipulation_interface.py +++ b/dimos/manipulation/manipulation_interface.py @@ -53,7 +53,7 @@ def __init__( self, output_dir: str, new_memory: bool = False, - perception_stream: ObjectDetectionStream = None, + perception_stream: ObjectDetectionStream = None, # type: ignore[assignment] ) -> None: """ Initialize a new ManipulationInterface instance. @@ -136,7 +136,7 @@ def add_manipulation_task( """ # Add task to history - self.manipulation_history.add_entry( + self.manipulation_history.add_entry( # type: ignore[call-arg] task=task, result=None, notes=None, manipulation_response=manipulation_response ) @@ -150,7 +150,7 @@ def get_manipulation_task(self, task_id: str) -> ManipulationTask | None: Returns: The task object or None if not found """ - return self.history.get_manipulation_task(task_id) + return self.history.get_manipulation_task(task_id) # type: ignore[attr-defined, no-any-return] def get_all_manipulation_tasks(self) -> list[ManipulationTask]: """ @@ -159,7 +159,7 @@ def get_all_manipulation_tasks(self) -> list[ManipulationTask]: Returns: List of all manipulation tasks """ - return self.history.get_all_manipulation_tasks() + return self.history.get_all_manipulation_tasks() # type: ignore[attr-defined, no-any-return] def update_task_status( self, task_id: str, status: str, result: dict[str, Any] | None = None @@ -175,7 +175,7 @@ def update_task_status( Returns: The updated task or None if task not found """ - return self.history.update_task_status(task_id, status, result) + return self.history.update_task_status(task_id, status, result) # type: ignore[attr-defined, no-any-return] # === Perception stream methods === @@ -185,13 +185,13 @@ def _setup_perception_subscription(self) -> None: """ if self.perception_stream: # Subscribe to the stream and update latest_objects - self.stream_subscription = self.perception_stream.get_stream().subscribe( + self.stream_subscription = self.perception_stream.get_stream().subscribe( # type: ignore[no-untyped-call] on_next=self._update_latest_objects, on_error=lambda e: logger.error(f"Error in perception stream: {e}"), ) logger.info("Subscribed to perception stream") - def _update_latest_objects(self, data) -> None: + def _update_latest_objects(self, data) -> None: # type: ignore[no-untyped-def] """ Update the latest detected objects. @@ -237,7 +237,7 @@ def get_objects_by_label(self, label: str) -> list[ObjectData]: """ return [obj for obj in self.latest_objects if obj["label"] == label] - def set_perception_stream(self, perception_stream) -> None: + def set_perception_stream(self, perception_stream) -> None: # type: ignore[no-untyped-def] """ Set or update the perception stream. diff --git a/dimos/manipulation/visual_servoing/detection3d.py b/dimos/manipulation/visual_servoing/detection3d.py index f7371f531a..d564dee3b1 100644 --- a/dimos/manipulation/visual_servoing/detection3d.py +++ b/dimos/manipulation/visual_servoing/detection3d.py @@ -17,7 +17,7 @@ """ import cv2 -from dimos_lcm.vision_msgs import ( +from dimos_lcm.vision_msgs import ( # type: ignore[import-untyped] BoundingBox2D, BoundingBox3D, Detection2D, @@ -91,7 +91,7 @@ def __init__( ) def process_frame( - self, rgb_image: np.ndarray, depth_image: np.ndarray, transform: np.ndarray | None = None + self, rgb_image: np.ndarray, depth_image: np.ndarray, transform: np.ndarray | None = None # type: ignore[type-arg] ) -> tuple[Detection3DArray, Detection2DArray]: """ Process a single RGB-D frame to extract 3D object detections. @@ -109,7 +109,7 @@ def process_frame( bgr_image = cv2.cvtColor(rgb_image, cv2.COLOR_RGB2BGR) # Run Sam segmentation with tracking - masks, bboxes, track_ids, probs, names = self.detector.process_image(bgr_image) + masks, bboxes, track_ids, probs, names = self.detector.process_image(bgr_image) # type: ignore[no-untyped-call] if not masks or len(masks) == 0: return Detection3DArray( @@ -233,11 +233,11 @@ def process_frame( def visualize_detections( self, - rgb_image: np.ndarray, + rgb_image: np.ndarray, # type: ignore[type-arg] detections_3d: list[Detection3D], detections_2d: list[Detection2D], show_coordinates: bool = True, - ) -> np.ndarray: + ) -> np.ndarray: # type: ignore[type-arg] """ Visualize detections with 3D position overlay next to bounding boxes. @@ -287,7 +287,7 @@ def get_closest_detection( return None # Sort by depth (Z coordinate) - def get_z_coord(d): + def get_z_coord(d): # type: ignore[no-untyped-def] return abs(d.bbox.center.position.z) return min(valid_detections, key=get_z_coord) diff --git a/dimos/manipulation/visual_servoing/manipulation_module.py b/dimos/manipulation/visual_servoing/manipulation_module.py index a89d43ed7b..8ed6a6adcc 100644 --- a/dimos/manipulation/visual_servoing/manipulation_module.py +++ b/dimos/manipulation/visual_servoing/manipulation_module.py @@ -24,7 +24,7 @@ from typing import Any import cv2 -from dimos_lcm.sensor_msgs import CameraInfo +from dimos_lcm.sensor_msgs import CameraInfo # type: ignore[import-untyped] import numpy as np from reactivex.disposable import Disposable @@ -107,16 +107,16 @@ class ManipulationModule(Module): """ # LCM inputs - rgb_image: In[Image] = None - depth_image: In[Image] = None - camera_info: In[CameraInfo] = None + rgb_image: In[Image] = None # type: ignore[assignment] + depth_image: In[Image] = None # type: ignore[assignment] + camera_info: In[CameraInfo] = None # type: ignore[assignment] # LCM outputs - viz_image: Out[Image] = None + viz_image: Out[Image] = None # type: ignore[assignment] - def __init__( + def __init__( # type: ignore[no-untyped-def] self, - ee_to_camera_6dof: list | None = None, + ee_to_camera_6dof: list | None = None, # type: ignore[type-arg] **kwargs, ) -> None: """ @@ -173,11 +173,11 @@ def __init__( self.pose_stabilization_threshold = 0.01 self.stabilization_timeout = 25.0 self.stabilization_start_time = None - self.reached_poses = deque(maxlen=self.pose_history_size) + self.reached_poses = deque(maxlen=self.pose_history_size) # type: ignore[var-annotated] self.adjustment_count = 0 # Pose reachability tracking - self.ee_pose_history = deque(maxlen=20) # Keep history of EE poses + self.ee_pose_history = deque(maxlen=20) # type: ignore[var-annotated] # Keep history of EE poses self.stuck_pose_threshold = 0.001 # 1mm movement threshold self.stuck_pose_adjustment_degrees = 5.0 self.stuck_count = 0 @@ -266,11 +266,11 @@ def _on_depth_image(self, msg: Image) -> None: def _on_camera_info(self, msg: CameraInfo) -> None: """Handle camera info messages.""" try: - self.camera_intrinsics = [msg.K[0], msg.K[4], msg.K[2], msg.K[5]] + self.camera_intrinsics = [msg.K[0], msg.K[4], msg.K[2], msg.K[5]] # type: ignore[assignment] if self.detector is None: - self.detector = Detection3DProcessor(self.camera_intrinsics) - self.pbvs = PBVS() + self.detector = Detection3DProcessor(self.camera_intrinsics) # type: ignore[arg-type, assignment] + self.pbvs = PBVS() # type: ignore[assignment] logger.info("Initialized detection and PBVS processors") self.latest_camera_info = msg @@ -278,7 +278,7 @@ def _on_camera_info(self, msg: CameraInfo) -> None: logger.error(f"Error processing camera info: {e}") @rpc - def get_single_rgb_frame(self) -> np.ndarray | None: + def get_single_rgb_frame(self) -> np.ndarray | None: # type: ignore[type-arg] """ get the latest rgb frame from the camera """ @@ -406,8 +406,8 @@ def _run_pick_and_place(self) -> None: time.sleep(0.01) continue - if feedback.success is not None: - if feedback.success: + if feedback.success is not None: # type: ignore[attr-defined] + if feedback.success: # type: ignore[attr-defined] logger.info("Pick and place completed successfully!") else: logger.warning("Pick and place failed") @@ -458,7 +458,7 @@ def calculate_dynamic_grasp_pitch(self, target_pose: Pose) -> float: normalized_dist * (self.max_grasp_pitch_degrees - self.min_grasp_pitch_degrees) ) - return pitch_degrees + return pitch_degrees # type: ignore[no-any-return] def check_within_workspace(self, target_pose: Pose) -> bool: """ @@ -507,7 +507,7 @@ def _check_if_stuck(self) -> bool: Returns: Tuple of (is_stuck, max_std_dev_mm) """ - if len(self.ee_pose_history) < self.ee_pose_history.maxlen: + if len(self.ee_pose_history) < self.ee_pose_history.maxlen: # type: ignore[operator] return False # Extract positions from pose history @@ -520,7 +520,7 @@ def _check_if_stuck(self) -> bool: # Check if all standard deviations are below stuck threshold is_stuck = np.all(std_devs < self.stuck_pose_threshold) - return is_stuck + return is_stuck # type: ignore[return-value] def check_reach_and_adjust(self) -> bool: """ @@ -642,10 +642,10 @@ def execute_pre_grasp(self) -> None: self.reset_to_idle() return - ee_pose = self.arm.get_ee_pose() - dynamic_pitch = self.calculate_dynamic_grasp_pitch(self.pbvs.current_target.bbox.center) + ee_pose = self.arm.get_ee_pose() # type: ignore[no-untyped-call] + dynamic_pitch = self.calculate_dynamic_grasp_pitch(self.pbvs.current_target.bbox.center) # type: ignore[attr-defined] - _, _, _, has_target, target_pose = self.pbvs.compute_control( + _, _, _, has_target, target_pose = self.pbvs.compute_control( # type: ignore[attr-defined] ee_pose, self.pregrasp_distance, dynamic_pitch ) if target_pose and has_target: @@ -665,7 +665,7 @@ def execute_pre_grasp(self) -> None: self.arm.cmd_ee_pose(target_pose) self.current_executed_pose = target_pose self.waiting_for_reach = True - self.waiting_start_time = time.time() + self.waiting_start_time = time.time() # type: ignore[assignment] self.target_updated = False self.adjustment_count += 1 time.sleep(0.2) @@ -674,7 +674,7 @@ def execute_grasp(self) -> None: """Execute grasp stage: move to final grasp position.""" if self.waiting_for_reach: if self.check_reach_and_adjust() and not self.grasp_reached_time: - self.grasp_reached_time = time.time() + self.grasp_reached_time = time.time() # type: ignore[assignment] return if self.grasp_reached_time: @@ -739,7 +739,7 @@ def execute_close_and_retract(self) -> None: self.current_executed_pose = self.final_pregrasp_pose self.arm.close_gripper() self.waiting_for_reach = True - self.waiting_start_time = time.time() + self.waiting_start_time = time.time() # type: ignore[assignment] def execute_place(self) -> None: """Execute place stage: move to place position and release object.""" @@ -759,13 +759,13 @@ def execute_place(self) -> None: if place_pose: logger.info("Moving to place position") self.arm.cmd_ee_pose(place_pose, line_mode=True) - self.current_executed_pose = place_pose + self.current_executed_pose = place_pose # type: ignore[assignment] self.waiting_for_reach = True - self.waiting_start_time = time.time() + self.waiting_start_time = time.time() # type: ignore[assignment] else: logger.error("Failed to get place target pose") self.task_failed = True - self.overall_success = False + self.overall_success = False # type: ignore[assignment] def execute_retract(self) -> None: """Execute retract stage: retract from place position.""" @@ -793,11 +793,11 @@ def execute_retract(self) -> None: else: logger.error("No place pose stored for retraction") self.task_failed = True - self.overall_success = False + self.overall_success = False # type: ignore[assignment] def capture_and_process( self, - ) -> tuple[np.ndarray | None, Detection3DArray | None, Detection2DArray | None, Pose | None]: + ) -> tuple[np.ndarray | None, Detection3DArray | None, Detection2DArray | None, Pose | None]: # type: ignore[type-arg] """Capture frame from camera data and process detections.""" if self.latest_rgb is None or self.latest_depth is None or self.detector is None: return None, None, None, None @@ -852,8 +852,8 @@ def update(self) -> dict[str, Any] | None: if rgb is None: return None - self.last_detection_3d_array = detection_3d_array - self.last_detection_2d_array = detection_2d_array + self.last_detection_3d_array = detection_3d_array # type: ignore[assignment] + self.last_detection_2d_array = detection_2d_array # type: ignore[assignment] if self.target_click: x, y = self.target_click if self.pick_target(x, y): @@ -877,7 +877,7 @@ def update(self) -> dict[str, Any] | None: stage_handlers[self.grasp_stage]() target_tracked = self.pbvs.get_current_target() is not None if self.pbvs else False - ee_pose = self.arm.get_ee_pose() + ee_pose = self.arm.get_ee_pose() # type: ignore[no-untyped-call] feedback = Feedback( grasp_stage=self.grasp_stage, target_tracked=target_tracked, @@ -890,34 +890,34 @@ def update(self) -> dict[str, Any] | None: ) if self.task_running: - self.current_visualization = create_manipulation_visualization( + self.current_visualization = create_manipulation_visualization( # type: ignore[assignment] rgb, feedback, detection_3d_array, detection_2d_array ) if self.current_visualization is not None: self._publish_visualization(self.current_visualization) - return feedback + return feedback # type: ignore[return-value] - def _publish_visualization(self, viz_image: np.ndarray) -> None: + def _publish_visualization(self, viz_image: np.ndarray) -> None: # type: ignore[type-arg] """Publish visualization image to LCM.""" try: viz_rgb = cv2.cvtColor(viz_image, cv2.COLOR_BGR2RGB) msg = Image.from_numpy(viz_rgb) - self.viz_image.publish(msg) + self.viz_image.publish(msg) # type: ignore[no-untyped-call] except Exception as e: logger.error(f"Error publishing visualization: {e}") def check_target_stabilized(self) -> bool: """Check if the commanded poses have stabilized.""" - if len(self.reached_poses) < self.reached_poses.maxlen: + if len(self.reached_poses) < self.reached_poses.maxlen: # type: ignore[operator] return False positions = np.array( [[p.position.x, p.position.y, p.position.z] for p in self.reached_poses] ) std_devs = np.std(positions, axis=0) - return np.all(std_devs < self.pose_stabilization_threshold) + return np.all(std_devs < self.pose_stabilization_threshold) # type: ignore[return-value] def get_place_target_pose(self) -> Pose | None: """Get the place target pose with z-offset applied based on object height.""" diff --git a/dimos/manipulation/visual_servoing/pbvs.py b/dimos/manipulation/visual_servoing/pbvs.py index 77bf83396e..ba1ab4c076 100644 --- a/dimos/manipulation/visual_servoing/pbvs.py +++ b/dimos/manipulation/visual_servoing/pbvs.py @@ -19,7 +19,7 @@ from collections import deque -from dimos_lcm.vision_msgs import Detection3D +from dimos_lcm.vision_msgs import Detection3D # type: ignore[import-untyped] import numpy as np from scipy.spatial.transform import Rotation as R @@ -84,7 +84,7 @@ def __init__( target_tolerance=target_tolerance, ) else: - self.controller = None + self.controller = None # type: ignore[assignment] # Store parameters for direct mode error computation self.target_tolerance = target_tolerance @@ -100,7 +100,7 @@ def __init__( # Detection history for robust tracking self.detection_history_size = 3 - self.detection_history = deque(maxlen=self.detection_history_size) + self.detection_history = deque(maxlen=self.detection_history_size) # type: ignore[var-annotated] # For direct control mode visualization self.last_position_error = None @@ -272,11 +272,11 @@ def compute_control( # Return has_target=True since we have a target, regardless of tracking status return velocity_cmd, angular_velocity_cmd, target_reached, True, None - def create_status_overlay( + def create_status_overlay( # type: ignore[no-untyped-def] self, - image: np.ndarray, + image: np.ndarray, # type: ignore[type-arg] grasp_stage=None, - ) -> np.ndarray: + ) -> np.ndarray: # type: ignore[type-arg] """ Create PBVS status overlay on image. @@ -374,7 +374,7 @@ def compute_control( grasp_pose.position.y - ee_pose.position.y, grasp_pose.position.z - ee_pose.position.z, ) - self.last_position_error = error + self.last_position_error = error # type: ignore[assignment] # Compute velocity command with proportional control velocity_cmd = Vector3( @@ -393,7 +393,7 @@ def compute_control( float(velocity_cmd.z * scale), ) - self.last_velocity_cmd = velocity_cmd + self.last_velocity_cmd = velocity_cmd # type: ignore[assignment] # Compute angular velocity for orientation control angular_velocity_cmd = self._compute_angular_velocity(grasp_pose.orientation, ee_pose) @@ -441,7 +441,7 @@ def _compute_angular_velocity(self, target_rot: Quaternion, current_pose: Pose) pitch_error = error_axis_angle[1] yaw_error = error_axis_angle[2] - self.last_rotation_error = Vector3(roll_error, pitch_error, yaw_error) + self.last_rotation_error = Vector3(roll_error, pitch_error, yaw_error) # type: ignore[assignment] # Apply proportional control angular_velocity = Vector3( @@ -460,15 +460,15 @@ def _compute_angular_velocity(self, target_rot: Quaternion, current_pose: Pose) angular_velocity.x * scale, angular_velocity.y * scale, angular_velocity.z * scale ) - self.last_angular_velocity_cmd = angular_velocity + self.last_angular_velocity_cmd = angular_velocity # type: ignore[assignment] return angular_velocity def create_status_overlay( self, - image: np.ndarray, + image: np.ndarray, # type: ignore[type-arg] current_target: Detection3D | None = None, - ) -> np.ndarray: + ) -> np.ndarray: # type: ignore[type-arg] """ Create PBVS status overlay on image. diff --git a/dimos/manipulation/visual_servoing/utils.py b/dimos/manipulation/visual_servoing/utils.py index 06479723f6..2edca60e44 100644 --- a/dimos/manipulation/visual_servoing/utils.py +++ b/dimos/manipulation/visual_servoing/utils.py @@ -16,7 +16,7 @@ from typing import Any import cv2 -from dimos_lcm.vision_msgs import Detection2D, Detection3D +from dimos_lcm.vision_msgs import Detection2D, Detection3D # type: ignore[import-untyped] import numpy as np from dimos.msgs.geometry_msgs import Pose, Quaternion, Vector3 @@ -56,9 +56,9 @@ def match_detection_by_id( def transform_pose( - obj_pos: np.ndarray, - obj_orientation: np.ndarray, - transform_matrix: np.ndarray, + obj_pos: np.ndarray, # type: ignore[type-arg] + obj_orientation: np.ndarray, # type: ignore[type-arg] + transform_matrix: np.ndarray, # type: ignore[type-arg] to_optical: bool = False, to_robot: bool = False, ) -> Pose: @@ -112,11 +112,11 @@ def transform_pose( def transform_points_3d( - points_3d: np.ndarray, - transform_matrix: np.ndarray, + points_3d: np.ndarray, # type: ignore[type-arg] + transform_matrix: np.ndarray, # type: ignore[type-arg] to_optical: bool = False, to_robot: bool = False, -) -> np.ndarray: +) -> np.ndarray: # type: ignore[type-arg] """ Transform 3D points with optional frame convention conversion. Applies the same transformation pipeline as transform_pose but for multiple points. @@ -181,11 +181,11 @@ def transform_points_3d( def select_points_from_depth( - depth_image: np.ndarray, + depth_image: np.ndarray, # type: ignore[type-arg] target_point: tuple[int, int], - camera_intrinsics: list[float] | np.ndarray, + camera_intrinsics: list[float] | np.ndarray, # type: ignore[type-arg] radius: int = 5, -) -> np.ndarray: +) -> np.ndarray: # type: ignore[type-arg] """ Select points around a target point within a bounding box and project them to 3D. @@ -349,12 +349,12 @@ def calculate_object_similarity( dim_similarities.append(dim_similarity) # Return average similarity across all dimensions - size_similarity = np.mean(dim_similarities) if dim_similarities else 0.0 + size_similarity = np.mean(dim_similarities) if dim_similarities else 0.0 # type: ignore[assignment] # Weighted combination total_similarity = distance_weight * distance_similarity + size_weight * size_similarity - return total_similarity, distance, size_similarity + return total_similarity, distance, size_similarity # type: ignore[return-value] def find_best_object_match( @@ -440,7 +440,7 @@ def parse_zed_pose(zed_pose_data: dict[str, Any]) -> Pose | None: def estimate_object_depth( - depth_image: np.ndarray, segmentation_mask: np.ndarray | None, bbox: list[float] + depth_image: np.ndarray, segmentation_mask: np.ndarray | None, bbox: list[float] # type: ignore[type-arg] ) -> float: """ Estimate object depth dimension using segmentation mask and depth data. @@ -478,7 +478,7 @@ def estimate_object_depth( depth_range = depth_90 - depth_10 # Clamp to reasonable bounds with single operation - return np.clip(depth_range, 0.02, 0.5) + return np.clip(depth_range, 0.02, 0.5) # type: ignore[no-any-return] # Fast fallback using area calculation bbox_area = (x2 - x1) * (y2 - y1) @@ -495,12 +495,12 @@ def estimate_object_depth( # ============= Visualization Functions ============= -def create_manipulation_visualization( - rgb_image: np.ndarray, +def create_manipulation_visualization( # type: ignore[no-untyped-def] + rgb_image: np.ndarray, # type: ignore[type-arg] feedback, detection_3d_array=None, detection_2d_array=None, -) -> np.ndarray: +) -> np.ndarray: # type: ignore[type-arg] """ Create simple visualization for manipulation class using feedback. @@ -630,13 +630,13 @@ def create_manipulation_visualization( return viz -def create_pbvs_visualization( - image: np.ndarray, +def create_pbvs_visualization( # type: ignore[no-untyped-def] + image: np.ndarray, # type: ignore[type-arg] current_target=None, position_error=None, target_reached: bool = False, grasp_stage: str = "idle", -) -> np.ndarray: +) -> np.ndarray: # type: ignore[type-arg] """ Create simple PBVS visualization overlay. @@ -720,11 +720,11 @@ def create_pbvs_visualization( def visualize_detections_3d( - rgb_image: np.ndarray, + rgb_image: np.ndarray, # type: ignore[type-arg] detections: list[Detection3D], show_coordinates: bool = True, bboxes_2d: list[list[float]] | None = None, -) -> np.ndarray: +) -> np.ndarray: # type: ignore[type-arg] """ Visualize detections with 3D position overlay next to bounding boxes. @@ -796,4 +796,4 @@ def visualize_detections_3d( 1, ) - return viz + return viz # type: ignore[no-any-return] diff --git a/dimos/mapping/google_maps/google_maps.py b/dimos/mapping/google_maps/google_maps.py index e75de042f4..7f99fc12f4 100644 --- a/dimos/mapping/google_maps/google_maps.py +++ b/dimos/mapping/google_maps/google_maps.py @@ -14,7 +14,7 @@ import os -import googlemaps +import googlemaps # type: ignore[import-untyped] from dimos.mapping.google_maps.types import ( Coordinates, diff --git a/dimos/mapping/osm/current_location_map.py b/dimos/mapping/osm/current_location_map.py index 88942935af..168350d5b3 100644 --- a/dimos/mapping/osm/current_location_map.py +++ b/dimos/mapping/osm/current_location_map.py @@ -40,16 +40,16 @@ def update_position(self, position: LatLon) -> None: self._position = position def query_for_one_position(self, query: str) -> LatLon | None: - return query_for_one_position(self._vl_model, self._get_current_map(), query) + return query_for_one_position(self._vl_model, self._get_current_map(), query) # type: ignore[no-untyped-call] def query_for_one_position_and_context( self, query: str, robot_position: LatLon ) -> tuple[LatLon, str] | None: return query_for_one_position_and_context( - self._vl_model, self._get_current_map(), query, robot_position + self._vl_model, self._get_current_map(), query, robot_position # type: ignore[no-untyped-call] ) - def _get_current_map(self): + def _get_current_map(self): # type: ignore[no-untyped-def] if not self._position: raise ValueError("Current position has not been set.") @@ -63,11 +63,11 @@ def _fetch_new_map(self) -> None: logger.info( f"Getting a new OSM map, position={self._position}, zoom={self._zoom_level} n_tiles={self._n_tiles}" ) - self._map_image = get_osm_map(self._position, self._zoom_level, self._n_tiles) + self._map_image = get_osm_map(self._position, self._zoom_level, self._n_tiles) # type: ignore[arg-type] def _position_is_too_far_off_center(self) -> bool: - x, y = self._map_image.latlon_to_pixel(self._position) - width = self._map_image.image.width + x, y = self._map_image.latlon_to_pixel(self._position) # type: ignore[arg-type, union-attr] + width = self._map_image.image.width # type: ignore[union-attr] size_min = width * (0.5 - self._center_width / 2) size_max = width * (0.5 + self._center_width / 2) diff --git a/dimos/models/depth/metric3d.py b/dimos/models/depth/metric3d.py index 0c10f31e63..0da829aa92 100644 --- a/dimos/models/depth/metric3d.py +++ b/dimos/models/depth/metric3d.py @@ -22,10 +22,10 @@ class Metric3D: - def __init__(self, camera_intrinsics=None, gt_depth_scale: float=256.0) -> None: + def __init__(self, camera_intrinsics=None, gt_depth_scale: float=256.0) -> None: # type: ignore[no-untyped-def] # self.conf = get_config("zoedepth", "infer") # self.depth_model = build_model(self.conf) - self.depth_model = torch.hub.load( + self.depth_model = torch.hub.load( # type: ignore[no-untyped-call] "yvanyin/metric3d", "metric3d_vit_small", pretrain=True ).cuda() if torch.cuda.device_count() > 1: @@ -44,7 +44,7 @@ def __init__(self, camera_intrinsics=None, gt_depth_scale: float=256.0) -> None: Output: Depth map """ - def update_intrinsic(self, intrinsic): + def update_intrinsic(self, intrinsic): # type: ignore[no-untyped-def] """ Update the intrinsic parameters dynamically. Ensure that the input intrinsic is valid. @@ -54,30 +54,30 @@ def update_intrinsic(self, intrinsic): self.intrinsic = intrinsic print(f"Intrinsics updated to: {self.intrinsic}") - def infer_depth(self, img, debug: bool=False): + def infer_depth(self, img, debug: bool=False): # type: ignore[no-untyped-def] if debug: print(f"Input image: {img}") try: if isinstance(img, str): print(f"Image type string: {type(img)}") - self.rgb_origin = cv2.imread(img)[:, :, ::-1] + self.rgb_origin = cv2.imread(img)[:, :, ::-1] # type: ignore[assignment] else: # print(f"Image type not string: {type(img)}, cv2 conversion assumed to be handled. If not, this will throw an error") self.rgb_origin = img except Exception as e: print(f"Error parsing into infer_depth: {e}") - img = self.rescale_input(img, self.rgb_origin) + img = self.rescale_input(img, self.rgb_origin) # type: ignore[no-untyped-call] with torch.no_grad(): pred_depth, confidence, output_dict = self.depth_model.inference({"input": img}) # Convert to PIL format - depth_image = self.unpad_transform_depth(pred_depth) + depth_image = self.unpad_transform_depth(pred_depth) # type: ignore[no-untyped-call] return depth_image.cpu().numpy() - def save_depth(self, pred_depth) -> None: + def save_depth(self, pred_depth) -> None: # type: ignore[no-untyped-def] # Save the depth map to a file pred_depth_np = pred_depth.cpu().numpy() output_depth_file = "output_depth_map.png" @@ -85,7 +85,7 @@ def save_depth(self, pred_depth) -> None: print(f"Depth map saved to {output_depth_file}") # Adjusts input size to fit pretrained ViT model - def rescale_input(self, rgb, rgb_origin): + def rescale_input(self, rgb, rgb_origin): # type: ignore[no-untyped-def] #### ajust input size to fit pretrained model # keep ratio resize input_size = (616, 1064) # for vit model @@ -96,7 +96,7 @@ def rescale_input(self, rgb, rgb_origin): rgb_origin, (int(w * scale), int(h * scale)), interpolation=cv2.INTER_LINEAR ) # remember to scale intrinsic, hold depth - self.intrinsic_scaled = [ + self.intrinsic_scaled = [ # type: ignore[assignment] self.intrinsic[0] * scale, self.intrinsic[1] * scale, self.intrinsic[2] * scale, @@ -118,7 +118,7 @@ def rescale_input(self, rgb, rgb_origin): cv2.BORDER_CONSTANT, value=padding, ) - self.pad_info = [pad_h_half, pad_h - pad_h_half, pad_w_half, pad_w - pad_w_half] + self.pad_info = [pad_h_half, pad_h - pad_h_half, pad_w_half, pad_w - pad_w_half] # type: ignore[assignment] #### normalize mean = torch.tensor([123.675, 116.28, 103.53]).float()[:, None, None] @@ -128,23 +128,23 @@ def rescale_input(self, rgb, rgb_origin): rgb = rgb[None, :, :, :].cuda() return rgb - def unpad_transform_depth(self, pred_depth): + def unpad_transform_depth(self, pred_depth): # type: ignore[no-untyped-def] # un pad pred_depth = pred_depth.squeeze() pred_depth = pred_depth[ - self.pad_info[0] : pred_depth.shape[0] - self.pad_info[1], - self.pad_info[2] : pred_depth.shape[1] - self.pad_info[3], + self.pad_info[0] : pred_depth.shape[0] - self.pad_info[1], # type: ignore[index] + self.pad_info[2] : pred_depth.shape[1] - self.pad_info[3], # type: ignore[index] ] # upsample to original size pred_depth = torch.nn.functional.interpolate( - pred_depth[None, None, :, :], self.rgb_origin.shape[:2], mode="bilinear" + pred_depth[None, None, :, :], self.rgb_origin.shape[:2], mode="bilinear" # type: ignore[attr-defined] ).squeeze() ###################### canonical camera space ###################### #### de-canonical transform canonical_to_real_scale = ( - self.intrinsic_scaled[0] / 1000.0 + self.intrinsic_scaled[0] / 1000.0 # type: ignore[index] ) # 1000.0 is the focal length of canonical camera pred_depth = pred_depth * canonical_to_real_scale # now the depth is metric pred_depth = torch.clamp(pred_depth, 0, 1000) @@ -152,14 +152,14 @@ def unpad_transform_depth(self, pred_depth): """Set new intrinsic value.""" - def update_intrinsic(self, intrinsic) -> None: + def update_intrinsic(self, intrinsic) -> None: # type: ignore[no-redef, no-untyped-def] self.intrinsic = intrinsic - def eval_predicted_depth(self, depth_file, pred_depth) -> None: + def eval_predicted_depth(self, depth_file, pred_depth) -> None: # type: ignore[no-untyped-def] if depth_file is not None: gt_depth = cv2.imread(depth_file, -1) gt_depth = gt_depth / self.gt_depth_scale - gt_depth = torch.from_numpy(gt_depth).float().cuda() + gt_depth = torch.from_numpy(gt_depth).float().cuda() # type: ignore[assignment] assert gt_depth.shape == pred_depth.shape mask = gt_depth > 1e-8 diff --git a/dimos/models/embedding/base.py b/dimos/models/embedding/base.py index f7c790ffbf..6633b6a5f1 100644 --- a/dimos/models/embedding/base.py +++ b/dimos/models/embedding/base.py @@ -34,9 +34,9 @@ class Embedding(Timestamped): Embeddings are kept as torch.Tensor on device by default for efficiency. """ - vector: torch.Tensor | np.ndarray + vector: torch.Tensor | np.ndarray # type: ignore[type-arg] - def __init__(self, vector: torch.Tensor | np.ndarray, timestamp: float | None = None) -> None: + def __init__(self, vector: torch.Tensor | np.ndarray, timestamp: float | None = None) -> None: # type: ignore[type-arg] self.vector = vector if timestamp: self.timestamp = timestamp @@ -51,7 +51,7 @@ def __matmul__(self, other: Embedding) -> float: return result.item() return float(self.vector @ other.to_numpy()) - def to_numpy(self) -> np.ndarray: + def to_numpy(self) -> np.ndarray: # type: ignore[type-arg] """Convert to numpy array (moves to CPU if needed).""" if isinstance(self.vector, torch.Tensor): return self.vector.detach().cpu().numpy() diff --git a/dimos/models/embedding/clip.py b/dimos/models/embedding/clip.py index 23ab5e94f2..6fd3f70009 100644 --- a/dimos/models/embedding/clip.py +++ b/dimos/models/embedding/clip.py @@ -15,7 +15,7 @@ from PIL import Image as PILImage import torch import torch.nn.functional as F -from transformers import CLIPModel as HFCLIPModel, CLIPProcessor +from transformers import CLIPModel as HFCLIPModel, CLIPProcessor # type: ignore[import-untyped] from dimos.models.embedding.base import Embedding, EmbeddingModel from dimos.msgs.sensor_msgs import Image diff --git a/dimos/models/embedding/embedding_models_disabled_tests.py b/dimos/models/embedding/embedding_models_disabled_tests.py index bb1f038410..6c80595571 100644 --- a/dimos/models/embedding/embedding_models_disabled_tests.py +++ b/dimos/models/embedding/embedding_models_disabled_tests.py @@ -20,7 +20,7 @@ @pytest.fixture(scope="session", params=["clip", "mobileclip", "treid"]) -def embedding_model(request): +def embedding_model(request): # type: ignore[no-untyped-def] """Load embedding model once for all tests. Parametrized for different models.""" if request.param == "mobileclip": from dimos.models.embedding.mobileclip import MobileCLIPModel @@ -30,11 +30,11 @@ def embedding_model(request): elif request.param == "clip": from dimos.models.embedding.clip import CLIPModel - model = CLIPModel(model_name="openai/clip-vit-base-patch32") + model = CLIPModel(model_name="openai/clip-vit-base-patch32") # type: ignore[assignment] elif request.param == "treid": from dimos.models.embedding.treid import TorchReIDModel - model = TorchReIDModel(model_name="osnet_x1_0") + model = TorchReIDModel(model_name="osnet_x1_0") # type: ignore[assignment] else: raise ValueError(f"Unknown model: {request.param}") @@ -43,13 +43,13 @@ def embedding_model(request): @pytest.fixture(scope="session") -def test_image(): +def test_image(): # type: ignore[no-untyped-def] """Load test image.""" - return Image.from_file(get_data("cafe.jpg")).to_rgb() + return Image.from_file(get_data("cafe.jpg")).to_rgb() # type: ignore[arg-type] @pytest.mark.heavy -def test_single_image_embedding(embedding_model, test_image) -> None: +def test_single_image_embedding(embedding_model, test_image) -> None: # type: ignore[no-untyped-def] """Test embedding a single image.""" embedding = embedding_model.embed(test_image) @@ -74,7 +74,7 @@ def test_single_image_embedding(embedding_model, test_image) -> None: @pytest.mark.heavy -def test_batch_image_embedding(embedding_model, test_image) -> None: +def test_batch_image_embedding(embedding_model, test_image) -> None: # type: ignore[no-untyped-def] """Test embedding multiple images at once.""" embeddings = embedding_model.embed(test_image, test_image, test_image) @@ -92,7 +92,7 @@ def test_batch_image_embedding(embedding_model, test_image) -> None: @pytest.mark.heavy -def test_single_text_embedding(embedding_model) -> None: +def test_single_text_embedding(embedding_model) -> None: # type: ignore[no-untyped-def] """Test embedding a single text string.""" import torch @@ -117,7 +117,7 @@ def test_single_text_embedding(embedding_model) -> None: @pytest.mark.heavy -def test_batch_text_embedding(embedding_model) -> None: +def test_batch_text_embedding(embedding_model) -> None: # type: ignore[no-untyped-def] """Test embedding multiple text strings at once.""" import torch @@ -137,7 +137,7 @@ def test_batch_text_embedding(embedding_model) -> None: @pytest.mark.heavy -def test_text_image_similarity(embedding_model, test_image) -> None: +def test_text_image_similarity(embedding_model, test_image) -> None: # type: ignore[no-untyped-def] """Test cross-modal text-image similarity using @ operator.""" if not hasattr(embedding_model, "embed_text"): pytest.skip("Model does not support text embeddings") @@ -161,7 +161,7 @@ def test_text_image_similarity(embedding_model, test_image) -> None: @pytest.mark.heavy -def test_cosine_distance(embedding_model, test_image) -> None: +def test_cosine_distance(embedding_model, test_image) -> None: # type: ignore[no-untyped-def] """Test cosine distance computation (1 - similarity).""" emb1 = embedding_model.embed(test_image) emb2 = embedding_model.embed(test_image) @@ -180,7 +180,7 @@ def test_cosine_distance(embedding_model, test_image) -> None: @pytest.mark.heavy -def test_query_functionality(embedding_model, test_image) -> None: +def test_query_functionality(embedding_model, test_image) -> None: # type: ignore[no-untyped-def] """Test query method for top-k retrieval.""" if not hasattr(embedding_model, "embed_text"): pytest.skip("Model does not support text embeddings") @@ -206,7 +206,7 @@ def test_query_functionality(embedding_model, test_image) -> None: @pytest.mark.heavy -def test_embedding_operator(embedding_model, test_image) -> None: +def test_embedding_operator(embedding_model, test_image) -> None: # type: ignore[no-untyped-def] """Test that @ operator works on embeddings.""" emb1 = embedding_model.embed(test_image) emb2 = embedding_model.embed(test_image) @@ -220,7 +220,7 @@ def test_embedding_operator(embedding_model, test_image) -> None: @pytest.mark.heavy -def test_warmup(embedding_model) -> None: +def test_warmup(embedding_model) -> None: # type: ignore[no-untyped-def] """Test that warmup runs without error.""" # Warmup is already called in fixture, but test it explicitly embedding_model.warmup() @@ -229,7 +229,7 @@ def test_warmup(embedding_model) -> None: @pytest.mark.heavy -def test_compare_one_to_many(embedding_model, test_image) -> None: +def test_compare_one_to_many(embedding_model, test_image) -> None: # type: ignore[no-untyped-def] """Test GPU-accelerated one-to-many comparison.""" import torch @@ -253,7 +253,7 @@ def test_compare_one_to_many(embedding_model, test_image) -> None: @pytest.mark.heavy -def test_compare_many_to_many(embedding_model) -> None: +def test_compare_many_to_many(embedding_model) -> None: # type: ignore[no-untyped-def] """Test GPU-accelerated many-to-many comparison.""" import torch @@ -280,7 +280,7 @@ def test_compare_many_to_many(embedding_model) -> None: @pytest.mark.heavy -def test_gpu_query_performance(embedding_model, test_image) -> None: +def test_gpu_query_performance(embedding_model, test_image) -> None: # type: ignore[no-untyped-def] """Test that query method uses GPU acceleration.""" # Create a larger gallery gallery_size = 20 @@ -303,7 +303,7 @@ def test_gpu_query_performance(embedding_model, test_image) -> None: @pytest.mark.heavy -def test_embedding_performance(embedding_model) -> None: +def test_embedding_performance(embedding_model) -> None: # type: ignore[no-untyped-def] """Measure embedding performance over multiple real video frames.""" import time @@ -313,7 +313,7 @@ def test_embedding_performance(embedding_model) -> None: data_dir = "unitree_go2_lidar_corrected" get_data(data_dir) - video_replay = TimedSensorReplay(f"{data_dir}/video") + video_replay = TimedSensorReplay(f"{data_dir}/video") # type: ignore[var-annotated] # Collect 10 real frames from the video test_images = [] diff --git a/dimos/models/embedding/mobileclip.py b/dimos/models/embedding/mobileclip.py index 8ddefd3c87..0aa157c118 100644 --- a/dimos/models/embedding/mobileclip.py +++ b/dimos/models/embedding/mobileclip.py @@ -14,7 +14,7 @@ from pathlib import Path -import open_clip +import open_clip # type: ignore[import-not-found] from PIL import Image as PILImage import torch import torch.nn.functional as F @@ -45,7 +45,7 @@ def __init__( device: Device to run on (cuda/cpu), auto-detects if None normalize: Whether to L2 normalize embeddings """ - if not OPEN_CLIP_AVAILABLE: + if not OPEN_CLIP_AVAILABLE: # type: ignore[name-defined] raise ImportError( "open_clip is required for MobileCLIPModel. " "Install it with: pip install open-clip-torch" diff --git a/dimos/models/embedding/treid.py b/dimos/models/embedding/treid.py index b00ad11250..db5db46a55 100644 --- a/dimos/models/embedding/treid.py +++ b/dimos/models/embedding/treid.py @@ -16,7 +16,7 @@ import torch import torch.nn.functional as F -from torchreid import utils as torchreid_utils +from torchreid import utils as torchreid_utils # type: ignore[import-not-found] from dimos.models.embedding.base import Embedding, EmbeddingModel from dimos.msgs.sensor_msgs import Image @@ -46,7 +46,7 @@ def __init__( device: Device to run on (cuda/cpu), auto-detects if None normalize: Whether to L2 normalize embeddings """ - if not TORCHREID_AVAILABLE: + if not TORCHREID_AVAILABLE: # type: ignore[name-defined] raise ImportError( "torchreid is required for TorchReIDModel. Install it with: pip install torchreid" ) diff --git a/dimos/models/manipulation/contact_graspnet_pytorch/inference.py b/dimos/models/manipulation/contact_graspnet_pytorch/inference.py index fe173dc017..9168049422 100644 --- a/dimos/models/manipulation/contact_graspnet_pytorch/inference.py +++ b/dimos/models/manipulation/contact_graspnet_pytorch/inference.py @@ -2,16 +2,16 @@ import glob import os -from contact_graspnet_pytorch import config_utils -from contact_graspnet_pytorch.checkpoints import CheckpointIO -from contact_graspnet_pytorch.contact_grasp_estimator import GraspEstimator -from contact_graspnet_pytorch.data import load_available_input_data +from contact_graspnet_pytorch import config_utils # type: ignore[import-not-found] +from contact_graspnet_pytorch.checkpoints import CheckpointIO # type: ignore[import-not-found] +from contact_graspnet_pytorch.contact_grasp_estimator import GraspEstimator # type: ignore[import-not-found] +from contact_graspnet_pytorch.data import load_available_input_data # type: ignore[import-not-found] import numpy as np from dimos.utils.data import get_data -def inference(global_config, +def inference(global_config, # type: ignore[no-untyped-def] ckpt_dir, input_paths, local_regions: bool=True, diff --git a/dimos/models/pointcloud/pointcloud_utils.py b/dimos/models/pointcloud/pointcloud_utils.py index 33b4b59607..3d79f3a33d 100644 --- a/dimos/models/pointcloud/pointcloud_utils.py +++ b/dimos/models/pointcloud/pointcloud_utils.py @@ -15,24 +15,24 @@ import random import numpy as np -import open3d as o3d +import open3d as o3d # type: ignore[import-untyped] -def save_pointcloud(pcd, file_path) -> None: +def save_pointcloud(pcd, file_path) -> None: # type: ignore[no-untyped-def] """ Save a point cloud to a file using Open3D. """ o3d.io.write_point_cloud(file_path, pcd) -def restore_pointclouds(pointcloud_paths): +def restore_pointclouds(pointcloud_paths): # type: ignore[no-untyped-def] restored_pointclouds = [] for path in pointcloud_paths: restored_pointclouds.append(o3d.io.read_point_cloud(path)) return restored_pointclouds -def create_point_cloud_from_rgbd(rgb_image, depth_image, intrinsic_parameters): +def create_point_cloud_from_rgbd(rgb_image, depth_image, intrinsic_parameters): # type: ignore[no-untyped-def] rgbd_image = o3d.geometry.RGBDImage.create_from_color_and_depth( o3d.geometry.Image(rgb_image), o3d.geometry.Image(depth_image), @@ -53,7 +53,7 @@ def create_point_cloud_from_rgbd(rgb_image, depth_image, intrinsic_parameters): return pcd -def canonicalize_point_cloud(pcd, canonicalize_threshold: float=0.3): +def canonicalize_point_cloud(pcd, canonicalize_threshold: float=0.3): # type: ignore[no-untyped-def] # Segment the largest plane, assumed to be the floor plane_model, inliers = pcd.segment_plane( distance_threshold=0.01, ransac_n=3, num_iterations=1000 @@ -96,7 +96,7 @@ def canonicalize_point_cloud(pcd, canonicalize_threshold: float=0.3): # Distance calculations -def human_like_distance(distance_meters) -> str: +def human_like_distance(distance_meters) -> str: # type: ignore[no-untyped-def] # Define the choices with units included, focusing on the 0.1 to 10 meters range if distance_meters < 1: # For distances less than 1 meter choices = [ @@ -139,7 +139,7 @@ def human_like_distance(distance_meters) -> str: cumulative_distribution = [] cumulative_sum = 0 for value, unit, probability in choices: - cumulative_sum += probability / total_probability # Normalize probabilities + cumulative_sum += probability / total_probability # type: ignore[assignment] # Normalize probabilities cumulative_distribution.append((cumulative_sum, value, unit)) # Randomly choose based on the cumulative distribution @@ -152,7 +152,7 @@ def human_like_distance(distance_meters) -> str: return f"{choices[-1][0]} {choices[-1][1]}" -def calculate_distances_between_point_clouds(A, B): +def calculate_distances_between_point_clouds(A, B): # type: ignore[no-untyped-def] dist_pcd1_to_pcd2 = np.asarray(A.compute_point_cloud_distance(B)) dist_pcd2_to_pcd1 = np.asarray(B.compute_point_cloud_distance(A)) combined_distances = np.concatenate((dist_pcd1_to_pcd2, dist_pcd2_to_pcd1)) @@ -160,14 +160,14 @@ def calculate_distances_between_point_clouds(A, B): return human_like_distance(avg_dist) -def calculate_centroid(pcd): +def calculate_centroid(pcd): # type: ignore[no-untyped-def] """Calculate the centroid of a point cloud.""" points = np.asarray(pcd.points) centroid = np.mean(points, axis=0) return centroid -def calculate_relative_positions(centroids): +def calculate_relative_positions(centroids): # type: ignore[no-untyped-def] """Calculate the relative positions between centroids of point clouds.""" num_centroids = len(centroids) relative_positions_info = [] @@ -184,7 +184,7 @@ def calculate_relative_positions(centroids): return relative_positions_info -def get_bounding_box_height(pcd): +def get_bounding_box_height(pcd): # type: ignore[no-untyped-def] """ Compute the height of the bounding box for a given point cloud. @@ -198,7 +198,7 @@ def get_bounding_box_height(pcd): return aabb.get_extent()[1] # Assuming the Y-axis is the up-direction -def compare_bounding_box_height(pcd_i, pcd_j): +def compare_bounding_box_height(pcd_i, pcd_j): # type: ignore[no-untyped-def] """ Compare the bounding box heights of two point clouds. @@ -209,7 +209,7 @@ def compare_bounding_box_height(pcd_i, pcd_j): Returns: bool: True if the bounding box of pcd_i is taller than that of pcd_j, False otherwise. """ - height_i = get_bounding_box_height(pcd_i) - height_j = get_bounding_box_height(pcd_j) + height_i = get_bounding_box_height(pcd_i) # type: ignore[no-untyped-call] + height_j = get_bounding_box_height(pcd_j) # type: ignore[no-untyped-call] return height_i > height_j diff --git a/dimos/models/qwen/video_query.py b/dimos/models/qwen/video_query.py index 80bb078bac..0b14bdfbc8 100644 --- a/dimos/models/qwen/video_query.py +++ b/dimos/models/qwen/video_query.py @@ -16,11 +16,11 @@ def query_single_frame_observable( - video_observable: Observable, + video_observable: Observable, # type: ignore[type-arg] query: str, api_key: str | None = None, model_name: str = "qwen2.5-vl-72b-instruct", -) -> Observable: +) -> Observable: # type: ignore[type-arg] """Process a single frame from a video observable with Qwen model. Args: @@ -54,7 +54,7 @@ def query_single_frame_observable( ) # Create response subject - response_subject = Subject() + response_subject = Subject() # type: ignore[var-annotated] # Create temporary agent for processing agent = OpenAIAgent( @@ -87,7 +87,7 @@ def query_single_frame_observable( def query_single_frame( - image: np.ndarray, + image: np.ndarray, # type: ignore[type-arg] query: str = "Return the center coordinates of the fridge handle as a tuple (x,y)", api_key: str | None = None, model_name: str = "qwen2.5-vl-72b-instruct", @@ -140,7 +140,7 @@ def query_single_frame( frame = image # Create a Subject that will emit the image once - frame_subject = Subject() + frame_subject = Subject() # type: ignore[var-annotated] # Subscribe to frame processing agent.subscribe_to_image_processing(frame_subject) @@ -158,11 +158,11 @@ def query_single_frame( # Clean up agent.dispose_all() - return response + return response # type: ignore[no-any-return] def get_bbox_from_qwen( - video_stream: Observable, object_name: str | None = None + video_stream: Observable, object_name: str | None = None # type: ignore[type-arg] ) -> tuple[BBox, float] | None: """Get bounding box coordinates from Qwen for a specific object or any object. @@ -201,7 +201,7 @@ def get_bbox_from_qwen( return None -def get_bbox_from_qwen_frame(frame, object_name: str | None = None) -> BBox | None: +def get_bbox_from_qwen_frame(frame, object_name: str | None = None) -> BBox | None: # type: ignore[no-untyped-def] """Get bounding box coordinates from Qwen for a specific object or any object using a single frame. Args: diff --git a/dimos/models/segmentation/segment_utils.py b/dimos/models/segmentation/segment_utils.py index 59a805afaa..e203c56bf6 100644 --- a/dimos/models/segmentation/segment_utils.py +++ b/dimos/models/segmentation/segment_utils.py @@ -16,7 +16,7 @@ import torch -def find_medoid_and_closest_points(points, num_closest: int=5): +def find_medoid_and_closest_points(points, num_closest: int=5): # type: ignore[no-untyped-def] """ Find the medoid from a collection of points and the closest points to the medoid. @@ -37,11 +37,11 @@ def find_medoid_and_closest_points(points, num_closest: int=5): return medoid, points[closest_indices] -def sample_points_from_heatmap(heatmap, original_size: int, num_points: int=5, percentile: float=0.95): +def sample_points_from_heatmap(heatmap, original_size: int, num_points: int=5, percentile: float=0.95): # type: ignore[no-untyped-def] """ Sample points from the given heatmap, focusing on areas with higher values. """ - width, height = original_size + width, height = original_size # type: ignore[misc] threshold = np.percentile(heatmap.numpy(), percentile) masked_heatmap = torch.where(heatmap > threshold, heatmap, torch.tensor(0.0)) probabilities = torch.softmax(masked_heatmap.flatten(), dim=0) @@ -57,13 +57,13 @@ def sample_points_from_heatmap(heatmap, original_size: int, num_points: int=5, p pts = [] for pt in sampled_coords.tolist(): x, y = pt - x = height * x / w - y = width * y / w + x = height * x / w # type: ignore[has-type] + y = width * y / w # type: ignore[has-type] pts.append([y, x]) return pts -def apply_mask_to_image(image, mask): +def apply_mask_to_image(image, mask): # type: ignore[no-untyped-def] """ Apply a binary mask to an image. The mask should be a binary array where the regions to keep are True. """ diff --git a/dimos/models/vl/base.py b/dimos/models/vl/base.py index 7e162b3ccf..acb998d274 100644 --- a/dimos/models/vl/base.py +++ b/dimos/models/vl/base.py @@ -12,7 +12,7 @@ def vlm_detection_to_detection2d( - vlm_detection: list, track_id: int, image: Image + vlm_detection: list, track_id: int, image: Image # type: ignore[type-arg] ) -> Detection2DBBox | None: """Convert a single VLM detection [label, x1, y1, x2, y2] to Detection2DBBox. @@ -50,7 +50,7 @@ def vlm_detection_to_detection2d( # Use -1 for class_id since VLM doesn't provide it # confidence defaults to 1.0 for VLM return Detection2DBBox( - bbox=bbox, + bbox=bbox, # type: ignore[arg-type] track_id=track_id, class_id=-1, confidence=1.0, @@ -62,22 +62,22 @@ def vlm_detection_to_detection2d( class VlModel(ABC): @abstractmethod - def query(self, image: Image, query: str, **kwargs) -> str: ... + def query(self, image: Image, query: str, **kwargs) -> str: ... # type: ignore[no-untyped-def] def warmup(self) -> None: try: - image = Image.from_file(get_data("cafe-smol.jpg")).to_rgb() - self._model.detect(image, "person", settings={"max_objects": 1}) + image = Image.from_file(get_data("cafe-smol.jpg")).to_rgb() # type: ignore[arg-type] + self._model.detect(image, "person", settings={"max_objects": 1}) # type: ignore[attr-defined] except Exception: pass # requery once if JSON parsing fails - @retry(max_retries=2, on_exception=json.JSONDecodeError, delay=0.0) - def query_json(self, image: Image, query: str) -> dict: + @retry(max_retries=2, on_exception=json.JSONDecodeError, delay=0.0) # type: ignore[misc] + def query_json(self, image: Image, query: str) -> dict: # type: ignore[type-arg] response = self.query(image, query) - return extract_json(response) + return extract_json(response) # type: ignore[return-value] - def query_detections(self, image: Image, query: str, **kwargs) -> ImageDetections2D: + def query_detections(self, image: Image, query: str, **kwargs) -> ImageDetections2D: # type: ignore[no-untyped-def] full_query = f"""show me bounding boxes in pixels for this query: `{query}` format should be: diff --git a/dimos/models/vl/moondream.py b/dimos/models/vl/moondream.py index 781f1adbf1..485377e305 100644 --- a/dimos/models/vl/moondream.py +++ b/dimos/models/vl/moondream.py @@ -4,7 +4,7 @@ import numpy as np from PIL import Image as PILImage import torch -from transformers import AutoModelForCausalLM +from transformers import AutoModelForCausalLM # type: ignore[import-untyped] from dimos.models.vl.base import VlModel from dimos.msgs.sensor_msgs import Image @@ -38,7 +38,7 @@ def _model(self) -> AutoModelForCausalLM: return model - def query(self, image: Image | np.ndarray, query: str, **kwargs) -> str: + def query(self, image: Image | np.ndarray, query: str, **kwargs) -> str: # type: ignore[no-untyped-def, type-arg] if isinstance(image, np.ndarray): warnings.warn( "MoondreamVlModel.query should receive standard dimos Image type, not a numpy array", @@ -57,11 +57,11 @@ def query(self, image: Image | np.ndarray, query: str, **kwargs) -> str: # Handle both dict and string responses if isinstance(result, dict): - return result.get("answer", str(result)) + return result.get("answer", str(result)) # type: ignore[no-any-return] return str(result) - def query_detections(self, image: Image, query: str, **kwargs) -> ImageDetections2D: + def query_detections(self, image: Image, query: str, **kwargs) -> ImageDetections2D: # type: ignore[no-untyped-def] """Detect objects using Moondream's native detect method. Args: diff --git a/dimos/models/vl/moondream_hosted.py b/dimos/models/vl/moondream_hosted.py index 1bf0f43e67..6754f4177c 100644 --- a/dimos/models/vl/moondream_hosted.py +++ b/dimos/models/vl/moondream_hosted.py @@ -2,7 +2,7 @@ import warnings from functools import cached_property -import moondream as md +import moondream as md # type: ignore[import-untyped] import numpy as np from PIL import Image as PILImage @@ -26,7 +26,7 @@ def _client(self) -> md.vl: ) return md.vl(api_key=api_key) - def _to_pil_image(self, image: Image | np.ndarray) -> PILImage.Image: + def _to_pil_image(self, image: Image | np.ndarray) -> PILImage.Image: # type: ignore[type-arg] if isinstance(image, np.ndarray): warnings.warn( "MoondreamHostedVlModel should receive standard dimos Image type, not a numpy array", @@ -38,13 +38,13 @@ def _to_pil_image(self, image: Image | np.ndarray) -> PILImage.Image: rgb_image = image.to_rgb() return PILImage.fromarray(rgb_image.data) - def query(self, image: Image | np.ndarray, query: str, **kwargs) -> str: + def query(self, image: Image | np.ndarray, query: str, **kwargs) -> str: # type: ignore[no-untyped-def, type-arg] pil_image = self._to_pil_image(image) result = self._client.query(pil_image, query) - return result.get("answer", str(result)) + return result.get("answer", str(result)) # type: ignore[no-any-return] - def caption(self, image: Image | np.ndarray, length: str = "normal") -> str: + def caption(self, image: Image | np.ndarray, length: str = "normal") -> str: # type: ignore[type-arg] """Generate a caption for the image. Args: @@ -53,9 +53,9 @@ def caption(self, image: Image | np.ndarray, length: str = "normal") -> str: """ pil_image = self._to_pil_image(image) result = self._client.caption(pil_image, length=length) - return result.get("caption", str(result)) + return result.get("caption", str(result)) # type: ignore[no-any-return] - def query_detections(self, image: Image, query: str, **kwargs) -> ImageDetections2D: + def query_detections(self, image: Image, query: str, **kwargs) -> ImageDetections2D: # type: ignore[no-untyped-def] """Detect objects using Moondream's hosted detect method. Args: diff --git a/dimos/models/vl/qwen.py b/dimos/models/vl/qwen.py index 773fcc35ad..4a5948b486 100644 --- a/dimos/models/vl/qwen.py +++ b/dimos/models/vl/qwen.py @@ -29,7 +29,7 @@ def _client(self) -> OpenAI: api_key=api_key, ) - def query(self, image: Image | np.ndarray, query: str) -> str: + def query(self, image: Image | np.ndarray, query: str) -> str: # type: ignore[override, type-arg] if isinstance(image, np.ndarray): import warnings @@ -59,4 +59,4 @@ def query(self, image: Image | np.ndarray, query: str) -> str: ], ) - return response.choices[0].message.content + return response.choices[0].message.content # type: ignore[return-value] diff --git a/dimos/msgs/foxglove_msgs/Color.py b/dimos/msgs/foxglove_msgs/Color.py index ed19911eb7..c813a4d78c 100644 --- a/dimos/msgs/foxglove_msgs/Color.py +++ b/dimos/msgs/foxglove_msgs/Color.py @@ -16,10 +16,10 @@ import hashlib -from dimos_lcm.foxglove_msgs import Color as LCMColor +from dimos_lcm.foxglove_msgs import Color as LCMColor # type: ignore[import-untyped] -class Color(LCMColor): +class Color(LCMColor): # type: ignore[misc] """Color with convenience methods.""" @classmethod diff --git a/dimos/msgs/foxglove_msgs/ImageAnnotations.py b/dimos/msgs/foxglove_msgs/ImageAnnotations.py index 1f58b09d73..6ee5f1228d 100644 --- a/dimos/msgs/foxglove_msgs/ImageAnnotations.py +++ b/dimos/msgs/foxglove_msgs/ImageAnnotations.py @@ -12,10 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -from dimos_lcm.foxglove_msgs.ImageAnnotations import ImageAnnotations as FoxgloveImageAnnotations +from dimos_lcm.foxglove_msgs.ImageAnnotations import ImageAnnotations as FoxgloveImageAnnotations # type: ignore[import-untyped] -class ImageAnnotations(FoxgloveImageAnnotations): +class ImageAnnotations(FoxgloveImageAnnotations): # type: ignore[misc] def __add__(self, other: "ImageAnnotations") -> "ImageAnnotations": points = self.points + other.points texts = self.texts + other.texts @@ -29,5 +29,5 @@ def __add__(self, other: "ImageAnnotations") -> "ImageAnnotations": def agent_encode(self) -> str: if len(self.texts) == 0: - return None - return list(map(lambda t: t.text, self.texts)) + return None # type: ignore[return-value] + return list(map(lambda t: t.text, self.texts)) # type: ignore[return-value] diff --git a/dimos/msgs/geometry_msgs/Pose.py b/dimos/msgs/geometry_msgs/Pose.py index 0bb69d84bf..6eb9b3aff1 100644 --- a/dimos/msgs/geometry_msgs/Pose.py +++ b/dimos/msgs/geometry_msgs/Pose.py @@ -16,14 +16,14 @@ from typing import TypeAlias -from dimos_lcm.geometry_msgs import Pose as LCMPose, Transform as LCMTransform +from dimos_lcm.geometry_msgs import Pose as LCMPose, Transform as LCMTransform # type: ignore[import-untyped] try: - from geometry_msgs.msg import Point as ROSPoint, Pose as ROSPose, Quaternion as ROSQuaternion + from geometry_msgs.msg import Point as ROSPoint, Pose as ROSPose, Quaternion as ROSQuaternion # type: ignore[attr-defined] except ImportError: - ROSPose = None - ROSPoint = None - ROSQuaternion = None + ROSPose = None # type: ignore[assignment, misc] + ROSPoint = None # type: ignore[assignment, misc] + ROSQuaternion = None # type: ignore[assignment, misc] from plum import dispatch @@ -40,7 +40,7 @@ ) -class Pose(LCMPose): +class Pose(LCMPose): # type: ignore[misc] position: Vector3 orientation: Quaternion msg_name = "geometry_msgs.Pose" @@ -51,13 +51,13 @@ def __init__(self) -> None: self.position = Vector3(0.0, 0.0, 0.0) self.orientation = Quaternion(0.0, 0.0, 0.0, 1.0) - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, x: int | float, y: int | float, z: int | float) -> None: """Initialize a pose with position and identity orientation.""" self.position = Vector3(x, y, z) self.orientation = Quaternion(0.0, 0.0, 0.0, 1.0) - @dispatch + @dispatch # type: ignore[no-redef] def __init__( self, x: int | float, @@ -72,7 +72,7 @@ def __init__( self.position = Vector3(x, y, z) self.orientation = Quaternion(qx, qy, qz, qw) - @dispatch + @dispatch # type: ignore[no-redef] def __init__( self, position: VectorConvertable | Vector3 | None = None, @@ -86,25 +86,25 @@ def __init__( self.position = Vector3(position) self.orientation = Quaternion(orientation) - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, pose_tuple: tuple[VectorConvertable, QuaternionConvertable]) -> None: """Initialize from a tuple of (position, orientation).""" self.position = Vector3(pose_tuple[0]) self.orientation = Quaternion(pose_tuple[1]) - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, pose_dict: dict[str, VectorConvertable | QuaternionConvertable]) -> None: """Initialize from a dictionary with 'position' and 'orientation' keys.""" self.position = Vector3(pose_dict["position"]) self.orientation = Quaternion(pose_dict["orientation"]) - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, pose: Pose) -> None: """Initialize from another Pose (copy constructor).""" self.position = Vector3(pose.position) self.orientation = Quaternion(pose.orientation) - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, lcm_pose: LCMPose) -> None: """Initialize from an LCM Pose.""" self.position = Vector3(lcm_pose.position.x, lcm_pose.position.y, lcm_pose.position.z) @@ -155,7 +155,7 @@ def __str__(self) -> str: f"quaternion=[{self.orientation}])" ) - def __eq__(self, other) -> bool: + def __eq__(self, other) -> bool: # type: ignore[no-untyped-def] """Check if two poses are equal.""" if not isinstance(other, Pose): return False @@ -240,11 +240,11 @@ def to_ros_msg(self) -> ROSPose: Returns: ROS Pose message """ - ros_msg = ROSPose() - ros_msg.position = ROSPoint( + ros_msg = ROSPose() # type: ignore[no-untyped-call] + ros_msg.position = ROSPoint( # type: ignore[no-untyped-call] x=float(self.position.x), y=float(self.position.y), z=float(self.position.z) ) - ros_msg.orientation = ROSQuaternion( + ros_msg.orientation = ROSQuaternion( # type: ignore[no-untyped-call] x=float(self.orientation.x), y=float(self.orientation.y), z=float(self.orientation.z), @@ -259,7 +259,7 @@ def to_pose(value: Pose) -> Pose: return value -@dispatch +@dispatch # type: ignore[no-redef] def to_pose(value: PoseConvertable) -> Pose: """Convert a pose-compatible value to a Pose object.""" return Pose(value) diff --git a/dimos/msgs/geometry_msgs/PoseStamped.py b/dimos/msgs/geometry_msgs/PoseStamped.py index 770f41b641..803a621d80 100644 --- a/dimos/msgs/geometry_msgs/PoseStamped.py +++ b/dimos/msgs/geometry_msgs/PoseStamped.py @@ -17,12 +17,12 @@ import time from typing import BinaryIO, TypeAlias -from dimos_lcm.geometry_msgs import PoseStamped as LCMPoseStamped +from dimos_lcm.geometry_msgs import PoseStamped as LCMPoseStamped # type: ignore[import-untyped] try: - from geometry_msgs.msg import PoseStamped as ROSPoseStamped + from geometry_msgs.msg import PoseStamped as ROSPoseStamped # type: ignore[attr-defined] except ImportError: - ROSPoseStamped = None + ROSPoseStamped = None # type: ignore[assignment, misc] from plum import dispatch @@ -40,7 +40,7 @@ ) -def sec_nsec(ts): +def sec_nsec(ts): # type: ignore[no-untyped-def] s = int(ts) return [s, int((ts - s) * 1_000_000_000)] @@ -51,7 +51,7 @@ class PoseStamped(Pose, Timestamped): frame_id: str @dispatch - def __init__(self, ts: float = 0.0, frame_id: str = "", **kwargs) -> None: + def __init__(self, ts: float = 0.0, frame_id: str = "", **kwargs) -> None: # type: ignore[no-untyped-def] self.frame_id = frame_id self.ts = ts if ts != 0 else time.time() super().__init__(**kwargs) @@ -59,9 +59,9 @@ def __init__(self, ts: float = 0.0, frame_id: str = "", **kwargs) -> None: def lcm_encode(self) -> bytes: lcm_mgs = LCMPoseStamped() lcm_mgs.pose = self - [lcm_mgs.header.stamp.sec, lcm_mgs.header.stamp.nsec] = sec_nsec(self.ts) + [lcm_mgs.header.stamp.sec, lcm_mgs.header.stamp.nsec] = sec_nsec(self.ts) # type: ignore[no-untyped-call] lcm_mgs.header.frame_id = self.frame_id - return lcm_mgs.lcm_encode() + return lcm_mgs.lcm_encode() # type: ignore[no-any-return] @classmethod def lcm_decode(cls, data: bytes | BinaryIO) -> PoseStamped: @@ -113,7 +113,7 @@ def find_transform(self, other: PoseStamped) -> Transform: ) @classmethod - def from_ros_msg(cls, ros_msg: ROSPoseStamped) -> PoseStamped: + def from_ros_msg(cls, ros_msg: ROSPoseStamped) -> PoseStamped: # type: ignore[override] """Create a PoseStamped from a ROS geometry_msgs/PoseStamped message. Args: @@ -135,13 +135,13 @@ def from_ros_msg(cls, ros_msg: ROSPoseStamped) -> PoseStamped: orientation=pose.orientation, ) - def to_ros_msg(self) -> ROSPoseStamped: + def to_ros_msg(self) -> ROSPoseStamped: # type: ignore[override] """Convert to a ROS geometry_msgs/PoseStamped message. Returns: ROS PoseStamped message """ - ros_msg = ROSPoseStamped() + ros_msg = ROSPoseStamped() # type: ignore[no-untyped-call] # Set header ros_msg.header.frame_id = self.frame_id diff --git a/dimos/msgs/geometry_msgs/PoseWithCovariance.py b/dimos/msgs/geometry_msgs/PoseWithCovariance.py index ba2c360935..dc2302986b 100644 --- a/dimos/msgs/geometry_msgs/PoseWithCovariance.py +++ b/dimos/msgs/geometry_msgs/PoseWithCovariance.py @@ -16,14 +16,14 @@ from typing import TYPE_CHECKING, TypeAlias -from dimos_lcm.geometry_msgs import PoseWithCovariance as LCMPoseWithCovariance +from dimos_lcm.geometry_msgs import PoseWithCovariance as LCMPoseWithCovariance # type: ignore[import-untyped] import numpy as np from plum import dispatch try: - from geometry_msgs.msg import PoseWithCovariance as ROSPoseWithCovariance + from geometry_msgs.msg import PoseWithCovariance as ROSPoseWithCovariance # type: ignore[attr-defined] except ImportError: - ROSPoseWithCovariance = None + ROSPoseWithCovariance = None # type: ignore[assignment, misc] from dimos.msgs.geometry_msgs.Pose import Pose, PoseConvertable @@ -33,13 +33,13 @@ # Types that can be converted to/from PoseWithCovariance PoseWithCovarianceConvertable: TypeAlias = ( - tuple[PoseConvertable, list[float] | np.ndarray] + tuple[PoseConvertable, list[float] | np.ndarray] # type: ignore[type-arg] | LCMPoseWithCovariance - | dict[str, PoseConvertable | list[float] | np.ndarray] + | dict[str, PoseConvertable | list[float] | np.ndarray] # type: ignore[type-arg] ) -class PoseWithCovariance(LCMPoseWithCovariance): +class PoseWithCovariance(LCMPoseWithCovariance): # type: ignore[misc] pose: Pose msg_name = "geometry_msgs.PoseWithCovariance" @@ -49,9 +49,9 @@ def __init__(self) -> None: self.pose = Pose() self.covariance = np.zeros(36) - @dispatch + @dispatch # type: ignore[no-redef] def __init__( - self, pose: Pose | PoseConvertable, covariance: list[float] | np.ndarray | None = None + self, pose: Pose | PoseConvertable, covariance: list[float] | np.ndarray | None = None # type: ignore[type-arg] ) -> None: """Initialize with pose and optional covariance.""" self.pose = Pose(pose) if not isinstance(pose, Pose) else pose @@ -60,20 +60,20 @@ def __init__( else: self.covariance = np.array(covariance, dtype=float).reshape(36) - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, pose_with_cov: PoseWithCovariance) -> None: """Initialize from another PoseWithCovariance (copy constructor).""" self.pose = Pose(pose_with_cov.pose) self.covariance = np.array(pose_with_cov.covariance).copy() - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, lcm_pose_with_cov: LCMPoseWithCovariance) -> None: """Initialize from an LCM PoseWithCovariance.""" self.pose = Pose(lcm_pose_with_cov.pose) self.covariance = np.array(lcm_pose_with_cov.covariance) - @dispatch - def __init__(self, pose_dict: dict[str, PoseConvertable | list[float] | np.ndarray]) -> None: + @dispatch # type: ignore[no-redef] + def __init__(self, pose_dict: dict[str, PoseConvertable | list[float] | np.ndarray]) -> None: # type: ignore[type-arg] """Initialize from a dictionary with 'pose' and 'covariance' keys.""" self.pose = Pose(pose_dict["pose"]) covariance = pose_dict.get("covariance") @@ -82,13 +82,13 @@ def __init__(self, pose_dict: dict[str, PoseConvertable | list[float] | np.ndarr else: self.covariance = np.array(covariance, dtype=float).reshape(36) - @dispatch - def __init__(self, pose_tuple: tuple[PoseConvertable, list[float] | np.ndarray]) -> None: + @dispatch # type: ignore[no-redef] + def __init__(self, pose_tuple: tuple[PoseConvertable, list[float] | np.ndarray]) -> None: # type: ignore[type-arg] """Initialize from a tuple of (pose, covariance).""" self.pose = Pose(pose_tuple[0]) self.covariance = np.array(pose_tuple[1], dtype=float).reshape(36) - def __getattribute__(self, name: str): + def __getattribute__(self, name: str): # type: ignore[no-untyped-def] """Override to ensure covariance is always returned as numpy array.""" if name == "covariance": cov = object.__getattribute__(self, "covariance") @@ -97,7 +97,7 @@ def __getattribute__(self, name: str): return cov return super().__getattribute__(name) - def __setattr__(self, name: str, value) -> None: + def __setattr__(self, name: str, value) -> None: # type: ignore[no-untyped-def] """Override to ensure covariance is stored as numpy array.""" if name == "covariance": if not isinstance(value, np.ndarray): @@ -145,17 +145,17 @@ def yaw(self) -> float: return self.pose.yaw @property - def covariance_matrix(self) -> np.ndarray: + def covariance_matrix(self) -> np.ndarray: # type: ignore[type-arg] """Get covariance as 6x6 matrix.""" - return self.covariance.reshape(6, 6) + return self.covariance.reshape(6, 6) # type: ignore[has-type, no-any-return] @covariance_matrix.setter - def covariance_matrix(self, value: np.ndarray) -> None: + def covariance_matrix(self, value: np.ndarray) -> None: # type: ignore[type-arg] """Set covariance from 6x6 matrix.""" - self.covariance = np.array(value).reshape(36) + self.covariance = np.array(value).reshape(36) # type: ignore[has-type] def __repr__(self) -> str: - return f"PoseWithCovariance(pose={self.pose!r}, covariance=<{self.covariance.shape[0] if isinstance(self.covariance, np.ndarray) else len(self.covariance)} elements>)" + return f"PoseWithCovariance(pose={self.pose!r}, covariance=<{self.covariance.shape[0] if isinstance(self.covariance, np.ndarray) else len(self.covariance)} elements>)" # type: ignore[has-type] def __str__(self) -> str: return ( @@ -164,22 +164,22 @@ def __str__(self) -> str: f"cov_trace={np.trace(self.covariance_matrix):.3f})" ) - def __eq__(self, other) -> bool: + def __eq__(self, other) -> bool: # type: ignore[no-untyped-def] """Check if two PoseWithCovariance are equal.""" if not isinstance(other, PoseWithCovariance): return False - return self.pose == other.pose and np.allclose(self.covariance, other.covariance) + return self.pose == other.pose and np.allclose(self.covariance, other.covariance) # type: ignore[has-type] def lcm_encode(self) -> bytes: """Encode to LCM binary format.""" lcm_msg = LCMPoseWithCovariance() lcm_msg.pose = self.pose # LCM expects list, not numpy array - if isinstance(self.covariance, np.ndarray): - lcm_msg.covariance = self.covariance.tolist() + if isinstance(self.covariance, np.ndarray): # type: ignore[has-type] + lcm_msg.covariance = self.covariance.tolist() # type: ignore[has-type] else: - lcm_msg.covariance = list(self.covariance) - return lcm_msg.lcm_encode() + lcm_msg.covariance = list(self.covariance) # type: ignore[has-type] + return lcm_msg.lcm_encode() # type: ignore[no-any-return] @classmethod def lcm_decode(cls, data: bytes) -> PoseWithCovariance: @@ -217,11 +217,11 @@ def to_ros_msg(self) -> ROSPoseWithCovariance: ROS PoseWithCovariance message """ - ros_msg = ROSPoseWithCovariance() + ros_msg = ROSPoseWithCovariance() # type: ignore[no-untyped-call] ros_msg.pose = self.pose.to_ros_msg() # ROS expects list, not numpy array - if isinstance(self.covariance, np.ndarray): - ros_msg.covariance = self.covariance.tolist() + if isinstance(self.covariance, np.ndarray): # type: ignore[has-type] + ros_msg.covariance = self.covariance.tolist() # type: ignore[has-type] else: - ros_msg.covariance = list(self.covariance) + ros_msg.covariance = list(self.covariance) # type: ignore[has-type] return ros_msg diff --git a/dimos/msgs/geometry_msgs/PoseWithCovarianceStamped.py b/dimos/msgs/geometry_msgs/PoseWithCovarianceStamped.py index 3683a15fbd..1174057be7 100644 --- a/dimos/msgs/geometry_msgs/PoseWithCovarianceStamped.py +++ b/dimos/msgs/geometry_msgs/PoseWithCovarianceStamped.py @@ -17,14 +17,14 @@ import time from typing import TypeAlias -from dimos_lcm.geometry_msgs import PoseWithCovarianceStamped as LCMPoseWithCovarianceStamped +from dimos_lcm.geometry_msgs import PoseWithCovarianceStamped as LCMPoseWithCovarianceStamped # type: ignore[import-untyped] import numpy as np from plum import dispatch try: - from geometry_msgs.msg import PoseWithCovarianceStamped as ROSPoseWithCovarianceStamped + from geometry_msgs.msg import PoseWithCovarianceStamped as ROSPoseWithCovarianceStamped # type: ignore[attr-defined] except ImportError: - ROSPoseWithCovarianceStamped = None + ROSPoseWithCovarianceStamped = None # type: ignore[assignment, misc] from dimos.msgs.geometry_msgs.Pose import Pose, PoseConvertable from dimos.msgs.geometry_msgs.PoseWithCovariance import PoseWithCovariance @@ -32,13 +32,13 @@ # Types that can be converted to/from PoseWithCovarianceStamped PoseWithCovarianceStampedConvertable: TypeAlias = ( - tuple[PoseConvertable, list[float] | np.ndarray] + tuple[PoseConvertable, list[float] | np.ndarray] # type: ignore[type-arg] | LCMPoseWithCovarianceStamped - | dict[str, PoseConvertable | list[float] | np.ndarray | float | str] + | dict[str, PoseConvertable | list[float] | np.ndarray | float | str] # type: ignore[type-arg] ) -def sec_nsec(ts): +def sec_nsec(ts): # type: ignore[no-untyped-def] s = int(ts) return [s, int((ts - s) * 1_000_000_000)] @@ -55,13 +55,13 @@ def __init__(self, ts: float = 0.0, frame_id: str = "", **kwargs) -> None: self.ts = ts if ts != 0 else time.time() super().__init__(**kwargs) - @dispatch + @dispatch # type: ignore[no-redef] def __init__( self, ts: float = 0.0, frame_id: str = "", pose: Pose | PoseConvertable | None = None, - covariance: list[float] | np.ndarray | None = None, + covariance: list[float] | np.ndarray | None = None, # type: ignore[type-arg] ) -> None: """Initialize with timestamp, frame_id, pose and covariance.""" self.frame_id = frame_id @@ -75,13 +75,13 @@ def lcm_encode(self) -> bytes: lcm_msg = LCMPoseWithCovarianceStamped() lcm_msg.pose.pose = self.pose # LCM expects list, not numpy array - if isinstance(self.covariance, np.ndarray): - lcm_msg.pose.covariance = self.covariance.tolist() + if isinstance(self.covariance, np.ndarray): # type: ignore[has-type] + lcm_msg.pose.covariance = self.covariance.tolist() # type: ignore[has-type] else: - lcm_msg.pose.covariance = list(self.covariance) - [lcm_msg.header.stamp.sec, lcm_msg.header.stamp.nsec] = sec_nsec(self.ts) + lcm_msg.pose.covariance = list(self.covariance) # type: ignore[has-type] + [lcm_msg.header.stamp.sec, lcm_msg.header.stamp.nsec] = sec_nsec(self.ts) # type: ignore[no-untyped-call] lcm_msg.header.frame_id = self.frame_id - return lcm_msg.lcm_encode() + return lcm_msg.lcm_encode() # type: ignore[no-any-return] @classmethod def lcm_decode(cls, data: bytes) -> PoseWithCovarianceStamped: @@ -113,7 +113,7 @@ def __str__(self) -> str: ) @classmethod - def from_ros_msg(cls, ros_msg: ROSPoseWithCovarianceStamped) -> PoseWithCovarianceStamped: + def from_ros_msg(cls, ros_msg: ROSPoseWithCovarianceStamped) -> PoseWithCovarianceStamped: # type: ignore[override] """Create a PoseWithCovarianceStamped from a ROS geometry_msgs/PoseWithCovarianceStamped message. Args: @@ -133,17 +133,17 @@ def from_ros_msg(cls, ros_msg: ROSPoseWithCovarianceStamped) -> PoseWithCovarian ts=ts, frame_id=ros_msg.header.frame_id, pose=pose_with_cov.pose, - covariance=pose_with_cov.covariance, + covariance=pose_with_cov.covariance, # type: ignore[has-type] ) - def to_ros_msg(self) -> ROSPoseWithCovarianceStamped: + def to_ros_msg(self) -> ROSPoseWithCovarianceStamped: # type: ignore[override] """Convert to a ROS geometry_msgs/PoseWithCovarianceStamped message. Returns: ROS PoseWithCovarianceStamped message """ - ros_msg = ROSPoseWithCovarianceStamped() + ros_msg = ROSPoseWithCovarianceStamped() # type: ignore[no-untyped-call] # Set header ros_msg.header.frame_id = self.frame_id @@ -153,9 +153,9 @@ def to_ros_msg(self) -> ROSPoseWithCovarianceStamped: # Set pose with covariance ros_msg.pose.pose = self.pose.to_ros_msg() # ROS expects list, not numpy array - if isinstance(self.covariance, np.ndarray): - ros_msg.pose.covariance = self.covariance.tolist() + if isinstance(self.covariance, np.ndarray): # type: ignore[has-type] + ros_msg.pose.covariance = self.covariance.tolist() # type: ignore[has-type] else: - ros_msg.pose.covariance = list(self.covariance) + ros_msg.pose.covariance = list(self.covariance) # type: ignore[has-type] return ros_msg diff --git a/dimos/msgs/geometry_msgs/Quaternion.py b/dimos/msgs/geometry_msgs/Quaternion.py index 6ce8c3bf2d..14933ab712 100644 --- a/dimos/msgs/geometry_msgs/Quaternion.py +++ b/dimos/msgs/geometry_msgs/Quaternion.py @@ -19,7 +19,7 @@ import struct from typing import BinaryIO, TypeAlias -from dimos_lcm.geometry_msgs import Quaternion as LCMQuaternion +from dimos_lcm.geometry_msgs import Quaternion as LCMQuaternion # type: ignore[import-untyped] import numpy as np from plum import dispatch from scipy.spatial.transform import Rotation as R @@ -27,10 +27,10 @@ from dimos.msgs.geometry_msgs.Vector3 import Vector3 # Types that can be converted to/from Quaternion -QuaternionConvertable: TypeAlias = Sequence[int | float] | LCMQuaternion | np.ndarray +QuaternionConvertable: TypeAlias = Sequence[int | float] | LCMQuaternion | np.ndarray # type: ignore[type-arg] -class Quaternion(LCMQuaternion): +class Quaternion(LCMQuaternion): # type: ignore[misc] x: float = 0.0 y: float = 0.0 z: float = 0.0 @@ -38,29 +38,29 @@ class Quaternion(LCMQuaternion): msg_name = "geometry_msgs.Quaternion" @classmethod - def lcm_decode(cls, data: bytes | BinaryIO): + def lcm_decode(cls, data: bytes | BinaryIO): # type: ignore[no-untyped-def] if not hasattr(data, "read"): data = BytesIO(data) if data.read(8) != cls._get_packed_fingerprint(): raise ValueError("Decode error") - return cls._lcm_decode_one(data) + return cls._lcm_decode_one(data) # type: ignore[no-untyped-call] @classmethod - def _lcm_decode_one(cls, buf): + def _lcm_decode_one(cls, buf): # type: ignore[no-untyped-def] return cls(struct.unpack(">dddd", buf.read(32))) @dispatch def __init__(self) -> None: ... - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, x: int | float, y: int | float, z: int | float, w: int | float) -> None: self.x = float(x) self.y = float(y) self.z = float(z) self.w = float(w) - @dispatch - def __init__(self, sequence: Sequence[int | float] | np.ndarray) -> None: + @dispatch # type: ignore[no-redef] + def __init__(self, sequence: Sequence[int | float] | np.ndarray) -> None: # type: ignore[type-arg] if isinstance(sequence, np.ndarray): if sequence.size != 4: raise ValueError("Quaternion requires exactly 4 components [x, y, z, w]") @@ -73,12 +73,12 @@ def __init__(self, sequence: Sequence[int | float] | np.ndarray) -> None: self.z = sequence[2] self.w = sequence[3] - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, quaternion: Quaternion) -> None: """Initialize from another Quaternion (copy constructor).""" self.x, self.y, self.z, self.w = quaternion.x, quaternion.y, quaternion.z, quaternion.w - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, lcm_quaternion: LCMQuaternion) -> None: """Initialize from an LCM Quaternion.""" self.x, self.y, self.z, self.w = ( @@ -96,7 +96,7 @@ def to_list(self) -> list[float]: """List representation of the quaternion (x, y, z, w).""" return [self.x, self.y, self.z, self.w] - def to_numpy(self) -> np.ndarray: + def to_numpy(self) -> np.ndarray: # type: ignore[type-arg] """Numpy array representation of the quaternion (x, y, z, w).""" return np.array([self.x, self.y, self.z, self.w]) @@ -170,7 +170,7 @@ def __repr__(self) -> str: def __str__(self) -> str: return self.__repr__() - def __eq__(self, other) -> bool: + def __eq__(self, other) -> bool: # type: ignore[no-untyped-def] if not isinstance(other, Quaternion): return False return self.x == other.x and self.y == other.y and self.z == other.z and self.w == other.w diff --git a/dimos/msgs/geometry_msgs/Transform.py b/dimos/msgs/geometry_msgs/Transform.py index fc22a30bf1..a2f8eaf58c 100644 --- a/dimos/msgs/geometry_msgs/Transform.py +++ b/dimos/msgs/geometry_msgs/Transform.py @@ -17,23 +17,23 @@ import time from typing import BinaryIO -from dimos_lcm.geometry_msgs import ( +from dimos_lcm.geometry_msgs import ( # type: ignore[import-untyped] Transform as LCMTransform, TransformStamped as LCMTransformStamped, ) try: - from geometry_msgs.msg import ( + from geometry_msgs.msg import ( # type: ignore[attr-defined] Quaternion as ROSQuaternion, Transform as ROSTransform, TransformStamped as ROSTransformStamped, Vector3 as ROSVector3, ) except ImportError: - ROSTransformStamped = None - ROSTransform = None - ROSVector3 = None - ROSQuaternion = None + ROSTransformStamped = None # type: ignore[assignment, misc] + ROSTransform = None # type: ignore[assignment, misc] + ROSVector3 = None # type: ignore[assignment, misc] + ROSQuaternion = None # type: ignore[assignment, misc] from dimos.msgs.geometry_msgs.Quaternion import Quaternion from dimos.msgs.geometry_msgs.Vector3 import Vector3 @@ -49,7 +49,7 @@ class Transform(Timestamped): child_frame_id: str msg_name = "tf2_msgs.TFMessage" - def __init__( + def __init__( # type: ignore[no-untyped-def] self, translation: Vector3 | None = None, rotation: Quaternion | None = None, @@ -80,7 +80,7 @@ def __repr__(self) -> str: def __str__(self) -> str: return f"Transform:\n {self.frame_id} -> {self.child_frame_id} Translation: {self.translation}\n Rotation: {self.rotation}" - def __eq__(self, other) -> bool: + def __eq__(self, other) -> bool: # type: ignore[no-untyped-def] """Check if two transforms are equal.""" if not isinstance(other, Transform): return False @@ -209,7 +209,7 @@ def to_ros_transform_stamped(self) -> ROSTransformStamped: ROS TransformStamped message """ - ros_msg = ROSTransformStamped() + ros_msg = ROSTransformStamped() # type: ignore[no-untyped-call] # Set header ros_msg.header.frame_id = self.frame_id @@ -220,10 +220,10 @@ def to_ros_transform_stamped(self) -> ROSTransformStamped: ros_msg.child_frame_id = self.child_frame_id # Set transform - ros_msg.transform.translation = ROSVector3( + ros_msg.transform.translation = ROSVector3( # type: ignore[no-untyped-call] x=self.translation.x, y=self.translation.y, z=self.translation.z ) - ros_msg.transform.rotation = ROSQuaternion( + ros_msg.transform.rotation = ROSQuaternion( # type: ignore[no-untyped-call] x=self.rotation.x, y=self.rotation.y, z=self.rotation.z, w=self.rotation.w ) @@ -234,7 +234,7 @@ def __neg__(self) -> Transform: return self.inverse() @classmethod - def from_pose(cls, frame_id: str, pose: Pose | PoseStamped) -> Transform: + def from_pose(cls, frame_id: str, pose: Pose | PoseStamped) -> Transform: # type: ignore[name-defined] """Create a Transform from a Pose or PoseStamped. Args: @@ -265,7 +265,7 @@ def from_pose(cls, frame_id: str, pose: Pose | PoseStamped) -> Transform: else: raise TypeError(f"Expected Pose or PoseStamped, got {type(pose).__name__}") - def to_pose(self, **kwargs) -> PoseStamped: + def to_pose(self, **kwargs) -> PoseStamped: # type: ignore[name-defined, no-untyped-def] """Create a Transform from a Pose or PoseStamped. Args: @@ -287,7 +287,7 @@ def to_pose(self, **kwargs) -> PoseStamped: **kwargs, ) - def to_matrix(self) -> np.ndarray: + def to_matrix(self) -> np.ndarray: # type: ignore[name-defined] """Convert Transform to a 4x4 transformation matrix. Returns a homogeneous transformation matrix that represents both @@ -327,7 +327,7 @@ def lcm_encode(self) -> bytes: @classmethod def lcm_decode(cls, data: bytes | BinaryIO) -> Transform: """Decode from LCM TFMessage bytes.""" - from dimos_lcm.tf2_msgs import TFMessage as LCMTFMessage + from dimos_lcm.tf2_msgs import TFMessage as LCMTFMessage # type: ignore[import-untyped] lcm_msg = LCMTFMessage.lcm_decode(data) diff --git a/dimos/msgs/geometry_msgs/Twist.py b/dimos/msgs/geometry_msgs/Twist.py index e824e1cfbf..98a2421f7d 100644 --- a/dimos/msgs/geometry_msgs/Twist.py +++ b/dimos/msgs/geometry_msgs/Twist.py @@ -14,14 +14,14 @@ from __future__ import annotations -from dimos_lcm.geometry_msgs import Twist as LCMTwist +from dimos_lcm.geometry_msgs import Twist as LCMTwist # type: ignore[import-untyped] from plum import dispatch try: - from geometry_msgs.msg import Twist as ROSTwist, Vector3 as ROSVector3 + from geometry_msgs.msg import Twist as ROSTwist, Vector3 as ROSVector3 # type: ignore[attr-defined] except ImportError: - ROSTwist = None - ROSVector3 = None + ROSTwist = None # type: ignore[assignment, misc] + ROSVector3 = None # type: ignore[assignment, misc] # Import Quaternion at runtime for beartype compatibility # (beartype needs to resolve forward references at runtime) @@ -29,7 +29,7 @@ from dimos.msgs.geometry_msgs.Vector3 import Vector3, VectorLike -class Twist(LCMTwist): +class Twist(LCMTwist): # type: ignore[misc] linear: Vector3 angular: Vector3 msg_name = "geometry_msgs.Twist" @@ -40,32 +40,32 @@ def __init__(self) -> None: self.linear = Vector3() self.angular = Vector3() - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, linear: VectorLike, angular: VectorLike) -> None: """Initialize a twist from linear and angular velocities.""" self.linear = Vector3(linear) self.angular = Vector3(angular) - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, linear: VectorLike, angular: Quaternion) -> None: """Initialize a twist from linear velocity and angular as quaternion (converted to euler).""" self.linear = Vector3(linear) self.angular = angular.to_euler() - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, twist: Twist) -> None: """Initialize from another Twist (copy constructor).""" self.linear = Vector3(twist.linear) self.angular = Vector3(twist.angular) - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, lcm_twist: LCMTwist) -> None: """Initialize from an LCM Twist.""" self.linear = Vector3(lcm_twist.linear) self.angular = Vector3(lcm_twist.angular) - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, **kwargs) -> None: """Handle keyword arguments for LCM compatibility.""" linear = kwargs.get("linear", Vector3()) @@ -79,7 +79,7 @@ def __repr__(self) -> str: def __str__(self) -> str: return f"Twist:\n Linear: {self.linear}\n Angular: {self.angular}" - def __eq__(self, other) -> bool: + def __eq__(self, other) -> bool: # type: ignore[no-untyped-def] """Check if two twists are equal.""" if not isinstance(other, Twist): return False @@ -127,9 +127,9 @@ def to_ros_msg(self) -> ROSTwist: ROS Twist message """ - ros_msg = ROSTwist() - ros_msg.linear = ROSVector3(x=self.linear.x, y=self.linear.y, z=self.linear.z) - ros_msg.angular = ROSVector3(x=self.angular.x, y=self.angular.y, z=self.angular.z) + ros_msg = ROSTwist() # type: ignore[no-untyped-call] + ros_msg.linear = ROSVector3(x=self.linear.x, y=self.linear.y, z=self.linear.z) # type: ignore[no-untyped-call] + ros_msg.angular = ROSVector3(x=self.angular.x, y=self.angular.y, z=self.angular.z) # type: ignore[no-untyped-call] return ros_msg diff --git a/dimos/msgs/geometry_msgs/TwistStamped.py b/dimos/msgs/geometry_msgs/TwistStamped.py index 1a14d8cb0d..71dfdbc6e4 100644 --- a/dimos/msgs/geometry_msgs/TwistStamped.py +++ b/dimos/msgs/geometry_msgs/TwistStamped.py @@ -17,13 +17,13 @@ import time from typing import BinaryIO, TypeAlias -from dimos_lcm.geometry_msgs import TwistStamped as LCMTwistStamped +from dimos_lcm.geometry_msgs import TwistStamped as LCMTwistStamped # type: ignore[import-untyped] from plum import dispatch try: - from geometry_msgs.msg import TwistStamped as ROSTwistStamped + from geometry_msgs.msg import TwistStamped as ROSTwistStamped # type: ignore[attr-defined] except ImportError: - ROSTwistStamped = None + ROSTwistStamped = None # type: ignore[assignment, misc] from dimos.msgs.geometry_msgs.Twist import Twist from dimos.msgs.geometry_msgs.Vector3 import VectorConvertable @@ -35,7 +35,7 @@ ) -def sec_nsec(ts): +def sec_nsec(ts): # type: ignore[no-untyped-def] s = int(ts) return [s, int((ts - s) * 1_000_000_000)] @@ -46,7 +46,7 @@ class TwistStamped(Twist, Timestamped): frame_id: str @dispatch - def __init__(self, ts: float = 0.0, frame_id: str = "", **kwargs) -> None: + def __init__(self, ts: float = 0.0, frame_id: str = "", **kwargs) -> None: # type: ignore[no-untyped-def] self.frame_id = frame_id self.ts = ts if ts != 0 else time.time() super().__init__(**kwargs) @@ -54,9 +54,9 @@ def __init__(self, ts: float = 0.0, frame_id: str = "", **kwargs) -> None: def lcm_encode(self) -> bytes: lcm_msg = LCMTwistStamped() lcm_msg.twist = self - [lcm_msg.header.stamp.sec, lcm_msg.header.stamp.nsec] = sec_nsec(self.ts) + [lcm_msg.header.stamp.sec, lcm_msg.header.stamp.nsec] = sec_nsec(self.ts) # type: ignore[no-untyped-call] lcm_msg.header.frame_id = self.frame_id - return lcm_msg.lcm_encode() + return lcm_msg.lcm_encode() # type: ignore[no-any-return] @classmethod def lcm_decode(cls, data: bytes | BinaryIO) -> TwistStamped: @@ -75,7 +75,7 @@ def __str__(self) -> str: ) @classmethod - def from_ros_msg(cls, ros_msg: ROSTwistStamped) -> TwistStamped: + def from_ros_msg(cls, ros_msg: ROSTwistStamped) -> TwistStamped: # type: ignore[override] """Create a TwistStamped from a ROS geometry_msgs/TwistStamped message. Args: @@ -98,14 +98,14 @@ def from_ros_msg(cls, ros_msg: ROSTwistStamped) -> TwistStamped: angular=twist.angular, ) - def to_ros_msg(self) -> ROSTwistStamped: + def to_ros_msg(self) -> ROSTwistStamped: # type: ignore[override] """Convert to a ROS geometry_msgs/TwistStamped message. Returns: ROS TwistStamped message """ - ros_msg = ROSTwistStamped() + ros_msg = ROSTwistStamped() # type: ignore[no-untyped-call] # Set header ros_msg.header.frame_id = self.frame_id diff --git a/dimos/msgs/geometry_msgs/TwistWithCovariance.py b/dimos/msgs/geometry_msgs/TwistWithCovariance.py index 53e77beaf7..67caf297fd 100644 --- a/dimos/msgs/geometry_msgs/TwistWithCovariance.py +++ b/dimos/msgs/geometry_msgs/TwistWithCovariance.py @@ -16,27 +16,27 @@ from typing import TypeAlias -from dimos_lcm.geometry_msgs import TwistWithCovariance as LCMTwistWithCovariance +from dimos_lcm.geometry_msgs import TwistWithCovariance as LCMTwistWithCovariance # type: ignore[import-untyped] import numpy as np from plum import dispatch try: - from geometry_msgs.msg import TwistWithCovariance as ROSTwistWithCovariance + from geometry_msgs.msg import TwistWithCovariance as ROSTwistWithCovariance # type: ignore[attr-defined] except ImportError: - ROSTwistWithCovariance = None + ROSTwistWithCovariance = None # type: ignore[assignment, misc] from dimos.msgs.geometry_msgs.Twist import Twist from dimos.msgs.geometry_msgs.Vector3 import Vector3, VectorConvertable # Types that can be converted to/from TwistWithCovariance TwistWithCovarianceConvertable: TypeAlias = ( - tuple[Twist | tuple[VectorConvertable, VectorConvertable], list[float] | np.ndarray] + tuple[Twist | tuple[VectorConvertable, VectorConvertable], list[float] | np.ndarray] # type: ignore[type-arg] | LCMTwistWithCovariance - | dict[str, Twist | tuple[VectorConvertable, VectorConvertable] | list[float] | np.ndarray] + | dict[str, Twist | tuple[VectorConvertable, VectorConvertable] | list[float] | np.ndarray] # type: ignore[type-arg] ) -class TwistWithCovariance(LCMTwistWithCovariance): +class TwistWithCovariance(LCMTwistWithCovariance): # type: ignore[misc] twist: Twist msg_name = "geometry_msgs.TwistWithCovariance" @@ -46,11 +46,11 @@ def __init__(self) -> None: self.twist = Twist() self.covariance = np.zeros(36) - @dispatch + @dispatch # type: ignore[no-redef] def __init__( self, twist: Twist | tuple[VectorConvertable, VectorConvertable], - covariance: list[float] | np.ndarray | None = None, + covariance: list[float] | np.ndarray | None = None, # type: ignore[type-arg] ) -> None: """Initialize with twist and optional covariance.""" if isinstance(twist, Twist): @@ -64,22 +64,22 @@ def __init__( else: self.covariance = np.array(covariance, dtype=float).reshape(36) - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, twist_with_cov: TwistWithCovariance) -> None: """Initialize from another TwistWithCovariance (copy constructor).""" self.twist = Twist(twist_with_cov.twist) self.covariance = np.array(twist_with_cov.covariance).copy() - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, lcm_twist_with_cov: LCMTwistWithCovariance) -> None: """Initialize from an LCM TwistWithCovariance.""" self.twist = Twist(lcm_twist_with_cov.twist) self.covariance = np.array(lcm_twist_with_cov.covariance) - @dispatch + @dispatch # type: ignore[no-redef] def __init__( self, - twist_dict: dict[ + twist_dict: dict[ # type: ignore[type-arg] str, Twist | tuple[VectorConvertable, VectorConvertable] | list[float] | np.ndarray ], ) -> None: @@ -97,10 +97,10 @@ def __init__( else: self.covariance = np.array(covariance, dtype=float).reshape(36) - @dispatch + @dispatch # type: ignore[no-redef] def __init__( self, - twist_tuple: tuple[ + twist_tuple: tuple[ # type: ignore[type-arg] Twist | tuple[VectorConvertable, VectorConvertable], list[float] | np.ndarray ], ) -> None: @@ -113,7 +113,7 @@ def __init__( self.twist = Twist(twist[0], twist[1]) self.covariance = np.array(twist_tuple[1], dtype=float).reshape(36) - def __getattribute__(self, name: str): + def __getattribute__(self, name: str): # type: ignore[no-untyped-def] """Override to ensure covariance is always returned as numpy array.""" if name == "covariance": cov = object.__getattribute__(self, "covariance") @@ -122,7 +122,7 @@ def __getattribute__(self, name: str): return cov return super().__getattribute__(name) - def __setattr__(self, name: str, value) -> None: + def __setattr__(self, name: str, value) -> None: # type: ignore[no-untyped-def] """Override to ensure covariance is stored as numpy array.""" if name == "covariance": if not isinstance(value, np.ndarray): @@ -140,17 +140,17 @@ def angular(self) -> Vector3: return self.twist.angular @property - def covariance_matrix(self) -> np.ndarray: + def covariance_matrix(self) -> np.ndarray: # type: ignore[type-arg] """Get covariance as 6x6 matrix.""" - return self.covariance.reshape(6, 6) + return self.covariance.reshape(6, 6) # type: ignore[has-type, no-any-return] @covariance_matrix.setter - def covariance_matrix(self, value: np.ndarray) -> None: + def covariance_matrix(self, value: np.ndarray) -> None: # type: ignore[type-arg] """Set covariance from 6x6 matrix.""" - self.covariance = np.array(value).reshape(36) + self.covariance = np.array(value).reshape(36) # type: ignore[has-type] def __repr__(self) -> str: - return f"TwistWithCovariance(twist={self.twist!r}, covariance=<{self.covariance.shape[0] if isinstance(self.covariance, np.ndarray) else len(self.covariance)} elements>)" + return f"TwistWithCovariance(twist={self.twist!r}, covariance=<{self.covariance.shape[0] if isinstance(self.covariance, np.ndarray) else len(self.covariance)} elements>)" # type: ignore[has-type] def __str__(self) -> str: return ( @@ -159,11 +159,11 @@ def __str__(self) -> str: f"cov_trace={np.trace(self.covariance_matrix):.3f})" ) - def __eq__(self, other) -> bool: + def __eq__(self, other) -> bool: # type: ignore[no-untyped-def] """Check if two TwistWithCovariance are equal.""" if not isinstance(other, TwistWithCovariance): return False - return self.twist == other.twist and np.allclose(self.covariance, other.covariance) + return self.twist == other.twist and np.allclose(self.covariance, other.covariance) # type: ignore[has-type] def is_zero(self) -> bool: """Check if this is a zero twist (no linear or angular velocity).""" @@ -178,11 +178,11 @@ def lcm_encode(self) -> bytes: lcm_msg = LCMTwistWithCovariance() lcm_msg.twist = self.twist # LCM expects list, not numpy array - if isinstance(self.covariance, np.ndarray): - lcm_msg.covariance = self.covariance.tolist() + if isinstance(self.covariance, np.ndarray): # type: ignore[has-type] + lcm_msg.covariance = self.covariance.tolist() # type: ignore[has-type] else: - lcm_msg.covariance = list(self.covariance) - return lcm_msg.lcm_encode() + lcm_msg.covariance = list(self.covariance) # type: ignore[has-type] + return lcm_msg.lcm_encode() # type: ignore[no-any-return] @classmethod def lcm_decode(cls, data: bytes) -> TwistWithCovariance: @@ -215,11 +215,11 @@ def to_ros_msg(self) -> ROSTwistWithCovariance: ROS TwistWithCovariance message """ - ros_msg = ROSTwistWithCovariance() + ros_msg = ROSTwistWithCovariance() # type: ignore[no-untyped-call] ros_msg.twist = self.twist.to_ros_msg() # ROS expects list, not numpy array - if isinstance(self.covariance, np.ndarray): - ros_msg.covariance = self.covariance.tolist() + if isinstance(self.covariance, np.ndarray): # type: ignore[has-type] + ros_msg.covariance = self.covariance.tolist() # type: ignore[has-type] else: - ros_msg.covariance = list(self.covariance) + ros_msg.covariance = list(self.covariance) # type: ignore[has-type] return ros_msg diff --git a/dimos/msgs/geometry_msgs/TwistWithCovarianceStamped.py b/dimos/msgs/geometry_msgs/TwistWithCovarianceStamped.py index 20684d9375..a4b212bdcf 100644 --- a/dimos/msgs/geometry_msgs/TwistWithCovarianceStamped.py +++ b/dimos/msgs/geometry_msgs/TwistWithCovarianceStamped.py @@ -17,14 +17,14 @@ import time from typing import TypeAlias -from dimos_lcm.geometry_msgs import TwistWithCovarianceStamped as LCMTwistWithCovarianceStamped +from dimos_lcm.geometry_msgs import TwistWithCovarianceStamped as LCMTwistWithCovarianceStamped # type: ignore[import-untyped] import numpy as np from plum import dispatch try: - from geometry_msgs.msg import TwistWithCovarianceStamped as ROSTwistWithCovarianceStamped + from geometry_msgs.msg import TwistWithCovarianceStamped as ROSTwistWithCovarianceStamped # type: ignore[attr-defined] except ImportError: - ROSTwistWithCovarianceStamped = None + ROSTwistWithCovarianceStamped = None # type: ignore[assignment, misc] from dimos.msgs.geometry_msgs.Twist import Twist from dimos.msgs.geometry_msgs.TwistWithCovariance import TwistWithCovariance @@ -33,21 +33,21 @@ # Types that can be converted to/from TwistWithCovarianceStamped TwistWithCovarianceStampedConvertable: TypeAlias = ( - tuple[Twist | tuple[VectorConvertable, VectorConvertable], list[float] | np.ndarray] + tuple[Twist | tuple[VectorConvertable, VectorConvertable], list[float] | np.ndarray] # type: ignore[type-arg] | LCMTwistWithCovarianceStamped | dict[ str, Twist | tuple[VectorConvertable, VectorConvertable] | list[float] - | np.ndarray + | np.ndarray # type: ignore[type-arg] | float | str, ] ) -def sec_nsec(ts): +def sec_nsec(ts): # type: ignore[no-untyped-def] s = int(ts) return [s, int((ts - s) * 1_000_000_000)] @@ -64,13 +64,13 @@ def __init__(self, ts: float = 0.0, frame_id: str = "", **kwargs) -> None: self.ts = ts if ts != 0 else time.time() super().__init__(**kwargs) - @dispatch + @dispatch # type: ignore[no-redef] def __init__( self, ts: float = 0.0, frame_id: str = "", twist: Twist | tuple[VectorConvertable, VectorConvertable] | None = None, - covariance: list[float] | np.ndarray | None = None, + covariance: list[float] | np.ndarray | None = None, # type: ignore[type-arg] ) -> None: """Initialize with timestamp, frame_id, twist and covariance.""" self.frame_id = frame_id @@ -84,13 +84,13 @@ def lcm_encode(self) -> bytes: lcm_msg = LCMTwistWithCovarianceStamped() lcm_msg.twist.twist = self.twist # LCM expects list, not numpy array - if isinstance(self.covariance, np.ndarray): - lcm_msg.twist.covariance = self.covariance.tolist() + if isinstance(self.covariance, np.ndarray): # type: ignore[has-type] + lcm_msg.twist.covariance = self.covariance.tolist() # type: ignore[has-type] else: - lcm_msg.twist.covariance = list(self.covariance) - [lcm_msg.header.stamp.sec, lcm_msg.header.stamp.nsec] = sec_nsec(self.ts) + lcm_msg.twist.covariance = list(self.covariance) # type: ignore[has-type] + [lcm_msg.header.stamp.sec, lcm_msg.header.stamp.nsec] = sec_nsec(self.ts) # type: ignore[no-untyped-call] lcm_msg.header.frame_id = self.frame_id - return lcm_msg.lcm_encode() + return lcm_msg.lcm_encode() # type: ignore[no-any-return] @classmethod def lcm_decode(cls, data: bytes) -> TwistWithCovarianceStamped: @@ -121,7 +121,7 @@ def __str__(self) -> str: ) @classmethod - def from_ros_msg(cls, ros_msg: ROSTwistWithCovarianceStamped) -> TwistWithCovarianceStamped: + def from_ros_msg(cls, ros_msg: ROSTwistWithCovarianceStamped) -> TwistWithCovarianceStamped: # type: ignore[override] """Create a TwistWithCovarianceStamped from a ROS geometry_msgs/TwistWithCovarianceStamped message. Args: @@ -141,17 +141,17 @@ def from_ros_msg(cls, ros_msg: ROSTwistWithCovarianceStamped) -> TwistWithCovari ts=ts, frame_id=ros_msg.header.frame_id, twist=twist_with_cov.twist, - covariance=twist_with_cov.covariance, + covariance=twist_with_cov.covariance, # type: ignore[has-type] ) - def to_ros_msg(self) -> ROSTwistWithCovarianceStamped: + def to_ros_msg(self) -> ROSTwistWithCovarianceStamped: # type: ignore[override] """Convert to a ROS geometry_msgs/TwistWithCovarianceStamped message. Returns: ROS TwistWithCovarianceStamped message """ - ros_msg = ROSTwistWithCovarianceStamped() + ros_msg = ROSTwistWithCovarianceStamped() # type: ignore[no-untyped-call] # Set header ros_msg.header.frame_id = self.frame_id @@ -161,9 +161,9 @@ def to_ros_msg(self) -> ROSTwistWithCovarianceStamped: # Set twist with covariance ros_msg.twist.twist = self.twist.to_ros_msg() # ROS expects list, not numpy array - if isinstance(self.covariance, np.ndarray): - ros_msg.twist.covariance = self.covariance.tolist() + if isinstance(self.covariance, np.ndarray): # type: ignore[has-type] + ros_msg.twist.covariance = self.covariance.tolist() # type: ignore[has-type] else: - ros_msg.twist.covariance = list(self.covariance) + ros_msg.twist.covariance = list(self.covariance) # type: ignore[has-type] return ros_msg diff --git a/dimos/msgs/geometry_msgs/Vector3.py b/dimos/msgs/geometry_msgs/Vector3.py index 05d3340a42..122b549fa7 100644 --- a/dimos/msgs/geometry_msgs/Vector3.py +++ b/dimos/msgs/geometry_msgs/Vector3.py @@ -17,15 +17,15 @@ from collections.abc import Sequence from typing import TypeAlias -from dimos_lcm.geometry_msgs import Vector3 as LCMVector3 +from dimos_lcm.geometry_msgs import Vector3 as LCMVector3 # type: ignore[import-untyped] import numpy as np from plum import dispatch # Types that can be converted to/from Vector -VectorConvertable: TypeAlias = Sequence[int | float] | LCMVector3 | np.ndarray +VectorConvertable: TypeAlias = Sequence[int | float] | LCMVector3 | np.ndarray # type: ignore[type-arg] -def _ensure_3d(data: np.ndarray) -> np.ndarray: +def _ensure_3d(data: np.ndarray) -> np.ndarray: # type: ignore[type-arg] """Ensure the data array is exactly 3D by padding with zeros or raising an exception if too long.""" if len(data) == 3: return data @@ -39,7 +39,7 @@ def _ensure_3d(data: np.ndarray) -> np.ndarray: ) -class Vector3(LCMVector3): +class Vector3(LCMVector3): # type: ignore[misc] x: float = 0.0 y: float = 0.0 z: float = 0.0 @@ -52,28 +52,28 @@ def __init__(self) -> None: self.y = 0.0 self.z = 0.0 - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, x: int | float) -> None: """Initialize a 3D vector from a single numeric value (x, 0, 0).""" self.x = float(x) self.y = 0.0 self.z = 0.0 - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, x: int | float, y: int | float) -> None: """Initialize a 3D vector from x, y components (z=0).""" self.x = float(x) self.y = float(y) self.z = 0.0 - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, x: int | float, y: int | float, z: int | float) -> None: """Initialize a 3D vector from x, y, z components.""" self.x = float(x) self.y = float(y) self.z = float(z) - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, sequence: Sequence[int | float]) -> None: """Initialize from a sequence (list, tuple) of numbers, ensuring 3D.""" data = _ensure_3d(np.array(sequence, dtype=float)) @@ -81,22 +81,22 @@ def __init__(self, sequence: Sequence[int | float]) -> None: self.y = float(data[1]) self.z = float(data[2]) - @dispatch - def __init__(self, array: np.ndarray) -> None: + @dispatch # type: ignore[no-redef] + def __init__(self, array: np.ndarray) -> None: # type: ignore[type-arg] """Initialize from a numpy array, ensuring 3D.""" data = _ensure_3d(np.array(array, dtype=float)) self.x = float(data[0]) self.y = float(data[1]) self.z = float(data[2]) - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, vector: Vector3) -> None: """Initialize from another Vector3 (copy constructor).""" self.x = vector.x self.y = vector.y self.z = vector.z - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, lcm_vector: LCMVector3) -> None: """Initialize from an LCM Vector3.""" self.x = float(lcm_vector.x) @@ -120,11 +120,11 @@ def roll(self) -> float: return self.x @property - def data(self) -> np.ndarray: + def data(self) -> np.ndarray: # type: ignore[type-arg] """Get the underlying numpy array.""" return np.array([self.x, self.y, self.z], dtype=float) - def __getitem__(self, idx: int): + def __getitem__(self, idx: int): # type: ignore[no-untyped-def] if idx == 0: return self.x elif idx == 1: @@ -138,7 +138,7 @@ def __repr__(self) -> str: return f"Vector({self.data})" def __str__(self) -> str: - def getArrow(): + def getArrow(): # type: ignore[no-untyped-def] repr = ["←", "↖", "↑", "↗", "→", "↘", "↓", "↙"] if self.x == 0 and self.y == 0: @@ -151,17 +151,17 @@ def getArrow(): # Get directional arrow symbol return repr[dir_index] - return f"{getArrow()} Vector {self.__repr__()}" + return f"{getArrow()} Vector {self.__repr__()}" # type: ignore[no-untyped-call] - def agent_encode(self) -> dict: + def agent_encode(self) -> dict: # type: ignore[type-arg] """Encode the vector for agent communication.""" return {"x": self.x, "y": self.y, "z": self.z} - def serialize(self) -> dict: + def serialize(self) -> dict: # type: ignore[type-arg] """Serialize the vector to a tuple.""" return {"type": "vector", "c": (self.x, self.y, self.z)} - def __eq__(self, other) -> bool: + def __eq__(self, other) -> bool: # type: ignore[no-untyped-def] """Check if two vectors are equal using numpy's allclose for floating point comparison.""" if not isinstance(other, Vector3): return False @@ -194,7 +194,7 @@ def __neg__(self) -> Vector3: def dot(self, other: VectorConvertable | Vector3) -> float: """Compute dot product.""" other_vector = to_vector(other) - return self.x * other_vector.x + self.y * other_vector.y + self.z * other_vector.z + return self.x * other_vector.x + self.y * other_vector.y + self.z * other_vector.z # type: ignore[no-any-return] def cross(self, other: VectorConvertable | Vector3) -> Vector3: """Compute cross product (3D vectors only).""" @@ -281,7 +281,7 @@ def project(self, onto: VectorConvertable | Vector3) -> Vector3: # this is here to test ros_observable_topic # doesn't happen irl afaik that we want a vector from ros message @classmethod - def from_msg(cls, msg) -> Vector3: + def from_msg(cls, msg) -> Vector3: # type: ignore[no-untyped-def] return cls(*msg) @classmethod @@ -317,7 +317,7 @@ def to_tuple(self) -> tuple[float, float, float]: """Convert the vector to a tuple.""" return (self.x, self.y, self.z) - def to_numpy(self) -> np.ndarray: + def to_numpy(self) -> np.ndarray: # type: ignore[type-arg] """Convert the vector to a numpy array.""" return np.array([self.x, self.y, self.z], dtype=float) @@ -330,10 +330,10 @@ def is_zero(self) -> bool: return np.allclose([self.x, self.y, self.z], 0.0) @property - def quaternion(self): - return self.to_quaternion() + def quaternion(self): # type: ignore[no-untyped-def] + return self.to_quaternion() # type: ignore[no-untyped-call] - def to_quaternion(self): + def to_quaternion(self): # type: ignore[no-untyped-def] """Convert Vector3 representing Euler angles (roll, pitch, yaw) to a Quaternion. Assumes this Vector3 contains Euler angles in radians: @@ -384,19 +384,19 @@ def __bool__(self) -> bool: @dispatch -def to_numpy(value: Vector3) -> np.ndarray: +def to_numpy(value: Vector3) -> np.ndarray: # type: ignore[type-arg] """Convert a Vector3 to a numpy array.""" return value.to_numpy() -@dispatch -def to_numpy(value: np.ndarray) -> np.ndarray: +@dispatch # type: ignore[no-redef] +def to_numpy(value: np.ndarray) -> np.ndarray: # type: ignore[type-arg] """Pass through numpy arrays.""" return value -@dispatch -def to_numpy(value: Sequence[int | float]) -> np.ndarray: +@dispatch # type: ignore[no-redef] +def to_numpy(value: Sequence[int | float]) -> np.ndarray: # type: ignore[type-arg] """Convert a sequence to a numpy array.""" return np.array(value, dtype=float) @@ -407,7 +407,7 @@ def to_vector(value: Vector3) -> Vector3: return value -@dispatch +@dispatch # type: ignore[no-redef] def to_vector(value: VectorConvertable | Vector3) -> Vector3: """Convert a vector-compatible value to a Vector3 object.""" return Vector3(value) @@ -419,13 +419,13 @@ def to_tuple(value: Vector3) -> tuple[float, float, float]: return value.to_tuple() -@dispatch -def to_tuple(value: np.ndarray) -> tuple[float, ...]: +@dispatch # type: ignore[no-redef] +def to_tuple(value: np.ndarray) -> tuple[float, ...]: # type: ignore[type-arg] """Convert a numpy array to a tuple.""" return tuple(value.tolist()) -@dispatch +@dispatch # type: ignore[no-redef] def to_tuple(value: Sequence[int | float]) -> tuple[float, ...]: """Convert a sequence to a tuple.""" if isinstance(value, tuple): @@ -440,13 +440,13 @@ def to_list(value: Vector3) -> list[float]: return value.to_list() -@dispatch -def to_list(value: np.ndarray) -> list[float]: +@dispatch # type: ignore[no-redef] +def to_list(value: np.ndarray) -> list[float]: # type: ignore[type-arg] """Convert a numpy array to a list.""" return value.tolist() -@dispatch +@dispatch # type: ignore[no-redef] def to_list(value: Sequence[int | float]) -> list[float]: """Convert a sequence to a list.""" if isinstance(value, list): diff --git a/dimos/msgs/nav_msgs/OccupancyGrid.py b/dimos/msgs/nav_msgs/OccupancyGrid.py index 3e144de74f..145cd74398 100644 --- a/dimos/msgs/nav_msgs/OccupancyGrid.py +++ b/dimos/msgs/nav_msgs/OccupancyGrid.py @@ -18,8 +18,8 @@ import time from typing import TYPE_CHECKING, BinaryIO -from dimos_lcm.nav_msgs import MapMetaData, OccupancyGrid as LCMOccupancyGrid -from dimos_lcm.std_msgs import Time as LCMTime +from dimos_lcm.nav_msgs import MapMetaData, OccupancyGrid as LCMOccupancyGrid # type: ignore[import-untyped] +from dimos_lcm.std_msgs import Time as LCMTime # type: ignore[import-untyped] import numpy as np from scipy import ndimage @@ -56,11 +56,11 @@ class OccupancyGrid(Timestamped): ts: float frame_id: str info: MapMetaData - grid: np.ndarray + grid: np.ndarray # type: ignore[type-arg] def __init__( self, - grid: np.ndarray | None = None, + grid: np.ndarray | None = None, # type: ignore[type-arg] width: int | None = None, height: int | None = None, resolution: float = 0.05, @@ -89,7 +89,7 @@ def __init__( raise ValueError("Grid must be a 2D array") height, width = grid.shape self.info = MapMetaData( - map_load_time=self._to_lcm_time(), + map_load_time=self._to_lcm_time(), # type: ignore[no-untyped-call] resolution=resolution, width=width, height=height, @@ -99,7 +99,7 @@ def __init__( elif width is not None and height is not None: # Initialize with dimensions self.info = MapMetaData( - map_load_time=self._to_lcm_time(), + map_load_time=self._to_lcm_time(), # type: ignore[no-untyped-call] resolution=resolution, width=width, height=height, @@ -108,10 +108,10 @@ def __init__( self.grid = np.full((height, width), -1, dtype=np.int8) else: # Initialize empty - self.info = MapMetaData(map_load_time=self._to_lcm_time()) + self.info = MapMetaData(map_load_time=self._to_lcm_time()) # type: ignore[no-untyped-call] self.grid = np.array([], dtype=np.int8) - def _to_lcm_time(self): + def _to_lcm_time(self): # type: ignore[no-untyped-def] """Convert timestamp to LCM Time.""" s = int(self.ts) @@ -120,22 +120,22 @@ def _to_lcm_time(self): @property def width(self) -> int: """Width of the grid in cells.""" - return self.info.width + return self.info.width # type: ignore[no-any-return] @property def height(self) -> int: """Height of the grid in cells.""" - return self.info.height + return self.info.height # type: ignore[no-any-return] @property def resolution(self) -> float: """Grid resolution in meters/cell.""" - return self.info.resolution + return self.info.resolution # type: ignore[no-any-return] @property def origin(self) -> Pose: """Origin pose of the grid.""" - return self.info.origin + return self.info.origin # type: ignore[no-any-return] @property def total_cells(self) -> int: @@ -296,7 +296,7 @@ def lcm_encode(self) -> bytes: lcm_msg.data_length = 0 lcm_msg.data = [] - return lcm_msg.lcm_encode() + return lcm_msg.lcm_encode() # type: ignore[no-any-return] @classmethod def lcm_decode(cls, data: bytes | BinaryIO) -> OccupancyGrid: @@ -494,7 +494,7 @@ def gradient(self, obstacle_threshold: int = 50, max_distance: float = 2.0) -> O distance_cells = ndimage.distance_transform_edt(1 - obstacle_map) # Convert to meters and clip to max distance - distance_meters = np.clip(distance_cells * self.resolution, 0, max_distance) + distance_meters = np.clip(distance_cells * self.resolution, 0, max_distance) # type: ignore[operator] # Invert and scale to 0-100 range # Far from obstacles (max_distance) -> 0 diff --git a/dimos/msgs/nav_msgs/Odometry.py b/dimos/msgs/nav_msgs/Odometry.py index 3a640b242d..d88a2269eb 100644 --- a/dimos/msgs/nav_msgs/Odometry.py +++ b/dimos/msgs/nav_msgs/Odometry.py @@ -17,14 +17,14 @@ import time from typing import TYPE_CHECKING, TypeAlias -from dimos_lcm.nav_msgs import Odometry as LCMOdometry +from dimos_lcm.nav_msgs import Odometry as LCMOdometry # type: ignore[import-untyped] import numpy as np from plum import dispatch try: - from nav_msgs.msg import Odometry as ROSOdometry + from nav_msgs.msg import Odometry as ROSOdometry # type: ignore[attr-defined] except ImportError: - ROSOdometry = None + ROSOdometry = None # type: ignore[assignment, misc] from dimos.msgs.geometry_msgs.Pose import Pose from dimos.msgs.geometry_msgs.PoseWithCovariance import PoseWithCovariance @@ -41,12 +41,12 @@ ) -def sec_nsec(ts): +def sec_nsec(ts): # type: ignore[no-untyped-def] s = int(ts) return [s, int((ts - s) * 1_000_000_000)] -class Odometry(LCMOdometry, Timestamped): +class Odometry(LCMOdometry, Timestamped): # type: ignore[misc] pose: PoseWithCovariance twist: TwistWithCovariance msg_name = "nav_msgs.Odometry" @@ -96,7 +96,7 @@ def __init__( else: self.twist = TwistWithCovariance(Twist(twist)) - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, odometry: Odometry) -> None: """Initialize from another Odometry (copy constructor).""" self.ts = odometry.ts @@ -105,7 +105,7 @@ def __init__(self, odometry: Odometry) -> None: self.pose = PoseWithCovariance(odometry.pose) self.twist = TwistWithCovariance(odometry.twist) - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, lcm_odometry: LCMOdometry) -> None: """Initialize from an LCM Odometry.""" self.ts = lcm_odometry.header.stamp.sec + (lcm_odometry.header.stamp.nsec / 1_000_000_000) @@ -114,7 +114,7 @@ def __init__(self, lcm_odometry: LCMOdometry) -> None: self.pose = PoseWithCovariance(lcm_odometry.pose) self.twist = TwistWithCovariance(lcm_odometry.twist) - @dispatch + @dispatch # type: ignore[no-redef] def __init__( self, odometry_dict: dict[ @@ -154,7 +154,7 @@ def position(self) -> Vector3: return self.pose.position @property - def orientation(self): + def orientation(self): # type: ignore[no-untyped-def] """Get orientation from pose.""" return self.pose.orientation @@ -245,7 +245,7 @@ def __str__(self) -> str: f" Angular Velocity: [{self.wx:.3f}, {self.wy:.3f}, {self.wz:.3f}]" ) - def __eq__(self, other) -> bool: + def __eq__(self, other) -> bool: # type: ignore[no-untyped-def] """Check if two Odometry messages are equal.""" if not isinstance(other, Odometry): return False @@ -262,25 +262,25 @@ def lcm_encode(self) -> bytes: lcm_msg = LCMOdometry() # Set header - [lcm_msg.header.stamp.sec, lcm_msg.header.stamp.nsec] = sec_nsec(self.ts) + [lcm_msg.header.stamp.sec, lcm_msg.header.stamp.nsec] = sec_nsec(self.ts) # type: ignore[no-untyped-call] lcm_msg.header.frame_id = self.frame_id lcm_msg.child_frame_id = self.child_frame_id # Set pose with covariance lcm_msg.pose.pose = self.pose.pose - if isinstance(self.pose.covariance, np.ndarray): - lcm_msg.pose.covariance = self.pose.covariance.tolist() + if isinstance(self.pose.covariance, np.ndarray): # type: ignore[has-type] + lcm_msg.pose.covariance = self.pose.covariance.tolist() # type: ignore[has-type] else: - lcm_msg.pose.covariance = list(self.pose.covariance) + lcm_msg.pose.covariance = list(self.pose.covariance) # type: ignore[has-type] # Set twist with covariance lcm_msg.twist.twist = self.twist.twist - if isinstance(self.twist.covariance, np.ndarray): - lcm_msg.twist.covariance = self.twist.covariance.tolist() + if isinstance(self.twist.covariance, np.ndarray): # type: ignore[has-type] + lcm_msg.twist.covariance = self.twist.covariance.tolist() # type: ignore[has-type] else: - lcm_msg.twist.covariance = list(self.twist.covariance) + lcm_msg.twist.covariance = list(self.twist.covariance) # type: ignore[has-type] - return lcm_msg.lcm_encode() + return lcm_msg.lcm_encode() # type: ignore[no-any-return] @classmethod def lcm_decode(cls, data: bytes) -> Odometry: @@ -362,7 +362,7 @@ def to_ros_msg(self) -> ROSOdometry: ROS Odometry message """ - ros_msg = ROSOdometry() + ros_msg = ROSOdometry() # type: ignore[no-untyped-call] # Set header ros_msg.header.frame_id = self.frame_id diff --git a/dimos/msgs/nav_msgs/Path.py b/dimos/msgs/nav_msgs/Path.py index fa05ae4d6f..e86396e678 100644 --- a/dimos/msgs/nav_msgs/Path.py +++ b/dimos/msgs/nav_msgs/Path.py @@ -17,19 +17,19 @@ import time from typing import TYPE_CHECKING, BinaryIO -from dimos_lcm.geometry_msgs import ( +from dimos_lcm.geometry_msgs import ( # type: ignore[import-untyped] Point as LCMPoint, Pose as LCMPose, PoseStamped as LCMPoseStamped, Quaternion as LCMQuaternion, ) -from dimos_lcm.nav_msgs import Path as LCMPath -from dimos_lcm.std_msgs import Header as LCMHeader, Time as LCMTime +from dimos_lcm.nav_msgs import Path as LCMPath # type: ignore[import-untyped] +from dimos_lcm.std_msgs import Header as LCMHeader, Time as LCMTime # type: ignore[import-untyped] try: - from nav_msgs.msg import Path as ROSPath + from nav_msgs.msg import Path as ROSPath # type: ignore[attr-defined] except ImportError: - ROSPath = None + ROSPath = None # type: ignore[assignment, misc] from dimos.msgs.geometry_msgs.PoseStamped import PoseStamped from dimos.types.timestamped import Timestamped @@ -38,7 +38,7 @@ from collections.abc import Iterator -def sec_nsec(ts): +def sec_nsec(ts): # type: ignore[no-untyped-def] s = int(ts) return [s, int((ts - s) * 1_000_000_000)] @@ -49,7 +49,7 @@ class Path(Timestamped): frame_id: str poses: list[PoseStamped] - def __init__( + def __init__( # type: ignore[no-untyped-def] self, ts: float = 0.0, frame_id: str = "world", @@ -116,16 +116,16 @@ def lcm_encode(self) -> bytes: lcm_pose.header.stamp = LCMTime() # Set the header with pose timestamp but path's frame_id - [lcm_pose.header.stamp.sec, lcm_pose.header.stamp.nsec] = sec_nsec(pose.ts) + [lcm_pose.header.stamp.sec, lcm_pose.header.stamp.nsec] = sec_nsec(pose.ts) # type: ignore[no-untyped-call] lcm_pose.header.frame_id = self.frame_id # All poses use path's frame_id lcm_poses.append(lcm_pose) lcm_msg.poses = lcm_poses # Set header with path's own timestamp - [lcm_msg.header.stamp.sec, lcm_msg.header.stamp.nsec] = sec_nsec(self.ts) + [lcm_msg.header.stamp.sec, lcm_msg.header.stamp.nsec] = sec_nsec(self.ts) # type: ignore[no-untyped-call] lcm_msg.header.frame_id = self.frame_id - return lcm_msg.lcm_encode() + return lcm_msg.lcm_encode() # type: ignore[no-any-return] @classmethod def lcm_decode(cls, data: bytes | BinaryIO) -> Path: @@ -167,7 +167,7 @@ def __getitem__(self, index: int | slice) -> PoseStamped | list[PoseStamped]: """Allow indexing and slicing of poses.""" return self.poses[index] - def __iter__(self) -> Iterator: + def __iter__(self) -> Iterator: # type: ignore[type-arg] """Allow iteration over poses.""" return iter(self.poses) @@ -219,7 +219,7 @@ def to_ros_msg(self) -> ROSPath: ROS Path message """ - ros_msg = ROSPath() + ros_msg = ROSPath() # type: ignore[no-untyped-call] # Set header ros_msg.header.frame_id = self.frame_id diff --git a/dimos/msgs/nav_msgs/__init__.py b/dimos/msgs/nav_msgs/__init__.py index 9df397c57c..54350b3f59 100644 --- a/dimos/msgs/nav_msgs/__init__.py +++ b/dimos/msgs/nav_msgs/__init__.py @@ -1,4 +1,4 @@ -from dimos.msgs.nav_msgs.OccupancyGrid import CostValues, MapMetaData, OccupancyGrid +from dimos.msgs.nav_msgs.OccupancyGrid import CostValues, MapMetaData, OccupancyGrid # type: ignore[attr-defined] from dimos.msgs.nav_msgs.Odometry import Odometry from dimos.msgs.nav_msgs.Path import Path diff --git a/dimos/msgs/sensor_msgs/CameraInfo.py b/dimos/msgs/sensor_msgs/CameraInfo.py index 3d2a118b0d..fe638195bb 100644 --- a/dimos/msgs/sensor_msgs/CameraInfo.py +++ b/dimos/msgs/sensor_msgs/CameraInfo.py @@ -17,14 +17,14 @@ import time # Import LCM types -from dimos_lcm.sensor_msgs import CameraInfo as LCMCameraInfo -from dimos_lcm.std_msgs.Header import Header +from dimos_lcm.sensor_msgs import CameraInfo as LCMCameraInfo # type: ignore[import-untyped] +from dimos_lcm.std_msgs.Header import Header # type: ignore[import-untyped] import numpy as np # Import ROS types try: - from sensor_msgs.msg import CameraInfo as ROSCameraInfo, RegionOfInterest as ROSRegionOfInterest - from std_msgs.msg import Header as ROSHeader + from sensor_msgs.msg import CameraInfo as ROSCameraInfo, RegionOfInterest as ROSRegionOfInterest # type: ignore[attr-defined] + from std_msgs.msg import Header as ROSHeader # type: ignore[attr-defined] ROS_AVAILABLE = True except ImportError: @@ -140,41 +140,41 @@ def from_yaml(cls, yaml_file: str) -> CameraInfo: frame_id="camera_optical", ) - def get_K_matrix(self) -> np.ndarray: + def get_K_matrix(self) -> np.ndarray: # type: ignore[type-arg] """Get intrinsic matrix as numpy array.""" return np.array(self.K, dtype=np.float64).reshape(3, 3) - def get_P_matrix(self) -> np.ndarray: + def get_P_matrix(self) -> np.ndarray: # type: ignore[type-arg] """Get projection matrix as numpy array.""" return np.array(self.P, dtype=np.float64).reshape(3, 4) - def get_R_matrix(self) -> np.ndarray: + def get_R_matrix(self) -> np.ndarray: # type: ignore[type-arg] """Get rectification matrix as numpy array.""" return np.array(self.R, dtype=np.float64).reshape(3, 3) - def get_D_coeffs(self) -> np.ndarray: + def get_D_coeffs(self) -> np.ndarray: # type: ignore[type-arg] """Get distortion coefficients as numpy array.""" return np.array(self.D, dtype=np.float64) - def set_K_matrix(self, K: np.ndarray): + def set_K_matrix(self, K: np.ndarray): # type: ignore[no-untyped-def, type-arg] """Set intrinsic matrix from numpy array.""" if K.shape != (3, 3): raise ValueError(f"K matrix must be 3x3, got {K.shape}") self.K = K.flatten().tolist() - def set_P_matrix(self, P: np.ndarray): + def set_P_matrix(self, P: np.ndarray): # type: ignore[no-untyped-def, type-arg] """Set projection matrix from numpy array.""" if P.shape != (3, 4): raise ValueError(f"P matrix must be 3x4, got {P.shape}") self.P = P.flatten().tolist() - def set_R_matrix(self, R: np.ndarray): + def set_R_matrix(self, R: np.ndarray): # type: ignore[no-untyped-def, type-arg] """Set rectification matrix from numpy array.""" if R.shape != (3, 3): raise ValueError(f"R matrix must be 3x3, got {R.shape}") self.R = R.flatten().tolist() - def set_D_coeffs(self, D: np.ndarray) -> None: + def set_D_coeffs(self, D: np.ndarray) -> None: # type: ignore[type-arg] """Set distortion coefficients from numpy array.""" self.D = D.flatten().tolist() @@ -216,7 +216,7 @@ def lcm_encode(self) -> bytes: msg.roi.width = self.roi_width msg.roi.do_rectify = self.roi_do_rectify - return msg.lcm_encode() + return msg.lcm_encode() # type: ignore[no-any-return] @classmethod def lcm_decode(cls, data: bytes) -> CameraInfo: @@ -298,10 +298,10 @@ def to_ros_msg(self) -> ROSCameraInfo: if not ROS_AVAILABLE: raise ImportError("ROS packages not available. Cannot convert to ROS message.") - ros_msg = ROSCameraInfo() + ros_msg = ROSCameraInfo() # type: ignore[no-untyped-call] # Set header - ros_msg.header = ROSHeader() + ros_msg.header = ROSHeader() # type: ignore[no-untyped-call] ros_msg.header.frame_id = self.frame_id ros_msg.header.stamp.sec = int(self.ts) ros_msg.header.stamp.nanosec = int((self.ts - int(self.ts)) * 1e9) @@ -324,7 +324,7 @@ def to_ros_msg(self) -> ROSCameraInfo: ros_msg.binning_y = self.binning_y # ROI - ros_msg.roi = ROSRegionOfInterest() + ros_msg.roi = ROSRegionOfInterest() # type: ignore[no-untyped-call] ros_msg.roi.x_offset = self.roi_x_offset ros_msg.roi.y_offset = self.roi_y_offset ros_msg.roi.height = self.roi_height @@ -351,7 +351,7 @@ def __str__(self) -> str: f" Binning: {self.binning_x}x{self.binning_y}" ) - def __eq__(self, other) -> bool: + def __eq__(self, other) -> bool: # type: ignore[no-untyped-def] """Check if two CameraInfo messages are equal.""" if not isinstance(other, CameraInfo): return False @@ -373,7 +373,7 @@ def __eq__(self, other) -> bool: class CalibrationProvider: """Provides lazy-loaded access to camera calibration YAML files in a directory.""" - def __init__(self, calibration_dir) -> None: + def __init__(self, calibration_dir) -> None: # type: ignore[no-untyped-def] """Initialize with a directory containing calibration YAML files. Args: @@ -382,7 +382,7 @@ def __init__(self, calibration_dir) -> None: from pathlib import Path self._calibration_dir = Path(calibration_dir) - self._cache = {} + self._cache = {} # type: ignore[var-annotated] def _to_snake_case(self, name: str) -> str: """Convert PascalCase to snake_case.""" @@ -393,7 +393,7 @@ def _to_snake_case(self, name: str) -> str: # Insert underscore before capital letter followed by lowercase return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower() - def _find_yaml_file(self, name: str): + def _find_yaml_file(self, name: str): # type: ignore[no-untyped-def] """Find YAML file matching the given name (tries both snake_case and exact match). Args: @@ -433,12 +433,12 @@ def __getattr__(self, name: str) -> CameraInfo: """ # Check cache first if name in self._cache: - return self._cache[name] + return self._cache[name] # type: ignore[no-any-return] # Also check if the snake_case version is cached (for PascalCase access) snake_name = self._to_snake_case(name) if snake_name != name and snake_name in self._cache: - return self._cache[snake_name] + return self._cache[snake_name] # type: ignore[no-any-return] # Find matching YAML file yaml_file = self._find_yaml_file(name) @@ -455,7 +455,7 @@ def __getattr__(self, name: str) -> CameraInfo: return camera_info - def __dir__(self): + def __dir__(self): # type: ignore[no-untyped-def] """List available calibrations in both snake_case and PascalCase.""" calibrations = [] if self._calibration_dir.exists() and self._calibration_dir.is_dir(): diff --git a/dimos/msgs/sensor_msgs/Image.py b/dimos/msgs/sensor_msgs/Image.py index b9ffc6a65b..e7ddb6c751 100644 --- a/dimos/msgs/sensor_msgs/Image.py +++ b/dimos/msgs/sensor_msgs/Image.py @@ -19,12 +19,12 @@ from typing import TYPE_CHECKING, Literal, TypedDict import cv2 -from dimos_lcm.sensor_msgs.Image import Image as LCMImage -from dimos_lcm.std_msgs.Header import Header +from dimos_lcm.sensor_msgs.Image import Image as LCMImage # type: ignore[import-untyped] +from dimos_lcm.std_msgs.Header import Header # type: ignore[import-untyped] import numpy as np import reactivex as rx from reactivex import operators as ops -from turbojpeg import TurboJPEG +from turbojpeg import TurboJPEG # type: ignore[import-untyped] from dimos.msgs.sensor_msgs.image_impls.AbstractImage import ( HAS_CUDA, @@ -47,12 +47,12 @@ try: import cupy as cp # type: ignore except Exception: - cp = None # type: ignore + cp = None try: - from sensor_msgs.msg import Image as ROSImage + from sensor_msgs.msg import Image as ROSImage # type: ignore[attr-defined] except ImportError: - ROSImage = None + ROSImage = None # type: ignore[assignment, misc] class AgentImageMessage(TypedDict): @@ -67,7 +67,7 @@ class AgentImageMessage(TypedDict): class Image(Timestamped): msg_name = "sensor_msgs.Image" - def __init__( + def __init__( # type: ignore[no-untyped-def] self, impl: AbstractImage | None = None, *, @@ -106,14 +106,14 @@ def __init__( # Detect CuPy array without a hard dependency is_cu = False try: - import cupy as _cp # type: ignore + import cupy as _cp is_cu = isinstance(data, _cp.ndarray) except Exception: is_cu = False if is_cu and HAS_CUDA: - self._impl = CudaImage(data, fmt, fid, tstamp) # type: ignore + self._impl = CudaImage(data, fmt, fid, tstamp) else: self._impl = NumpyImage(np.asarray(data), fmt, fid, tstamp) @@ -129,9 +129,9 @@ def from_impl(cls, impl: AbstractImage) -> Image: return cls(impl) @classmethod - def from_numpy( + def from_numpy( # type: ignore[no-untyped-def] cls, - np_image: np.ndarray, + np_image: np.ndarray, # type: ignore[type-arg] format: ImageFormat = ImageFormat.BGR, to_cuda: bool = False, **kwargs, @@ -146,7 +146,7 @@ def from_numpy( kwargs.get("frame_id", ""), kwargs.get("ts", time.time()), ) - ) # type: ignore + ) return cls( NumpyImage( np.asarray(np_image), @@ -157,7 +157,7 @@ def from_numpy( ) @classmethod - def from_file( + def from_file( # type: ignore[no-untyped-def] cls, filepath: str, format: ImageFormat = ImageFormat.RGB, to_cuda: bool = False, **kwargs ) -> Image: if kwargs.pop("to_gpu", False): @@ -173,11 +173,11 @@ def from_file( detected = ImageFormat.BGRA # OpenCV default else: detected = format - return cls(CudaImage(arr, detected) if to_cuda and HAS_CUDA else NumpyImage(arr, detected)) # type: ignore + return cls(CudaImage(arr, detected) if to_cuda and HAS_CUDA else NumpyImage(arr, detected)) @classmethod - def from_opencv( - cls, cv_image: np.ndarray, format: ImageFormat = ImageFormat.BGR, **kwargs + def from_opencv( # type: ignore[no-untyped-def] + cls, cv_image: np.ndarray, format: ImageFormat = ImageFormat.BGR, **kwargs # type: ignore[type-arg] ) -> Image: """Construct from an OpenCV image (NumPy array).""" return cls( @@ -185,7 +185,7 @@ def from_opencv( ) @classmethod - def from_depth( + def from_depth( # type: ignore[no-untyped-def] cls, depth_data, frame_id: str = "", ts: float | None = None, to_cuda: bool = False ) -> Image: arr = np.asarray(depth_data) @@ -195,7 +195,7 @@ def from_depth( CudaImage(arr, ImageFormat.DEPTH, frame_id, time.time() if ts is None else ts) if to_cuda and HAS_CUDA else NumpyImage(arr, ImageFormat.DEPTH, frame_id, time.time() if ts is None else ts) - ) # type: ignore + ) return cls(impl) # Delegation @@ -204,18 +204,18 @@ def is_cuda(self) -> bool: return self._impl.is_cuda @property - def data(self): + def data(self): # type: ignore[no-untyped-def] return self._impl.data @data.setter - def data(self, value) -> None: + def data(self, value) -> None: # type: ignore[no-untyped-def] # Preserve backend semantics: ensure array type matches implementation if isinstance(self._impl, NumpyImage): self._impl.data = np.asarray(value) - elif isinstance(self._impl, CudaImage): # type: ignore + elif isinstance(self._impl, CudaImage): if cp is None: raise RuntimeError("CuPy not available to set CUDA image data") - self._impl.data = cp.asarray(value) # type: ignore + self._impl.data = cp.asarray(value) else: self._impl.data = value @@ -224,7 +224,7 @@ def format(self) -> ImageFormat: return self._impl.format @format.setter - def format(self, value) -> None: + def format(self, value) -> None: # type: ignore[no-untyped-def] if isinstance(value, ImageFormat): self._impl.format = value elif isinstance(value, str): @@ -264,11 +264,11 @@ def channels(self) -> int: return self._impl.channels @property - def shape(self): + def shape(self): # type: ignore[no-untyped-def] return self._impl.shape @property - def dtype(self): + def dtype(self): # type: ignore[no-untyped-def] return self._impl.dtype def copy(self) -> Image: @@ -296,9 +296,9 @@ def to_cupy(self) -> Image: CudaImage( np.asarray(self._impl.data), self._impl.format, self._impl.frame_id, self._impl.ts ) - ) # type: ignore + ) - def to_opencv(self) -> np.ndarray: + def to_opencv(self) -> np.ndarray: # type: ignore[type-arg] return self._impl.to_opencv() def to_rgb(self) -> Image: @@ -314,7 +314,7 @@ def resize(self, width: int, height: int, interpolation: int = cv2.INTER_LINEAR) return Image(self._impl.resize(width, height, interpolation)) def crop(self, x: int, y: int, width: int, height: int) -> Image: - return Image(self._impl.crop(x, y, width, height)) + return Image(self._impl.crop(x, y, width, height)) # type: ignore[attr-defined] @property def sharpness(self) -> float: @@ -363,7 +363,7 @@ def to_base64( return base64.b64encode(buffer.tobytes()).decode("utf-8") def agent_encode(self) -> AgentImageMessage: - return [ + return [ # type: ignore[return-value] { "type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{self.to_base64()}"}, @@ -404,10 +404,10 @@ def lcm_encode(self, frame_id: str | None = None) -> bytes: msg.data_length = len(image_bytes) msg.data = image_bytes - return msg.lcm_encode() + return msg.lcm_encode() # type: ignore[no-any-return] @classmethod - def lcm_decode(cls, data: bytes, **kwargs) -> Image: + def lcm_decode(cls, data: bytes, **kwargs) -> Image: # type: ignore[no-untyped-def] msg = LCMImage.lcm_decode(data) fmt, dtype, channels = _parse_lcm_encoding(msg.encoding) arr = np.frombuffer(msg.data, dtype=dtype) @@ -473,10 +473,10 @@ def lcm_jpeg_encode(self, quality: int = 75, frame_id: str | None = None) -> byt msg.data_length = len(jpeg_data) msg.data = jpeg_data - return msg.lcm_encode() + return msg.lcm_encode() # type: ignore[no-any-return] @classmethod - def lcm_jpeg_decode(cls, data: bytes, **kwargs) -> Image: + def lcm_jpeg_decode(cls, data: bytes, **kwargs) -> Image: # type: ignore[no-untyped-def] """Decode an LCM Image message with JPEG-compressed data. Args: @@ -510,19 +510,19 @@ def lcm_jpeg_decode(cls, data: bytes, **kwargs) -> Image: ) # PnP wrappers - def solve_pnp(self, *args, **kwargs): + def solve_pnp(self, *args, **kwargs): # type: ignore[no-untyped-def] return self._impl.solve_pnp(*args, **kwargs) # type: ignore - def solve_pnp_ransac(self, *args, **kwargs): + def solve_pnp_ransac(self, *args, **kwargs): # type: ignore[no-untyped-def] return self._impl.solve_pnp_ransac(*args, **kwargs) # type: ignore - def solve_pnp_batch(self, *args, **kwargs): + def solve_pnp_batch(self, *args, **kwargs): # type: ignore[no-untyped-def] return self._impl.solve_pnp_batch(*args, **kwargs) # type: ignore - def create_csrt_tracker(self, *args, **kwargs): + def create_csrt_tracker(self, *args, **kwargs): # type: ignore[no-untyped-def] return self._impl.create_csrt_tracker(*args, **kwargs) # type: ignore - def csrt_update(self, *args, **kwargs): + def csrt_update(self, *args, **kwargs): # type: ignore[no-untyped-def] return self._impl.csrt_update(*args, **kwargs) # type: ignore @classmethod @@ -587,7 +587,7 @@ def from_ros_msg(cls, ros_msg: ROSImage) -> Image: ) @staticmethod - def _parse_encoding(encoding: str) -> dict: + def _parse_encoding(encoding: str) -> dict: # type: ignore[type-arg] """Translate ROS encoding strings into format metadata.""" encoding_map = { "mono8": {"format": ImageFormat.GRAY, "dtype": np.uint8, "channels": 1}, @@ -614,7 +614,7 @@ def __repr__(self) -> str: dev = "cuda" if self.is_cuda else "cpu" return f"Image(shape={self.shape}, format={self.format.value}, dtype={self.dtype}, dev={dev}, frame_id='{self.frame_id}', ts={self.ts})" - def __eq__(self, other) -> bool: + def __eq__(self, other) -> bool: # type: ignore[no-untyped-def] if not isinstance(other, Image): return False return ( @@ -627,11 +627,11 @@ def __eq__(self, other) -> bool: def __len__(self) -> int: return int(self.height * self.width) - def __getstate__(self): + def __getstate__(self): # type: ignore[no-untyped-def] return {"data": self.data, "format": self.format, "frame_id": self.frame_id, "ts": self.ts} - def __setstate__(self, state) -> None: - self.__init__( + def __setstate__(self, state) -> None: # type: ignore[no-untyped-def] + self.__init__( # type: ignore[misc] data=state.get("data"), format=state.get("format"), frame_id=state.get("frame_id"), @@ -659,12 +659,12 @@ def sharpness_window(target_frequency: float, source: Observable[Image]) -> Obse if target_frequency <= 0: raise ValueError("target_frequency must be positive") - window = TimestampedBufferCollection(1.0 / target_frequency) + window = TimestampedBufferCollection(1.0 / target_frequency) # type: ignore[var-annotated] source.subscribe(window.add) - thread_scheduler = ThreadPoolScheduler(max_workers=1) + thread_scheduler = ThreadPoolScheduler(max_workers=1) # type: ignore[name-defined] - def find_best(*_args): + def find_best(*_args): # type: ignore[no-untyped-def] if not window._items: return None return max(window._items, key=lambda img: img.sharpness) @@ -676,14 +676,14 @@ def find_best(*_args): ) -def sharpness_barrier(target_frequency: float): +def sharpness_barrier(target_frequency: float): # type: ignore[no-untyped-def] """Select the sharpest Image within each time window.""" if target_frequency <= 0: raise ValueError("target_frequency must be positive") - return quality_barrier(lambda image: image.sharpness, target_frequency) + return quality_barrier(lambda image: image.sharpness, target_frequency) # type: ignore[attr-defined] -def _get_lcm_encoding(fmt: ImageFormat, dtype: np.dtype) -> str: +def _get_lcm_encoding(fmt: ImageFormat, dtype: np.dtype) -> str: # type: ignore[type-arg] if fmt == ImageFormat.GRAY: if dtype == np.uint8: return "mono8" @@ -712,7 +712,7 @@ def _get_lcm_encoding(fmt: ImageFormat, dtype: np.dtype) -> str: raise ValueError(f"Unsupported LCM encoding for fmt={fmt}, dtype={dtype}") -def _parse_lcm_encoding(enc: str): +def _parse_lcm_encoding(enc: str): # type: ignore[no-untyped-def] m = { "mono8": (ImageFormat.GRAY, np.uint8, 1), "mono16": (ImageFormat.GRAY16, np.uint16, 1), diff --git a/dimos/msgs/sensor_msgs/Joy.py b/dimos/msgs/sensor_msgs/Joy.py index aa8611655a..3cf083c553 100644 --- a/dimos/msgs/sensor_msgs/Joy.py +++ b/dimos/msgs/sensor_msgs/Joy.py @@ -17,12 +17,12 @@ import time from typing import TypeAlias -from dimos_lcm.sensor_msgs import Joy as LCMJoy +from dimos_lcm.sensor_msgs import Joy as LCMJoy # type: ignore[import-untyped] try: - from sensor_msgs.msg import Joy as ROSJoy + from sensor_msgs.msg import Joy as ROSJoy # type: ignore[attr-defined] except ImportError: - ROSJoy = None + ROSJoy = None # type: ignore[assignment, misc] from plum import dispatch @@ -34,7 +34,7 @@ ) -def sec_nsec(ts): +def sec_nsec(ts): # type: ignore[no-untyped-def] s = int(ts) return [s, int((ts - s) * 1_000_000_000)] @@ -67,7 +67,7 @@ def __init__( self.axes = axes if axes is not None else [] self.buttons = buttons if buttons is not None else [] - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, joy_tuple: tuple[list[float], list[int]]) -> None: """Initialize from a tuple of (axes, buttons).""" self.ts = time.time() @@ -75,7 +75,7 @@ def __init__(self, joy_tuple: tuple[list[float], list[int]]) -> None: self.axes = list(joy_tuple[0]) self.buttons = list(joy_tuple[1]) - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, joy_dict: dict[str, list[float] | list[int]]) -> None: """Initialize from a dictionary with 'axes' and 'buttons' keys.""" self.ts = joy_dict.get("ts", time.time()) @@ -83,7 +83,7 @@ def __init__(self, joy_dict: dict[str, list[float] | list[int]]) -> None: self.axes = list(joy_dict.get("axes", [])) self.buttons = list(joy_dict.get("buttons", [])) - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, joy: Joy) -> None: """Initialize from another Joy (copy constructor).""" self.ts = joy.ts @@ -91,7 +91,7 @@ def __init__(self, joy: Joy) -> None: self.axes = list(joy.axes) self.buttons = list(joy.buttons) - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, lcm_joy: LCMJoy) -> None: """Initialize from an LCM Joy message.""" self.ts = lcm_joy.header.stamp.sec + (lcm_joy.header.stamp.nsec / 1_000_000_000) @@ -101,13 +101,13 @@ def __init__(self, lcm_joy: LCMJoy) -> None: def lcm_encode(self) -> bytes: lcm_msg = LCMJoy() - [lcm_msg.header.stamp.sec, lcm_msg.header.stamp.nsec] = sec_nsec(self.ts) + [lcm_msg.header.stamp.sec, lcm_msg.header.stamp.nsec] = sec_nsec(self.ts) # type: ignore[no-untyped-call] lcm_msg.header.frame_id = self.frame_id lcm_msg.axes_length = len(self.axes) lcm_msg.axes = self.axes lcm_msg.buttons_length = len(self.buttons) lcm_msg.buttons = self.buttons - return lcm_msg.lcm_encode() + return lcm_msg.lcm_encode() # type: ignore[no-any-return] @classmethod def lcm_decode(cls, data: bytes) -> Joy: @@ -131,7 +131,7 @@ def __repr__(self) -> str: f"axes={self.axes}, buttons={self.buttons})" ) - def __eq__(self, other) -> bool: + def __eq__(self, other) -> bool: # type: ignore[no-untyped-def] """Check if two Joy messages are equal.""" if not isinstance(other, Joy): return False @@ -167,7 +167,7 @@ def to_ros_msg(self) -> ROSJoy: Returns: ROS Joy message """ - ros_msg = ROSJoy() + ros_msg = ROSJoy() # type: ignore[no-untyped-call] # Set header ros_msg.header.frame_id = self.frame_id diff --git a/dimos/msgs/sensor_msgs/PointCloud2.py b/dimos/msgs/sensor_msgs/PointCloud2.py index b8de431fa0..20e3a70c49 100644 --- a/dimos/msgs/sensor_msgs/PointCloud2.py +++ b/dimos/msgs/sensor_msgs/PointCloud2.py @@ -18,20 +18,20 @@ import struct # Import LCM types -from dimos_lcm.sensor_msgs.PointCloud2 import ( +from dimos_lcm.sensor_msgs.PointCloud2 import ( # type: ignore[import-untyped] PointCloud2 as LCMPointCloud2, ) -from dimos_lcm.sensor_msgs.PointField import PointField -from dimos_lcm.std_msgs.Header import Header +from dimos_lcm.sensor_msgs.PointField import PointField # type: ignore[import-untyped] +from dimos_lcm.std_msgs.Header import Header # type: ignore[import-untyped] import numpy as np -import open3d as o3d +import open3d as o3d # type: ignore[import-untyped] from dimos.msgs.geometry_msgs import Vector3 # Import ROS types try: - from sensor_msgs.msg import PointCloud2 as ROSPointCloud2, PointField as ROSPointField - from std_msgs.msg import Header as ROSHeader + from sensor_msgs.msg import PointCloud2 as ROSPointCloud2, PointField as ROSPointField # type: ignore[attr-defined] + from std_msgs.msg import Header as ROSHeader # type: ignore[attr-defined] ROS_AVAILABLE = True except ImportError: @@ -50,13 +50,13 @@ def __init__( frame_id: str = "world", ts: float | None = None, ) -> None: - self.ts = ts + self.ts = ts # type: ignore[assignment] self.pointcloud = pointcloud if pointcloud is not None else o3d.geometry.PointCloud() self.frame_id = frame_id @classmethod def from_numpy( - cls, points: np.ndarray, frame_id: str = "world", timestamp: float | None = None + cls, points: np.ndarray, frame_id: str = "world", timestamp: float | None = None # type: ignore[type-arg] ) -> PointCloud2: """Create PointCloud2 from numpy array of shape (N, 3). @@ -81,7 +81,7 @@ def center(self) -> Vector3: center = np.asarray(self.pointcloud.points).mean(axis=0) return Vector3(*center) - def points(self): + def points(self): # type: ignore[no-untyped-def] return self.pointcloud.points def __add__(self, other: PointCloud2) -> PointCloud2: @@ -106,7 +106,7 @@ def __add__(self, other: PointCloud2) -> PointCloud2: ) # TODO what's the usual storage here? is it already numpy? - def as_numpy(self) -> np.ndarray: + def as_numpy(self) -> np.ndarray: # type: ignore[type-arg] """Get points as numpy array.""" return np.asarray(self.pointcloud.points) @@ -140,7 +140,7 @@ def bounding_box_intersects(self, other: PointCloud2) -> bool: # Check overlap in all three dimensions # Boxes intersect if they overlap in ALL dimensions - return ( + return ( # type: ignore[no-any-return] min1[0] <= max2[0] and max1[0] >= min2[0] and min1[1] <= max2[1] @@ -174,7 +174,7 @@ def lcm_encode(self, frame_id: str | None = None) -> bytes: msg.is_bigendian = False msg.fields_length = 4 # x, y, z, intensity msg.fields = self._create_xyz_field() - return msg.lcm_encode() + return msg.lcm_encode() # type: ignore[no-any-return] # Point cloud dimensions msg.height = 1 # Unorganized point cloud @@ -204,7 +204,7 @@ def lcm_encode(self, frame_id: str | None = None) -> bytes: msg.is_dense = True # No invalid points msg.is_bigendian = False # Little endian - return msg.lcm_encode() + return msg.lcm_encode() # type: ignore[no-any-return] @classmethod def lcm_decode(cls, data: bytes) -> PointCloud2: @@ -265,7 +265,7 @@ def lcm_decode(cls, data: bytes) -> PointCloud2: else None, ) - def _create_xyz_field(self) -> list: + def _create_xyz_field(self) -> list: # type: ignore[type-arg] """Create standard X, Y, Z field definitions for LCM PointCloud2.""" fields = [] @@ -452,18 +452,18 @@ def from_ros_msg(cls, ros_msg: ROSPointCloud2) -> PointCloud2: dt_fields = [] # Add padding before x if needed - if x_offset > 0: + if x_offset > 0: # type: ignore[operator] dt_fields.append(("_pad_x", f"V{x_offset}")) dt_fields.append(("x", f"{byte_order}f4")) # Add padding between x and y if needed - gap_xy = y_offset - x_offset - 4 + gap_xy = y_offset - x_offset - 4 # type: ignore[operator] if gap_xy > 0: dt_fields.append(("_pad_xy", f"V{gap_xy}")) dt_fields.append(("y", f"{byte_order}f4")) # Add padding between y and z if needed - gap_yz = z_offset - y_offset - 4 + gap_yz = z_offset - y_offset - 4 # type: ignore[operator] if gap_yz > 0: dt_fields.append(("_pad_yz", f"V{gap_yz}")) dt_fields.append(("z", f"{byte_order}f4")) @@ -504,10 +504,10 @@ def to_ros_msg(self) -> ROSPointCloud2: if not ROS_AVAILABLE: raise ImportError("ROS packages not available. Cannot convert to ROS message.") - ros_msg = ROSPointCloud2() + ros_msg = ROSPointCloud2() # type: ignore[no-untyped-call] # Set header - ros_msg.header = ROSHeader() + ros_msg.header = ROSHeader() # type: ignore[no-untyped-call] ros_msg.header.frame_id = self.frame_id ros_msg.header.stamp.sec = int(self.ts) ros_msg.header.stamp.nanosec = int((self.ts - int(self.ts)) * 1e9) @@ -532,9 +532,9 @@ def to_ros_msg(self) -> ROSPointCloud2: # Define fields (X, Y, Z as float32) ros_msg.fields = [ - ROSPointField(name="x", offset=0, datatype=ROSPointField.FLOAT32, count=1), - ROSPointField(name="y", offset=4, datatype=ROSPointField.FLOAT32, count=1), - ROSPointField(name="z", offset=8, datatype=ROSPointField.FLOAT32, count=1), + ROSPointField(name="x", offset=0, datatype=ROSPointField.FLOAT32, count=1), # type: ignore[no-untyped-call] + ROSPointField(name="y", offset=4, datatype=ROSPointField.FLOAT32, count=1), # type: ignore[no-untyped-call] + ROSPointField(name="z", offset=8, datatype=ROSPointField.FLOAT32, count=1), # type: ignore[no-untyped-call] ] # Set point step and row step diff --git a/dimos/msgs/sensor_msgs/image_impls/AbstractImage.py b/dimos/msgs/sensor_msgs/image_impls/AbstractImage.py index 9dd0c647d2..10d8133402 100644 --- a/dimos/msgs/sensor_msgs/image_impls/AbstractImage.py +++ b/dimos/msgs/sensor_msgs/image_impls/AbstractImage.py @@ -28,7 +28,7 @@ HAS_CUDA = True except Exception: # pragma: no cover - optional dependency - cp = None # type: ignore + cp = None HAS_CUDA = False # Optional nvImageCodec (preferred GPU codec) @@ -39,16 +39,16 @@ from nvidia import nvimgcodec # type: ignore try: - _enc_probe = nvimgcodec.Encoder() # type: ignore[attr-defined] + _enc_probe = nvimgcodec.Encoder() HAS_NVIMGCODEC = True except Exception: - nvimgcodec = None # type: ignore + nvimgcodec = None HAS_NVIMGCODEC = False else: - nvimgcodec = None # type: ignore + nvimgcodec = None HAS_NVIMGCODEC = False except Exception: # pragma: no cover - optional dependency - nvimgcodec = None # type: ignore + nvimgcodec = None HAS_NVIMGCODEC = False @@ -63,42 +63,42 @@ class ImageFormat(Enum): DEPTH16 = "DEPTH16" -def _is_cu(x) -> bool: - return HAS_CUDA and cp is not None and isinstance(x, cp.ndarray) # type: ignore +def _is_cu(x) -> bool: # type: ignore[no-untyped-def] + return HAS_CUDA and cp is not None and isinstance(x, cp.ndarray) -def _ascontig(x): +def _ascontig(x): # type: ignore[no-untyped-def] if _is_cu(x): - return x if x.flags["C_CONTIGUOUS"] else cp.ascontiguousarray(x) # type: ignore + return x if x.flags["C_CONTIGUOUS"] else cp.ascontiguousarray(x) return x if x.flags["C_CONTIGUOUS"] else np.ascontiguousarray(x) -def _to_cpu(x): - return cp.asnumpy(x) if _is_cu(x) else x # type: ignore +def _to_cpu(x): # type: ignore[no-untyped-def] + return cp.asnumpy(x) if _is_cu(x) else x -def _to_cu(x): - if HAS_CUDA and cp is not None and isinstance(x, np.ndarray): # type: ignore - return cp.asarray(x) # type: ignore +def _to_cu(x): # type: ignore[no-untyped-def] + if HAS_CUDA and cp is not None and isinstance(x, np.ndarray): + return cp.asarray(x) return x -def _encode_nvimgcodec_cuda(bgr_cu, quality: int = 80) -> bytes: # pragma: no cover - optional +def _encode_nvimgcodec_cuda(bgr_cu, quality: int = 80) -> bytes: # type: ignore[no-untyped-def] # pragma: no cover - optional if not HAS_NVIMGCODEC or nvimgcodec is None: raise RuntimeError("nvimgcodec not available") if bgr_cu.ndim != 3 or bgr_cu.shape[2] != 3: raise RuntimeError("nvimgcodec expects HxWx3 image") - if bgr_cu.dtype != cp.uint8: # type: ignore[attr-defined] + if bgr_cu.dtype != cp.uint8: raise RuntimeError("nvimgcodec requires uint8 input") if not bgr_cu.flags["C_CONTIGUOUS"]: - bgr_cu = cp.ascontiguousarray(bgr_cu) # type: ignore[attr-defined] - encoder = nvimgcodec.Encoder() # type: ignore[attr-defined] + bgr_cu = cp.ascontiguousarray(bgr_cu) + encoder = nvimgcodec.Encoder() try: - img = nvimgcodec.Image(bgr_cu, nvimgcodec.PixelFormat.BGR) # type: ignore[attr-defined] + img = nvimgcodec.Image(bgr_cu, nvimgcodec.PixelFormat.BGR) except Exception: - img = nvimgcodec.Image(cp.asnumpy(bgr_cu), nvimgcodec.PixelFormat.BGR) # type: ignore[attr-defined] + img = nvimgcodec.Image(cp.asnumpy(bgr_cu), nvimgcodec.PixelFormat.BGR) if hasattr(nvimgcodec, "EncodeParams"): - params = nvimgcodec.EncodeParams(quality=quality) # type: ignore[attr-defined] + params = nvimgcodec.EncodeParams(quality=quality) bitstreams = encoder.encode([img], [params]) else: bitstreams = encoder.encode([img]) @@ -136,15 +136,15 @@ def channels(self) -> int: raise ValueError("Invalid image dimensions") @property - def shape(self): + def shape(self): # type: ignore[no-untyped-def] return tuple(self.data.shape) @property - def dtype(self): + def dtype(self): # type: ignore[no-untyped-def] return self.data.dtype @abstractmethod - def to_opencv(self) -> np.ndarray: # pragma: no cover - abstract + def to_opencv(self) -> np.ndarray: # type: ignore[type-arg] # pragma: no cover - abstract ... @abstractmethod @@ -203,7 +203,7 @@ def to_base64(self, quality: int = 80) -> str: NVIMGCODEC_LAST_USED = False bgr = self.to_bgr() success, buffer = cv2.imencode( - ".jpg", _to_cpu(bgr.data), [int(cv2.IMWRITE_JPEG_QUALITY), int(quality)] + ".jpg", _to_cpu(bgr.data), [int(cv2.IMWRITE_JPEG_QUALITY), int(quality)] # type: ignore[no-untyped-call] ) if not success: raise ValueError("Failed to encode image as JPEG") diff --git a/dimos/msgs/sensor_msgs/image_impls/CudaImage.py b/dimos/msgs/sensor_msgs/image_impls/CudaImage.py index 3067138d36..44c0851ec8 100644 --- a/dimos/msgs/sensor_msgs/image_impls/CudaImage.py +++ b/dimos/msgs/sensor_msgs/image_impls/CudaImage.py @@ -31,14 +31,14 @@ try: import cupy as cp # type: ignore - from cupyx.scipy import ( - ndimage as cndimage, # type: ignore - signal as csignal, # type: ignore + from cupyx.scipy import ( # type: ignore[import-not-found] + ndimage as cndimage, + signal as csignal, ) except Exception: # pragma: no cover - cp = None # type: ignore - cndimage = None # type: ignore - csignal = None # type: ignore + cp = None + cndimage = None + csignal = None _CUDA_SRC = r""" @@ -267,7 +267,7 @@ _pnp_kernel = _mod.get_function("pnp_gn_batch") -def _solve_pnp_cuda_kernel(obj, img, K, iterations: int = 15, damping: float = 1e-6): +def _solve_pnp_cuda_kernel(obj, img, K, iterations: int = 15, damping: float = 1e-6): # type: ignore[no-untyped-def] if cp is None: raise RuntimeError("CuPy/CUDA not available") @@ -291,7 +291,7 @@ def _solve_pnp_cuda_kernel(obj, img, K, iterations: int = 15, damping: float = 1 obj_cu = cp.ascontiguousarray(obj_cu) img_cu = cp.ascontiguousarray(img_cu) - K_np = np.asarray(_to_cpu(K), dtype=np.float32) + K_np = np.asarray(_to_cpu(K), dtype=np.float32) # type: ignore[no-untyped-call] np_intri = np.empty((B, 4), dtype=np.float32) if K_np.ndim == 2: if K_np.shape != (3, 3): @@ -344,42 +344,42 @@ def _solve_pnp_cuda_kernel(obj, img, K, iterations: int = 15, damping: float = 1 return r_host, t_host -def _bgr_to_rgb_cuda(img): +def _bgr_to_rgb_cuda(img): # type: ignore[no-untyped-def] return img[..., ::-1] -def _rgb_to_bgr_cuda(img): +def _rgb_to_bgr_cuda(img): # type: ignore[no-untyped-def] return img[..., ::-1] -def _bgra_to_rgba_cuda(img): +def _bgra_to_rgba_cuda(img): # type: ignore[no-untyped-def] out = img.copy() out[..., 0], out[..., 2] = img[..., 2], img[..., 0] return out -def _rgba_to_bgra_cuda(img): +def _rgba_to_bgra_cuda(img): # type: ignore[no-untyped-def] out = img.copy() out[..., 0], out[..., 2] = img[..., 2], img[..., 0] return out -def _gray_to_rgb_cuda(gray): - return cp.stack([gray, gray, gray], axis=-1) # type: ignore +def _gray_to_rgb_cuda(gray): # type: ignore[no-untyped-def] + return cp.stack([gray, gray, gray], axis=-1) -def _rgb_to_gray_cuda(rgb): - r = rgb[..., 0].astype(cp.float32) # type: ignore - g = rgb[..., 1].astype(cp.float32) # type: ignore - b = rgb[..., 2].astype(cp.float32) # type: ignore +def _rgb_to_gray_cuda(rgb): # type: ignore[no-untyped-def] + r = rgb[..., 0].astype(cp.float32) + g = rgb[..., 1].astype(cp.float32) + b = rgb[..., 2].astype(cp.float32) # These come from the Rec.601 conversion for YUV. R = 0.299, G = 0.587, B = 0.114 y = 0.299 * r + 0.587 * g + 0.114 * b - if rgb.dtype == cp.uint8: # type: ignore - y = cp.clip(y, 0, 255).astype(cp.uint8) # type: ignore + if rgb.dtype == cp.uint8: + y = cp.clip(y, 0, 255).astype(cp.uint8) return y -def _resize_bilinear_hwc_cuda(img, out_h: int, out_w: int): +def _resize_bilinear_hwc_cuda(img, out_h: int, out_w: int): # type: ignore[no-untyped-def] if cp is None or cndimage is None: raise RuntimeError("CuPy/CUDA not available") if img.ndim not in (2, 3): @@ -410,11 +410,11 @@ def _resize_bilinear_hwc_cuda(img, out_h: int, out_w: int): return out -def _rodrigues(x, inverse: bool = False): +def _rodrigues(x, inverse: bool = False): # type: ignore[no-untyped-def] """Unified Rodrigues transform (vector<->matrix) for NumPy/CuPy arrays.""" if cp is not None and ( - isinstance(x, cp.ndarray) # type: ignore[arg-type] + isinstance(x, cp.ndarray) or getattr(x, "__cuda_array_interface__", None) is not None ): xp = cp @@ -437,7 +437,7 @@ def _rodrigues(x, inverse: bool = False): theta = xp.linalg.norm(vec, axis=1) small = theta < 1e-12 - def _skew(v): + def _skew(v): # type: ignore[no-untyped-def] vx, vy, vz = v[:, 0], v[:, 1], v[:, 2] O = xp.zeros_like(vx) return xp.stack( @@ -449,7 +449,7 @@ def _skew(v): axis=-2, ) - K = _skew(vec) + K = _skew(vec) # type: ignore[no-untyped-call] theta2 = theta * theta theta4 = theta2 * theta2 theta_safe = xp.where(small, 1.0, theta) @@ -543,12 +543,12 @@ def _undistort_points_cuda( @dataclass class CudaImage(AbstractImage): - data: any # cupy.ndarray + data: any # type: ignore[valid-type] # cupy.ndarray format: ImageFormat = field(default=ImageFormat.BGR) frame_id: str = field(default="") ts: float = field(default_factory=time.time) - def __post_init__(self): + def __post_init__(self): # type: ignore[no-untyped-def] if not HAS_CUDA or cp is None: raise RuntimeError("CuPy/CUDA not available") if not _is_cu(self.data): @@ -557,51 +557,51 @@ def __post_init__(self): self.data = cp.asarray(self.data) except Exception as e: raise ValueError("CudaImage requires a CuPy array") from e - if self.data.ndim < 2: + if self.data.ndim < 2: # type: ignore[attr-defined] raise ValueError("Image data must be at least 2D") - self.data = _ascontig(self.data) + self.data = _ascontig(self.data) # type: ignore[no-untyped-call] @property def is_cuda(self) -> bool: return True - def to_opencv(self) -> np.ndarray: + def to_opencv(self) -> np.ndarray: # type: ignore[type-arg] if self.format in (ImageFormat.BGR, ImageFormat.RGB, ImageFormat.RGBA, ImageFormat.BGRA): - return _to_cpu(self.to_bgr().data) - return _to_cpu(self.data) + return _to_cpu(self.to_bgr().data) # type: ignore[no-any-return, no-untyped-call] + return _to_cpu(self.data) # type: ignore[no-any-return, no-untyped-call] def to_rgb(self) -> CudaImage: if self.format == ImageFormat.RGB: return self.copy() # type: ignore if self.format == ImageFormat.BGR: - return CudaImage(_bgr_to_rgb_cuda(self.data), ImageFormat.RGB, self.frame_id, self.ts) + return CudaImage(_bgr_to_rgb_cuda(self.data), ImageFormat.RGB, self.frame_id, self.ts) # type: ignore[no-untyped-call] if self.format == ImageFormat.RGBA: return self.copy() # type: ignore if self.format == ImageFormat.BGRA: return CudaImage( - _bgra_to_rgba_cuda(self.data), ImageFormat.RGBA, self.frame_id, self.ts + _bgra_to_rgba_cuda(self.data), ImageFormat.RGBA, self.frame_id, self.ts # type: ignore[no-untyped-call] ) if self.format == ImageFormat.GRAY: - return CudaImage(_gray_to_rgb_cuda(self.data), ImageFormat.RGB, self.frame_id, self.ts) + return CudaImage(_gray_to_rgb_cuda(self.data), ImageFormat.RGB, self.frame_id, self.ts) # type: ignore[no-untyped-call] if self.format in (ImageFormat.GRAY16, ImageFormat.DEPTH16): gray8 = (self.data.astype(cp.float32) / 256.0).clip(0, 255).astype(cp.uint8) # type: ignore - return CudaImage(_gray_to_rgb_cuda(gray8), ImageFormat.RGB, self.frame_id, self.ts) + return CudaImage(_gray_to_rgb_cuda(gray8), ImageFormat.RGB, self.frame_id, self.ts) # type: ignore[no-untyped-call] return self.copy() # type: ignore def to_bgr(self) -> CudaImage: if self.format == ImageFormat.BGR: return self.copy() # type: ignore if self.format == ImageFormat.RGB: - return CudaImage(_rgb_to_bgr_cuda(self.data), ImageFormat.BGR, self.frame_id, self.ts) + return CudaImage(_rgb_to_bgr_cuda(self.data), ImageFormat.BGR, self.frame_id, self.ts) # type: ignore[no-untyped-call] if self.format == ImageFormat.RGBA: return CudaImage( - _rgba_to_bgra_cuda(self.data)[..., :3], ImageFormat.BGR, self.frame_id, self.ts + _rgba_to_bgra_cuda(self.data)[..., :3], ImageFormat.BGR, self.frame_id, self.ts # type: ignore[no-untyped-call] ) if self.format == ImageFormat.BGRA: - return CudaImage(self.data[..., :3], ImageFormat.BGR, self.frame_id, self.ts) + return CudaImage(self.data[..., :3], ImageFormat.BGR, self.frame_id, self.ts) # type: ignore[index] if self.format in (ImageFormat.GRAY, ImageFormat.DEPTH): return CudaImage( - _rgb_to_bgr_cuda(_gray_to_rgb_cuda(self.data)), + _rgb_to_bgr_cuda(_gray_to_rgb_cuda(self.data)), # type: ignore[no-untyped-call] ImageFormat.BGR, self.frame_id, self.ts, @@ -609,7 +609,7 @@ def to_bgr(self) -> CudaImage: if self.format in (ImageFormat.GRAY16, ImageFormat.DEPTH16): gray8 = (self.data.astype(cp.float32) / 256.0).clip(0, 255).astype(cp.uint8) # type: ignore return CudaImage( - _rgb_to_bgr_cuda(_gray_to_rgb_cuda(gray8)), ImageFormat.BGR, self.frame_id, self.ts + _rgb_to_bgr_cuda(_gray_to_rgb_cuda(gray8)), ImageFormat.BGR, self.frame_id, self.ts # type: ignore[no-untyped-call] ) return self.copy() # type: ignore @@ -618,20 +618,20 @@ def to_grayscale(self) -> CudaImage: return self.copy() # type: ignore if self.format == ImageFormat.BGR: return CudaImage( - _rgb_to_gray_cuda(_bgr_to_rgb_cuda(self.data)), + _rgb_to_gray_cuda(_bgr_to_rgb_cuda(self.data)), # type: ignore[no-untyped-call] ImageFormat.GRAY, self.frame_id, self.ts, ) if self.format == ImageFormat.RGB: - return CudaImage(_rgb_to_gray_cuda(self.data), ImageFormat.GRAY, self.frame_id, self.ts) + return CudaImage(_rgb_to_gray_cuda(self.data), ImageFormat.GRAY, self.frame_id, self.ts) # type: ignore[no-untyped-call] if self.format in (ImageFormat.RGBA, ImageFormat.BGRA): rgb = ( - self.data[..., :3] + self.data[..., :3] # type: ignore[index] if self.format == ImageFormat.RGBA - else _bgra_to_rgba_cuda(self.data)[..., :3] + else _bgra_to_rgba_cuda(self.data)[..., :3] # type: ignore[no-untyped-call] ) - return CudaImage(_rgb_to_gray_cuda(rgb), ImageFormat.GRAY, self.frame_id, self.ts) + return CudaImage(_rgb_to_gray_cuda(rgb), ImageFormat.GRAY, self.frame_id, self.ts) # type: ignore[no-untyped-call] raise ValueError(f"Unsupported format: {self.format}") def resize(self, width: int, height: int, interpolation: int = cv2.INTER_LINEAR) -> CudaImage: @@ -652,7 +652,7 @@ def crop(self, x: int, y: int, width: int, height: int) -> CudaImage: A new CudaImage containing the cropped region """ # Get current image dimensions - img_height, img_width = self.data.shape[:2] + img_height, img_width = self.data.shape[:2] # type: ignore[attr-defined] # Clamp the crop region to image bounds x = max(0, min(x, img_width)) @@ -661,12 +661,12 @@ def crop(self, x: int, y: int, width: int, height: int) -> CudaImage: y_end = min(y + height, img_height) # Perform the crop using array slicing - if self.data.ndim == 2: + if self.data.ndim == 2: # type: ignore[attr-defined] # Grayscale image - cropped_data = self.data[y:y_end, x:x_end] + cropped_data = self.data[y:y_end, x:x_end] # type: ignore[index] else: # Color image (HxWxC) - cropped_data = self.data[y:y_end, x:x_end, :] + cropped_data = self.data[y:y_end, x:x_end, :] # type: ignore[index] # Return a new CudaImage with the cropped data return CudaImage(cropped_data, self.format, self.frame_id, self.ts) @@ -675,17 +675,17 @@ def sharpness(self) -> float: if cp is None: return 0.0 try: - from cupyx.scipy import ndimage as cndimage # type: ignore + from cupyx.scipy import ndimage as cndimage - gray = self.to_grayscale().data.astype(cp.float32) + gray = self.to_grayscale().data.astype(cp.float32) # type: ignore[attr-defined] deriv5 = cp.asarray([1, 2, 0, -2, -1], dtype=cp.float32) smooth5 = cp.asarray([1, 4, 6, 4, 1], dtype=cp.float32) - gx = cndimage.convolve1d(gray, deriv5, axis=1, mode="reflect") # type: ignore - gx = cndimage.convolve1d(gx, smooth5, axis=0, mode="reflect") # type: ignore - gy = cndimage.convolve1d(gray, deriv5, axis=0, mode="reflect") # type: ignore - gy = cndimage.convolve1d(gy, smooth5, axis=1, mode="reflect") # type: ignore - magnitude = cp.hypot(gx, gy) # type: ignore - mean_mag = float(cp.asnumpy(magnitude.mean())) # type: ignore + gx = cndimage.convolve1d(gray, deriv5, axis=1, mode="reflect") + gx = cndimage.convolve1d(gx, smooth5, axis=0, mode="reflect") + gy = cndimage.convolve1d(gray, deriv5, axis=0, mode="reflect") + gy = cndimage.convolve1d(gy, smooth5, axis=1, mode="reflect") + magnitude = cp.hypot(gx, gy) + mean_mag = float(cp.asnumpy(magnitude.mean())) except Exception: return 0.0 if mean_mag <= 0: @@ -700,38 +700,38 @@ class BBox: w: int h: int - def create_csrt_tracker(self, bbox: BBox): + def create_csrt_tracker(self, bbox: BBox): # type: ignore[no-untyped-def] if csignal is None: raise RuntimeError("cupyx.scipy.signal not available for CUDA tracker") - x, y, w, h = map(int, bbox) - gray = self.to_grayscale().data.astype(cp.float32) + x, y, w, h = map(int, bbox) # type: ignore[call-overload] + gray = self.to_grayscale().data.astype(cp.float32) # type: ignore[attr-defined] tmpl = gray[y : y + h, x : x + w] if tmpl.size == 0: raise ValueError("Invalid bbox for CUDA tracker") return _CudaTemplateTracker(tmpl, x0=x, y0=y) - def csrt_update(self, tracker) -> tuple[bool, tuple[int, int, int, int]]: + def csrt_update(self, tracker) -> tuple[bool, tuple[int, int, int, int]]: # type: ignore[no-untyped-def] if not isinstance(tracker, _CudaTemplateTracker): raise TypeError("Expected CUDA tracker instance") - gray = self.to_grayscale().data.astype(cp.float32) + gray = self.to_grayscale().data.astype(cp.float32) # type: ignore[attr-defined] x, y, w, h = tracker.update(gray) return True, (int(x), int(y), int(w), int(h)) # PnP – Gauss–Newton (no distortion in batch), iterative per-instance def solve_pnp( self, - object_points: np.ndarray, - image_points: np.ndarray, - camera_matrix: np.ndarray, - dist_coeffs: np.ndarray | None = None, + object_points: np.ndarray, # type: ignore[type-arg] + image_points: np.ndarray, # type: ignore[type-arg] + camera_matrix: np.ndarray, # type: ignore[type-arg] + dist_coeffs: np.ndarray | None = None, # type: ignore[type-arg] flags: int = cv2.SOLVEPNP_ITERATIVE, - ) -> tuple[bool, np.ndarray, np.ndarray]: + ) -> tuple[bool, np.ndarray, np.ndarray]: # type: ignore[type-arg] if not HAS_CUDA or cp is None or (dist_coeffs is not None and np.any(dist_coeffs)): obj = np.asarray(object_points, dtype=np.float32).reshape(-1, 3) img = np.asarray(image_points, dtype=np.float32).reshape(-1, 2) K = np.asarray(camera_matrix, dtype=np.float64) dist = None if dist_coeffs is None else np.asarray(dist_coeffs, dtype=np.float64) - ok, rvec, tvec = cv2.solvePnP(obj, img, K, dist, flags=flags) + ok, rvec, tvec = cv2.solvePnP(obj, img, K, dist, flags=flags) # type: ignore[arg-type] return bool(ok), rvec.astype(np.float64), tvec.astype(np.float64) rvec, tvec = _solve_pnp_cuda_kernel(object_points, image_points, camera_matrix) @@ -740,13 +740,13 @@ def solve_pnp( def solve_pnp_batch( self, - object_points_batch: np.ndarray, - image_points_batch: np.ndarray, - camera_matrix: np.ndarray, - dist_coeffs: np.ndarray | None = None, + object_points_batch: np.ndarray, # type: ignore[type-arg] + image_points_batch: np.ndarray, # type: ignore[type-arg] + camera_matrix: np.ndarray, # type: ignore[type-arg] + dist_coeffs: np.ndarray | None = None, # type: ignore[type-arg] iterations: int = 15, damping: float = 1e-6, - ) -> tuple[np.ndarray, np.ndarray]: + ) -> tuple[np.ndarray, np.ndarray]: # type: ignore[type-arg] """Batched PnP (each block = one instance).""" if not HAS_CUDA or cp is None or (dist_coeffs is not None and np.any(dist_coeffs)): obj = np.asarray(object_points_batch, dtype=np.float32) @@ -771,7 +771,7 @@ def solve_pnp_batch( else: raise ValueError("dist_coeffs must be 1D or batched 2D") ok, rvec, tvec = cv2.solvePnP( - obj[b], img[b], K_b, dist_b, flags=cv2.SOLVEPNP_ITERATIVE + obj[b], img[b], K_b, dist_b, flags=cv2.SOLVEPNP_ITERATIVE # type: ignore[arg-type] ) if not ok: raise RuntimeError(f"cv2.solvePnP failed for batch index {b}") @@ -779,7 +779,7 @@ def solve_pnp_batch( t_list[b] = tvec.astype(np.float64) return r_list, t_list - return _solve_pnp_cuda_kernel( + return _solve_pnp_cuda_kernel( # type: ignore[no-any-return] object_points_batch, image_points_batch, camera_matrix, @@ -789,15 +789,15 @@ def solve_pnp_batch( def solve_pnp_ransac( self, - object_points: np.ndarray, - image_points: np.ndarray, - camera_matrix: np.ndarray, - dist_coeffs: np.ndarray | None = None, + object_points: np.ndarray, # type: ignore[type-arg] + image_points: np.ndarray, # type: ignore[type-arg] + camera_matrix: np.ndarray, # type: ignore[type-arg] + dist_coeffs: np.ndarray | None = None, # type: ignore[type-arg] iterations_count: int = 100, reprojection_error: float = 3.0, confidence: float = 0.99, min_sample: int = 6, - ) -> tuple[bool, np.ndarray, np.ndarray, np.ndarray]: + ) -> tuple[bool, np.ndarray, np.ndarray, np.ndarray]: # type: ignore[type-arg] """RANSAC with CUDA PnP solver.""" if not HAS_CUDA or cp is None or (dist_coeffs is not None and np.any(dist_coeffs)): obj = np.asarray(object_points, dtype=np.float32) @@ -808,7 +808,7 @@ def solve_pnp_ransac( obj, img, K, - dist, + dist, # type: ignore[arg-type] iterationsCount=int(iterations_count), reprojectionError=float(reprojection_error), confidence=float(confidence), @@ -821,7 +821,7 @@ def solve_pnp_ransac( obj = cp.asarray(object_points, dtype=cp.float32) img = cp.asarray(image_points, dtype=cp.float32) - camera_matrix_np = np.asarray(_to_cpu(camera_matrix), dtype=np.float32) + camera_matrix_np = np.asarray(_to_cpu(camera_matrix), dtype=np.float32) # type: ignore[no-untyped-call] fx = float(camera_matrix_np[0, 0]) fy = float(camera_matrix_np[1, 1]) cx = float(camera_matrix_np[0, 2]) @@ -877,7 +877,7 @@ def __init__( self.y = int(y0) self.x = int(x0) - def update(self, gray: cp.ndarray): + def update(self, gray: cp.ndarray): # type: ignore[no-untyped-def] H, W = int(gray.shape[0]), int(gray.shape[1]) r = self.search_radius x0 = max(0, self.x - r) @@ -901,17 +901,17 @@ def update(self, gray: cp.ndarray): tmpl_energy = cp.sqrt(cp.sum(tmpl_zm * tmpl_zm)) + 1e-6 # NCC via correlate2d and local std ones = cp.ones((th, tw), dtype=cp.float32) - num = csignal.correlate2d(search, tmpl_zm, mode="valid") # type: ignore - sumS = csignal.correlate2d(search, ones, mode="valid") # type: ignore - sumS2 = csignal.correlate2d(search * search, ones, mode="valid") # type: ignore + num = csignal.correlate2d(search, tmpl_zm, mode="valid") + sumS = csignal.correlate2d(search, ones, mode="valid") + sumS2 = csignal.correlate2d(search * search, ones, mode="valid") n = float(th * tw) meanS = sumS / n varS = cp.clip(sumS2 - n * meanS * meanS, 0.0, None) stdS = cp.sqrt(varS) + 1e-6 res = num / (stdS * tmpl_energy) ij = cp.unravel_index(cp.argmax(res), res.shape) - dy, dx = int(ij[0].get()), int(ij[1].get()) # type: ignore - score = float(res[ij].get()) # type: ignore + dy, dx = int(ij[0].get()), int(ij[1].get()) + score = float(res[ij].get()) if score > best_score: best_score = score best = (x0 + dx, y0 + dy, tw, th) diff --git a/dimos/msgs/sensor_msgs/image_impls/NumpyImage.py b/dimos/msgs/sensor_msgs/image_impls/NumpyImage.py index d75adc66ea..6369792c64 100644 --- a/dimos/msgs/sensor_msgs/image_impls/NumpyImage.py +++ b/dimos/msgs/sensor_msgs/image_impls/NumpyImage.py @@ -28,12 +28,12 @@ @dataclass class NumpyImage(AbstractImage): - data: np.ndarray + data: np.ndarray # type: ignore[type-arg] format: ImageFormat = field(default=ImageFormat.BGR) frame_id: str = field(default="") ts: float = field(default_factory=time.time) - def __post_init__(self): + def __post_init__(self): # type: ignore[no-untyped-def] if not isinstance(self.data, np.ndarray) or self.data.ndim < 2: raise ValueError("NumpyImage requires a 2D/3D NumPy array") @@ -41,7 +41,7 @@ def __post_init__(self): def is_cuda(self) -> bool: return False - def to_opencv(self) -> np.ndarray: + def to_opencv(self) -> np.ndarray: # type: ignore[type-arg] arr = self.data if self.format == ImageFormat.BGR: return arr @@ -69,7 +69,7 @@ def to_rgb(self) -> NumpyImage: cv2.cvtColor(arr, cv2.COLOR_BGR2RGB), ImageFormat.RGB, self.frame_id, self.ts ) if self.format == ImageFormat.RGBA: - return self.copy() # RGBA contains RGB + alpha + return self.copy() # type: ignore[return-value] # RGBA contains RGB + alpha if self.format == ImageFormat.BGRA: rgba = cv2.cvtColor(arr, cv2.COLOR_BGRA2RGBA) return NumpyImage(rgba, ImageFormat.RGBA, self.frame_id, self.ts) @@ -179,20 +179,20 @@ def sharpness(self) -> float: # PnP wrappers def solve_pnp( self, - object_points: np.ndarray, - image_points: np.ndarray, - camera_matrix: np.ndarray, - dist_coeffs: np.ndarray | None = None, + object_points: np.ndarray, # type: ignore[type-arg] + image_points: np.ndarray, # type: ignore[type-arg] + camera_matrix: np.ndarray, # type: ignore[type-arg] + dist_coeffs: np.ndarray | None = None, # type: ignore[type-arg] flags: int = cv2.SOLVEPNP_ITERATIVE, - ) -> tuple[bool, np.ndarray, np.ndarray]: + ) -> tuple[bool, np.ndarray, np.ndarray]: # type: ignore[type-arg] obj = np.asarray(object_points, dtype=np.float32).reshape(-1, 3) img = np.asarray(image_points, dtype=np.float32).reshape(-1, 2) K = np.asarray(camera_matrix, dtype=np.float64) dist = None if dist_coeffs is None else np.asarray(dist_coeffs, dtype=np.float64) - ok, rvec, tvec = cv2.solvePnP(obj, img, K, dist, flags=flags) + ok, rvec, tvec = cv2.solvePnP(obj, img, K, dist, flags=flags) # type: ignore[arg-type] return bool(ok), rvec.astype(np.float64), tvec.astype(np.float64) - def create_csrt_tracker(self, bbox: tuple[int, int, int, int]): + def create_csrt_tracker(self, bbox: tuple[int, int, int, int]): # type: ignore[no-untyped-def] tracker = None if hasattr(cv2, "legacy") and hasattr(cv2.legacy, "TrackerCSRT_create"): tracker = cv2.legacy.TrackerCSRT_create() @@ -205,7 +205,7 @@ def create_csrt_tracker(self, bbox: tuple[int, int, int, int]): raise RuntimeError("Failed to initialize CSRT tracker") return tracker - def csrt_update(self, tracker) -> tuple[bool, tuple[int, int, int, int]]: + def csrt_update(self, tracker) -> tuple[bool, tuple[int, int, int, int]]: # type: ignore[no-untyped-def] ok, box = tracker.update(self.to_bgr().to_opencv()) if not ok: return False, (0, 0, 0, 0) @@ -214,15 +214,15 @@ def csrt_update(self, tracker) -> tuple[bool, tuple[int, int, int, int]]: def solve_pnp_ransac( self, - object_points: np.ndarray, - image_points: np.ndarray, - camera_matrix: np.ndarray, - dist_coeffs: np.ndarray | None = None, + object_points: np.ndarray, # type: ignore[type-arg] + image_points: np.ndarray, # type: ignore[type-arg] + camera_matrix: np.ndarray, # type: ignore[type-arg] + dist_coeffs: np.ndarray | None = None, # type: ignore[type-arg] iterations_count: int = 100, reprojection_error: float = 3.0, confidence: float = 0.99, min_sample: int = 6, - ) -> tuple[bool, np.ndarray, np.ndarray, np.ndarray]: + ) -> tuple[bool, np.ndarray, np.ndarray, np.ndarray]: # type: ignore[type-arg] obj = np.asarray(object_points, dtype=np.float32).reshape(-1, 3) img = np.asarray(image_points, dtype=np.float32).reshape(-1, 2) K = np.asarray(camera_matrix, dtype=np.float64) @@ -231,7 +231,7 @@ def solve_pnp_ransac( obj, img, K, - dist, + dist, # type: ignore[arg-type] iterationsCount=int(iterations_count), reprojectionError=float(reprojection_error), confidence=float(confidence), diff --git a/dimos/msgs/std_msgs/Bool.py b/dimos/msgs/std_msgs/Bool.py index 55751a41eb..293b216882 100644 --- a/dimos/msgs/std_msgs/Bool.py +++ b/dimos/msgs/std_msgs/Bool.py @@ -15,15 +15,15 @@ """Bool message type.""" -from dimos_lcm.std_msgs import Bool as LCMBool +from dimos_lcm.std_msgs import Bool as LCMBool # type: ignore[import-untyped] try: - from std_msgs.msg import Bool as ROSBool + from std_msgs.msg import Bool as ROSBool # type: ignore[attr-defined] except ImportError: - ROSBool = None + ROSBool = None # type: ignore[assignment, misc] -class Bool(LCMBool): +class Bool(LCMBool): # type: ignore[misc] """ROS-compatible Bool message.""" msg_name = "std_msgs.Bool" @@ -52,6 +52,6 @@ def to_ros_msg(self) -> ROSBool: """ if ROSBool is None: raise ImportError("ROS std_msgs not available") - ros_msg = ROSBool() + ros_msg = ROSBool() # type: ignore[no-untyped-call] ros_msg.data = bool(self.data) return ros_msg diff --git a/dimos/msgs/std_msgs/Header.py b/dimos/msgs/std_msgs/Header.py index 1d17913941..48c52c9e01 100644 --- a/dimos/msgs/std_msgs/Header.py +++ b/dimos/msgs/std_msgs/Header.py @@ -17,17 +17,17 @@ from datetime import datetime import time -from dimos_lcm.std_msgs import Header as LCMHeader, Time as LCMTime +from dimos_lcm.std_msgs import Header as LCMHeader, Time as LCMTime # type: ignore[import-untyped] from plum import dispatch # Import the actual LCM header type that's returned from decoding try: - from lcm_msgs.std_msgs.Header import Header as DecodedLCMHeader + from lcm_msgs.std_msgs.Header import Header as DecodedLCMHeader # type: ignore[import-not-found] except ImportError: DecodedLCMHeader = None -class Header(LCMHeader): +class Header(LCMHeader): # type: ignore[misc] msg_name = "std_msgs.Header" ts: float @@ -39,7 +39,7 @@ def __init__(self) -> None: nsec = int((self.ts - sec) * 1_000_000_000) super().__init__(seq=0, stamp=LCMTime(sec=sec, nsec=nsec), frame_id="") - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, frame_id: str) -> None: """Initialize a Header with current time and specified frame_id.""" self.ts = time.time() @@ -47,14 +47,14 @@ def __init__(self, frame_id: str) -> None: nsec = int((self.ts - sec) * 1_000_000_000) super().__init__(seq=1, stamp=LCMTime(sec=sec, nsec=nsec), frame_id=frame_id) - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, timestamp: float, frame_id: str = "", seq: int = 1) -> None: """Initialize a Header with Unix timestamp, frame_id, and optional seq.""" sec = int(timestamp) nsec = int((timestamp - sec) * 1_000_000_000) super().__init__(seq=seq, stamp=LCMTime(sec=sec, nsec=nsec), frame_id=frame_id) - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, timestamp: datetime, frame_id: str = "") -> None: """Initialize a Header with datetime object and frame_id.""" self.ts = timestamp.timestamp() @@ -62,17 +62,17 @@ def __init__(self, timestamp: datetime, frame_id: str = "") -> None: nsec = int((self.ts - sec) * 1_000_000_000) super().__init__(seq=1, stamp=LCMTime(sec=sec, nsec=nsec), frame_id=frame_id) - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, seq: int, stamp: LCMTime, frame_id: str) -> None: """Initialize with explicit seq, stamp, and frame_id (LCM compatibility).""" super().__init__(seq=seq, stamp=stamp, frame_id=frame_id) - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, header: LCMHeader) -> None: """Initialize from another Header (copy constructor).""" super().__init__(seq=header.seq, stamp=header.stamp, frame_id=header.frame_id) - @dispatch + @dispatch # type: ignore[no-redef] def __init__(self, header: object) -> None: """Initialize from a decoded LCM header object.""" # Handle the case where we get an lcm_msgs.std_msgs.Header.Header object @@ -90,7 +90,7 @@ def now(cls, frame_id: str = "", seq: int = 1) -> Header: @property def timestamp(self) -> float: """Get timestamp as Unix time (float).""" - return self.stamp.sec + (self.stamp.nsec / 1_000_000_000) + return self.stamp.sec + (self.stamp.nsec / 1_000_000_000) # type: ignore[no-any-return] @property def datetime(self) -> datetime: diff --git a/dimos/msgs/std_msgs/Int32.py b/dimos/msgs/std_msgs/Int32.py index 0ce2f03f60..cac4fa98db 100644 --- a/dimos/msgs/std_msgs/Int32.py +++ b/dimos/msgs/std_msgs/Int32.py @@ -19,10 +19,10 @@ from typing import ClassVar -from dimos_lcm.std_msgs import Int32 as LCMInt32 +from dimos_lcm.std_msgs import Int32 as LCMInt32 # type: ignore[import-untyped] -class Int32(LCMInt32): +class Int32(LCMInt32): # type: ignore[misc] """ROS-compatible Int32 message.""" msg_name: ClassVar[str] = "std_msgs.Int32" diff --git a/dimos/msgs/std_msgs/Int8.py b/dimos/msgs/std_msgs/Int8.py index d76b479d41..f7bb580981 100644 --- a/dimos/msgs/std_msgs/Int8.py +++ b/dimos/msgs/std_msgs/Int8.py @@ -19,15 +19,15 @@ from typing import ClassVar -from dimos_lcm.std_msgs import Int8 as LCMInt8 +from dimos_lcm.std_msgs import Int8 as LCMInt8 # type: ignore[import-untyped] try: - from std_msgs.msg import Int8 as ROSInt8 + from std_msgs.msg import Int8 as ROSInt8 # type: ignore[attr-defined] except ImportError: - ROSInt8 = None + ROSInt8 = None # type: ignore[assignment, misc] -class Int8(LCMInt8): +class Int8(LCMInt8): # type: ignore[misc] """ROS-compatible Int32 message.""" msg_name: ClassVar[str] = "std_msgs.Int8" @@ -56,6 +56,6 @@ def to_ros_msg(self) -> ROSInt8: """ if ROSInt8 is None: raise ImportError("ROS std_msgs not available") - ros_msg = ROSInt8() + ros_msg = ROSInt8() # type: ignore[no-untyped-call] ros_msg.data = self.data return ros_msg diff --git a/dimos/msgs/tf2_msgs/TFMessage.py b/dimos/msgs/tf2_msgs/TFMessage.py index 5aabfa4b23..a3b7f3e664 100644 --- a/dimos/msgs/tf2_msgs/TFMessage.py +++ b/dimos/msgs/tf2_msgs/TFMessage.py @@ -29,14 +29,14 @@ from typing import TYPE_CHECKING, BinaryIO -from dimos_lcm.tf2_msgs import TFMessage as LCMTFMessage +from dimos_lcm.tf2_msgs import TFMessage as LCMTFMessage # type: ignore[import-untyped] try: - from geometry_msgs.msg import TransformStamped as ROSTransformStamped - from tf2_msgs.msg import TFMessage as ROSTFMessage + from geometry_msgs.msg import TransformStamped as ROSTransformStamped # type: ignore[attr-defined] + from tf2_msgs.msg import TFMessage as ROSTFMessage # type: ignore[attr-defined] except ImportError: - ROSTFMessage = None - ROSTransformStamped = None + ROSTFMessage = None # type: ignore[assignment, misc] + ROSTransformStamped = None # type: ignore[assignment, misc] from dimos.msgs.geometry_msgs.Quaternion import Quaternion from dimos.msgs.geometry_msgs.Transform import Transform @@ -75,7 +75,7 @@ def lcm_encode(self) -> bytes: transforms=res, ) - return lcm_msg.lcm_encode() + return lcm_msg.lcm_encode() # type: ignore[no-any-return] @classmethod def lcm_decode(cls, data: bytes | BinaryIO) -> TFMessage: @@ -113,7 +113,7 @@ def __getitem__(self, index: int) -> Transform: """Get transform by index.""" return self.transforms[index] - def __iter__(self) -> Iterator: + def __iter__(self) -> Iterator: # type: ignore[type-arg] """Iterate over transforms.""" return iter(self.transforms) @@ -150,7 +150,7 @@ def to_ros_msg(self) -> ROSTFMessage: Returns: ROS TFMessage message """ - ros_msg = ROSTFMessage() + ros_msg = ROSTFMessage() # type: ignore[no-untyped-call] # Convert each Transform to ROS TransformStamped for transform in self.transforms: diff --git a/dimos/msgs/vision_msgs/BoundingBox2DArray.py b/dimos/msgs/vision_msgs/BoundingBox2DArray.py index 6568656884..6cfd2b6cfd 100644 --- a/dimos/msgs/vision_msgs/BoundingBox2DArray.py +++ b/dimos/msgs/vision_msgs/BoundingBox2DArray.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from dimos_lcm.vision_msgs.BoundingBox2DArray import BoundingBox2DArray as LCMBoundingBox2DArray +from dimos_lcm.vision_msgs.BoundingBox2DArray import BoundingBox2DArray as LCMBoundingBox2DArray # type: ignore[import-untyped] -class BoundingBox2DArray(LCMBoundingBox2DArray): +class BoundingBox2DArray(LCMBoundingBox2DArray): # type: ignore[misc] msg_name = "vision_msgs.BoundingBox2DArray" diff --git a/dimos/msgs/vision_msgs/BoundingBox3DArray.py b/dimos/msgs/vision_msgs/BoundingBox3DArray.py index afa3d793f9..32ce36c8df 100644 --- a/dimos/msgs/vision_msgs/BoundingBox3DArray.py +++ b/dimos/msgs/vision_msgs/BoundingBox3DArray.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from dimos_lcm.vision_msgs.BoundingBox3DArray import BoundingBox3DArray as LCMBoundingBox3DArray +from dimos_lcm.vision_msgs.BoundingBox3DArray import BoundingBox3DArray as LCMBoundingBox3DArray # type: ignore[import-untyped] -class BoundingBox3DArray(LCMBoundingBox3DArray): +class BoundingBox3DArray(LCMBoundingBox3DArray): # type: ignore[misc] msg_name = "vision_msgs.BoundingBox3DArray" diff --git a/dimos/msgs/vision_msgs/Detection2DArray.py b/dimos/msgs/vision_msgs/Detection2DArray.py index 79c84f7609..f66e743409 100644 --- a/dimos/msgs/vision_msgs/Detection2DArray.py +++ b/dimos/msgs/vision_msgs/Detection2DArray.py @@ -11,12 +11,12 @@ # 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 dimos_lcm.vision_msgs.Detection2DArray import Detection2DArray as LCMDetection2DArray +from dimos_lcm.vision_msgs.Detection2DArray import Detection2DArray as LCMDetection2DArray # type: ignore[import-untyped] from dimos.types.timestamped import to_timestamp -class Detection2DArray(LCMDetection2DArray): +class Detection2DArray(LCMDetection2DArray): # type: ignore[misc] msg_name = "vision_msgs.Detection2DArray" # for _get_field_type() to work when decoding in _decode_one() diff --git a/dimos/msgs/vision_msgs/Detection3DArray.py b/dimos/msgs/vision_msgs/Detection3DArray.py index 21dabb8057..9a195005b4 100644 --- a/dimos/msgs/vision_msgs/Detection3DArray.py +++ b/dimos/msgs/vision_msgs/Detection3DArray.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from dimos_lcm.vision_msgs.Detection3DArray import Detection3DArray as LCMDetection3DArray +from dimos_lcm.vision_msgs.Detection3DArray import Detection3DArray as LCMDetection3DArray # type: ignore[import-untyped] -class Detection3DArray(LCMDetection3DArray): +class Detection3DArray(LCMDetection3DArray): # type: ignore[misc] msg_name = "vision_msgs.Detection3DArray" diff --git a/dimos/navigation/bbox_navigation.py b/dimos/navigation/bbox_navigation.py index db66ab8349..8d84509dc8 100644 --- a/dimos/navigation/bbox_navigation.py +++ b/dimos/navigation/bbox_navigation.py @@ -14,7 +14,7 @@ import logging -from dimos_lcm.sensor_msgs import CameraInfo +from dimos_lcm.sensor_msgs import CameraInfo # type: ignore[import-untyped] from reactivex.disposable import Disposable from dimos.core import In, Module, Out, rpc @@ -28,9 +28,9 @@ class BBoxNavigationModule(Module): """Minimal module that converts 2D bbox center to navigation goals.""" - detection2d: In[Detection2DArray] = None - camera_info: In[CameraInfo] = None - goal_request: Out[PoseStamped] = None + detection2d: In[Detection2DArray] = None # type: ignore[assignment] + camera_info: In[CameraInfo] = None # type: ignore[assignment] + goal_request: Out[PoseStamped] = None # type: ignore[assignment] def __init__(self, goal_distance: float = 1.0) -> None: super().__init__() diff --git a/dimos/navigation/bt_navigator/goal_validator.py b/dimos/navigation/bt_navigator/goal_validator.py index f0c4a9ce37..6583f157cb 100644 --- a/dimos/navigation/bt_navigator/goal_validator.py +++ b/dimos/navigation/bt_navigator/goal_validator.py @@ -228,7 +228,7 @@ def _find_safe_goal_voronoi( """ from scipy import ndimage - from skimage.morphology import skeletonize + from skimage.morphology import skeletonize # type: ignore[import-not-found] # Convert goal to grid coordinates goal_grid = costmap.world_to_grid(goal) @@ -245,7 +245,7 @@ def _find_safe_goal_voronoi( # Filter skeleton points by minimum clearance clearance_cells = int(np.ceil(min_clearance / costmap.resolution)) - valid_skeleton = skeleton & (distance_field >= clearance_cells) + valid_skeleton = skeleton & (distance_field >= clearance_cells) # type: ignore[operator] if not np.any(valid_skeleton): # Fall back to BFS if no valid skeleton points diff --git a/dimos/navigation/bt_navigator/navigator.py b/dimos/navigation/bt_navigator/navigator.py index aa5c1458a1..dc0ef35943 100644 --- a/dimos/navigation/bt_navigator/navigator.py +++ b/dimos/navigation/bt_navigator/navigator.py @@ -22,7 +22,7 @@ import threading import time -from dimos_lcm.std_msgs import Bool, String +from dimos_lcm.std_msgs import Bool, String # type: ignore[import-untyped] from reactivex.disposable import Disposable from dimos.core import In, Module, Out, rpc @@ -54,16 +54,16 @@ class BehaviorTreeNavigator(Module, NavigationInterface): """ # LCM inputs - odom: In[PoseStamped] = None - goal_request: In[PoseStamped] = None # Input for receiving goal requests - global_costmap: In[OccupancyGrid] = None + odom: In[PoseStamped] = None # type: ignore[assignment] + goal_request: In[PoseStamped] = None # type: ignore[assignment] # Input for receiving goal requests + global_costmap: In[OccupancyGrid] = None # type: ignore[assignment] # LCM outputs - target: Out[PoseStamped] = None - goal_reached: Out[Bool] = None - navigation_state: Out[String] = None + target: Out[PoseStamped] = None # type: ignore[assignment] + goal_reached: Out[Bool] = None # type: ignore[assignment] + navigation_state: Out[String] = None # type: ignore[assignment] - def __init__( + def __init__( # type: ignore[no-untyped-def] self, publishing_frequency: float = 1.0, reset_local_planner: Callable[[], None] | None = None, @@ -117,12 +117,12 @@ def __init__( @rpc def set_HolonomicLocalPlanner_reset(self, callable: RpcCall) -> None: self.reset_local_planner = callable - self.reset_local_planner.set_rpc(self.rpc) + self.reset_local_planner.set_rpc(self.rpc) # type: ignore[arg-type] @rpc def set_HolonomicLocalPlanner_is_goal_reached(self, callable: RpcCall) -> None: self.check_goal_reached = callable - self.check_goal_reached.set_rpc(self.rpc) + self.check_goal_reached.set_rpc(self.rpc) # type: ignore[arg-type] @rpc def start(self) -> None: @@ -253,7 +253,7 @@ def _transform_goal_to_odom_frame(self, goal: PoseStamped) -> PoseStamped | None ) return None - pose = apply_transform(goal, transform) + pose = apply_transform(goal, transform) # type: ignore[arg-type] transformed_goal = PoseStamped( position=pose.position, orientation=pose.orientation, @@ -271,7 +271,7 @@ def _control_loop(self) -> None: while not self.stop_event.is_set(): with self.state_lock: current_state = self.state - self.navigation_state.publish(String(data=current_state.value)) + self.navigation_state.publish(String(data=current_state.value)) # type: ignore[no-untyped-call] if current_state == NavigationState.FOLLOWING_PATH: with self.goal_lock: @@ -290,7 +290,7 @@ def _control_loop(self) -> None: # Find safe goal position safe_goal_pos = find_safe_goal( costmap, - original_goal.position, + original_goal.position, # type: ignore[union-attr] algorithm="bfs", cost_threshold=60, min_clearance=0.25, @@ -305,17 +305,17 @@ def _control_loop(self) -> None: frame_id=goal.frame_id, ts=goal.ts, ) - self.target.publish(safe_goal) + self.target.publish(safe_goal) # type: ignore[no-untyped-call] self.current_goal = safe_goal else: logger.warning("Could not find safe goal position, cancelling goal") self.cancel_goal() # Check if goal is reached - if self.check_goal_reached(): + if self.check_goal_reached(): # type: ignore[misc] reached_msg = Bool() reached_msg.data = True - self.goal_reached.publish(reached_msg) + self.goal_reached.publish(reached_msg) # type: ignore[no-untyped-call] self.stop_navigation() self._goal_reached = True logger.info("Goal reached, resetting local planner") @@ -345,7 +345,7 @@ def stop_navigation(self) -> None: with self.state_lock: self.state = NavigationState.IDLE - self.reset_local_planner() + self.reset_local_planner() # type: ignore[misc] self.recovery_server.reset() # Reset recovery server when stopping logger.info("Navigator stopped") diff --git a/dimos/navigation/bt_navigator/recovery_server.py b/dimos/navigation/bt_navigator/recovery_server.py index 5b05d35de5..264c6f0b63 100644 --- a/dimos/navigation/bt_navigator/recovery_server.py +++ b/dimos/navigation/bt_navigator/recovery_server.py @@ -67,12 +67,12 @@ def update_odom(self, odom: PoseStamped) -> None: return # Store current odom for checking stuck - self.current_odom = odom + self.current_odom = odom # type: ignore[assignment] # Initialize on first update if self.last_moved_pose is None: - self.last_moved_pose = odom - self.last_moved_time = odom.ts + self.last_moved_pose = odom # type: ignore[assignment] + self.last_moved_time = odom.ts # type: ignore[assignment] return # Calculate distance from the reference position (last significant movement) diff --git a/dimos/navigation/demo_ros_navigation.py b/dimos/navigation/demo_ros_navigation.py index fac19e1b24..f2e9763212 100644 --- a/dimos/navigation/demo_ros_navigation.py +++ b/dimos/navigation/demo_ros_navigation.py @@ -28,10 +28,10 @@ def main() -> None: - pubsub.lcm.autoconf() + pubsub.lcm.autoconf() # type: ignore[attr-defined] dimos = core.start(2) - ros_nav = dimos.deploy(ROSNav) + ros_nav = dimos.deploy(ROSNav) # type: ignore[attr-defined] ros_nav.goal_req.transport = core.LCMTransport("/goal", PoseStamped) ros_nav.pointcloud.transport = core.LCMTransport("/pointcloud_map", PointCloud2) @@ -64,7 +64,7 @@ def main() -> None: logger.info("\nShutting down...") ros_nav.stop() - if rclpy.ok(): + if rclpy.ok(): # type: ignore[attr-defined] rclpy.shutdown() diff --git a/dimos/navigation/frontier_exploration/wavefront_frontier_goal_selector.py b/dimos/navigation/frontier_exploration/wavefront_frontier_goal_selector.py index 71677635f5..e8818a810f 100644 --- a/dimos/navigation/frontier_exploration/wavefront_frontier_goal_selector.py +++ b/dimos/navigation/frontier_exploration/wavefront_frontier_goal_selector.py @@ -24,7 +24,7 @@ from enum import IntFlag import threading -from dimos_lcm.std_msgs import Bool +from dimos_lcm.std_msgs import Bool # type: ignore[import-untyped] import numpy as np from reactivex.disposable import Disposable @@ -60,14 +60,14 @@ class FrontierCache: """Cache for grid points to avoid duplicate point creation.""" def __init__(self) -> None: - self.points = {} + self.points = {} # type: ignore[var-annotated] def get_point(self, x: int, y: int) -> GridPoint: """Get or create a grid point at the given coordinates.""" key = (x, y) if key not in self.points: self.points[key] = GridPoint(x, y) - return self.points[key] + return self.points[key] # type: ignore[no-any-return] def clear(self) -> None: """Clear the point cache.""" @@ -90,16 +90,16 @@ class WavefrontFrontierExplorer(Module): """ # LCM inputs - global_costmap: In[OccupancyGrid] = None - odom: In[PoseStamped] = None - goal_reached: In[Bool] = None - explore_cmd: In[Bool] = None - stop_explore_cmd: In[Bool] = None + global_costmap: In[OccupancyGrid] = None # type: ignore[assignment] + odom: In[PoseStamped] = None # type: ignore[assignment] + goal_reached: In[Bool] = None # type: ignore[assignment] + explore_cmd: In[Bool] = None # type: ignore[assignment] + stop_explore_cmd: In[Bool] = None # type: ignore[assignment] # LCM outputs - goal_request: Out[PoseStamped] = None + goal_request: Out[PoseStamped] = None # type: ignore[assignment] - def __init__( + def __init__( # type: ignore[no-untyped-def] self, min_frontier_perimeter: float = 0.5, occupancy_threshold: int = 99, @@ -130,7 +130,7 @@ def __init__( self.info_gain_threshold = info_gain_threshold self.num_no_gain_attempts = num_no_gain_attempts self._cache = FrontierCache() - self.explored_goals = [] # list of explored goals + self.explored_goals = [] # type: ignore[var-annotated] # list of explored goals self.exploration_direction = Vector3(0.0, 0.0, 0.0) # current exploration direction self.last_costmap = None # store last costmap for information comparison self.no_gain_counter = 0 # track consecutive no-gain attempts @@ -651,7 +651,7 @@ def get_exploration_goal(self, robot_pose: Vector3, costmap: OccupancyGrid) -> V if not frontiers: # Store current costmap before returning - self.last_costmap = costmap + self.last_costmap = costmap # type: ignore[assignment] self.reset_exploration_session() return None @@ -664,12 +664,12 @@ def get_exploration_goal(self, robot_pose: Vector3, costmap: OccupancyGrid) -> V self.mark_explored_goal(selected_goal) # Store current costmap for next comparison - self.last_costmap = costmap + self.last_costmap = costmap # type: ignore[assignment] return selected_goal # Store current costmap before returning - self.last_costmap = costmap + self.last_costmap = costmap # type: ignore[assignment] return None def mark_explored_goal(self, goal: Vector3) -> None: @@ -775,7 +775,7 @@ def _exploration_loop(self) -> None: goal_msg.frame_id = "world" goal_msg.ts = self.latest_costmap.ts - self.goal_request.publish(goal_msg) + self.goal_request.publish(goal_msg) # type: ignore[no-untyped-call] logger.info(f"Published frontier goal: ({goal.x:.2f}, {goal.y:.2f})") goals_published += 1 diff --git a/dimos/navigation/global_planner/algo.py b/dimos/navigation/global_planner/algo.py index 16f8dc3600..fcd58cf5f0 100644 --- a/dimos/navigation/global_planner/algo.py +++ b/dimos/navigation/global_planner/algo.py @@ -72,22 +72,22 @@ def astar( movement_costs = [sc, sc, sc, sc, dc, dc, dc, dc] # A* algorithm implementation - open_set = [] # Priority queue for nodes to explore + open_set = [] # type: ignore[var-annotated] # Priority queue for nodes to explore closed_set = set() # Set of explored nodes # Dictionary to store cost from start and parents for each node g_score = {start_tuple: 0} - parents = {} + parents = {} # type: ignore[var-annotated] # Heuristic function (Octile distance for 8-connected grid) - def heuristic(x1, y1, x2, y2): + def heuristic(x1, y1, x2, y2): # type: ignore[no-untyped-def] dx = abs(x2 - x1) dy = abs(y2 - y1) # Octile distance: optimal for 8-connected grids with diagonal movement return (dx + dy) + (dc - 2 * sc) * min(dx, dy) # Start with the starting node - f_score = g_score[start_tuple] + heuristic( + f_score = g_score[start_tuple] + heuristic( # type: ignore[no-untyped-call] start_tuple[0], start_tuple[1], goal_tuple[0], goal_tuple[1] ) heapq.heappush(open_set, (f_score, start_tuple)) @@ -200,8 +200,8 @@ def heuristic(x1, y1, x2, y2): if tentative_g_score < neighbor_g_score: # Update the neighbor's scores and parent parents[neighbor] = current - g_score[neighbor] = tentative_g_score - f_score = tentative_g_score + heuristic( + g_score[neighbor] = tentative_g_score # type: ignore[assignment] + f_score = tentative_g_score + heuristic( # type: ignore[no-untyped-call] neighbor_x, neighbor_y, goal_tuple[0], goal_tuple[1] ) diff --git a/dimos/navigation/global_planner/planner.py b/dimos/navigation/global_planner/planner.py index 89ac134b08..7d4dbf8327 100644 --- a/dimos/navigation/global_planner/planner.py +++ b/dimos/navigation/global_planner/planner.py @@ -29,7 +29,7 @@ from dimos.msgs.geometry_msgs import Quaternion, Vector3 -def add_orientations_to_path(path: Path, goal_orientation: Quaternion = None) -> Path: +def add_orientations_to_path(path: Path, goal_orientation: Quaternion = None) -> Path: # type: ignore[assignment] """Add orientations to path poses based on direction of movement. Args: @@ -142,12 +142,12 @@ def resample_path(path: Path, spacing: float) -> Path: class AstarPlanner(Module): # LCM inputs - target: In[PoseStamped] = None - global_costmap: In[OccupancyGrid] = None - odom: In[PoseStamped] = None + target: In[PoseStamped] = None # type: ignore[assignment] + global_costmap: In[OccupancyGrid] = None # type: ignore[assignment] + odom: In[PoseStamped] = None # type: ignore[assignment] # LCM outputs - path: Out[Path] = None + path: Out[Path] = None # type: ignore[assignment] def __init__(self) -> None: super().__init__() @@ -193,7 +193,7 @@ def _on_target(self, msg: PoseStamped) -> None: if path: # Add orientations to the path, using the goal's orientation for the final pose path = add_orientations_to_path(path, msg.orientation) - self.path.publish(path) + self.path.publish(path) # type: ignore[no-untyped-call] def plan(self, goal: Pose) -> Path | None: """Plan a path from current position to goal.""" diff --git a/dimos/navigation/local_planner/holonomic_local_planner.py b/dimos/navigation/local_planner/holonomic_local_planner.py index acb8dcec98..4b16a3c3a0 100644 --- a/dimos/navigation/local_planner/holonomic_local_planner.py +++ b/dimos/navigation/local_planner/holonomic_local_planner.py @@ -42,7 +42,7 @@ class HolonomicLocalPlanner(BaseLocalPlanner): control_frequency: Control loop frequency in Hz (default: 10.0) """ - def __init__( + def __init__( # type: ignore[no-untyped-def] self, lookahead_dist: float = 1.0, k_rep: float = 0.5, @@ -163,7 +163,7 @@ def compute_velocity(self) -> Twist | None: angular=Vector3(0.0, 0.0, v_filtered[2]), ) - def _compute_path_following(self, pose: np.ndarray, path: np.ndarray) -> np.ndarray: + def _compute_path_following(self, pose: np.ndarray, path: np.ndarray) -> np.ndarray: # type: ignore[type-arg] """ Compute path following velocity using pure pursuit. @@ -186,9 +186,9 @@ def _compute_path_following(self, pose: np.ndarray, path: np.ndarray) -> np.ndar v_follow = self.v_max * direction / distance - return v_follow + return v_follow # type: ignore[no-any-return] - def _compute_obstacle_repulsion(self, pose: np.ndarray, costmap: np.ndarray) -> np.ndarray: + def _compute_obstacle_repulsion(self, pose: np.ndarray, costmap: np.ndarray) -> np.ndarray: # type: ignore[type-arg] """ Compute obstacle repulsion velocity from costmap gradient. @@ -199,7 +199,7 @@ def _compute_obstacle_repulsion(self, pose: np.ndarray, costmap: np.ndarray) -> Returns: Repulsion velocity vector [vx, vy] """ - grid_point = self.latest_costmap.world_to_grid(pose) + grid_point = self.latest_costmap.world_to_grid(pose) # type: ignore[union-attr] grid_x = int(grid_point.x) grid_y = int(grid_point.y) @@ -210,10 +210,10 @@ def _compute_obstacle_repulsion(self, pose: np.ndarray, costmap: np.ndarray) -> # Compute gradient using central differences # Note: costmap is in row-major order (y, x) gx = (costmap[grid_y, grid_x + 1] - costmap[grid_y, grid_x - 1]) / ( - 2.0 * self.latest_costmap.resolution + 2.0 * self.latest_costmap.resolution # type: ignore[union-attr] ) gy = (costmap[grid_y + 1, grid_x] - costmap[grid_y - 1, grid_x]) / ( - 2.0 * self.latest_costmap.resolution + 2.0 * self.latest_costmap.resolution # type: ignore[union-attr] ) # Gradient points towards higher cost, so negate for repulsion @@ -222,8 +222,8 @@ def _compute_obstacle_repulsion(self, pose: np.ndarray, costmap: np.ndarray) -> return v_rep def _find_closest_point_on_path( - self, pose: np.ndarray, path: np.ndarray - ) -> tuple[int, np.ndarray]: + self, pose: np.ndarray, path: np.ndarray # type: ignore[type-arg] + ) -> tuple[int, np.ndarray]: # type: ignore[type-arg] """ Find the closest point on the path to current pose. @@ -236,9 +236,9 @@ def _find_closest_point_on_path( """ distances = np.linalg.norm(path - pose, axis=1) closest_idx = np.argmin(distances) - return closest_idx, path[closest_idx] + return closest_idx, path[closest_idx] # type: ignore[return-value] - def _find_lookahead_point(self, path: np.ndarray, start_idx: int) -> np.ndarray: + def _find_lookahead_point(self, path: np.ndarray, start_idx: int) -> np.ndarray: # type: ignore[type-arg] """ Find look-ahead point on path at specified distance. @@ -258,13 +258,13 @@ def _find_lookahead_point(self, path: np.ndarray, start_idx: int) -> np.ndarray: remaining_dist = self.lookahead_dist - accumulated_dist t = remaining_dist / segment_dist carrot = path[i] + t * (path[i + 1] - path[i]) - return carrot + return carrot # type: ignore[no-any-return] - accumulated_dist += segment_dist + accumulated_dist += segment_dist # type: ignore[assignment] - return path[-1] + return path[-1] # type: ignore[no-any-return] - def _clip(self, v: np.ndarray) -> np.ndarray: + def _clip(self, v: np.ndarray) -> np.ndarray: # type: ignore[type-arg] """Instance method to clip velocity with access to v_max.""" return np.clip(v, -self.v_max, self.v_max) diff --git a/dimos/navigation/local_planner/local_planner.py b/dimos/navigation/local_planner/local_planner.py index 0a569f00ed..6cc08e7485 100644 --- a/dimos/navigation/local_planner/local_planner.py +++ b/dimos/navigation/local_planner/local_planner.py @@ -48,14 +48,14 @@ class BaseLocalPlanner(Module): """ # LCM inputs - local_costmap: In[OccupancyGrid] = None - odom: In[PoseStamped] = None - path: In[Path] = None + local_costmap: In[OccupancyGrid] = None # type: ignore[assignment] + odom: In[PoseStamped] = None # type: ignore[assignment] + path: In[Path] = None # type: ignore[assignment] # LCM outputs - cmd_vel: Out[Twist] = None + cmd_vel: Out[Twist] = None # type: ignore[assignment] - def __init__( + def __init__( # type: ignore[no-untyped-def] self, goal_tolerance: float = 0.5, orientation_tolerance: float = 0.2, @@ -132,7 +132,7 @@ def _follow_path_loop(self) -> None: if self.is_goal_reached(): self.stop_planning.set() stop_cmd = Twist() - self.cmd_vel.publish(stop_cmd) + self.cmd_vel.publish(stop_cmd) # type: ignore[no-untyped-call] break # Compute and publish velocity @@ -145,7 +145,7 @@ def _plan(self) -> None: cmd_vel = self.compute_velocity() if cmd_vel is not None: - self.cmd_vel.publish(cmd_vel) + self.cmd_vel.publish(cmd_vel) # type: ignore[no-untyped-call] @abstractmethod def compute_velocity(self) -> Twist | None: @@ -206,4 +206,4 @@ def cancel_planning(self) -> None: self.planning_thread.join(timeout=1.0) self.planning_thread = None stop_cmd = Twist() - self.cmd_vel.publish(stop_cmd) + self.cmd_vel.publish(stop_cmd) # type: ignore[no-untyped-call] diff --git a/dimos/navigation/rosnav.py b/dimos/navigation/rosnav.py index bd67fbd532..b60e7abbd7 100644 --- a/dimos/navigation/rosnav.py +++ b/dimos/navigation/rosnav.py @@ -24,22 +24,22 @@ import threading import time -from geometry_msgs.msg import ( +from geometry_msgs.msg import ( # type: ignore[attr-defined] PointStamped as ROSPointStamped, PoseStamped as ROSPoseStamped, TwistStamped as ROSTwistStamped, ) -from nav_msgs.msg import Path as ROSPath +from nav_msgs.msg import Path as ROSPath # type: ignore[attr-defined] import rclpy from rclpy.node import Node from reactivex import operators as ops from reactivex.subject import Subject -from sensor_msgs.msg import Joy as ROSJoy, PointCloud2 as ROSPointCloud2 -from std_msgs.msg import Bool as ROSBool, Int8 as ROSInt8 -from tf2_msgs.msg import TFMessage as ROSTFMessage +from sensor_msgs.msg import Joy as ROSJoy, PointCloud2 as ROSPointCloud2 # type: ignore[attr-defined] +from std_msgs.msg import Bool as ROSBool, Int8 as ROSInt8 # type: ignore[attr-defined] +from tf2_msgs.msg import TFMessage as ROSTFMessage # type: ignore[attr-defined] from dimos import spec -from dimos.agents2 import Reducer, Stream, skill +from dimos.agents2 import Reducer, Stream, skill # type: ignore[attr-defined] from dimos.core import DimosCluster, In, LCMTransport, Module, Out, pSHMTransport, rpc from dimos.core.module import ModuleConfig from dimos.msgs.geometry_msgs import ( @@ -85,8 +85,8 @@ class ROSNav( cmd_vel: Out[Twist] = None # type: ignore # Using RxPY Subjects for reactive data flow instead of storing state - _local_pointcloud_subject: Subject - _global_pointcloud_subject: Subject + _local_pointcloud_subject: Subject # type: ignore[type-arg] + _global_pointcloud_subject: Subject # type: ignore[type-arg] _current_position_running: bool = False _spin_thread: threading.Thread | None = None @@ -99,7 +99,7 @@ class ROSNav( _current_goal: PoseStamped | None = None _goal_reached: bool = False - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(*args, **kwargs) # Initialize RxPY Subjects for streaming data @@ -111,7 +111,7 @@ def __init__(self, *args, **kwargs) -> None: self._navigation_state = NavigationState.IDLE self._goal_reached = False - if not rclpy.ok(): + if not rclpy.ok(): # type: ignore[attr-defined] rclpy.init() self._node = Node("navigation_module") @@ -152,7 +152,7 @@ def start(self) -> None: self._disposables.add( self._local_pointcloud_subject.pipe( ops.sample(1.0 / self.config.local_pointcloud_freq), # Sample at desired frequency - ops.map(lambda msg: PointCloud2.from_ros_msg(msg)), + ops.map(lambda msg: PointCloud2.from_ros_msg(msg)), # type: ignore[arg-type] ).subscribe( on_next=self.pointcloud.publish, on_error=lambda e: logger.error(f"Lidar stream error: {e}"), @@ -162,7 +162,7 @@ def start(self) -> None: self._disposables.add( self._global_pointcloud_subject.pipe( ops.sample(1.0 / self.config.global_pointcloud_freq), # Sample at desired frequency - ops.map(lambda msg: PointCloud2.from_ros_msg(msg)), + ops.map(lambda msg: PointCloud2.from_ros_msg(msg)), # type: ignore[arg-type] ).subscribe( on_next=self.global_pointcloud.publish, on_error=lambda e: logger.error(f"Map stream error: {e}"), @@ -179,7 +179,7 @@ def start(self) -> None: logger.info("NavigationModule started with ROS2 spinning and RxPY streams") def _spin_node(self) -> None: - while self._running and rclpy.ok(): + while self._running and rclpy.ok(): # type: ignore[attr-defined] try: rclpy.spin_once(self._node, timeout_sec=0.1) except Exception as e: @@ -200,10 +200,10 @@ def _on_ros_goal_waypoint(self, msg: ROSPointStamped) -> None: position=Vector3(msg.point.x, msg.point.y, msg.point.z), orientation=Quaternion(0.0, 0.0, 0.0, 1.0), ) - self.goal_active.publish(dimos_pose) + self.goal_active.publish(dimos_pose) # type: ignore[no-untyped-call] def _on_ros_cmd_vel(self, msg: ROSTwistStamped) -> None: - self.cmd_vel.publish(Twist.from_ros_msg(msg.twist)) + self.cmd_vel.publish(Twist.from_ros_msg(msg.twist)) # type: ignore[no-untyped-call] def _on_ros_registered_scan(self, msg: ROSPointCloud2) -> None: self._local_pointcloud_subject.on_next(msg) @@ -214,7 +214,7 @@ def _on_ros_global_pointcloud(self, msg: ROSPointCloud2) -> None: def _on_ros_path(self, msg: ROSPath) -> None: dimos_path = Path.from_ros_msg(msg) dimos_path.frame_id = "base_link" - self.path_active.publish(dimos_path) + self.path_active.publish(dimos_path) # type: ignore[no-untyped-call] def _on_ros_tf(self, msg: ROSTFMessage) -> None: ros_tf = TFMessage.from_ros_msg(msg) @@ -241,7 +241,7 @@ def _on_cancel_goal(self, msg: Bool) -> None: self.stop() def _set_autonomy_mode(self) -> None: - joy_msg = ROSJoy() + joy_msg = ROSJoy() # type: ignore[no-untyped-call] joy_msg.axes = [ 0.0, # axis 0 0.0, # axis 1 @@ -268,8 +268,8 @@ def _set_autonomy_mode(self) -> None: self.joy_pub.publish(joy_msg) logger.info("Setting autonomy mode via Joy message") - @skill(stream=Stream.passive, reducer=Reducer.latest) - def current_position(self): + @skill(stream=Stream.passive, reducer=Reducer.latest) # type: ignore[arg-type] + def current_position(self): # type: ignore[no-untyped-def] """passively stream the current position of the robot every second""" if self._current_position_running: return "already running" @@ -281,8 +281,8 @@ def current_position(self): continue yield f"current position {tf.translation.x}, {tf.translation.y}" - @skill(stream=Stream.call_agent, reducer=Reducer.string) - def goto(self, x: float, y: float): + @skill(stream=Stream.call_agent, reducer=Reducer.string) # type: ignore[arg-type] + def goto(self, x: float, y: float): # type: ignore[no-untyped-def] """ move the robot in relative coordinates x is forward, y is left @@ -300,7 +300,7 @@ def goto(self, x: float, y: float): self.navigate_to(pose_to) yield "arrived" - @skill(stream=Stream.call_agent, reducer=Reducer.string) + @skill(stream=Stream.call_agent, reducer=Reducer.string) # type: ignore[arg-type] def goto_global(self, x: float, y: float) -> Generator[str, None, None]: """ go to coordinates x,y in the map frame @@ -341,7 +341,7 @@ def navigate_to(self, pose: PoseStamped, timeout: float = 60.0) -> bool: self._set_autonomy_mode() # Enable soft stop (0 = enable) - soft_stop_msg = ROSInt8() + soft_stop_msg = ROSInt8() # type: ignore[no-untyped-call] soft_stop_msg.data = 0 self.soft_stop_pub.publish(soft_stop_msg) @@ -371,11 +371,11 @@ def stop_navigation(self) -> bool: """ logger.info("Stopping navigation") - cancel_msg = ROSBool() + cancel_msg = ROSBool() # type: ignore[no-untyped-call] cancel_msg.data = True self.cancel_goal_pub.publish(cancel_msg) - soft_stop_msg = ROSInt8() + soft_stop_msg = ROSInt8() # type: ignore[no-untyped-call] soft_stop_msg.data = 2 self.soft_stop_pub.publish(soft_stop_msg) @@ -461,7 +461,7 @@ def stop(self) -> None: self._spin_thread.join(timeout=1.0) if hasattr(self, "_node") and self._node: - self._node.destroy_node() + self._node.destroy_node() # type: ignore[no-untyped-call] except Exception as e: logger.error(f"Error during shutdown: {e}") @@ -472,8 +472,8 @@ def stop(self) -> None: ros_nav = ROSNav.blueprint -def deploy(dimos: DimosCluster): - nav = dimos.deploy(ROSNav) +def deploy(dimos: DimosCluster): # type: ignore[no-untyped-def] + nav = dimos.deploy(ROSNav) # type: ignore[attr-defined] nav.pointcloud.transport = pSHMTransport("/lidar") nav.global_pointcloud.transport = pSHMTransport("/map") diff --git a/dimos/perception/common/detection2d_tracker.py b/dimos/perception/common/detection2d_tracker.py index 7645acd380..97cf253083 100644 --- a/dimos/perception/common/detection2d_tracker.py +++ b/dimos/perception/common/detection2d_tracker.py @@ -18,7 +18,7 @@ import numpy as np -def compute_iou(bbox1, bbox2): +def compute_iou(bbox1, bbox2): # type: ignore[no-untyped-def] """ Compute Intersection over Union (IoU) of two bounding boxes. Each bbox is [x1, y1, x2, y2]. @@ -38,7 +38,7 @@ def compute_iou(bbox1, bbox2): return inter_area / union_area -def get_tracked_results(tracked_targets): +def get_tracked_results(tracked_targets): # type: ignore[no-untyped-def] """ Extract tracked results from a list of target2d objects. @@ -77,7 +77,7 @@ class target2d: detection probabilities, and computed texture values. """ - def __init__( + def __init__( # type: ignore[no-untyped-def] self, initial_mask, initial_bbox, @@ -106,14 +106,14 @@ def __init__( self.score = 1.0 self.track_id = track_id - self.probs_history = deque(maxlen=history_size) - self.texture_history = deque(maxlen=history_size) + self.probs_history = deque(maxlen=history_size) # type: ignore[var-annotated] + self.texture_history = deque(maxlen=history_size) # type: ignore[var-annotated] - self.frame_count = deque(maxlen=history_size) # Total frames this target has been seen. + self.frame_count = deque(maxlen=history_size) # type: ignore[var-annotated] # Total frames this target has been seen. self.missed_frames = 0 # Consecutive frames when no detection was assigned. self.history_size = history_size - def update(self, mask, bbox, track_id, prob: float, name: str, texture_value) -> None: + def update(self, mask, bbox, track_id, prob: float, name: str, texture_value) -> None: # type: ignore[no-untyped-def] """ Update the target with a new detection. """ @@ -135,7 +135,7 @@ def mark_missed(self) -> None: self.missed_frames += 1 self.frame_count.append(0) - def compute_score( + def compute_score( # type: ignore[no-untyped-def] self, frame_shape, min_area_ratio, @@ -249,7 +249,7 @@ class target2dTracker: falls below the stop threshold or if they are missed for too many consecutive frames. """ - def __init__( + def __init__( # type: ignore[no-untyped-def] self, history_size: int = 10, score_threshold_start: float = 0.5, @@ -290,10 +290,10 @@ def __init__( weights = {"prob": 1.0, "temporal": 1.0, "texture": 1.0, "border": 1.0, "size": 1.0} self.weights = weights - self.targets = {} # Dictionary mapping target_id -> target2d instance. + self.targets = {} # type: ignore[var-annotated] # Dictionary mapping target_id -> target2d instance. self.next_target_id = 0 - def update( + def update( # type: ignore[no-untyped-def] self, frame, masks, @@ -339,7 +339,7 @@ def update( if matched_target is None: best_iou = 0 for target in self.targets.values(): - iou = compute_iou(bbox, target.latest_bbox) + iou = compute_iou(bbox, target.latest_bbox) # type: ignore[no-untyped-call] if iou > 0.5 and iou > best_iou: best_iou = iou matched_target = target diff --git a/dimos/perception/common/export_tensorrt.py b/dimos/perception/common/export_tensorrt.py index 9d73b4ae3f..71499173ca 100644 --- a/dimos/perception/common/export_tensorrt.py +++ b/dimos/perception/common/export_tensorrt.py @@ -17,7 +17,7 @@ from ultralytics import YOLO, FastSAM -def parse_args(): +def parse_args(): # type: ignore[no-untyped-def] parser = argparse.ArgumentParser(description="Export YOLO/FastSAM models to different formats") parser.add_argument("--model_path", type=str, required=True, help="Path to the model weights") parser.add_argument( @@ -41,7 +41,7 @@ def parse_args(): def main() -> None: - args = parse_args() + args = parse_args() # type: ignore[no-untyped-call] half = args.precision == "fp16" int8 = args.precision == "int8" # Load the appropriate model diff --git a/dimos/perception/common/ibvs.py b/dimos/perception/common/ibvs.py index 2978aff84f..654c9facd7 100644 --- a/dimos/perception/common/ibvs.py +++ b/dimos/perception/common/ibvs.py @@ -16,7 +16,7 @@ class PersonDistanceEstimator: - def __init__(self, K, camera_pitch, camera_height) -> None: + def __init__(self, K, camera_pitch, camera_height) -> None: # type: ignore[no-untyped-def] """ Initialize the distance estimator using ground plane constraint. @@ -49,7 +49,7 @@ def __init__(self, K, camera_pitch, camera_height) -> None: self.fx = K[0, 0] self.cx = K[0, 2] - def estimate_distance_angle(self, bbox: tuple, robot_pitch: float | None = None): + def estimate_distance_angle(self, bbox: tuple, robot_pitch: float | None = None): # type: ignore[no-untyped-def, type-arg] """ Estimate distance and angle to person using ground plane constraint. @@ -123,7 +123,7 @@ class ObjectDistanceEstimator: camera's intrinsic parameters to estimate the distance to a detected object. """ - def __init__(self, K, camera_pitch, camera_height) -> None: + def __init__(self, K, camera_pitch, camera_height) -> None: # type: ignore[no-untyped-def] """ Initialize the distance estimator using ground plane constraint. @@ -158,7 +158,7 @@ def __init__(self, K, camera_pitch, camera_height) -> None: self.cx = K[0, 2] self.estimated_object_size = None - def estimate_object_size(self, bbox: tuple, distance: float): + def estimate_object_size(self, bbox: tuple, distance: float): # type: ignore[no-untyped-def, type-arg] """ Estimate the physical size of an object based on its bbox and known distance. @@ -188,9 +188,9 @@ def set_estimated_object_size(self, size: float) -> None: Args: size: Estimated physical size of the object (in meters) """ - self.estimated_object_size = size + self.estimated_object_size = size # type: ignore[assignment] - def estimate_distance_angle(self, bbox: tuple): + def estimate_distance_angle(self, bbox: tuple): # type: ignore[no-untyped-def, type-arg] """ Estimate distance and angle to object using size-based estimation. diff --git a/dimos/perception/common/utils.py b/dimos/perception/common/utils.py index 2676206bd7..6a246f15e5 100644 --- a/dimos/perception/common/utils.py +++ b/dimos/perception/common/utils.py @@ -15,8 +15,8 @@ from typing import Union import cv2 -from dimos_lcm.sensor_msgs import CameraInfo -from dimos_lcm.vision_msgs import BoundingBox2D, Detection2D, Detection3D +from dimos_lcm.sensor_msgs import CameraInfo # type: ignore[import-untyped] +from dimos_lcm.vision_msgs import BoundingBox2D, Detection2D, Detection3D # type: ignore[import-untyped] import numpy as np import torch import yaml @@ -36,22 +36,22 @@ _HAS_CUDA = True except Exception: # pragma: no cover - optional dependency - cp = None # type: ignore + cp = None _HAS_CUDA = False -def _is_cu_array(x) -> bool: - return _HAS_CUDA and cp is not None and isinstance(x, cp.ndarray) # type: ignore +def _is_cu_array(x) -> bool: # type: ignore[no-untyped-def] + return _HAS_CUDA and cp is not None and isinstance(x, cp.ndarray) -def _to_numpy(x): - return cp.asnumpy(x) if _is_cu_array(x) else x # type: ignore +def _to_numpy(x): # type: ignore[no-untyped-def] + return cp.asnumpy(x) if _is_cu_array(x) else x -def _to_cupy(x): - if _HAS_CUDA and cp is not None and isinstance(x, np.ndarray): # type: ignore +def _to_cupy(x): # type: ignore[no-untyped-def] + if _HAS_CUDA and cp is not None and isinstance(x, np.ndarray): try: - return cp.asarray(x) # type: ignore + return cp.asarray(x) except Exception: return x return x @@ -114,7 +114,7 @@ def load_camera_info(yaml_path: str, frame_id: str = "camera_link") -> CameraInf ) -def load_camera_info_opencv(yaml_path: str) -> tuple[np.ndarray, np.ndarray]: +def load_camera_info_opencv(yaml_path: str) -> tuple[np.ndarray, np.ndarray]: # type: ignore[type-arg] """ Load ROS-style camera_info YAML file and convert to OpenCV camera matrix and distortion coefficients. @@ -143,27 +143,27 @@ def load_camera_info_opencv(yaml_path: str) -> tuple[np.ndarray, np.ndarray]: return K, dist -def rectify_image_cpu(image: Image, camera_matrix: np.ndarray, dist_coeffs: np.ndarray) -> Image: +def rectify_image_cpu(image: Image, camera_matrix: np.ndarray, dist_coeffs: np.ndarray) -> Image: # type: ignore[type-arg] """CPU rectification using OpenCV. Preserves backend by caller. Returns an Image with numpy or cupy data depending on caller choice. """ - src = _to_numpy(image.data) + src = _to_numpy(image.data) # type: ignore[no-untyped-call] rect = cv2.undistort(src, camera_matrix, dist_coeffs) # Caller decides whether to convert back to GPU. return Image(data=rect, format=image.format, frame_id=image.frame_id, ts=image.ts) -def rectify_image_cuda(image: Image, camera_matrix: np.ndarray, dist_coeffs: np.ndarray) -> Image: +def rectify_image_cuda(image: Image, camera_matrix: np.ndarray, dist_coeffs: np.ndarray) -> Image: # type: ignore[type-arg] """GPU rectification using CuPy bilinear sampling. Generates an undistorted output grid and samples from the distorted source. Falls back to CPU if CUDA not available. """ - if not _HAS_CUDA or cp is None or not image.is_cuda: # type: ignore + if not _HAS_CUDA or cp is None or not image.is_cuda: return rectify_image_cpu(image, camera_matrix, dist_coeffs) - xp = cp # type: ignore + xp = cp # Source (distorted) image on device src = image.data @@ -205,7 +205,7 @@ def rectify_image_cuda(image: Image, camera_matrix: np.ndarray, dist_coeffs: np. vs = fy * yd + cy # Bilinear sample from src at (vs, us) - def _bilinear_sample_cuda(img, x_src, y_src): + def _bilinear_sample_cuda(img, x_src, y_src): # type: ignore[no-untyped-def] h, w = int(img.shape[0]), int(img.shape[1]) # Base integer corners (not clamped) x0i = xp.floor(x_src).astype(xp.int32) @@ -268,11 +268,11 @@ def _bilinear_sample_cuda(img, x_src, y_src): out = out.astype(img.dtype, copy=False) return out - rect = _bilinear_sample_cuda(src, us, vs) + rect = _bilinear_sample_cuda(src, us, vs) # type: ignore[no-untyped-call] return Image(data=rect, format=image.format, frame_id=image.frame_id, ts=image.ts) -def rectify_image(image: Image, camera_matrix: np.ndarray, dist_coeffs: np.ndarray) -> Image: +def rectify_image(image: Image, camera_matrix: np.ndarray, dist_coeffs: np.ndarray) -> Image: # type: ignore[type-arg] """ Rectify (undistort) an image using camera calibration parameters. @@ -292,7 +292,7 @@ def rectify_image(image: Image, camera_matrix: np.ndarray, dist_coeffs: np.ndarr def project_3d_points_to_2d_cuda( points_3d: "cp.ndarray", camera_intrinsics: Union[list[float], "cp.ndarray"] ) -> "cp.ndarray": - xp = cp # type: ignore + xp = cp pts = points_3d.astype(xp.float64, copy=False) mask = pts[:, 2] > 0 if not bool(xp.any(mask)): @@ -301,7 +301,7 @@ def project_3d_points_to_2d_cuda( if isinstance(camera_intrinsics, list) and len(camera_intrinsics) == 4: fx, fy, cx, cy = [xp.asarray(v, dtype=xp.float64) for v in camera_intrinsics] else: - K = camera_intrinsics.astype(xp.float64, copy=False) + K = camera_intrinsics.astype(xp.float64, copy=False) # type: ignore[union-attr] fx, fy, cx, cy = K[0, 0], K[1, 1], K[0, 2], K[1, 2] u = (valid[:, 0] * fx / valid[:, 2]) + cx v = (valid[:, 1] * fy / valid[:, 2]) + cy @@ -309,8 +309,8 @@ def project_3d_points_to_2d_cuda( def project_3d_points_to_2d_cpu( - points_3d: np.ndarray, camera_intrinsics: list[float] | np.ndarray -) -> np.ndarray: + points_3d: np.ndarray, camera_intrinsics: list[float] | np.ndarray # type: ignore[type-arg] +) -> np.ndarray: # type: ignore[type-arg] pts = np.asarray(points_3d, dtype=np.float64) valid_mask = pts[:, 2] > 0 if not np.any(valid_mask): @@ -327,9 +327,9 @@ def project_3d_points_to_2d_cpu( def project_3d_points_to_2d( - points_3d: Union[np.ndarray, "cp.ndarray"], - camera_intrinsics: Union[list[float], np.ndarray, "cp.ndarray"], -) -> Union[np.ndarray, "cp.ndarray"]: + points_3d: Union[np.ndarray, "cp.ndarray"], # type: ignore[type-arg] + camera_intrinsics: Union[list[float], np.ndarray, "cp.ndarray"], # type: ignore[type-arg] +) -> Union[np.ndarray, "cp.ndarray"]: # type: ignore[type-arg] """ Project 3D points to 2D image coordinates using camera intrinsics. @@ -349,10 +349,10 @@ def project_3d_points_to_2d( # Filter out points with zero or negative depth if _is_cu_array(points_3d) or _is_cu_array(camera_intrinsics): - xp = cp # type: ignore + xp = cp pts = points_3d if _is_cu_array(points_3d) else xp.asarray(points_3d) K = camera_intrinsics if _is_cu_array(camera_intrinsics) else camera_intrinsics - return project_3d_points_to_2d_cuda(pts, K) # type: ignore[arg-type] + return project_3d_points_to_2d_cuda(pts, K) return project_3d_points_to_2d_cpu(np.asarray(points_3d), np.asarray(camera_intrinsics)) @@ -361,7 +361,7 @@ def project_2d_points_to_3d_cuda( depth_values: "cp.ndarray", camera_intrinsics: Union[list[float], "cp.ndarray"], ) -> "cp.ndarray": - xp = cp # type: ignore + xp = cp pts = points_2d.astype(xp.float64, copy=False) depths = depth_values.astype(xp.float64, copy=False) valid = depths > 0 @@ -372,7 +372,7 @@ def project_2d_points_to_3d_cuda( if isinstance(camera_intrinsics, list) and len(camera_intrinsics) == 4: fx, fy, cx, cy = [xp.asarray(v, dtype=xp.float64) for v in camera_intrinsics] else: - K = camera_intrinsics.astype(xp.float64, copy=False) + K = camera_intrinsics.astype(xp.float64, copy=False) # type: ignore[union-attr] fx, fy, cx, cy = K[0, 0], K[1, 1], K[0, 2], K[1, 2] X = (uv[:, 0] - cx) * Z / fx Y = (uv[:, 1] - cy) * Z / fy @@ -380,10 +380,10 @@ def project_2d_points_to_3d_cuda( def project_2d_points_to_3d_cpu( - points_2d: np.ndarray, - depth_values: np.ndarray, - camera_intrinsics: list[float] | np.ndarray, -) -> np.ndarray: + points_2d: np.ndarray, # type: ignore[type-arg] + depth_values: np.ndarray, # type: ignore[type-arg] + camera_intrinsics: list[float] | np.ndarray, # type: ignore[type-arg] +) -> np.ndarray: # type: ignore[type-arg] pts = np.asarray(points_2d, dtype=np.float64) depths = np.asarray(depth_values, dtype=np.float64) valid_mask = depths > 0 @@ -406,10 +406,10 @@ def project_2d_points_to_3d_cpu( def project_2d_points_to_3d( - points_2d: Union[np.ndarray, "cp.ndarray"], - depth_values: Union[np.ndarray, "cp.ndarray"], - camera_intrinsics: Union[list[float], np.ndarray, "cp.ndarray"], -) -> Union[np.ndarray, "cp.ndarray"]: + points_2d: Union[np.ndarray, "cp.ndarray"], # type: ignore[type-arg] + depth_values: Union[np.ndarray, "cp.ndarray"], # type: ignore[type-arg] + camera_intrinsics: Union[list[float], np.ndarray, "cp.ndarray"], # type: ignore[type-arg] +) -> Union[np.ndarray, "cp.ndarray"]: # type: ignore[type-arg] """ Project 2D image points to 3D coordinates using depth values and camera intrinsics. @@ -430,19 +430,19 @@ def project_2d_points_to_3d( # Ensure depth_values is a numpy array if _is_cu_array(points_2d) or _is_cu_array(depth_values) or _is_cu_array(camera_intrinsics): - xp = cp # type: ignore + xp = cp pts = points_2d if _is_cu_array(points_2d) else xp.asarray(points_2d) depths = depth_values if _is_cu_array(depth_values) else xp.asarray(depth_values) K = camera_intrinsics if _is_cu_array(camera_intrinsics) else camera_intrinsics - return project_2d_points_to_3d_cuda(pts, depths, K) # type: ignore[arg-type] + return project_2d_points_to_3d_cuda(pts, depths, K) return project_2d_points_to_3d_cpu( np.asarray(points_2d), np.asarray(depth_values), np.asarray(camera_intrinsics) ) def colorize_depth( - depth_img: Union[np.ndarray, "cp.ndarray"], max_depth: float = 5.0, overlay_stats: bool = True -) -> Union[np.ndarray, "cp.ndarray"] | None: + depth_img: Union[np.ndarray, "cp.ndarray"], max_depth: float = 5.0, overlay_stats: bool = True # type: ignore[type-arg] +) -> Union[np.ndarray, "cp.ndarray"] | None: # type: ignore[type-arg] """ Normalize and colorize depth image using COLORMAP_JET with optional statistics overlay. @@ -458,7 +458,7 @@ def colorize_depth( return None was_cu = _is_cu_array(depth_img) - xp = cp if was_cu else np # type: ignore + xp = cp if was_cu else np depth = depth_img if was_cu else np.asarray(depth_img) valid_mask = xp.isfinite(depth) & (depth > 0) @@ -467,26 +467,26 @@ def colorize_depth( depth_norm = xp.where(valid_mask, xp.clip(depth / max_depth, 0, 1), depth_norm) # Use CPU for colormap/text; convert back to GPU if needed - depth_norm_np = _to_numpy(depth_norm) + depth_norm_np = _to_numpy(depth_norm) # type: ignore[no-untyped-call] depth_colored = cv2.applyColorMap((depth_norm_np * 255).astype(np.uint8), cv2.COLORMAP_JET) depth_rgb_np = cv2.cvtColor(depth_colored, cv2.COLOR_BGR2RGB) depth_rgb_np = (depth_rgb_np * 0.6).astype(np.uint8) - if overlay_stats and (np.any(_to_numpy(valid_mask))): - valid_depths = _to_numpy(depth)[_to_numpy(valid_mask)] + if overlay_stats and (np.any(_to_numpy(valid_mask))): # type: ignore[no-untyped-call] + valid_depths = _to_numpy(depth)[_to_numpy(valid_mask)] # type: ignore[no-untyped-call] min_depth = float(np.min(valid_depths)) max_depth_actual = float(np.max(valid_depths)) h, w = depth_rgb_np.shape[:2] center_y, center_x = h // 2, w // 2 - center_region = _to_numpy(depth)[ + center_region = _to_numpy(depth)[ # type: ignore[no-untyped-call] max(0, center_y - 2) : min(h, center_y + 3), max(0, center_x - 2) : min(w, center_x + 3) ] center_mask = np.isfinite(center_region) & (center_region > 0) if center_mask.any(): center_depth = float(np.median(center_region[center_mask])) else: - depth_np = _to_numpy(depth) - vm_np = _to_numpy(valid_mask) + depth_np = _to_numpy(depth) # type: ignore[no-untyped-call] + vm_np = _to_numpy(valid_mask) # type: ignore[no-untyped-call] center_depth = float(depth_np[center_y, center_x]) if vm_np[center_y, center_x] else 0.0 font = cv2.FONT_HERSHEY_SIMPLEX @@ -576,11 +576,11 @@ def colorize_depth( line_type, ) - return _to_cupy(depth_rgb_np) if was_cu else depth_rgb_np + return _to_cupy(depth_rgb_np) if was_cu else depth_rgb_np # type: ignore[no-untyped-call] def draw_bounding_box( - image: Union[np.ndarray, "cp.ndarray"], + image: Union[np.ndarray, "cp.ndarray"], # type: ignore[type-arg] bbox: list[float], color: tuple[int, int, int] = (0, 255, 0), thickness: int = 2, @@ -588,7 +588,7 @@ def draw_bounding_box( confidence: float | None = None, object_id: int | None = None, font_scale: float = 0.6, -) -> Union[np.ndarray, "cp.ndarray"]: +) -> Union[np.ndarray, "cp.ndarray"]: # type: ignore[type-arg] """ Draw a bounding box with optional label on an image. @@ -606,7 +606,7 @@ def draw_bounding_box( Image with bounding box drawn """ was_cu = _is_cu_array(image) - img_np = _to_numpy(image) + img_np = _to_numpy(image) # type: ignore[no-untyped-call] x1, y1, x2, y2 = map(int, bbox) cv2.rectangle(img_np, (x1, y1), (x2, y2), color, thickness) @@ -643,17 +643,17 @@ def draw_bounding_box( 1, ) - return _to_cupy(img_np) if was_cu else img_np + return _to_cupy(img_np) if was_cu else img_np # type: ignore[no-untyped-call] def draw_segmentation_mask( - image: Union[np.ndarray, "cp.ndarray"], - mask: Union[np.ndarray, "cp.ndarray"], + image: Union[np.ndarray, "cp.ndarray"], # type: ignore[type-arg] + mask: Union[np.ndarray, "cp.ndarray"], # type: ignore[type-arg] color: tuple[int, int, int] = (0, 200, 200), alpha: float = 0.5, draw_contours: bool = True, contour_thickness: int = 2, -) -> Union[np.ndarray, "cp.ndarray"]: +) -> Union[np.ndarray, "cp.ndarray"]: # type: ignore[type-arg] """ Draw segmentation mask overlay on an image. @@ -672,8 +672,8 @@ def draw_segmentation_mask( return image was_cu = _is_cu_array(image) - img_np = _to_numpy(image) - mask_np = _to_numpy(mask) + img_np = _to_numpy(image) # type: ignore[no-untyped-call] + mask_np = _to_numpy(mask) # type: ignore[no-untyped-call] try: mask_np = mask_np.astype(np.uint8) @@ -689,17 +689,17 @@ def draw_segmentation_mask( except Exception as e: logger.warning(f"Error drawing segmentation mask: {e}") - return _to_cupy(img_np) if was_cu else img_np + return _to_cupy(img_np) if was_cu else img_np # type: ignore[no-untyped-call] def draw_object_detection_visualization( - image: Union[np.ndarray, "cp.ndarray"], + image: Union[np.ndarray, "cp.ndarray"], # type: ignore[type-arg] objects: list[ObjectData], draw_masks: bool = False, bbox_color: tuple[int, int, int] = (0, 255, 0), mask_color: tuple[int, int, int] = (0, 200, 200), font_scale: float = 0.6, -) -> Union[np.ndarray, "cp.ndarray"]: +) -> Union[np.ndarray, "cp.ndarray"]: # type: ignore[type-arg] """ Create object detection visualization with bounding boxes and optional masks. @@ -715,7 +715,7 @@ def draw_object_detection_visualization( Image with detection visualization """ was_cu = _is_cu_array(image) - viz_image = _to_numpy(image).copy() + viz_image = _to_numpy(image).copy() # type: ignore[no-untyped-call] for obj in objects: try: @@ -732,7 +732,7 @@ def draw_object_detection_visualization( if "color" in obj and obj["color"] is not None: obj_color = obj["color"] if isinstance(obj_color, np.ndarray): - color = tuple(int(c) for c in obj_color) + color = tuple(int(c) for c in obj_color) # type: ignore[assignment] elif isinstance(obj_color, list | tuple): color = tuple(int(c) for c in obj_color[:3]) @@ -749,7 +749,7 @@ def draw_object_detection_visualization( except Exception as e: logger.warning(f"Error drawing object visualization: {e}") - return _to_cupy(viz_image) if was_cu else viz_image + return _to_cupy(viz_image) if was_cu else viz_image # type: ignore[no-untyped-call] def detection_results_to_object_data( @@ -758,7 +758,7 @@ def detection_results_to_object_data( class_ids: list[int], confidences: list[float], names: list[str], - masks: list[np.ndarray] | None = None, + masks: list[np.ndarray] | None = None, # type: ignore[type-arg] source: str = "detection", ) -> list[ObjectData]: """ @@ -795,14 +795,14 @@ def detection_results_to_object_data( "class_id": class_ids[i] if i < len(class_ids) else 0, "label": names[i] if i < len(names) else f"{source}_object", "movement_tolerance": 1.0, # Default to freely movable - "segmentation_mask": masks[i].cpu().numpy() + "segmentation_mask": masks[i].cpu().numpy() # type: ignore[attr-defined, typeddict-item] if masks and i < len(masks) and isinstance(masks[i], torch.Tensor) else masks[i] if masks and i < len(masks) else None, # Initialize 3D properties (will be populated by point cloud processing) - "position": Vector(0, 0, 0), - "rotation": Vector(0, 0, 0), + "position": Vector(0, 0, 0), # type: ignore[arg-type] + "rotation": Vector(0, 0, 0), # type: ignore[arg-type] "size": { "width": 0.0, "height": 0.0, @@ -835,7 +835,7 @@ def combine_object_data( # Check mask overlap mask2 = obj2.get("segmentation_mask") - m2 = _to_numpy(mask2) if mask2 is not None else None + m2 = _to_numpy(mask2) if mask2 is not None else None # type: ignore[no-untyped-call] if m2 is None or np.sum(m2 > 0) == 0: combined.append(obj_copy) continue @@ -848,7 +848,7 @@ def combine_object_data( if mask1 is None: continue - m1 = _to_numpy(mask1) + m1 = _to_numpy(mask1) # type: ignore[no-untyped-call] intersection = np.sum((m1 > 0) & (m2 > 0)) if intersection / mask2_area >= overlap_threshold: is_duplicate = True @@ -925,7 +925,7 @@ def find_clicked_detection( return None -def extract_pose_from_detection3d(detection3d: Detection3D): +def extract_pose_from_detection3d(detection3d: Detection3D): # type: ignore[no-untyped-def] """Extract PoseStamped from Detection3D message. Args: diff --git a/dimos/perception/detection/detectors/detic.py b/dimos/perception/detection/detectors/detic.py index 4432988f28..4ad8c39af9 100644 --- a/dimos/perception/detection/detectors/detic.py +++ b/dimos/perception/detection/detectors/detic.py @@ -36,8 +36,8 @@ PIL.Image.LINEAR = PIL.Image.BILINEAR # type: ignore[attr-defined] # Detectron2 imports -from detectron2.config import get_cfg -from detectron2.data import MetadataCatalog +from detectron2.config import get_cfg # type: ignore[import-not-found] +from detectron2.data import MetadataCatalog # type: ignore[import-not-found] # Simple tracking implementation @@ -48,9 +48,9 @@ def __init__(self, iou_threshold: float = 0.3, max_age: int = 5) -> None: self.iou_threshold = iou_threshold self.max_age = max_age self.next_id = 1 - self.tracks = {} # id -> {bbox, class_id, age, mask, etc} + self.tracks = {} # type: ignore[var-annotated] # id -> {bbox, class_id, age, mask, etc} - def _calculate_iou(self, bbox1, bbox2): + def _calculate_iou(self, bbox1, bbox2): # type: ignore[no-untyped-def] """Calculate IoU between two bboxes in format [x1,y1,x2,y2]""" x1 = max(bbox1[0], bbox2[0]) y1 = max(bbox1[1], bbox2[1]) @@ -67,7 +67,7 @@ def _calculate_iou(self, bbox1, bbox2): return intersection / union if union > 0 else 0 - def update(self, detections, masks): + def update(self, detections, masks): # type: ignore[no-untyped-def] """Update tracker with new detections Args: @@ -113,7 +113,7 @@ def update(self, detections, masks): if det[5] != track["class_id"]: continue - iou = self._calculate_iou(track["bbox"], det[:4]) + iou = self._calculate_iou(track["bbox"], det[:4]) # type: ignore[no-untyped-call] if iou > best_iou: best_iou = iou best_idx = i @@ -161,7 +161,7 @@ def update(self, detections, masks): class Detic2DDetector(Detector): - def __init__( + def __init__( # type: ignore[no-untyped-def] self, model_path=None, device: str = "cuda", vocabulary=None, threshold: float = 0.5 ) -> None: """ @@ -179,10 +179,10 @@ def __init__( # Set up Detic paths - already added to sys.path at module level # Import Detic modules - from centernet.config import add_centernet_config - from detic.config import add_detic_config - from detic.modeling.text.text_encoder import build_text_encoder - from detic.modeling.utils import reset_cls_test + from centernet.config import add_centernet_config # type: ignore[import-not-found] + from detic.config import add_detic_config # type: ignore[import-not-found] + from detic.modeling.text.text_encoder import build_text_encoder # type: ignore[import-not-found] + from detic.modeling.utils import reset_cls_test # type: ignore[import-not-found] # Keep reference to these functions for later use self.reset_cls_test = reset_cls_test @@ -249,12 +249,12 @@ def __init__( # Setup with initial vocabulary vocabulary = vocabulary or "lvis" - self.setup_vocabulary(vocabulary) + self.setup_vocabulary(vocabulary) # type: ignore[no-untyped-call] # Initialize our simple tracker self.tracker = SimpleTracker(iou_threshold=0.5, max_age=5) - def setup_vocabulary(self, vocabulary): + def setup_vocabulary(self, vocabulary): # type: ignore[no-untyped-def] """ Setup the model's vocabulary. @@ -264,7 +264,7 @@ def setup_vocabulary(self, vocabulary): """ if self.predictor is None: # Initialize the model - from detectron2.engine import DefaultPredictor + from detectron2.engine import DefaultPredictor # type: ignore[import-not-found] self.predictor = DefaultPredictor(self.cfg) @@ -285,7 +285,7 @@ def setup_vocabulary(self, vocabulary): except: # Default to LVIS if there's an issue print(f"Error loading vocabulary from {vocabulary}, using LVIS") - return self.setup_vocabulary("lvis") + return self.setup_vocabulary("lvis") # type: ignore[no-untyped-call] else: # Assume it's a list of class names class_names = vocabulary @@ -300,10 +300,10 @@ def setup_vocabulary(self, vocabulary): num_classes = len(class_names) # Reset model with new vocabulary - self.reset_cls_test(self.predictor.model, classifier, num_classes) + self.reset_cls_test(self.predictor.model, classifier, num_classes) # type: ignore[attr-defined] return self.class_names - def _get_clip_embeddings(self, vocabulary, prompt: str = "a "): + def _get_clip_embeddings(self, vocabulary, prompt: str = "a "): # type: ignore[no-untyped-def] """ Generate CLIP embeddings for a vocabulary list. @@ -320,7 +320,7 @@ def _get_clip_embeddings(self, vocabulary, prompt: str = "a "): emb = text_encoder(texts).detach().permute(1, 0).contiguous().cpu() return emb - def process_image(self, image: Image): + def process_image(self, image: Image): # type: ignore[no-untyped-def] """ Process an image and return detection results. @@ -337,7 +337,7 @@ def process_image(self, image: Image): - masks: list of segmentation masks (numpy arrays) """ # Run inference with Detic - outputs = self.predictor(image.to_opencv()) + outputs = self.predictor(image.to_opencv()) # type: ignore[misc] instances = outputs["instances"].to("cpu") # Extract bounding boxes, classes, scores, and masks @@ -371,7 +371,7 @@ def process_image(self, image: Image): return [], [], [], [], [] # , [] # Update tracker with detections and correctly aligned masks - track_results = self.tracker.update(detections, filtered_masks) + track_results = self.tracker.update(detections, filtered_masks) # type: ignore[no-untyped-call] # Process tracking results track_ids = [] @@ -398,7 +398,7 @@ def process_image(self, image: Image): # tracked_masks, ) - def visualize_results( + def visualize_results( # type: ignore[no-untyped-def] self, image, bboxes, track_ids, class_ids, confidences, names: Sequence[str] ): """ diff --git a/dimos/perception/detection/detectors/person/yolo.py b/dimos/perception/detection/detectors/person/yolo.py index 6421ab7d1d..3f58dc75ce 100644 --- a/dimos/perception/detection/detectors/person/yolo.py +++ b/dimos/perception/detection/detectors/person/yolo.py @@ -39,7 +39,7 @@ def __init__( self.device = device return - if is_cuda_available(): + if is_cuda_available(): # type: ignore[no-untyped-call] self.device = "cuda" logger.info("Using CUDA for YOLO person detector") else: diff --git a/dimos/perception/detection/detectors/yolo.py b/dimos/perception/detection/detectors/yolo.py index 64e56ad456..22ecf16259 100644 --- a/dimos/perception/detection/detectors/yolo.py +++ b/dimos/perception/detection/detectors/yolo.py @@ -40,7 +40,7 @@ def __init__( self.device = device return - if is_cuda_available(): + if is_cuda_available(): # type: ignore[no-untyped-call] self.device = "cuda" logger.debug("Using CUDA for YOLO 2d detector") else: diff --git a/dimos/perception/detection/module2D.py b/dimos/perception/detection/module2D.py index 2d55346409..e0ccb9b658 100644 --- a/dimos/perception/detection/module2D.py +++ b/dimos/perception/detection/module2D.py @@ -15,7 +15,7 @@ from dataclasses import dataclass from typing import Any -from dimos_lcm.foxglove_msgs.ImageAnnotations import ( +from dimos_lcm.foxglove_msgs.ImageAnnotations import ( # type: ignore[import-untyped] ImageAnnotations, ) from reactivex import operators as ops @@ -29,7 +29,7 @@ from dimos.msgs.sensor_msgs import CameraInfo, Image from dimos.msgs.sensor_msgs.Image import sharpness_barrier from dimos.msgs.vision_msgs import Detection2DArray -from dimos.perception.detection.detectors import Detector +from dimos.perception.detection.detectors import Detector # type: ignore[attr-defined] from dimos.perception.detection.detectors.yolo import Yolo2DDetector from dimos.perception.detection.type import Filter2D, ImageDetections2D from dimos.utils.decorators.decorators import simple_mcache @@ -67,22 +67,22 @@ class Detection2DModule(Module): cnt: int = 0 - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(*args, **kwargs) - self.detector = self.config.detector() - self.vlm_detections_subject = Subject() + self.detector = self.config.detector() # type: ignore[call-arg, misc] + self.vlm_detections_subject = Subject() # type: ignore[var-annotated] self.previous_detection_count = 0 def process_image_frame(self, image: Image) -> ImageDetections2D: imageDetections = self.detector.process_image(image) if not self.config.filter: return imageDetections - return imageDetections.filter(*self.config.filter) + return imageDetections.filter(*self.config.filter) # type: ignore[misc, return-value] @simple_mcache def sharp_image_stream(self) -> Observable[Image]: return backpressure( - self.image.pure_observable().pipe( + self.image.pure_observable().pipe( # type: ignore[no-untyped-call] sharpness_barrier(self.config.max_freq), ) ) @@ -111,8 +111,8 @@ def track(self, detections: ImageDetections2D) -> None: if index < current_count: # Active detection - compute real position detection = detections.detections[index] - position_3d = self.pixel_to_3d( - detection.center_bbox, self.config.camera_info, assumed_depth=1.0 + position_3d = self.pixel_to_3d( # type: ignore[attr-defined] + detection.center_bbox, self.config.camera_info, assumed_depth=1.0 # type: ignore[attr-defined] ) else: # No detection at this index - publish zero transform @@ -135,11 +135,11 @@ def start(self) -> None: # self.detection_stream_2d().subscribe(self.track) self.detection_stream_2d().subscribe( - lambda det: self.detections.publish(det.to_ros_detection2d_array()) + lambda det: self.detections.publish(det.to_ros_detection2d_array()) # type: ignore[no-untyped-call] ) self.detection_stream_2d().subscribe( - lambda det: self.annotations.publish(det.to_foxglove_annotations()) + lambda det: self.annotations.publish(det.to_foxglove_annotations()) # type: ignore[no-untyped-call] ) def publish_cropped_images(detections: ImageDetections2D) -> None: @@ -152,10 +152,10 @@ def publish_cropped_images(detections: ImageDetections2D) -> None: @rpc def stop(self) -> None: - return super().stop() + return super().stop() # type: ignore[no-any-return] -def deploy( +def deploy( # type: ignore[no-untyped-def] dimos: DimosCluster, camera: spec.Camera, prefix: str = "/detector2d", diff --git a/dimos/perception/detection/module3D.py b/dimos/perception/detection/module3D.py index 6952fafd56..2bb3037542 100644 --- a/dimos/perception/detection/module3D.py +++ b/dimos/perception/detection/module3D.py @@ -13,13 +13,13 @@ # limitations under the License. -from dimos_lcm.foxglove_msgs.ImageAnnotations import ImageAnnotations -from lcm_msgs.foxglove_msgs import SceneUpdate +from dimos_lcm.foxglove_msgs.ImageAnnotations import ImageAnnotations # type: ignore[import-untyped] +from lcm_msgs.foxglove_msgs import SceneUpdate # type: ignore[import-not-found] from reactivex import operators as ops from reactivex.observable import Observable from dimos import spec -from dimos.agents2 import skill +from dimos.agents2 import skill # type: ignore[attr-defined] from dimos.core import DimosCluster, In, Out, rpc from dimos.msgs.geometry_msgs import PoseStamped, Quaternion, Transform, Vector3 from dimos.msgs.sensor_msgs import Image, PointCloud2 @@ -134,14 +134,14 @@ def nav_vlm(self, question: str) -> str: print("VLM result:", result, "for", image, "and question", question) if isinstance(result, str) or not result or not len(result): - return None + return None # type: ignore[return-value] detections: ImageDetections2D = result print(detections) if not len(detections): print("No 2d detections") - return None + return None # type: ignore[return-value] pc = self.pointcloud.get_next() transform = self.tf.get("camera_optical", pc.frame_id, detections.image.ts, 5.0) @@ -149,7 +149,7 @@ def nav_vlm(self, question: str) -> str: detections3d = self.process_frame(detections, pc, transform) if len(detections3d): - return detections3d[0].pose + return detections3d[0].pose # type: ignore[no-any-return] print("No 3d detections, projecting 2d") center = detections[0].get_bbox_center() @@ -164,14 +164,14 @@ def nav_vlm(self, question: str) -> str: def start(self) -> None: super().start() - def detection2d_to_3d(args): + def detection2d_to_3d(args): # type: ignore[no-untyped-def] detections, pc = args transform = self.tf.get("camera_optical", pc.frame_id, detections.image.ts, 5.0) return self.process_frame(detections, pc, transform) self.detection_stream_3d = align_timestamped( backpressure(self.detection_stream_2d()), - self.pointcloud.observable(), + self.pointcloud.observable(), # type: ignore[no-untyped-call] match_tolerance=0.25, buffer_size=20.0, ).pipe(ops.map(detection2d_to_3d)) @@ -190,10 +190,10 @@ def _publish_detections(self, detections: ImageDetections3DPC) -> None: pointcloud_topic = getattr(self, "detected_pointcloud_" + str(index)) pointcloud_topic.publish(detection.pointcloud) - self.scene_update.publish(detections.to_foxglove_scene_update()) + self.scene_update.publish(detections.to_foxglove_scene_update()) # type: ignore[no-untyped-call] -def deploy( +def deploy( # type: ignore[no-untyped-def] dimos: DimosCluster, lidar: spec.Pointcloud, camera: spec.Camera, @@ -202,7 +202,7 @@ def deploy( ) -> Detection3DModule: from dimos.core import LCMTransport - detector = dimos.deploy(Detection3DModule, camera_info=camera.hardware_camera_info, **kwargs) + detector = dimos.deploy(Detection3DModule, camera_info=camera.hardware_camera_info, **kwargs) # type: ignore[attr-defined] detector.image.connect(camera.image) detector.pointcloud.connect(lidar.pointcloud) @@ -220,4 +220,4 @@ def deploy( detector.detected_pointcloud_2.transport = LCMTransport(f"{prefix}/pointcloud/2", PointCloud2) detector.start() - return detector + return detector # type: ignore[no-any-return] diff --git a/dimos/perception/detection/moduleDB.py b/dimos/perception/detection/moduleDB.py index 88547ed427..5bc8c87318 100644 --- a/dimos/perception/detection/moduleDB.py +++ b/dimos/perception/detection/moduleDB.py @@ -17,8 +17,8 @@ import time from typing import Any -from dimos_lcm.foxglove_msgs.ImageAnnotations import ImageAnnotations -from lcm_msgs.foxglove_msgs import SceneUpdate +from dimos_lcm.foxglove_msgs.ImageAnnotations import ImageAnnotations # type: ignore[import-untyped] +from lcm_msgs.foxglove_msgs import SceneUpdate # type: ignore[import-not-found] from reactivex.observable import Observable from dimos import spec @@ -33,7 +33,7 @@ # Represents an object in space, as collection of 3d detections over time class Object3D(Detection3DPC): - best_detection: Detection3DPC | None = None # type: ignore + best_detection: Detection3DPC | None = None center: Vector3 | None = None # type: ignore track_id: str | None = None # type: ignore detections: int = 0 @@ -51,7 +51,7 @@ def to_repr_dict(self) -> dict[str, Any]: "center": center_str, } - def __init__( + def __init__( # type: ignore[no-untyped-def] self, track_id: str, detection: Detection3DPC | None = None, *args, **kwargs ) -> None: if detection is None: @@ -98,7 +98,7 @@ def get_image(self) -> Image | None: def scene_entity_label(self) -> str: return f"{self.name} ({self.detections})" - def agent_encode(self): + def agent_encode(self): # type: ignore[no-untyped-def] return { "id": self.track_id, "name": self.name, @@ -172,14 +172,14 @@ def update_objects(imageDetections: ImageDetections3DPC) -> None: def scene_thread() -> None: while True: scene_update = self.to_foxglove_scene_update() - self.scene_update.publish(scene_update) + self.scene_update.publish(scene_update) # type: ignore[no-untyped-call] time.sleep(1.0) threading.Thread(target=scene_thread, daemon=True).start() self.detection_stream_3d.subscribe(update_objects) - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(*args, **kwargs) self.goto = None self.objects = {} @@ -202,7 +202,7 @@ def add_detections(self, detections: list[Detection3DPC]) -> list[Object3D]: detection for detection in map(self.add_detection, detections) if detection is not None ] - def add_detection(self, detection: Detection3DPC): + def add_detection(self, detection: Detection3DPC): # type: ignore[no-untyped-def] """Add detection to existing object or create new one.""" closest = self.closest_object(detection) if closest and closest.bounding_box_intersects(detection): @@ -210,13 +210,13 @@ def add_detection(self, detection: Detection3DPC): else: return self.create_new_object(detection) - def add_to_object(self, closest: Object3D, detection: Detection3DPC): + def add_to_object(self, closest: Object3D, detection: Detection3DPC): # type: ignore[no-untyped-def] new_object = closest + detection if closest.track_id is not None: self.objects[closest.track_id] = new_object return new_object - def create_new_object(self, detection: Detection3DPC): + def create_new_object(self, detection: Detection3DPC): # type: ignore[no-untyped-def] new_object = Object3D(f"obj_{self.cnt}", detection) if new_object.track_id is not None: self.objects[new_object.track_id] = new_object @@ -228,9 +228,9 @@ def agent_encode(self) -> str: for obj in copy(self.objects).values(): # we need at least 3 detectieons to consider it a valid object # for this to be serious we need a ratio of detections within the window of observations - if len(obj.detections) < 4: + if len(obj.detections) < 4: # type: ignore[arg-type] continue - ret.append(str(obj.agent_encode())) + ret.append(str(obj.agent_encode())) # type: ignore[no-untyped-call] if not ret: return "No objects detected yet." return "\n".join(ret) @@ -271,7 +271,7 @@ def lookup(self, label: str) -> list[Detection3DPC]: return [] @rpc - def stop(self): + def stop(self): # type: ignore[no-untyped-def] return super().stop() def goto_object(self, object_id: str) -> Object3D | None: @@ -294,7 +294,7 @@ def to_foxglove_scene_update(self) -> "SceneUpdate": for obj in self.objects: try: scene_update.entities.append( - obj.to_foxglove_scene_entity(entity_id=f"{obj.name}_{obj.track_id}") + obj.to_foxglove_scene_entity(entity_id=f"{obj.name}_{obj.track_id}") # type: ignore[attr-defined] ) except Exception: pass @@ -306,7 +306,7 @@ def __len__(self) -> int: return len(self.objects.values()) -def deploy( +def deploy( # type: ignore[no-untyped-def] dimos: DimosCluster, lidar: spec.Pointcloud, camera: spec.Camera, @@ -315,7 +315,7 @@ def deploy( ) -> Detection3DModule: from dimos.core import LCMTransport - detector = dimos.deploy(ObjectDBModule, camera_info=camera.camera_info_stream, **kwargs) + detector = dimos.deploy(ObjectDBModule, camera_info=camera.camera_info_stream, **kwargs) # type: ignore[attr-defined] detector.image.connect(camera.image) detector.pointcloud.connect(lidar.pointcloud) @@ -333,4 +333,4 @@ def deploy( detector.detected_pointcloud_2.transport = LCMTransport(f"{prefix}/pointcloud/2", PointCloud2) detector.start() - return detector + return detector # type: ignore[no-any-return] diff --git a/dimos/perception/detection/reid/embedding_id_system.py b/dimos/perception/detection/reid/embedding_id_system.py index c1c406fe56..69bbed584d 100644 --- a/dimos/perception/detection/reid/embedding_id_system.py +++ b/dimos/perception/detection/reid/embedding_id_system.py @@ -70,7 +70,7 @@ def __init__( self.min_embeddings_for_matching = min_embeddings_for_matching # Track embeddings (list of all embeddings as numpy arrays) - self.track_embeddings: dict[int, list[np.ndarray]] = {} + self.track_embeddings: dict[int, list[np.ndarray]] = {} # type: ignore[type-arg] # Negative constraints (track_ids that co-occurred = different objects) self.negative_pairs: dict[int, set[int]] = {} @@ -129,7 +129,7 @@ def update_embedding(self, track_id: int, new_embedding: Embedding) -> None: embeddings.pop(0) # Remove oldest def _compute_group_similarity( - self, query_embeddings: list[np.ndarray], candidate_embeddings: list[np.ndarray] + self, query_embeddings: list[np.ndarray], candidate_embeddings: list[np.ndarray] # type: ignore[type-arg] ) -> float: """Compute similarity between two groups of embeddings. diff --git a/dimos/perception/detection/reid/module.py b/dimos/perception/detection/reid/module.py index 3cef9f2ff2..483e83e427 100644 --- a/dimos/perception/detection/reid/module.py +++ b/dimos/perception/detection/reid/module.py @@ -12,11 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -from dimos_lcm.foxglove_msgs.ImageAnnotations import ( +from dimos_lcm.foxglove_msgs.ImageAnnotations import ( # type: ignore[import-untyped] ImageAnnotations, TextAnnotation, ) -from dimos_lcm.foxglove_msgs.Point2 import Point2 +from dimos_lcm.foxglove_msgs.Point2 import Point2 # type: ignore[import-untyped] from reactivex import operators as ops from reactivex.observable import Observable @@ -42,13 +42,13 @@ class ReidModule(Module): image: In[Image] = None # type: ignore annotations: Out[ImageAnnotations] = None # type: ignore - def __init__(self, idsystem: IDSystem | None = None, **kwargs) -> None: + def __init__(self, idsystem: IDSystem | None = None, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(**kwargs) if idsystem is None: try: from dimos.models.embedding import TorchReIDModel - idsystem = EmbeddingIDSystem(model=TorchReIDModel, padding=0) + idsystem = EmbeddingIDSystem(model=TorchReIDModel, padding=0) # type: ignore[arg-type] except Exception as e: raise RuntimeError( "TorchReIDModel not available. Please install with: pip install dimos[torchreid]" @@ -59,8 +59,8 @@ def __init__(self, idsystem: IDSystem | None = None, **kwargs) -> None: def detections_stream(self) -> Observable[ImageDetections2D]: return backpressure( align_timestamped( - self.image.pure_observable(), - self.detections.pure_observable().pipe( + self.image.pure_observable(), # type: ignore[no-untyped-call] + self.detections.pure_observable().pipe( # type: ignore[no-untyped-call] ops.filter(lambda d: d.detections_length > 0) # type: ignore[attr-defined] ), match_tolerance=0.0, @@ -109,4 +109,4 @@ def ingress(self, imageDetections: ImageDetections2D) -> None: points=[], points_length=0, ) - self.annotations.publish(annotations) + self.annotations.publish(annotations) # type: ignore[no-untyped-call] diff --git a/dimos/perception/detection/type/__init__.py b/dimos/perception/detection/type/__init__.py index 624784776f..f34598c3a1 100644 --- a/dimos/perception/detection/type/__init__.py +++ b/dimos/perception/detection/type/__init__.py @@ -1,4 +1,4 @@ -from dimos.perception.detection.type.detection2d import ( +from dimos.perception.detection.type.detection2d import ( # type: ignore[attr-defined] Detection2D, Detection2DBBox, Detection2DPerson, diff --git a/dimos/perception/detection/type/detection2d/base.py b/dimos/perception/detection/type/detection2d/base.py index 11a4d729f6..da356985d0 100644 --- a/dimos/perception/detection/type/detection2d/base.py +++ b/dimos/perception/detection/type/detection2d/base.py @@ -15,8 +15,8 @@ from abc import abstractmethod from collections.abc import Callable -from dimos_lcm.foxglove_msgs.ImageAnnotations import PointsAnnotation, TextAnnotation -from dimos_lcm.vision_msgs import Detection2D as ROSDetection2D +from dimos_lcm.foxglove_msgs.ImageAnnotations import PointsAnnotation, TextAnnotation # type: ignore[import-untyped] +from dimos_lcm.vision_msgs import Detection2D as ROSDetection2D # type: ignore[import-untyped] from dimos.msgs.foxglove_msgs import ImageAnnotations from dimos.msgs.sensor_msgs import Image diff --git a/dimos/perception/detection/type/detection2d/bbox.py b/dimos/perception/detection/type/detection2d/bbox.py index 46e8fe2cc7..9b11a3354a 100644 --- a/dimos/perception/detection/type/detection2d/bbox.py +++ b/dimos/perception/detection/type/detection2d/bbox.py @@ -23,12 +23,12 @@ from dimos.msgs.sensor_msgs import Image -from dimos_lcm.foxglove_msgs.ImageAnnotations import ( +from dimos_lcm.foxglove_msgs.ImageAnnotations import ( # type: ignore[import-untyped] PointsAnnotation, TextAnnotation, ) -from dimos_lcm.foxglove_msgs.Point2 import Point2 -from dimos_lcm.vision_msgs import ( +from dimos_lcm.foxglove_msgs.Point2 import Point2 # type: ignore[import-untyped] +from dimos_lcm.vision_msgs import ( # type: ignore[import-untyped] BoundingBox2D, Detection2D as ROSDetection2D, ObjectHypothesis, @@ -100,9 +100,9 @@ def to_repr_dict(self) -> dict[str, Any]: def center_to_3d( self, pixel: tuple[int, int], - camera_info: CameraInfo, + camera_info: CameraInfo, # type: ignore[name-defined] assumed_depth: float = 1.0, - ) -> PoseStamped: + ) -> PoseStamped: # type: ignore[name-defined] """Unproject 2D pixel coordinates to 3D position in camera optical frame. Args: @@ -122,7 +122,7 @@ def center_to_3d( # Create 3D point at assumed depth in camera optical frame # Camera optical frame: X right, Y down, Z forward - return Vector3(x_norm * assumed_depth, y_norm * assumed_depth, assumed_depth) + return Vector3(x_norm * assumed_depth, y_norm * assumed_depth, assumed_depth) # type: ignore[name-defined] # return focused image, only on the bbox def cropped_image(self, padding: int = 20) -> Image: @@ -269,7 +269,7 @@ def to_ros_bbox(self) -> BoundingBox2D: size_y=height, ) - def lcm_encode(self): + def lcm_encode(self): # type: ignore[no-untyped-def] return self.to_image_annotations().lcm_encode() def to_text_annotation(self) -> list[TextAnnotation]: @@ -349,7 +349,7 @@ def to_image_annotations(self) -> ImageAnnotations: ) @classmethod - def from_ros_detection2d(cls, ros_det: ROSDetection2D, **kwargs) -> Detection2D: + def from_ros_detection2d(cls, ros_det: ROSDetection2D, **kwargs) -> Detection2D: # type: ignore[no-untyped-def] """Convert from ROS Detection2D message to Detection2D object.""" # Extract bbox from ROS format center_x = ros_det.bbox.center.position.x diff --git a/dimos/perception/detection/type/detection2d/imageDetections2D.py b/dimos/perception/detection/type/detection2d/imageDetections2D.py index 0c505ae2b5..10d3bebefb 100644 --- a/dimos/perception/detection/type/detection2d/imageDetections2D.py +++ b/dimos/perception/detection/type/detection2d/imageDetections2D.py @@ -21,7 +21,7 @@ from dimos.perception.detection.type.imageDetections import ImageDetections if TYPE_CHECKING: - from dimos_lcm.vision_msgs import Detection2DArray + from dimos_lcm.vision_msgs import Detection2DArray # type: ignore[import-untyped] from ultralytics.engine.results import Results from dimos.msgs.sensor_msgs import Image @@ -29,7 +29,7 @@ class ImageDetections2D(ImageDetections[Detection2D]): @classmethod - def from_ros_detection2d_array( + def from_ros_detection2d_array( # type: ignore[no-untyped-def] cls, image: Image, ros_detections: Detection2DArray, **kwargs ) -> ImageDetections2D: """Convert from ROS Detection2DArray message to ImageDetections2D object.""" @@ -42,7 +42,7 @@ def from_ros_detection2d_array( return cls(image=image, detections=detections) @classmethod - def from_ultralytics_result( + def from_ultralytics_result( # type: ignore[no-untyped-def] cls, image: Image, results: list[Results], **kwargs ) -> ImageDetections2D: """Create ImageDetections2D from ultralytics Results. diff --git a/dimos/perception/detection/type/detection2d/person.py b/dimos/perception/detection/type/detection2d/person.py index 1d84613051..7822c48129 100644 --- a/dimos/perception/detection/type/detection2d/person.py +++ b/dimos/perception/detection/type/detection2d/person.py @@ -17,8 +17,8 @@ # Import for type checking only to avoid circular imports from typing import TYPE_CHECKING -from dimos_lcm.foxglove_msgs.ImageAnnotations import PointsAnnotation, TextAnnotation -from dimos_lcm.foxglove_msgs.Point2 import Point2 +from dimos_lcm.foxglove_msgs.ImageAnnotations import PointsAnnotation, TextAnnotation # type: ignore[import-untyped] +from dimos_lcm.foxglove_msgs.Point2 import Point2 # type: ignore[import-untyped] import numpy as np from dimos.msgs.foxglove_msgs.Color import Color @@ -36,12 +36,12 @@ class Detection2DPerson(Detection2DBBox): """Represents a detected person with pose keypoints.""" # Pose keypoints - additional fields beyond Detection2DBBox - keypoints: np.ndarray # [17, 2] - x,y coordinates - keypoint_scores: np.ndarray # [17] - confidence scores + keypoints: np.ndarray # type: ignore[type-arg] # [17, 2] - x,y coordinates + keypoint_scores: np.ndarray # type: ignore[type-arg] # [17] - confidence scores # Optional normalized coordinates - bbox_normalized: np.ndarray | None = None # [x1, y1, x2, y2] in 0-1 range - keypoints_normalized: np.ndarray | None = None # [17, 2] in 0-1 range + bbox_normalized: np.ndarray | None = None # type: ignore[type-arg] # [x1, y1, x2, y2] in 0-1 range + keypoints_normalized: np.ndarray | None = None # type: ignore[type-arg] # [17, 2] in 0-1 range # Image dimensions for context image_width: int | None = None @@ -173,7 +173,7 @@ def from_yolo(cls, result: "Results", idx: int, image: Image) -> "Detection2DPer return cls.from_ultralytics_result(result, idx, image) @classmethod - def from_ros_detection2d(cls, *args, **kwargs) -> "Detection2DPerson": + def from_ros_detection2d(cls, *args, **kwargs) -> "Detection2DPerson": # type: ignore[no-untyped-def] """Conversion from ROS Detection2D is not supported for Detection2DPerson. The ROS Detection2D message format does not include keypoint data, @@ -191,7 +191,7 @@ def from_ros_detection2d(cls, *args, **kwargs) -> "Detection2DPerson": "message format that includes pose keypoints." ) - def get_keypoint(self, name: str) -> tuple[np.ndarray, float]: + def get_keypoint(self, name: str) -> tuple[np.ndarray, float]: # type: ignore[type-arg] """Get specific keypoint by name. Returns: Tuple of (xy_coordinates, confidence_score) @@ -202,7 +202,7 @@ def get_keypoint(self, name: str) -> tuple[np.ndarray, float]: idx = self.KEYPOINT_NAMES.index(name) return self.keypoints[idx], self.keypoint_scores[idx] - def get_visible_keypoints(self, threshold: float = 0.5) -> list[tuple[str, np.ndarray, float]]: + def get_visible_keypoints(self, threshold: float = 0.5) -> list[tuple[str, np.ndarray, float]]: # type: ignore[type-arg] """Get all keypoints above confidence threshold. Returns: List of tuples: (keypoint_name, xy_coordinates, confidence) diff --git a/dimos/perception/detection/type/detection3d/base.py b/dimos/perception/detection/type/detection3d/base.py index 7988c19a47..dc362fafdc 100644 --- a/dimos/perception/detection/type/detection3d/base.py +++ b/dimos/perception/detection/type/detection3d/base.py @@ -21,7 +21,7 @@ from dimos.perception.detection.type.detection2d import Detection2DBBox if TYPE_CHECKING: - from dimos_lcm.sensor_msgs import CameraInfo + from dimos_lcm.sensor_msgs import CameraInfo # type: ignore[import-untyped] from dimos.msgs.geometry_msgs import Transform diff --git a/dimos/perception/detection/type/detection3d/imageDetections3DPC.py b/dimos/perception/detection/type/detection3d/imageDetections3DPC.py index f843fb96fd..db93a206e6 100644 --- a/dimos/perception/detection/type/detection3d/imageDetections3DPC.py +++ b/dimos/perception/detection/type/detection3d/imageDetections3DPC.py @@ -14,7 +14,7 @@ from __future__ import annotations -from lcm_msgs.foxglove_msgs import SceneUpdate +from lcm_msgs.foxglove_msgs import SceneUpdate # type: ignore[import-not-found] from dimos.perception.detection.type.detection3d.pointcloud import Detection3DPC from dimos.perception.detection.type.imageDetections import ImageDetections diff --git a/dimos/perception/detection/type/detection3d/pointcloud.py b/dimos/perception/detection/type/detection3d/pointcloud.py index 56423d2f29..032a78b774 100644 --- a/dimos/perception/detection/type/detection3d/pointcloud.py +++ b/dimos/perception/detection/type/detection3d/pointcloud.py @@ -18,9 +18,9 @@ import functools from typing import TYPE_CHECKING, Any -from lcm_msgs.builtin_interfaces import Duration -from lcm_msgs.foxglove_msgs import CubePrimitive, SceneEntity, TextPrimitive -from lcm_msgs.geometry_msgs import Point, Pose, Quaternion, Vector3 as LCMVector3 +from lcm_msgs.builtin_interfaces import Duration # type: ignore[import-not-found] +from lcm_msgs.foxglove_msgs import CubePrimitive, SceneEntity, TextPrimitive # type: ignore[import-not-found] +from lcm_msgs.geometry_msgs import Point, Pose, Quaternion, Vector3 as LCMVector3 # type: ignore[import-not-found] import numpy as np from dimos.msgs.foxglove_msgs.Color import Color @@ -36,7 +36,7 @@ from dimos.types.timestamped import to_ros_stamp if TYPE_CHECKING: - from dimos_lcm.sensor_msgs import CameraInfo + from dimos_lcm.sensor_msgs import CameraInfo # type: ignore[import-untyped] from dimos.perception.detection.type.detection2d import Detection2DBBox @@ -63,11 +63,11 @@ def pose(self) -> PoseStamped: orientation=(0.0, 0.0, 0.0, 1.0), # Identity quaternion ) - def get_bounding_box(self): + def get_bounding_box(self): # type: ignore[no-untyped-def] """Get axis-aligned bounding box of the detection's pointcloud.""" return self.pointcloud.get_axis_aligned_bounding_box() - def get_oriented_bounding_box(self): + def get_oriented_bounding_box(self): # type: ignore[no-untyped-def] """Get oriented bounding box of the detection's pointcloud.""" return self.pointcloud.get_oriented_bounding_box() @@ -112,7 +112,7 @@ def to_foxglove_scene_entity(self, entity_id: str | None = None) -> SceneEntity: cube = CubePrimitive() # Get the axis-aligned bounding box - aabb = self.get_bounding_box() + aabb = self.get_bounding_box() # type: ignore[no-untyped-call] # Set pose from axis-aligned bounding box cube.pose = Pose() diff --git a/dimos/perception/detection/type/detection3d/pointcloud_filters.py b/dimos/perception/detection/type/detection3d/pointcloud_filters.py index 1c6085b690..4bd1ec1e99 100644 --- a/dimos/perception/detection/type/detection3d/pointcloud_filters.py +++ b/dimos/perception/detection/type/detection3d/pointcloud_filters.py @@ -16,7 +16,7 @@ from collections.abc import Callable -from dimos_lcm.sensor_msgs import CameraInfo +from dimos_lcm.sensor_msgs import CameraInfo # type: ignore[import-untyped] from dimos.msgs.geometry_msgs import Transform from dimos.msgs.sensor_msgs import PointCloud2 diff --git a/dimos/perception/detection/type/imageDetections.py b/dimos/perception/detection/type/imageDetections.py index 5ea2b61e45..5f919267dc 100644 --- a/dimos/perception/detection/type/imageDetections.py +++ b/dimos/perception/detection/type/imageDetections.py @@ -16,7 +16,7 @@ from typing import TYPE_CHECKING, Generic, TypeVar -from dimos_lcm.vision_msgs import Detection2DArray +from dimos_lcm.vision_msgs import Detection2DArray # type: ignore[import-untyped] from dimos.msgs.foxglove_msgs import ImageAnnotations from dimos.msgs.std_msgs import Header @@ -53,10 +53,10 @@ def __init__(self, image: Image, detections: list[T] | None = None) -> None: def __len__(self) -> int: return len(self.detections) - def __iter__(self) -> Iterator: + def __iter__(self) -> Iterator: # type: ignore[type-arg] return iter(self.detections) - def __getitem__(self, index): + def __getitem__(self, index): # type: ignore[no-untyped-def] return self.detections[index] def filter(self, *predicates: Callable[[T], bool]) -> ImageDetections[T]: @@ -83,11 +83,11 @@ def to_ros_detection2d_array(self) -> Detection2DArray: ) def to_foxglove_annotations(self) -> ImageAnnotations: - def flatten(xss): + def flatten(xss): # type: ignore[no-untyped-def] return [x for xs in xss for x in xs] - texts = flatten(det.to_text_annotation() for det in self.detections) - points = flatten(det.to_points_annotation() for det in self.detections) + texts = flatten(det.to_text_annotation() for det in self.detections) # type: ignore[no-untyped-call] + points = flatten(det.to_points_annotation() for det in self.detections) # type: ignore[no-untyped-call] return ImageAnnotations( texts=texts, diff --git a/dimos/perception/detection/type/utils.py b/dimos/perception/detection/type/utils.py index 89cf41b404..4de18acbdd 100644 --- a/dimos/perception/detection/type/utils.py +++ b/dimos/perception/detection/type/utils.py @@ -55,19 +55,19 @@ def __str__(self) -> str: # Create a table for detections table = Table( - title=f"{self.__class__.__name__} [{len(self.detections)} detections @ {to_timestamp(self.image.ts):.3f}]", + title=f"{self.__class__.__name__} [{len(self.detections)} detections @ {to_timestamp(self.image.ts):.3f}]", # type: ignore[attr-defined] show_header=True, show_edge=True, ) # Dynamically build columns based on the first detection's dict keys - if not self.detections: + if not self.detections: # type: ignore[attr-defined] return ( - f" {self.__class__.__name__} [0 detections @ {to_timestamp(self.image.ts):.3f}]" + f" {self.__class__.__name__} [0 detections @ {to_timestamp(self.image.ts):.3f}]" # type: ignore[attr-defined] ) # Cache all repr_dicts to avoid double computation - detection_dicts = [det.to_repr_dict() for det in self] + detection_dicts = [det.to_repr_dict() for det in self] # type: ignore[attr-defined] first_dict = detection_dicts[0] table.add_column("#", style="dim") @@ -89,9 +89,9 @@ def __str__(self) -> str: if float(d[key]) > 0.5 else "red" ) - row.append(Text(f"{d[key]}", style=conf_color)) + row.append(Text(f"{d[key]}", style=conf_color)) # type: ignore[arg-type] elif key == "points" and d.get(key) == "None": - row.append(Text(d.get(key, ""), style="dim")) + row.append(Text(d.get(key, ""), style="dim")) # type: ignore[arg-type] else: row.append(str(d.get(key, ""))) table.add_row(*row) diff --git a/dimos/perception/detection2d/utils.py b/dimos/perception/detection2d/utils.py index c44a013325..ade216dd28 100644 --- a/dimos/perception/detection2d/utils.py +++ b/dimos/perception/detection2d/utils.py @@ -18,7 +18,7 @@ import numpy as np -def filter_detections( +def filter_detections( # type: ignore[no-untyped-def] bboxes, track_ids, class_ids, @@ -93,7 +93,7 @@ def filter_detections( ) -def extract_detection_results(result, class_filter=None, name_filter=None, track_id_filter=None): +def extract_detection_results(result, class_filter=None, name_filter=None, track_id_filter=None): # type: ignore[no-untyped-def] """ Extract and optionally filter detection information from a YOLO result object. @@ -111,11 +111,11 @@ def extract_detection_results(result, class_filter=None, name_filter=None, track - confidences: list of detection confidences - names: list of class names """ - bboxes = [] - track_ids = [] - class_ids = [] - confidences = [] - names = [] + bboxes = [] # type: ignore[var-annotated] + track_ids = [] # type: ignore[var-annotated] + class_ids = [] # type: ignore[var-annotated] + confidences = [] # type: ignore[var-annotated] + names = [] # type: ignore[var-annotated] if result.boxes is None: return bboxes, track_ids, class_ids, confidences, names @@ -155,7 +155,7 @@ def extract_detection_results(result, class_filter=None, name_filter=None, track return bboxes, track_ids, class_ids, confidences, names -def plot_results( +def plot_results( # type: ignore[no-untyped-def] image, bboxes, track_ids, class_ids, confidences, names: Sequence[str], alpha: float = 0.5 ): """ @@ -208,7 +208,7 @@ def plot_results( return vis_img -def calculate_depth_from_bbox(depth_map, bbox): +def calculate_depth_from_bbox(depth_map, bbox): # type: ignore[no-untyped-def] """ Calculate the average depth of an object within a bounding box. Uses the 25th to 75th percentile range to filter outliers. @@ -245,7 +245,7 @@ def calculate_depth_from_bbox(depth_map, bbox): return None -def calculate_distance_angle_from_bbox(bbox, depth: int, camera_intrinsics): +def calculate_distance_angle_from_bbox(bbox, depth: int, camera_intrinsics): # type: ignore[no-untyped-def] """ Calculate distance and angle to object center based on bbox and depth. @@ -280,7 +280,7 @@ def calculate_distance_angle_from_bbox(bbox, depth: int, camera_intrinsics): return distance, angle -def calculate_object_size_from_bbox(bbox, depth: int, camera_intrinsics): +def calculate_object_size_from_bbox(bbox, depth: int, camera_intrinsics): # type: ignore[no-untyped-def] """ Estimate physical width and height of object in meters. diff --git a/dimos/perception/grasp_generation/grasp_generation.py b/dimos/perception/grasp_generation/grasp_generation.py index adca8dd3e0..91256356f0 100644 --- a/dimos/perception/grasp_generation/grasp_generation.py +++ b/dimos/perception/grasp_generation/grasp_generation.py @@ -19,7 +19,7 @@ import asyncio import numpy as np -import open3d as o3d +import open3d as o3d # type: ignore[import-untyped] from dimos.perception.grasp_generation.utils import parse_grasp_results from dimos.types.manipulation import ObjectData @@ -45,7 +45,7 @@ def __init__(self, server_url: str) -> None: def generate_grasps_from_objects( self, objects: list[ObjectData], full_pcd: o3d.geometry.PointCloud - ) -> list[dict]: + ) -> list[dict]: # type: ignore[type-arg] """ Generate grasps from ObjectData objects using grasp generator. @@ -74,8 +74,8 @@ def generate_grasps_from_objects( continue colors = None - if "colors_numpy" in obj and obj["colors_numpy"] is not None: - colors = obj["colors_numpy"] + if "colors_numpy" in obj and obj["colors_numpy"] is not None: # type: ignore[typeddict-item] + colors = obj["colors_numpy"] # type: ignore[typeddict-item] if isinstance(colors, np.ndarray) and colors.size > 0: if ( colors.shape[0] != points.shape[0] @@ -112,8 +112,8 @@ def generate_grasps_from_objects( return [] def _send_grasp_request_sync( - self, points: np.ndarray, colors: np.ndarray | None - ) -> list[dict] | None: + self, points: np.ndarray, colors: np.ndarray | None # type: ignore[type-arg] + ) -> list[dict] | None: # type: ignore[type-arg] """Send synchronous grasp request to grasp server.""" try: @@ -148,8 +148,8 @@ def _send_grasp_request_sync( return None async def _async_grasp_request( - self, points: np.ndarray, colors: np.ndarray - ) -> list[dict] | None: + self, points: np.ndarray, colors: np.ndarray # type: ignore[type-arg] + ) -> list[dict] | None: # type: ignore[type-arg] """Async grasp request helper.""" import json @@ -184,7 +184,7 @@ async def _async_grasp_request( logger.error(f"Async grasp request failed: {e}") return None - def _convert_grasp_format(self, grasps: list[dict]) -> list[dict]: + def _convert_grasp_format(self, grasps: list[dict]) -> list[dict]: # type: ignore[type-arg] """Convert Dimensional Grasp format to visualization format.""" converted = [] @@ -207,7 +207,7 @@ def _convert_grasp_format(self, grasps: list[dict]) -> list[dict]: converted.sort(key=lambda x: x["score"], reverse=True) return converted - def _rotation_matrix_to_euler(self, rotation_matrix: np.ndarray) -> dict[str, float]: + def _rotation_matrix_to_euler(self, rotation_matrix: np.ndarray) -> dict[str, float]: # type: ignore[type-arg] """Convert rotation matrix to Euler angles (in radians).""" sy = np.sqrt(rotation_matrix[0, 0] ** 2 + rotation_matrix[1, 0] ** 2) diff --git a/dimos/perception/grasp_generation/utils.py b/dimos/perception/grasp_generation/utils.py index d83d02e596..ea30c4911d 100644 --- a/dimos/perception/grasp_generation/utils.py +++ b/dimos/perception/grasp_generation/utils.py @@ -16,13 +16,13 @@ import cv2 import numpy as np -import open3d as o3d +import open3d as o3d # type: ignore[import-untyped] from dimos.perception.common.utils import project_3d_points_to_2d def create_gripper_geometry( - grasp_data: dict, + grasp_data: dict, # type: ignore[type-arg] finger_length: float = 0.08, finger_thickness: float = 0.004, ) -> list[o3d.geometry.TriangleMesh]: @@ -146,7 +146,7 @@ def create_gripper_geometry( def create_all_gripper_geometries( - grasp_list: list[dict], max_grasps: int = -1 + grasp_list: list[dict], max_grasps: int = -1 # type: ignore[type-arg] ) -> list[o3d.geometry.TriangleMesh]: """ Create gripper geometries for multiple grasps. @@ -170,13 +170,13 @@ def create_all_gripper_geometries( def draw_grasps_on_image( - image: np.ndarray, - grasp_data: dict | dict[int | str, list[dict]] | list[dict], - camera_intrinsics: list[float] | np.ndarray, # [fx, fy, cx, cy] or 3x3 matrix + image: np.ndarray, # type: ignore[type-arg] + grasp_data: dict | dict[int | str, list[dict]] | list[dict], # type: ignore[type-arg] + camera_intrinsics: list[float] | np.ndarray, # type: ignore[type-arg] # [fx, fy, cx, cy] or 3x3 matrix max_grasps: int = -1, # -1 means show all grasps finger_length: float = 0.08, # Match 3D gripper finger_thickness: float = 0.004, # Match 3D gripper -) -> np.ndarray: +) -> np.ndarray: # type: ignore[type-arg] """ Draw fork-like gripper visualizations on the image matching 3D gripper design. @@ -224,7 +224,7 @@ def draw_grasps_on_image( grasps_to_draw = grasps_to_draw[:max_grasps] # Define grasp colors (solid red to match 3D design) - def get_grasp_color(index: int) -> tuple: + def get_grasp_color(index: int) -> tuple: # type: ignore[type-arg] # Use solid red color for all grasps to match 3D design return (0, 0, 255) # Red in BGR format for OpenCV @@ -256,22 +256,22 @@ def get_grasp_color(index: int) -> tuple: left_finger_points = np.array( [ [ - width / 2 - finger_width / 2, + width / 2 - finger_width / 2, # type: ignore[operator] -finger_length, -finger_thickness / 2, ], # Back left [ - width / 2 + finger_width / 2, + width / 2 + finger_width / 2, # type: ignore[operator] -finger_length, -finger_thickness / 2, ], # Back right [ - width / 2 + finger_width / 2, + width / 2 + finger_width / 2, # type: ignore[operator] 0, -finger_thickness / 2, ], # Front right (at origin) [ - width / 2 - finger_width / 2, + width / 2 - finger_width / 2, # type: ignore[operator] 0, -finger_thickness / 2, ], # Front left (at origin) @@ -282,22 +282,22 @@ def get_grasp_color(index: int) -> tuple: right_finger_points = np.array( [ [ - -width / 2 - finger_width / 2, + -width / 2 - finger_width / 2, # type: ignore[operator] -finger_length, -finger_thickness / 2, ], # Back left [ - -width / 2 + finger_width / 2, + -width / 2 + finger_width / 2, # type: ignore[operator] -finger_length, -finger_thickness / 2, ], # Back right [ - -width / 2 + finger_width / 2, + -width / 2 + finger_width / 2, # type: ignore[operator] 0, -finger_thickness / 2, ], # Front right (at origin) [ - -width / 2 - finger_width / 2, + -width / 2 - finger_width / 2, # type: ignore[operator] 0, -finger_thickness / 2, ], # Front left (at origin) @@ -308,22 +308,22 @@ def get_grasp_color(index: int) -> tuple: base_points = np.array( [ [ - -width / 2 - finger_width / 2, + -width / 2 - finger_width / 2, # type: ignore[operator] -finger_length - finger_thickness, -finger_thickness / 2, ], # Back left [ - width / 2 + finger_width / 2, + width / 2 + finger_width / 2, # type: ignore[operator] -finger_length - finger_thickness, -finger_thickness / 2, ], # Back right [ - width / 2 + finger_width / 2, + width / 2 + finger_width / 2, # type: ignore[operator] -finger_length, -finger_thickness / 2, ], # Front right [ - -width / 2 - finger_width / 2, + -width / 2 - finger_width / 2, # type: ignore[operator] -finger_length, -finger_thickness / 2, ], # Front left @@ -357,15 +357,15 @@ def get_grasp_color(index: int) -> tuple: ) # Transform all points to world frame - def transform_points(points): + def transform_points(points): # type: ignore[no-untyped-def] # Apply rotation and translation world_points = (rotation_matrix @ points.T).T + translation return world_points - left_finger_world = transform_points(left_finger_points) - right_finger_world = transform_points(right_finger_points) - base_world = transform_points(base_points) - handle_world = transform_points(handle_points) + left_finger_world = transform_points(left_finger_points) # type: ignore[no-untyped-call] + right_finger_world = transform_points(right_finger_points) # type: ignore[no-untyped-call] + base_world = transform_points(base_points) # type: ignore[no-untyped-call] + handle_world = transform_points(handle_points) # type: ignore[no-untyped-call] # Project to 2D left_finger_2d = project_3d_points_to_2d(left_finger_world, camera_matrix) @@ -400,7 +400,7 @@ def transform_points(points): return result -def get_standard_coordinate_transform(): +def get_standard_coordinate_transform(): # type: ignore[no-untyped-def] """ Get a standard coordinate transformation matrix for consistent visualization. @@ -426,7 +426,7 @@ def get_standard_coordinate_transform(): def visualize_grasps_3d( point_cloud: o3d.geometry.PointCloud, - grasp_list: list[dict], + grasp_list: list[dict], # type: ignore[type-arg] max_grasps: int = -1, ) -> None: """ @@ -438,7 +438,7 @@ def visualize_grasps_3d( max_grasps: Maximum number of grasps to visualize """ # Apply standard coordinate transformation - transform = get_standard_coordinate_transform() + transform = get_standard_coordinate_transform() # type: ignore[no-untyped-call] # Transform point cloud pc_copy = o3d.geometry.PointCloud(point_cloud) @@ -459,7 +459,7 @@ def visualize_grasps_3d( o3d.visualization.draw_geometries(geometries, window_name="3D Grasp Visualization") -def parse_grasp_results(grasps: list[dict]) -> list[dict]: +def parse_grasp_results(grasps: list[dict]) -> list[dict]: # type: ignore[type-arg] """ Parse grasp results into visualization format. @@ -499,10 +499,10 @@ def parse_grasp_results(grasps: list[dict]) -> list[dict]: def create_grasp_overlay( - rgb_image: np.ndarray, - grasps: list[dict], - camera_intrinsics: list[float] | np.ndarray, -) -> np.ndarray: + rgb_image: np.ndarray, # type: ignore[type-arg] + grasps: list[dict], # type: ignore[type-arg] + camera_intrinsics: list[float] | np.ndarray, # type: ignore[type-arg] +) -> np.ndarray: # type: ignore[type-arg] """ Create grasp visualization overlay on RGB image. diff --git a/dimos/perception/object_detection_stream.py b/dimos/perception/object_detection_stream.py index a82cbe9db5..a72824fdf0 100644 --- a/dimos/perception/object_detection_stream.py +++ b/dimos/perception/object_detection_stream.py @@ -16,10 +16,10 @@ import numpy as np from reactivex import Observable, operators as ops -from dimos.perception.detection2d.yolo_2d_det import Yolo2DDetector +from dimos.perception.detection2d.yolo_2d_det import Yolo2DDetector # type: ignore[import-untyped] try: - from dimos.perception.detection2d.detic_2d_det import Detic2DDetector + from dimos.perception.detection2d.detic_2d_det import Detic2DDetector # type: ignore[import-untyped] DETIC_AVAILABLE = True except (ModuleNotFoundError, ImportError): @@ -30,14 +30,14 @@ from dimos.models.depth.metric3d import Metric3D from dimos.perception.common.utils import draw_object_detection_visualization -from dimos.perception.detection2d.utils import ( +from dimos.perception.detection2d.utils import ( # type: ignore[attr-defined] calculate_depth_from_bbox, calculate_object_size_from_bbox, calculate_position_rotation_from_bbox, ) from dimos.types.vector import Vector from dimos.utils.logging_config import setup_logger -from dimos.utils.transform_utils import transform_robot_to_map +from dimos.utils.transform_utils import transform_robot_to_map # type: ignore[attr-defined] if TYPE_CHECKING: from dimos.types.manipulation import ObjectData @@ -58,16 +58,16 @@ class ObjectDetectionStream: Provides a stream of structured object data with position and rotation information. """ - def __init__( + def __init__( # type: ignore[no-untyped-def] self, camera_intrinsics=None, # [fx, fy, cx, cy] device: str = "cuda", gt_depth_scale: float = 1000.0, min_confidence: float = 0.7, class_filter=None, # Optional list of class names to filter (e.g., ["person", "car"]) - get_pose: Callable | None = None, # Optional function to transform coordinates to map frame + get_pose: Callable | None = None, # type: ignore[type-arg] # Optional function to transform coordinates to map frame detector: Detic2DDetector | Yolo2DDetector | None = None, - video_stream: Observable = None, + video_stream: Observable = None, # type: ignore[assignment, type-arg] disable_depth: bool = False, # Flag to disable monocular Metric3D depth estimation draw_masks: bool = False, # Flag to enable drawing segmentation masks ) -> None: @@ -117,7 +117,7 @@ def __init__( self.depth_model = Metric3D(gt_depth_scale) if camera_intrinsics is not None: - self.depth_model.update_intrinsic(camera_intrinsics) + self.depth_model.update_intrinsic(camera_intrinsics) # type: ignore[no-untyped-call] # Create 3x3 camera matrix for calculations fx, fy, cx, cy = camera_intrinsics @@ -141,7 +141,7 @@ def __init__( if video_stream is not None: self.stream = self.create_stream(video_stream) - def create_stream(self, video_stream: Observable) -> Observable: + def create_stream(self, video_stream: Observable) -> Observable: # type: ignore[type-arg] """ Create an Observable stream of object data from a video stream. @@ -153,17 +153,17 @@ def create_stream(self, video_stream: Observable) -> Observable: with position and rotation information """ - def process_frame(frame): + def process_frame(frame): # type: ignore[no-untyped-def] # TODO: More modular detector output interface - bboxes, track_ids, class_ids, confidences, names, *mask_data = ( + bboxes, track_ids, class_ids, confidences, names, *mask_data = ( # type: ignore[misc] *self.detector.process_image(frame), [], ) masks = ( - mask_data[0] - if mask_data and len(mask_data[0]) == len(bboxes) - else [None] * len(bboxes) + mask_data[0] # type: ignore[has-type] + if mask_data and len(mask_data[0]) == len(bboxes) # type: ignore[has-type] + else [None] * len(bboxes) # type: ignore[has-type] ) # Create visualization @@ -172,24 +172,24 @@ def process_frame(frame): # Process detections objects = [] if not self.disable_depth: - depth_map = self.depth_model.infer_depth(frame) + depth_map = self.depth_model.infer_depth(frame) # type: ignore[union-attr] depth_map = np.array(depth_map) else: depth_map = None - for i, bbox in enumerate(bboxes): + for i, bbox in enumerate(bboxes): # type: ignore[has-type] # Skip if confidence is too low - if i < len(confidences) and confidences[i] < self.min_confidence: + if i < len(confidences) and confidences[i] < self.min_confidence: # type: ignore[has-type] continue # Skip if class filter is active and class not in filter - class_name = names[i] if i < len(names) else None + class_name = names[i] if i < len(names) else None # type: ignore[has-type] if self.class_filter and class_name not in self.class_filter: continue if not self.disable_depth and depth_map is not None: # Get depth for this object - depth = calculate_depth_from_bbox(depth_map, bbox) + depth = calculate_depth_from_bbox(depth_map, bbox) # type: ignore[no-untyped-call] if depth is None: # Skip objects with invalid depth continue @@ -216,19 +216,19 @@ def process_frame(frame): else: depth = -1 - position = Vector(0, 0, 0) - rotation = Vector(0, 0, 0) + position = Vector(0, 0, 0) # type: ignore[arg-type] + rotation = Vector(0, 0, 0) # type: ignore[arg-type] width = -1 height = -1 # Create a properly typed ObjectData instance object_data: ObjectData = { - "object_id": track_ids[i] if i < len(track_ids) else -1, + "object_id": track_ids[i] if i < len(track_ids) else -1, # type: ignore[has-type] "bbox": bbox, "depth": depth, - "confidence": confidences[i] if i < len(confidences) else None, - "class_id": class_ids[i] if i < len(class_ids) else None, - "label": class_name, + "confidence": confidences[i] if i < len(confidences) else None, # type: ignore[has-type, typeddict-item] + "class_id": class_ids[i] if i < len(class_ids) else None, # type: ignore[has-type, typeddict-item] + "label": class_name, # type: ignore[typeddict-item] "position": position, "rotation": rotation, "size": {"width": width, "height": height}, @@ -248,7 +248,7 @@ def process_frame(frame): return self.stream - def get_stream(self): + def get_stream(self): # type: ignore[no-untyped-def] """ Returns the current detection stream if available. Creates a new one with the provided video_stream if not already created. @@ -262,7 +262,7 @@ def get_stream(self): ) return self.stream - def get_formatted_stream(self): + def get_formatted_stream(self): # type: ignore[no-untyped-def] """ Returns a formatted stream of object detection data for better readability. This is especially useful for LLMs like Claude that need structured text input. @@ -275,7 +275,7 @@ def get_formatted_stream(self): "Stream not initialized. Either provide a video_stream during initialization or call create_stream first." ) - def format_detection_data(result): + def format_detection_data(result): # type: ignore[no-untyped-def] # Extract objects from result objects = result.get("objects", []) diff --git a/dimos/perception/object_tracker.py b/dimos/perception/object_tracker.py index f5fa48581a..5a7bad00fa 100644 --- a/dimos/perception/object_tracker.py +++ b/dimos/perception/object_tracker.py @@ -16,10 +16,10 @@ import time import cv2 -from dimos_lcm.sensor_msgs import CameraInfo +from dimos_lcm.sensor_msgs import CameraInfo # type: ignore[import-untyped] # Import LCM messages -from dimos_lcm.vision_msgs import ( +from dimos_lcm.vision_msgs import ( # type: ignore[import-untyped] Detection2D, Detection3D, ObjectHypothesisWithPose, @@ -49,14 +49,14 @@ class ObjectTracking(Module): """Module for object tracking with LCM input/output.""" # LCM inputs - color_image: In[Image] = None - depth: In[Image] = None - camera_info: In[CameraInfo] = None + color_image: In[Image] = None # type: ignore[assignment] + depth: In[Image] = None # type: ignore[assignment] + camera_info: In[CameraInfo] = None # type: ignore[assignment] # LCM outputs - detection2darray: Out[Detection2DArray] = None - detection3darray: Out[Detection3DArray] = None - tracked_overlay: Out[Image] = None # Visualization output + detection2darray: Out[Detection2DArray] = None # type: ignore[assignment] + detection3darray: Out[Detection3DArray] = None # type: ignore[assignment] + tracked_overlay: Out[Image] = None # type: ignore[assignment] # Visualization output def __init__( self, @@ -86,12 +86,12 @@ def __init__( self.tracker = None self.tracking_bbox = None # Stores (x, y, w, h) for tracker initialization self.tracking_initialized = False - self.orb = cv2.ORB_create() + self.orb = cv2.ORB_create() # type: ignore[attr-defined] self.bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=False) self.original_des = None # Store original ORB descriptors self.original_kps = None # Store original ORB keypoints self.reid_fail_count = 0 # Counter for consecutive re-id failures - self.last_good_matches = [] # Store good matches for visualization + self.last_good_matches = [] # type: ignore[var-annotated] # Store good matches for visualization self.last_roi_kps = None # Store last ROI keypoints for visualization self.last_roi_bbox = None # Store last ROI bbox for visualization self.reid_confirmed = False # Store current reid confirmation state @@ -99,8 +99,8 @@ def __init__( self.reid_warmup_frames = 3 # Number of frames before REID starts self._frame_lock = threading.Lock() - self._latest_rgb_frame: np.ndarray | None = None - self._latest_depth_frame: np.ndarray | None = None + self._latest_rgb_frame: np.ndarray | None = None # type: ignore[type-arg] + self._latest_depth_frame: np.ndarray | None = None # type: ignore[type-arg] self._latest_camera_info: CameraInfo | None = None # Tracking thread control @@ -122,7 +122,7 @@ def start(self) -> None: super().start() # Subscribe to aligned rgb and depth streams - def on_aligned_frames(frames_tuple) -> None: + def on_aligned_frames(frames_tuple) -> None: # type: ignore[no-untyped-def] rgb_msg, depth_msg = frames_tuple with self._frame_lock: self._latest_rgb_frame = rgb_msg.data @@ -135,8 +135,8 @@ def on_aligned_frames(frames_tuple) -> None: # Create aligned observable for RGB and depth aligned_frames = align_timestamped( - self.color_image.observable(), - self.depth.observable(), + self.color_image.observable(), # type: ignore[no-untyped-call] + self.depth.observable(), # type: ignore[no-untyped-call] buffer_size=2.0, # 2 second buffer match_tolerance=0.5, # 500ms tolerance ) @@ -148,15 +148,15 @@ def on_camera_info(camera_info_msg: CameraInfo) -> None: self._latest_camera_info = camera_info_msg # Extract intrinsics from camera info K matrix # K is a 3x3 matrix in row-major order: [fx, 0, cx, 0, fy, cy, 0, 0, 1] - self.camera_intrinsics = [ + self.camera_intrinsics = [ # type: ignore[assignment] camera_info_msg.K[0], camera_info_msg.K[4], camera_info_msg.K[2], camera_info_msg.K[5], ] - unsub = self.camera_info.subscribe(on_camera_info) - self._disposables.add(Disposable(unsub)) + unsub = self.camera_info.subscribe(on_camera_info) # type: ignore[assignment] + self._disposables.add(Disposable(unsub)) # type: ignore[arg-type] @rpc def stop(self) -> None: @@ -173,7 +173,7 @@ def stop(self) -> None: def track( self, bbox: list[float], - ) -> dict: + ) -> dict: # type: ignore[type-arg] """ Initialize tracking with a bounding box and process current frame. @@ -193,15 +193,15 @@ def track( logger.warning(f"Invalid initial bbox provided: {bbox}. Tracking not started.") # Set tracking parameters - self.tracking_bbox = (x1, y1, w, h) # Store in (x, y, w, h) format - self.tracker = cv2.legacy.TrackerCSRT_create() + self.tracking_bbox = (x1, y1, w, h) # type: ignore[assignment] # Store in (x, y, w, h) format + self.tracker = cv2.legacy.TrackerCSRT_create() # type: ignore[attr-defined] self.tracking_initialized = False self.original_des = None self.reid_fail_count = 0 logger.info(f"Tracking target set with bbox: {self.tracking_bbox}") # Extract initial features - roi = self._latest_rgb_frame[y1:y2, x1:x2] + roi = self._latest_rgb_frame[y1:y2, x1:x2] # type: ignore[index] if roi.size > 0: self.original_kps, self.original_des = self.orb.detectAndCompute(roi, None) if self.original_des is None: @@ -210,7 +210,7 @@ def track( logger.info(f"Initial ORB features extracted: {len(self.original_des)}") # Initialize the tracker - init_success = self.tracker.init(self._latest_rgb_frame, self.tracking_bbox) + init_success = self.tracker.init(self._latest_rgb_frame, self.tracking_bbox) # type: ignore[attr-defined] if init_success: self.tracking_initialized = True self.tracking_frame_count = 0 # Reset frame counter @@ -228,7 +228,7 @@ def track( # Return initial tracking result return {"status": "tracking_started", "bbox": self.tracking_bbox} - def reid(self, frame, current_bbox) -> bool: + def reid(self, frame, current_bbox) -> bool: # type: ignore[no-untyped-def] """Check if features in current_bbox match stored original features.""" # During warm-up period, always return True if self.tracking_frame_count < self.reid_warmup_frames: @@ -307,8 +307,8 @@ def _reset_tracking_state(self) -> None: self._latest_detection2d = empty_2d self._latest_detection3d = empty_3d self._detection_event.clear() - self.detection2darray.publish(empty_2d) - self.detection3darray.publish(empty_3d) + self.detection2darray.publish(empty_2d) # type: ignore[no-untyped-call] + self.detection3darray.publish(empty_3d) # type: ignore[no-untyped-call] @rpc def stop_track(self) -> bool: @@ -546,20 +546,20 @@ def _process_tracking(self) -> None: viz_msg = Image.from_numpy(viz_image) self.tracked_overlay.publish(viz_msg) - def _draw_reid_matches(self, image: np.ndarray) -> np.ndarray: + def _draw_reid_matches(self, image: np.ndarray) -> np.ndarray: # type: ignore[type-arg] """Draw REID feature matches on the image.""" viz_image = image.copy() - x1, y1, _x2, _y2 = self.last_roi_bbox + x1, y1, _x2, _y2 = self.last_roi_bbox # type: ignore[misc] # Draw keypoints from current ROI in green - for kp in self.last_roi_kps: - pt = (int(kp.pt[0] + x1), int(kp.pt[1] + y1)) + for kp in self.last_roi_kps: # type: ignore[attr-defined] + pt = (int(kp.pt[0] + x1), int(kp.pt[1] + y1)) # type: ignore[has-type] cv2.circle(viz_image, pt, 3, (0, 255, 0), -1) for match in self.last_good_matches: - current_kp = self.last_roi_kps[match.trainIdx] - pt_current = (int(current_kp.pt[0] + x1), int(current_kp.pt[1] + y1)) + current_kp = self.last_roi_kps[match.trainIdx] # type: ignore[index] + pt_current = (int(current_kp.pt[0] + x1), int(current_kp.pt[1] + y1)) # type: ignore[has-type] # Draw a larger circle for matched points in yellow cv2.circle(viz_image, pt_current, 5, (0, 255, 255), 2) # Yellow for matched points @@ -590,7 +590,7 @@ def _draw_reid_matches(self, image: np.ndarray) -> np.ndarray: return viz_image - def _get_depth_from_bbox(self, bbox: list[int], depth_frame: np.ndarray) -> float | None: + def _get_depth_from_bbox(self, bbox: list[int], depth_frame: np.ndarray) -> float | None: # type: ignore[type-arg] """Calculate depth from bbox using the 25th percentile of closest points. Args: diff --git a/dimos/perception/object_tracker_2d.py b/dimos/perception/object_tracker_2d.py index 0256b7beb9..dbc7a5f772 100644 --- a/dimos/perception/object_tracker_2d.py +++ b/dimos/perception/object_tracker_2d.py @@ -19,7 +19,7 @@ import cv2 # Import LCM messages -from dimos_lcm.vision_msgs import ( +from dimos_lcm.vision_msgs import ( # type: ignore[import-untyped] BoundingBox2D, Detection2D, ObjectHypothesis, @@ -42,10 +42,10 @@ class ObjectTracker2D(Module): """Pure 2D object tracking module using OpenCV's CSRT tracker.""" - color_image: In[Image] = None + color_image: In[Image] = None # type: ignore[assignment] - detection2darray: Out[Detection2DArray] = None - tracked_overlay: Out[Image] = None # Visualization output + detection2darray: Out[Detection2DArray] = None # type: ignore[assignment] + tracked_overlay: Out[Image] = None # type: ignore[assignment] # Visualization output def __init__( self, @@ -73,7 +73,7 @@ def __init__( # Frame management self._frame_lock = threading.Lock() - self._latest_rgb_frame: np.ndarray | None = None + self._latest_rgb_frame: np.ndarray | None = None # type: ignore[type-arg] self._frame_arrival_time: float | None = None # Tracking thread control @@ -109,7 +109,7 @@ def stop(self) -> None: super().stop() @rpc - def track(self, bbox: list[float]) -> dict: + def track(self, bbox: list[float]) -> dict: # type: ignore[type-arg] """ Initialize tracking with a bounding box. @@ -130,14 +130,14 @@ def track(self, bbox: list[float]) -> dict: logger.warning(f"Invalid initial bbox provided: {bbox}. Tracking not started.") return {"status": "invalid_bbox"} - self.tracking_bbox = (x1, y1, w, h) - self.tracker = cv2.legacy.TrackerCSRT_create() + self.tracking_bbox = (x1, y1, w, h) # type: ignore[assignment] + self.tracker = cv2.legacy.TrackerCSRT_create() # type: ignore[attr-defined] self.tracking_initialized = False logger.info(f"Tracking target set with bbox: {self.tracking_bbox}") # Convert RGB to BGR for CSRT (OpenCV expects BGR) frame_bgr = cv2.cvtColor(self._latest_rgb_frame, cv2.COLOR_RGB2BGR) - init_success = self.tracker.init(frame_bgr, self.tracking_bbox) + init_success = self.tracker.init(frame_bgr, self.tracking_bbox) # type: ignore[attr-defined] if init_success: self.tracking_initialized = True logger.info("Tracker initialized successfully.") @@ -178,7 +178,7 @@ def _reset_tracking_state(self) -> None: detections_length=0, header=Header(time.time(), self.frame_id), detections=[] ) self._latest_detection2d = empty_2d - self.detection2darray.publish(empty_2d) + self.detection2darray.publish(empty_2d) # type: ignore[no-untyped-call] @rpc def stop_track(self) -> bool: @@ -290,7 +290,7 @@ def _process_tracking(self) -> None: viz_msg = Image.from_numpy(viz_copy, format=ImageFormat.RGB) self.tracked_overlay.publish(viz_msg) - def _draw_visualization(self, image: np.ndarray, bbox: list[int]) -> np.ndarray: + def _draw_visualization(self, image: np.ndarray, bbox: list[int]) -> np.ndarray: # type: ignore[type-arg] """Draw tracking visualization.""" viz_image = image.copy() x1, y1, x2, y2 = bbox diff --git a/dimos/perception/object_tracker_3d.py b/dimos/perception/object_tracker_3d.py index 231ae26748..e19d1c55e3 100644 --- a/dimos/perception/object_tracker_3d.py +++ b/dimos/perception/object_tracker_3d.py @@ -14,8 +14,8 @@ # Import LCM messages -from dimos_lcm.sensor_msgs import CameraInfo -from dimos_lcm.vision_msgs import Detection3D, ObjectHypothesisWithPose +from dimos_lcm.sensor_msgs import CameraInfo # type: ignore[import-untyped] +from dimos_lcm.vision_msgs import Detection3D, ObjectHypothesisWithPose # type: ignore[import-untyped] import numpy as np from dimos.core import In, Out, rpc @@ -41,13 +41,13 @@ class ObjectTracker3D(ObjectTracker2D): """3D object tracking module extending ObjectTracker2D with depth capabilities.""" # Additional inputs (2D tracker already has color_image) - depth: In[Image] = None - camera_info: In[CameraInfo] = None + depth: In[Image] = None # type: ignore[assignment] + camera_info: In[CameraInfo] = None # type: ignore[assignment] # Additional outputs (2D tracker already has detection2darray and tracked_overlay) - detection3darray: Out[Detection3DArray] = None + detection3darray: Out[Detection3DArray] = None # type: ignore[assignment] - def __init__(self, **kwargs) -> None: + def __init__(self, **kwargs) -> None: # type: ignore[no-untyped-def] """ Initialize 3D object tracking module. @@ -58,7 +58,7 @@ def __init__(self, **kwargs) -> None: # Additional state for 3D tracking self.camera_intrinsics = None - self._latest_depth_frame: np.ndarray | None = None + self._latest_depth_frame: np.ndarray | None = None # type: ignore[type-arg] self._latest_camera_info: CameraInfo | None = None # TF publisher for tracked object @@ -72,7 +72,7 @@ def start(self) -> None: super().start() # Subscribe to aligned RGB and depth streams - def on_aligned_frames(frames_tuple) -> None: + def on_aligned_frames(frames_tuple) -> None: # type: ignore[no-untyped-def] rgb_msg, depth_msg = frames_tuple with self._frame_lock: self._latest_rgb_frame = rgb_msg.data @@ -85,8 +85,8 @@ def on_aligned_frames(frames_tuple) -> None: # Create aligned observable for RGB and depth aligned_frames = align_timestamped( - self.color_image.observable(), - self.depth.observable(), + self.color_image.observable(), # type: ignore[no-untyped-call] + self.depth.observable(), # type: ignore[no-untyped-call] buffer_size=2.0, # 2 second buffer match_tolerance=0.5, # 500ms tolerance ) @@ -97,7 +97,7 @@ def on_aligned_frames(frames_tuple) -> None: def on_camera_info(camera_info_msg: CameraInfo) -> None: self._latest_camera_info = camera_info_msg # Extract intrinsics: K is [fx, 0, cx, 0, fy, cy, 0, 0, 1] - self.camera_intrinsics = [ + self.camera_intrinsics = [ # type: ignore[assignment] camera_info_msg.K[0], camera_info_msg.K[4], camera_info_msg.K[2], @@ -174,17 +174,17 @@ def _create_detection3d_from_2d(self, detection2d: Detection2DArray) -> Detectio y2 = int(center_y + height / 2) # Get depth value - depth_value = self._get_depth_from_bbox([x1, y1, x2, y2], self._latest_depth_frame) + depth_value = self._get_depth_from_bbox([x1, y1, x2, y2], self._latest_depth_frame) # type: ignore[arg-type] if depth_value is None or depth_value <= 0: return None - fx, fy, cx, cy = self.camera_intrinsics + fx, fy, cx, cy = self.camera_intrinsics # type: ignore[misc] # Convert pixel coordinates to 3D in optical frame z_optical = depth_value - x_optical = (center_x - cx) * z_optical / fx - y_optical = (center_y - cy) * z_optical / fy + x_optical = (center_x - cx) * z_optical / fx # type: ignore[has-type] + y_optical = (center_y - cy) * z_optical / fy # type: ignore[has-type] # Create pose in optical frame optical_pose = Pose() @@ -200,8 +200,8 @@ def _create_detection3d_from_2d(self, detection2d: Detection2DArray) -> Detectio robot_pose.orientation = euler_to_quaternion(euler) # Estimate object size in meters - size_x = width * z_optical / fx - size_y = height * z_optical / fy + size_x = width * z_optical / fx # type: ignore[has-type] + size_y = height * z_optical / fy # type: ignore[has-type] size_z = 0.1 # Default depth size # Create Detection3D @@ -240,7 +240,7 @@ def _create_detection3d_from_2d(self, detection2d: Detection2DArray) -> Detectio return detection3darray - def _get_depth_from_bbox(self, bbox: list[int], depth_frame: np.ndarray) -> float | None: + def _get_depth_from_bbox(self, bbox: list[int], depth_frame: np.ndarray) -> float | None: # type: ignore[type-arg] """ Calculate depth from bbox using the 25th percentile of closest points. @@ -273,21 +273,21 @@ def _get_depth_from_bbox(self, bbox: list[int], depth_frame: np.ndarray) -> floa return None - def _draw_reid_overlay(self, image: np.ndarray) -> np.ndarray: + def _draw_reid_overlay(self, image: np.ndarray) -> np.ndarray: # type: ignore[type-arg] """Draw Re-ID feature matches on visualization.""" import cv2 viz_image = image.copy() - x1, y1, _x2, _y2 = self.last_roi_bbox + x1, y1, _x2, _y2 = self.last_roi_bbox # type: ignore[attr-defined] # Draw keypoints - for kp in self.last_roi_kps: + for kp in self.last_roi_kps: # type: ignore[attr-defined] pt = (int(kp.pt[0] + x1), int(kp.pt[1] + y1)) cv2.circle(viz_image, pt, 3, (0, 255, 0), -1) # Draw matches - for match in self.last_good_matches: - current_kp = self.last_roi_kps[match.trainIdx] + for match in self.last_good_matches: # type: ignore[attr-defined] + current_kp = self.last_roi_kps[match.trainIdx] # type: ignore[attr-defined] pt_current = (int(current_kp.pt[0] + x1), int(current_kp.pt[1] + y1)) cv2.circle(viz_image, pt_current, 5, (0, 255, 255), 2) @@ -295,7 +295,7 @@ def _draw_reid_overlay(self, image: np.ndarray) -> np.ndarray: cv2.circle(viz_image, pt_current, 2, (intensity, intensity, 255), -1) # Draw match count - text = f"REID: {len(self.last_good_matches)}/{len(self.last_roi_kps)}" + text = f"REID: {len(self.last_good_matches)}/{len(self.last_roi_kps)}" # type: ignore[attr-defined] cv2.putText(viz_image, text, (10, 90), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2) return viz_image diff --git a/dimos/perception/person_tracker.py b/dimos/perception/person_tracker.py index 16b505578b..e6decf39b7 100644 --- a/dimos/perception/person_tracker.py +++ b/dimos/perception/person_tracker.py @@ -22,7 +22,7 @@ from dimos.msgs.sensor_msgs import Image from dimos.perception.common.ibvs import PersonDistanceEstimator from dimos.perception.detection2d.utils import filter_detections -from dimos.perception.detection2d.yolo_2d_det import Yolo2DDetector +from dimos.perception.detection2d.yolo_2d_det import Yolo2DDetector # type: ignore[import-untyped] from dimos.utils.logging_config import setup_logger logger = setup_logger("dimos.perception.person_tracker") @@ -32,12 +32,12 @@ class PersonTrackingStream(Module): """Module for person tracking with LCM input/output.""" # LCM inputs - video: In[Image] = None + video: In[Image] = None # type: ignore[assignment] # LCM outputs - tracking_data: Out[dict] = None + tracking_data: Out[dict] = None # type: ignore[assignment, type-arg] - def __init__( + def __init__( # type: ignore[no-untyped-def] self, camera_intrinsics=None, camera_pitch: float = 0.0, @@ -84,7 +84,7 @@ def __init__( ) # For tracking latest frame data - self._latest_frame: np.ndarray | None = None + self._latest_frame: np.ndarray | None = None # type: ignore[type-arg] self._process_interval = 0.1 # Process at 10Hz # Tracking state - starts disabled @@ -107,8 +107,8 @@ def set_video(image_msg: Image) -> None: self._disposables.add(Disposable(unsub)) # Start periodic processing - unsub = interval(self._process_interval).subscribe(lambda _: self._process_frame()) - self._disposables.add(unsub) + unsub = interval(self._process_interval).subscribe(lambda _: self._process_frame()) # type: ignore[assignment] + self._disposables.add(unsub) # type: ignore[arg-type] logger.info("PersonTracking module started and subscribed to LCM streams") @@ -126,13 +126,13 @@ def _process_frame(self) -> None: return # Process frame through tracking pipeline - result = self._process_tracking(self._latest_frame) + result = self._process_tracking(self._latest_frame) # type: ignore[no-untyped-call] # Publish result to LCM if result: - self.tracking_data.publish(result) + self.tracking_data.publish(result) # type: ignore[no-untyped-call] - def _process_tracking(self, frame): + def _process_tracking(self, frame): # type: ignore[no-untyped-def] """Process a single frame for person tracking.""" # Detect people in the frame bboxes, track_ids, class_ids, confidences, names = self.detector.process_image(frame) @@ -236,17 +236,17 @@ def is_tracking_enabled(self) -> bool: return self._tracking_enabled @rpc - def get_tracking_data(self) -> dict: + def get_tracking_data(self) -> dict: # type: ignore[type-arg] """Get the latest tracking data. Returns: Dictionary containing tracking results """ if self._latest_frame is not None: - return self._process_tracking(self._latest_frame) + return self._process_tracking(self._latest_frame) # type: ignore[no-any-return, no-untyped-call] return {"frame": None, "viz_frame": None, "targets": []} - def create_stream(self, video_stream: Observable) -> Observable: + def create_stream(self, video_stream: Observable) -> Observable: # type: ignore[type-arg] """ Create an Observable stream of person tracking results from a video stream. diff --git a/dimos/perception/pointcloud/cuboid_fit.py b/dimos/perception/pointcloud/cuboid_fit.py index 376ae08da0..1e3f451507 100644 --- a/dimos/perception/pointcloud/cuboid_fit.py +++ b/dimos/perception/pointcloud/cuboid_fit.py @@ -15,12 +15,12 @@ import cv2 import numpy as np -import open3d as o3d +import open3d as o3d # type: ignore[import-untyped] def fit_cuboid( - points: np.ndarray | o3d.geometry.PointCloud, method: str = "minimal" -) -> dict | None: + points: np.ndarray | o3d.geometry.PointCloud, method: str = "minimal" # type: ignore[type-arg] +) -> dict | None: # type: ignore[type-arg] """ Fit a cuboid to a point cloud using Open3D's built-in methods. @@ -103,7 +103,7 @@ def fit_cuboid( return None -def fit_cuboid_simple(points: np.ndarray | o3d.geometry.PointCloud) -> dict | None: +def fit_cuboid_simple(points: np.ndarray | o3d.geometry.PointCloud) -> dict | None: # type: ignore[type-arg] """ Simple wrapper for minimal oriented bounding box fitting. @@ -118,7 +118,7 @@ def fit_cuboid_simple(points: np.ndarray | o3d.geometry.PointCloud) -> dict | No def _compute_fitting_error( - points: np.ndarray, center: np.ndarray, dimensions: np.ndarray, rotation: np.ndarray + points: np.ndarray, center: np.ndarray, dimensions: np.ndarray, rotation: np.ndarray # type: ignore[type-arg] ) -> float: """ Compute fitting error as mean squared distance from points to cuboid surface. @@ -154,8 +154,8 @@ def _compute_fitting_error( def get_cuboid_corners( - center: np.ndarray, dimensions: np.ndarray, rotation: np.ndarray -) -> np.ndarray: + center: np.ndarray, dimensions: np.ndarray, rotation: np.ndarray # type: ignore[type-arg] +) -> np.ndarray: # type: ignore[type-arg] """ Get the 8 corners of a cuboid. @@ -185,19 +185,19 @@ def get_cuboid_corners( ) # Apply rotation and translation - return corners_local @ rotation.T + center + return corners_local @ rotation.T + center # type: ignore[no-any-return] def visualize_cuboid_on_image( - image: np.ndarray, - cuboid_params: dict, - camera_matrix: np.ndarray, - extrinsic_rotation: np.ndarray | None = None, - extrinsic_translation: np.ndarray | None = None, + image: np.ndarray, # type: ignore[type-arg] + cuboid_params: dict, # type: ignore[type-arg] + camera_matrix: np.ndarray, # type: ignore[type-arg] + extrinsic_rotation: np.ndarray | None = None, # type: ignore[type-arg] + extrinsic_translation: np.ndarray | None = None, # type: ignore[type-arg] color: tuple[int, int, int] = (0, 255, 0), thickness: int = 2, show_dimensions: bool = True, -) -> np.ndarray: +) -> np.ndarray: # type: ignore[type-arg] """ Draw a fitted cuboid on an image using camera projection. @@ -244,7 +244,7 @@ def visualize_cuboid_on_image( try: # Project 3D corners to image coordinates - corners_img, _ = cv2.projectPoints( + corners_img, _ = cv2.projectPoints( # type: ignore[call-overload] corners.astype(np.float32), np.zeros(3), np.zeros(3), # No additional rotation/translation @@ -320,7 +320,7 @@ def visualize_cuboid_on_image( return vis_img -def compute_cuboid_volume(cuboid_params: dict) -> float: +def compute_cuboid_volume(cuboid_params: dict) -> float: # type: ignore[type-arg] """ Compute the volume of a cuboid. @@ -337,7 +337,7 @@ def compute_cuboid_volume(cuboid_params: dict) -> float: return float(np.prod(dims)) -def compute_cuboid_surface_area(cuboid_params: dict) -> float: +def compute_cuboid_surface_area(cuboid_params: dict) -> float: # type: ignore[type-arg] """ Compute the surface area of a cuboid. @@ -351,10 +351,10 @@ def compute_cuboid_surface_area(cuboid_params: dict) -> float: raise ValueError("cuboid_params must contain 'dimensions' key") dims = cuboid_params["dimensions"] - return 2.0 * (dims[0] * dims[1] + dims[1] * dims[2] + dims[2] * dims[0]) + return 2.0 * (dims[0] * dims[1] + dims[1] * dims[2] + dims[2] * dims[0]) # type: ignore[no-any-return] -def check_cuboid_quality(cuboid_params: dict, points: np.ndarray) -> dict: +def check_cuboid_quality(cuboid_params: dict, points: np.ndarray) -> dict: # type: ignore[type-arg] """ Assess the quality of a cuboid fit. @@ -404,7 +404,7 @@ def check_cuboid_quality(cuboid_params: dict, points: np.ndarray) -> dict: # Backward compatibility -def visualize_fit(image, cuboid_params, camera_matrix, R=None, t=None): +def visualize_fit(image, cuboid_params, camera_matrix, R=None, t=None): # type: ignore[no-untyped-def] """ Legacy function for backward compatibility. Use visualize_cuboid_on_image instead. diff --git a/dimos/perception/pointcloud/pointcloud_filtering.py b/dimos/perception/pointcloud/pointcloud_filtering.py index 4ca8a0c84b..2d189ce0c1 100644 --- a/dimos/perception/pointcloud/pointcloud_filtering.py +++ b/dimos/perception/pointcloud/pointcloud_filtering.py @@ -15,7 +15,7 @@ import cv2 import numpy as np -import open3d as o3d +import open3d as o3d # type: ignore[import-untyped] import torch from dimos.perception.pointcloud.cuboid_fit import fit_cuboid @@ -37,8 +37,8 @@ class PointcloudFiltering: def __init__( self, - color_intrinsics: str | list[float] | np.ndarray | None = None, - depth_intrinsics: str | list[float] | np.ndarray | None = None, + color_intrinsics: str | list[float] | np.ndarray | None = None, # type: ignore[type-arg] + depth_intrinsics: str | list[float] | np.ndarray | None = None, # type: ignore[type-arg] color_weight: float = 0.3, enable_statistical_filtering: bool = True, statistical_neighbors: int = 20, @@ -106,21 +106,21 @@ def __init__( # Store the full point cloud self.full_pcd = None - def generate_color_from_id(self, object_id: int) -> np.ndarray: + def generate_color_from_id(self, object_id: int) -> np.ndarray: # type: ignore[type-arg] """Generate a consistent color for a given object ID.""" np.random.seed(object_id) color = np.random.randint(0, 255, 3, dtype=np.uint8) np.random.seed(None) return color - def _validate_inputs( - self, color_img: np.ndarray, depth_img: np.ndarray, objects: list[ObjectData] + def _validate_inputs( # type: ignore[no-untyped-def] + self, color_img: np.ndarray, depth_img: np.ndarray, objects: list[ObjectData] # type: ignore[type-arg] ): """Validate input parameters.""" if color_img.shape[:2] != depth_img.shape: raise ValueError("Color and depth image dimensions don't match") - def _prepare_masks(self, masks: list[np.ndarray], target_shape: tuple) -> list[np.ndarray]: + def _prepare_masks(self, masks: list[np.ndarray], target_shape: tuple) -> list[np.ndarray]: # type: ignore[type-arg] """Prepare and validate masks to match target shape.""" processed_masks = [] for mask in masks: @@ -147,7 +147,7 @@ def _prepare_masks(self, masks: list[np.ndarray], target_shape: tuple) -> list[n return processed_masks def _apply_color_mask( - self, pcd: o3d.geometry.PointCloud, rgb_color: np.ndarray + self, pcd: o3d.geometry.PointCloud, rgb_color: np.ndarray # type: ignore[type-arg] ) -> o3d.geometry.PointCloud: """Apply weighted color mask to point cloud.""" if len(np.asarray(pcd.colors)) > 0: @@ -184,7 +184,7 @@ def _apply_subsampling(self, pcd: o3d.geometry.PointCloud) -> o3d.geometry.Point return pcd.voxel_down_sample(self.voxel_size) return pcd - def _extract_masks_from_objects(self, objects: list[ObjectData]) -> list[np.ndarray]: + def _extract_masks_from_objects(self, objects: list[ObjectData]) -> list[np.ndarray]: # type: ignore[type-arg] """Extract segmentation masks from ObjectData objects.""" return [obj["segmentation_mask"] for obj in objects] @@ -193,7 +193,7 @@ def get_full_point_cloud(self) -> o3d.geometry.PointCloud: return self._apply_subsampling(self.full_pcd) def process_images( - self, color_img: np.ndarray, depth_img: np.ndarray, objects: list[ObjectData] + self, color_img: np.ndarray, depth_img: np.ndarray, objects: list[ObjectData] # type: ignore[type-arg] ) -> list[ObjectData]: """ Process color and depth images with object detection results to create filtered point clouds. @@ -267,7 +267,7 @@ def process_images( # Create point clouds efficiently self.full_pcd, masked_pcds = create_point_cloud_and_extract_masks( - color_img, depth_img, processed_masks, self.depth_camera_matrix, depth_scale=1.0 + color_img, depth_img, processed_masks, self.depth_camera_matrix, depth_scale=1.0 # type: ignore[arg-type] ) # Process each object and update ObjectData @@ -346,7 +346,7 @@ def process_images( colors_array = np.zeros((len(points_array), 3), dtype=np.float32) updated_obj["point_cloud_numpy"] = points_array - updated_obj["colors_numpy"] = colors_array + updated_obj["colors_numpy"] = colors_array # type: ignore[typeddict-unknown-key] updated_objects.append(updated_obj) diff --git a/dimos/perception/pointcloud/utils.py b/dimos/perception/pointcloud/utils.py index 97dc8d3716..f79d1c9d74 100644 --- a/dimos/perception/pointcloud/utils.py +++ b/dimos/perception/pointcloud/utils.py @@ -24,7 +24,7 @@ import cv2 import numpy as np -import open3d as o3d +import open3d as o3d # type: ignore[import-untyped] from scipy.spatial import cKDTree import yaml @@ -32,8 +32,8 @@ def load_camera_matrix_from_yaml( - camera_info: str | list[float] | np.ndarray | dict | None, -) -> np.ndarray | None: + camera_info: str | list[float] | np.ndarray | dict | None, # type: ignore[type-arg] +) -> np.ndarray | None: # type: ignore[type-arg] """ Load camera intrinsic matrix from various input formats. @@ -85,7 +85,7 @@ def load_camera_matrix_from_yaml( ) -def _extract_matrix_from_dict(data: dict) -> np.ndarray: +def _extract_matrix_from_dict(data: dict) -> np.ndarray: # type: ignore[type-arg] """Extract camera matrix from dictionary with various formats.""" # ROS format with 'K' field (most common) if "K" in data: @@ -122,9 +122,9 @@ def _extract_matrix_from_dict(data: dict) -> np.ndarray: def create_o3d_point_cloud_from_rgbd( - color_img: np.ndarray, - depth_img: np.ndarray, - intrinsic: np.ndarray, + color_img: np.ndarray, # type: ignore[type-arg] + depth_img: np.ndarray, # type: ignore[type-arg] + intrinsic: np.ndarray, # type: ignore[type-arg] depth_scale: float = 1.0, depth_trunc: float = 3.0, ) -> o3d.geometry.PointCloud: @@ -199,10 +199,10 @@ def create_o3d_point_cloud_from_rgbd( def create_point_cloud_and_extract_masks( - color_img: np.ndarray, - depth_img: np.ndarray, - masks: list[np.ndarray], - intrinsic: np.ndarray, + color_img: np.ndarray, # type: ignore[type-arg] + depth_img: np.ndarray, # type: ignore[type-arg] + masks: list[np.ndarray], # type: ignore[type-arg] + intrinsic: np.ndarray, # type: ignore[type-arg] depth_scale: float = 1.0, depth_trunc: float = 3.0, ) -> tuple[o3d.geometry.PointCloud, list[o3d.geometry.PointCloud]]: @@ -269,7 +269,7 @@ def create_point_cloud_and_extract_masks( def filter_point_cloud_statistical( pcd: o3d.geometry.PointCloud, nb_neighbors: int = 20, std_ratio: float = 2.0 -) -> tuple[o3d.geometry.PointCloud, np.ndarray]: +) -> tuple[o3d.geometry.PointCloud, np.ndarray]: # type: ignore[type-arg] """ Apply statistical outlier filtering to point cloud. @@ -284,12 +284,12 @@ def filter_point_cloud_statistical( if len(np.asarray(pcd.points)) == 0: return pcd, np.array([]) - return pcd.remove_statistical_outlier(nb_neighbors=nb_neighbors, std_ratio=std_ratio) + return pcd.remove_statistical_outlier(nb_neighbors=nb_neighbors, std_ratio=std_ratio) # type: ignore[no-any-return] def filter_point_cloud_radius( pcd: o3d.geometry.PointCloud, nb_points: int = 16, radius: float = 0.05 -) -> tuple[o3d.geometry.PointCloud, np.ndarray]: +) -> tuple[o3d.geometry.PointCloud, np.ndarray]: # type: ignore[type-arg] """ Apply radius-based outlier filtering to point cloud. @@ -304,17 +304,17 @@ def filter_point_cloud_radius( if len(np.asarray(pcd.points)) == 0: return pcd, np.array([]) - return pcd.remove_radius_outlier(nb_points=nb_points, radius=radius) + return pcd.remove_radius_outlier(nb_points=nb_points, radius=radius) # type: ignore[no-any-return] def overlay_point_clouds_on_image( - base_image: np.ndarray, + base_image: np.ndarray, # type: ignore[type-arg] point_clouds: list[o3d.geometry.PointCloud], - camera_intrinsics: list[float] | np.ndarray, + camera_intrinsics: list[float] | np.ndarray, # type: ignore[type-arg] colors: list[tuple[int, int, int]], point_size: int = 2, alpha: float = 0.7, -) -> np.ndarray: +) -> np.ndarray: # type: ignore[type-arg] """ Overlay multiple colored point clouds onto an image. @@ -368,7 +368,7 @@ def overlay_point_clouds_on_image( # Ensure color is a tuple of integers for OpenCV if isinstance(color, list | tuple | np.ndarray): - color = tuple(int(c) for c in color[:3]) + color = tuple(int(c) for c in color[:3]) # type: ignore[assignment] else: color = (255, 255, 255) @@ -385,10 +385,10 @@ def overlay_point_clouds_on_image( def create_point_cloud_overlay_visualization( - base_image: np.ndarray, - objects: list[dict], - intrinsics: np.ndarray, -) -> np.ndarray: + base_image: np.ndarray, # type: ignore[type-arg] + objects: list[dict], # type: ignore[type-arg] + intrinsics: np.ndarray, # type: ignore[type-arg] +) -> np.ndarray: # type: ignore[type-arg] """ Create a visualization showing object point clouds and bounding boxes overlaid on a base image. @@ -457,7 +457,7 @@ def create_point_cloud_overlay_visualization( return result -def create_3d_bounding_box_corners(position, rotation, size: int): +def create_3d_bounding_box_corners(position, rotation, size: int): # type: ignore[no-untyped-def] """ Create 8 corners of a 3D bounding box from position, rotation, and size. @@ -504,9 +504,9 @@ def create_3d_bounding_box_corners(position, rotation, size: int): ) # Get dimensions - width = size.get("width", 0.1) - height = size.get("height", 0.1) - depth = size.get("depth", 0.1) + width = size.get("width", 0.1) # type: ignore[attr-defined] + height = size.get("height", 0.1) # type: ignore[attr-defined] + depth = size.get("depth", 0.1) # type: ignore[attr-defined] # Create 8 corners of the bounding box (before rotation) corners = np.array( @@ -528,7 +528,7 @@ def create_3d_bounding_box_corners(position, rotation, size: int): return rotated_corners -def draw_3d_bounding_box_on_image(image, corners_2d, color, thickness: int = 2) -> None: +def draw_3d_bounding_box_on_image(image, corners_2d, color, thickness: int = 2) -> None: # type: ignore[no-untyped-def] """ Draw a 3D bounding box on an image using projected 2D corners. @@ -563,7 +563,7 @@ def draw_3d_bounding_box_on_image(image, corners_2d, color, thickness: int = 2) def extract_and_cluster_misc_points( full_pcd: o3d.geometry.PointCloud, - all_objects: list[dict], + all_objects: list[dict], # type: ignore[type-arg] eps: float = 0.03, min_points: int = 100, enable_filtering: bool = True, @@ -813,7 +813,7 @@ def _cluster_point_cloud_dbscan( return [pcd] # Return original point cloud as fallback -def get_standard_coordinate_transform(): +def get_standard_coordinate_transform(): # type: ignore[no-untyped-def] """ Get a standard coordinate transformation matrix for consistent visualization. @@ -859,7 +859,7 @@ def visualize_clustered_point_clouds( return # Apply standard coordinate transformation - transform = get_standard_coordinate_transform() + transform = get_standard_coordinate_transform() # type: ignore[no-untyped-call] geometries = [] for pcd in clustered_pcds: pcd_copy = o3d.geometry.PointCloud(pcd) @@ -919,7 +919,7 @@ def visualize_pcd( return # Apply standard coordinate transformation - transform = get_standard_coordinate_transform() + transform = get_standard_coordinate_transform() # type: ignore[no-untyped-call] pcd_copy = o3d.geometry.PointCloud(pcd) pcd_copy.transform(transform) geometries = [pcd_copy] @@ -982,7 +982,7 @@ def visualize_voxel_grid( coordinate_frame = o3d.geometry.TriangleMesh.create_coordinate_frame( size=coordinate_frame_size ) - coordinate_frame.transform(get_standard_coordinate_transform()) + coordinate_frame.transform(get_standard_coordinate_transform()) # type: ignore[no-untyped-call] geometries.append(coordinate_frame) print(f"Visualizing voxel grid with {len(voxel_grid.get_voxels())} voxels") @@ -1002,8 +1002,8 @@ def visualize_voxel_grid( def combine_object_pointclouds( - point_clouds: list[np.ndarray] | list[o3d.geometry.PointCloud], - colors: list[np.ndarray] | None = None, + point_clouds: list[np.ndarray] | list[o3d.geometry.PointCloud], # type: ignore[type-arg] + colors: list[np.ndarray] | None = None, # type: ignore[type-arg] ) -> o3d.geometry.PointCloud: """ Combine multiple point clouds into a single Open3D point cloud. @@ -1028,8 +1028,8 @@ def combine_object_pointclouds( points = np.asarray(pcd.points) all_points.append(points) if pcd.has_colors(): - colors = np.asarray(pcd.colors) - all_colors.append(colors) + colors = np.asarray(pcd.colors) # type: ignore[assignment] + all_colors.append(colors) # type: ignore[arg-type] if not all_points: return o3d.geometry.PointCloud() @@ -1044,10 +1044,10 @@ def combine_object_pointclouds( def extract_centroids_from_masks( - rgb_image: np.ndarray, - depth_image: np.ndarray, - masks: list[np.ndarray], - camera_intrinsics: list[float] | np.ndarray, + rgb_image: np.ndarray, # type: ignore[type-arg] + depth_image: np.ndarray, # type: ignore[type-arg] + masks: list[np.ndarray], # type: ignore[type-arg] + camera_intrinsics: list[float] | np.ndarray, # type: ignore[type-arg] ) -> list[dict[str, Any]]: """ Extract 3D centroids and orientations from segmentation masks. @@ -1069,10 +1069,10 @@ def extract_centroids_from_masks( if isinstance(camera_intrinsics, list) and len(camera_intrinsics) == 4: fx, fy, cx, cy = camera_intrinsics else: - fx = camera_intrinsics[0, 0] - fy = camera_intrinsics[1, 1] - cx = camera_intrinsics[0, 2] - cy = camera_intrinsics[1, 2] + fx = camera_intrinsics[0, 0] # type: ignore[call-overload] + fy = camera_intrinsics[1, 1] # type: ignore[call-overload] + cx = camera_intrinsics[0, 2] # type: ignore[call-overload] + cy = camera_intrinsics[1, 2] # type: ignore[call-overload] results = [] diff --git a/dimos/perception/segmentation/image_analyzer.py b/dimos/perception/segmentation/image_analyzer.py index 074ee7d605..8a4e7eac90 100644 --- a/dimos/perception/segmentation/image_analyzer.py +++ b/dimos/perception/segmentation/image_analyzer.py @@ -40,7 +40,7 @@ def __init__(self) -> None: """ self.client = OpenAI() - def encode_image(self, image): + def encode_image(self, image): # type: ignore[no-untyped-def] """ Encodes an image to Base64. @@ -53,7 +53,7 @@ def encode_image(self, image): _, buffer = cv2.imencode(".jpg", image) return base64.b64encode(buffer).decode("utf-8") - def analyze_images(self, images, detail: str = "auto", prompt_type: str = "normal"): + def analyze_images(self, images, detail: str = "auto", prompt_type: str = "normal"): # type: ignore[no-untyped-def] """ Takes a list of cropped images and returns descriptions from OpenAI's Vision model. @@ -69,7 +69,7 @@ def analyze_images(self, images, detail: str = "auto", prompt_type: str = "norma { "type": "image_url", "image_url": { - "url": f"data:image/jpeg;base64,{self.encode_image(img)}", + "url": f"data:image/jpeg;base64,{self.encode_image(img)}", # type: ignore[no-untyped-call] "detail": detail, }, } @@ -86,7 +86,7 @@ def analyze_images(self, images, detail: str = "auto", prompt_type: str = "norma response = self.client.chat.completions.create( model="gpt-4o-mini", messages=[ - { + { # type: ignore[list-item, misc] "role": "user", "content": [{"type": "text", "text": prompt}, *image_data], } diff --git a/dimos/perception/segmentation/sam_2d_seg.py b/dimos/perception/segmentation/sam_2d_seg.py index b13ebc4c65..0fe5507d93 100644 --- a/dimos/perception/segmentation/sam_2d_seg.py +++ b/dimos/perception/segmentation/sam_2d_seg.py @@ -19,7 +19,7 @@ import time import cv2 -import onnxruntime +import onnxruntime # type: ignore[import-untyped] from ultralytics import FastSAM from dimos.perception.common.detection2d_tracker import get_tracked_results, target2dTracker @@ -48,7 +48,7 @@ def __init__( use_rich_labeling: bool = False, use_filtering: bool = True, ) -> None: - if is_cuda_available(): + if is_cuda_available(): # type: ignore[no-untyped-call] logger.info("Using CUDA for SAM 2d segmenter") if hasattr(onnxruntime, "preload_dlls"): # Handles CUDA 11 / onnxruntime-gpu<=1.18 onnxruntime.preload_dlls(cuda=True, cudnn=True) @@ -86,13 +86,13 @@ def __init__( self.image_analyzer = ImageAnalyzer() self.min_analysis_interval = min_analysis_interval self.last_analysis_time = 0 - self.to_be_analyzed = deque() - self.object_names = {} + self.to_be_analyzed = deque() # type: ignore[var-annotated] + self.object_names = {} # type: ignore[var-annotated] self.analysis_executor = ThreadPoolExecutor(max_workers=1) self.current_future = None self.current_queue_ids = None - def process_image(self, image): + def process_image(self, image): # type: ignore[no-untyped-def] """Process an image and return segmentation results.""" results = self.model.track( source=image, @@ -145,7 +145,7 @@ def process_image(self, image): # Get tracked results tracked_masks, tracked_bboxes, tracked_target_ids, tracked_probs, tracked_names = ( - get_tracked_results(tracked_targets) + get_tracked_results(tracked_targets) # type: ignore[no-untyped-call] ) if self.use_analyzer: @@ -211,7 +211,7 @@ def process_image(self, image): ) return [], [], [], [], [] - def check_analysis_status(self, tracked_target_ids): + def check_analysis_status(self, tracked_target_ids): # type: ignore[no-untyped-def] """Check if analysis is complete and prepare new queue if needed.""" if not self.use_analyzer: return None, None @@ -255,12 +255,12 @@ def check_analysis_status(self, tracked_target_ids): return queue_indices, queue_ids return None, None - def run_analysis(self, frame, tracked_bboxes, tracked_target_ids) -> None: + def run_analysis(self, frame, tracked_bboxes, tracked_target_ids) -> None: # type: ignore[no-untyped-def] """Run queue image analysis in background.""" if not self.use_analyzer: return - queue_indices, queue_ids = self.check_analysis_status(tracked_target_ids) + queue_indices, queue_ids = self.check_analysis_status(tracked_target_ids) # type: ignore[no-untyped-call] if queue_indices: selected_bboxes = [tracked_bboxes[i] for i in queue_indices] cropped_images = crop_images_from_bboxes(frame, selected_bboxes) @@ -274,11 +274,11 @@ def run_analysis(self, frame, tracked_bboxes, tracked_target_ids) -> None: else: prompt_type = "normal" - self.current_future = self.analysis_executor.submit( + self.current_future = self.analysis_executor.submit( # type: ignore[assignment] self.image_analyzer.analyze_images, cropped_images, prompt_type=prompt_type ) - def get_object_names(self, track_ids, tracked_names: Sequence[str]): + def get_object_names(self, track_ids, tracked_names: Sequence[str]): # type: ignore[no-untyped-def] """Get object names for the given track IDs, falling back to tracked names.""" if not self.use_analyzer: return tracked_names @@ -288,7 +288,7 @@ def get_object_names(self, track_ids, tracked_names: Sequence[str]): for track_id, tracked_name in zip(track_ids, tracked_names, strict=False) ] - def visualize_results( + def visualize_results( # type: ignore[no-untyped-def] self, image, masks, bboxes, track_ids, probs: Sequence[float], names: Sequence[str] ): """Generate an overlay visualization with segmentation results and object names.""" @@ -333,7 +333,7 @@ def main() -> None: time.time() # Process image and get results - masks, bboxes, target_ids, probs, names = segmenter.process_image(frame) + masks, bboxes, target_ids, probs, names = segmenter.process_image(frame) # type: ignore[no-untyped-call] # Run analysis if enabled if segmenter.use_analyzer: diff --git a/dimos/perception/segmentation/utils.py b/dimos/perception/segmentation/utils.py index 24d6ce4bf2..eae892247a 100644 --- a/dimos/perception/segmentation/utils.py +++ b/dimos/perception/segmentation/utils.py @@ -29,13 +29,13 @@ def __init__( :param min_count: Minimum number of appearances required :param count_window: Number of latest frames to consider for counting """ - self.history = [] + self.history = [] # type: ignore[var-annotated] self.history_size = history_size self.min_count = min_count self.count_window = count_window - self.total_counts = {} + self.total_counts = {} # type: ignore[var-annotated] - def update(self, track_ids): + def update(self, track_ids): # type: ignore[no-untyped-def] # Add new frame's track IDs to history self.history.append(track_ids) if len(self.history) > self.history_size: @@ -57,12 +57,12 @@ def update(self, track_ids): # Return IDs that appear often enough return [track_id for track_id, count in id_counts.items() if count >= self.min_count] - def get_total_counts(self): + def get_total_counts(self): # type: ignore[no-untyped-def] """Returns the total count of each tracking ID seen over time, limited to history size.""" return self.total_counts -def extract_masks_bboxes_probs_names(result, max_size: float = 0.7): +def extract_masks_bboxes_probs_names(result, max_size: float = 0.7): # type: ignore[no-untyped-def] """ Extracts masks, bounding boxes, probabilities, and class names from one Ultralytics result object. @@ -73,12 +73,12 @@ def extract_masks_bboxes_probs_names(result, max_size: float = 0.7): Returns: tuple: (masks, bboxes, track_ids, probs, names, areas) """ - masks = [] - bboxes = [] - track_ids = [] - probs = [] - names = [] - areas = [] + masks = [] # type: ignore[var-annotated] + bboxes = [] # type: ignore[var-annotated] + track_ids = [] # type: ignore[var-annotated] + probs = [] # type: ignore[var-annotated] + names = [] # type: ignore[var-annotated] + areas = [] # type: ignore[var-annotated] if result.masks is None: return masks, bboxes, track_ids, probs, names, areas @@ -114,7 +114,7 @@ def extract_masks_bboxes_probs_names(result, max_size: float = 0.7): return masks, bboxes, track_ids, probs, names, areas -def compute_texture_map(frame, blur_size: int = 3): +def compute_texture_map(frame, blur_size: int = 3): # type: ignore[no-untyped-def] """ Compute texture map using gradient statistics. Returns high values for textured regions and low values for smooth regions. @@ -152,7 +152,7 @@ def compute_texture_map(frame, blur_size: int = 3): return texture_map -def filter_segmentation_results( +def filter_segmentation_results( # type: ignore[no-untyped-def] frame, masks, bboxes, @@ -210,7 +210,7 @@ def filter_segmentation_results( if texture_value >= texture_threshold: mask_sum[mask > 0] = i filtered_texture_values.append( - texture_value.item() + texture_value.item() # type: ignore[union-attr] ) # Store the texture value as a Python float # Get indices that appear in mask_sum (these are the masks we want to keep) @@ -240,7 +240,7 @@ def filter_segmentation_results( ) -def plot_results( +def plot_results( # type: ignore[no-untyped-def] image, masks, bboxes, @@ -313,7 +313,7 @@ def plot_results( return result -def crop_images_from_bboxes(image, bboxes, buffer: int = 0): +def crop_images_from_bboxes(image, bboxes, buffer: int = 0): # type: ignore[no-untyped-def] """ Crops regions from an image based on bounding boxes with an optional buffer. diff --git a/dimos/perception/spatial_perception.py b/dimos/perception/spatial_perception.py index 3986c0f7c7..15425edc03 100644 --- a/dimos/perception/spatial_perception.py +++ b/dimos/perception/spatial_perception.py @@ -61,7 +61,7 @@ class SpatialMemory(Module): """ # LCM inputs - color_image: In[Image] = None + color_image: In[Image] = None # type: ignore[assignment] def __init__( self, @@ -149,7 +149,7 @@ def __init__( try: logger.info(f"Loading existing visual memory from {visual_memory_path}...") self._visual_memory = VisualMemory.load( - visual_memory_path, output_dir=output_dir + visual_memory_path, output_dir=output_dir # type: ignore[arg-type] ) logger.info(f"Loaded {self._visual_memory.count()} images from previous runs") except Exception as e: @@ -177,7 +177,7 @@ def __init__( self.robot_locations: list[RobotLocation] = [] # Track latest data for processing - self._latest_video_frame: np.ndarray | None = None + self._latest_video_frame: np.ndarray | None = None # type: ignore[type-arg] self._process_interval = 1 logger.info(f"SpatialMemory initialized with model {embedding_model}") @@ -200,7 +200,7 @@ def set_video(image_msg: Image) -> None: self._disposables.add(Disposable(unsub)) # Start periodic processing using interval - unsub = interval(self._process_interval).subscribe(lambda _: self._process_frame()) + unsub = interval(self._process_interval).subscribe(lambda _: self._process_frame()) # type: ignore[assignment] self._disposables.add(Disposable(unsub)) @rpc @@ -301,7 +301,7 @@ def _process_frame(self) -> None: @rpc def query_by_location( self, x: float, y: float, radius: float = 2.0, limit: int = 5 - ) -> list[dict]: + ) -> list[dict]: # type: ignore[type-arg] """ Query the vector database for images near the specified location. @@ -333,7 +333,7 @@ def save(self) -> bool: logger.error(f"Failed to save visual memory: {e}") return False - def process_stream(self, combined_stream: Observable) -> Observable: + def process_stream(self, combined_stream: Observable) -> Observable: # type: ignore[type-arg] """ Process a combined stream of video frames and positions. @@ -348,7 +348,7 @@ def process_stream(self, combined_stream: Observable) -> Observable: Observable of processing results, including the stored frame and its metadata """ - def process_combined_data(data): + def process_combined_data(data): # type: ignore[no-untyped-def] self.frame_count += 1 frame = data.get("frame") @@ -427,7 +427,7 @@ def process_combined_data(data): ) @rpc - def query_by_image(self, image: np.ndarray, limit: int = 5) -> list[dict]: + def query_by_image(self, image: np.ndarray, limit: int = 5) -> list[dict]: # type: ignore[type-arg] """ Query the vector database for images similar to the provided image. @@ -442,7 +442,7 @@ def query_by_image(self, image: np.ndarray, limit: int = 5) -> list[dict]: return self.vector_db.query_by_embedding(embedding, limit) @rpc - def query_by_text(self, text: str, limit: int = 5) -> list[dict]: + def query_by_text(self, text: str, limit: int = 5) -> list[dict]: # type: ignore[type-arg] """ Query the vector database for images matching the provided text description. @@ -506,7 +506,7 @@ def add_named_location( return False # Create RobotLocation object - location = RobotLocation( + location = RobotLocation( # type: ignore[call-arg] name=name, position=tf.translation, rotation=tf.rotation.to_euler(), @@ -514,7 +514,7 @@ def add_named_location( timestamp=time.time(), ) - return self.add_robot_location(location) + return self.add_robot_location(location) # type: ignore[no-any-return] @rpc def get_robot_locations(self) -> list[RobotLocation]: @@ -571,11 +571,11 @@ def query_tagged_location(self, query: str) -> RobotLocation | None: return None -def deploy( +def deploy( # type: ignore[no-untyped-def] dimos: DimosCluster, camera: spec.Camera, ): - spatial_memory = dimos.deploy(SpatialMemory, db_path="/tmp/spatial_memory_db") + spatial_memory = dimos.deploy(SpatialMemory, db_path="/tmp/spatial_memory_db") # type: ignore[attr-defined] spatial_memory.color_image.connect(camera.image) spatial_memory.start() return spatial_memory diff --git a/dimos/protocol/encode/__init__.py b/dimos/protocol/encode/__init__.py index 66bbbbb21c..87386a09e5 100644 --- a/dimos/protocol/encode/__init__.py +++ b/dimos/protocol/encode/__init__.py @@ -44,7 +44,7 @@ def encode(msg: MsgT) -> bytes: @staticmethod def decode(data: bytes) -> MsgT: - return json.loads(data.decode("utf-8")) + return json.loads(data.decode("utf-8")) # type: ignore[no-any-return] class LCM(Encoder[LCMMsgT, bytes]): @@ -64,7 +64,7 @@ def decode(data: bytes) -> LCMMsgT: ) -class LCMTypedEncoder(LCM, Generic[LCMMsgT]): +class LCMTypedEncoder(LCM, Generic[LCMMsgT]): # type: ignore[type-arg] """Typed LCM encoder for specific message types.""" def __init__(self, message_type: type[LCMMsgT]) -> None: @@ -81,7 +81,7 @@ def decode(data: bytes) -> LCMMsgT: def create_lcm_typed_encoder(message_type: type[LCMMsgT]) -> type[LCMTypedEncoder[LCMMsgT]]: """Factory function to create a typed LCM encoder for a specific message type.""" - class SpecificLCMEncoder(LCMTypedEncoder): + class SpecificLCMEncoder(LCMTypedEncoder): # type: ignore[type-arg] @staticmethod def decode(data: bytes) -> LCMMsgT: return message_type.decode(data) # type: ignore[return-value] diff --git a/dimos/protocol/pubsub/jpeg_shm.py b/dimos/protocol/pubsub/jpeg_shm.py index 68a97ec6b6..e6fadc1e6f 100644 --- a/dimos/protocol/pubsub/jpeg_shm.py +++ b/dimos/protocol/pubsub/jpeg_shm.py @@ -16,5 +16,5 @@ from dimos.protocol.pubsub.shmpubsub import SharedMemoryPubSubBase -class JpegSharedMemory(JpegSharedMemoryEncoderMixin, SharedMemoryPubSubBase): +class JpegSharedMemory(JpegSharedMemoryEncoderMixin, SharedMemoryPubSubBase): # type: ignore[misc] pass diff --git a/dimos/protocol/pubsub/lcmpubsub.py b/dimos/protocol/pubsub/lcmpubsub.py index c348b1dda7..a12b96e762 100644 --- a/dimos/protocol/pubsub/lcmpubsub.py +++ b/dimos/protocol/pubsub/lcmpubsub.py @@ -17,7 +17,7 @@ from dataclasses import dataclass from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable -from turbojpeg import TurboJPEG +from turbojpeg import TurboJPEG # type: ignore[import-untyped] from dimos.msgs.sensor_msgs import Image from dimos.msgs.sensor_msgs.image_impls.AbstractImage import ImageFormat @@ -63,7 +63,7 @@ class LCMPubSubBase(LCMService, PubSub[Topic, Any]): _thread: threading.Thread | None _callbacks: dict[str, list[Callable[[Any], None]]] - def __init__(self, **kwargs) -> None: + def __init__(self, **kwargs) -> None: # type: ignore[no-untyped-def] LCMService.__init__(self, **kwargs) super().__init__(**kwargs) self._callbacks = {} @@ -110,18 +110,18 @@ def decode(self, msg: bytes, topic: Topic) -> LCMMsg: class JpegEncoderMixin(PubSubEncoderMixin[Topic, Any]): def encode(self, msg: LCMMsg, _: Topic) -> bytes: - return msg.lcm_jpeg_encode() + return msg.lcm_jpeg_encode() # type: ignore[attr-defined, no-any-return] def decode(self, msg: bytes, topic: Topic) -> LCMMsg: if topic.lcm_type is None: raise ValueError( f"Cannot decode message for topic '{topic.topic}': no lcm_type specified" ) - return topic.lcm_type.lcm_jpeg_decode(msg) + return topic.lcm_type.lcm_jpeg_decode(msg) # type: ignore[attr-defined, no-any-return] class JpegSharedMemoryEncoderMixin(PubSubEncoderMixin[str, Image]): - def __init__(self, quality: int = 75, **kwargs) -> None: + def __init__(self, quality: int = 75, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(**kwargs) self.jpeg = TurboJPEG() self.quality = quality @@ -131,7 +131,7 @@ def encode(self, msg: Any, _topic: str) -> bytes: raise ValueError("Can only encode images.") bgr_image = msg.to_bgr().to_opencv() - return self.jpeg.encode(bgr_image, quality=self.quality) + return self.jpeg.encode(bgr_image, quality=self.quality) # type: ignore[no-any-return] def decode(self, msg: bytes, _topic: str) -> Image: bgr_array = self.jpeg.decode(msg) @@ -145,7 +145,7 @@ class LCM( class PickleLCM( - PickleEncoderMixin, + PickleEncoderMixin, # type: ignore[type-arg] LCMPubSubBase, ): ... diff --git a/dimos/protocol/pubsub/memory.py b/dimos/protocol/pubsub/memory.py index 513dfd32cd..0434da7493 100644 --- a/dimos/protocol/pubsub/memory.py +++ b/dimos/protocol/pubsub/memory.py @@ -50,7 +50,7 @@ def unsubscribe(self, topic: str, callback: Callable[[Any, str], None]) -> None: pass -class MemoryWithJSONEncoder(PubSubEncoderMixin, Memory): +class MemoryWithJSONEncoder(PubSubEncoderMixin, Memory): # type: ignore[type-arg] """Memory PubSub with JSON encoding/decoding.""" def encode(self, msg: Any, topic: str) -> bytes: diff --git a/dimos/protocol/pubsub/redispubsub.py b/dimos/protocol/pubsub/redispubsub.py index 7d6c798f2c..72826d9612 100644 --- a/dimos/protocol/pubsub/redispubsub.py +++ b/dimos/protocol/pubsub/redispubsub.py @@ -21,7 +21,7 @@ from types import TracebackType from typing import Any -import redis +import redis # type: ignore[import-not-found] from dimos.protocol.pubsub.spec import PubSub from dimos.protocol.service.spec import Service @@ -40,7 +40,7 @@ class Redis(PubSub[str, Any], Service[RedisConfig]): default_config = RedisConfig - def __init__(self, **kwargs) -> None: + def __init__(self, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(**kwargs) # Redis connections @@ -56,13 +56,13 @@ def start(self) -> None: """Start the Redis pub/sub service.""" if self._running: return - self._connect() + self._connect() # type: ignore[no-untyped-call] def stop(self) -> None: """Stop the Redis pub/sub service.""" self.close() - def _connect(self): + def _connect(self): # type: ignore[no-untyped-def] """Connect to Redis and set up pub/sub.""" try: self._client = redis.Redis( @@ -73,14 +73,14 @@ def _connect(self): **self.config.kwargs, ) # Test connection - self._client.ping() + self._client.ping() # type: ignore[attr-defined] - self._pubsub = self._client.pubsub() + self._pubsub = self._client.pubsub() # type: ignore[attr-defined] self._running = True # Start listener thread - self._listener_thread = threading.Thread(target=self._listen_loop, daemon=True) - self._listener_thread.start() + self._listener_thread = threading.Thread(target=self._listen_loop, daemon=True) # type: ignore[assignment] + self._listener_thread.start() # type: ignore[attr-defined] except Exception as e: raise ConnectionError( @@ -186,7 +186,7 @@ def close(self) -> None: self._callbacks.clear() - def __enter__(self): + def __enter__(self): # type: ignore[no-untyped-def] return self def __exit__( diff --git a/dimos/protocol/pubsub/shm/ipc_factory.py b/dimos/protocol/pubsub/shm/ipc_factory.py index 9aedbfa1c4..8da37085f0 100644 --- a/dimos/protocol/pubsub/shm/ipc_factory.py +++ b/dimos/protocol/pubsub/shm/ipc_factory.py @@ -62,30 +62,30 @@ def device(self) -> str: # "cpu" or "cuda" @property @abstractmethod - def shape(self) -> tuple: ... + def shape(self) -> tuple: ... # type: ignore[type-arg] @property @abstractmethod - def dtype(self) -> np.dtype: ... + def dtype(self) -> np.dtype: ... # type: ignore[type-arg] @abstractmethod - def publish(self, frame) -> None: + def publish(self, frame) -> None: # type: ignore[no-untyped-def] """Write into inactive buffer, then flip visible index (write control last).""" ... @abstractmethod - def read(self, last_seq: int = -1, require_new: bool = True): + def read(self, last_seq: int = -1, require_new: bool = True): # type: ignore[no-untyped-def] """Return (seq:int, ts_ns:int, view-or-None).""" ... @abstractmethod - def descriptor(self) -> dict: + def descriptor(self) -> dict: # type: ignore[type-arg] """Tiny JSON-safe descriptor (names/handles/shape/dtype/device).""" ... @classmethod @abstractmethod - def attach(cls, desc: dict) -> "FrameChannel": + def attach(cls, desc: dict) -> "FrameChannel": # type: ignore[type-arg] """Attach in another process.""" ... @@ -116,7 +116,7 @@ def _safe_unlink(name: str) -> None: class CpuShmChannel(FrameChannel): - def __init__( + def __init__( # type: ignore[no-untyped-def] self, shape, dtype=np.uint8, @@ -128,7 +128,7 @@ def __init__( self._dtype = np.dtype(dtype) self._nbytes = int(self._dtype.itemsize * np.prod(self._shape)) - def _create_or_open(name: str, size: int): + def _create_or_open(name: str, size: int): # type: ignore[no-untyped-def] try: shm = SharedMemory(create=True, size=size, name=name) owner = True @@ -147,7 +147,7 @@ def _create_or_open(name: str, size: int): self._shm_ctrl, own_c = _create_or_open(ctrl_name, 24) self._is_owner = own_d and own_c - self._ctrl = np.ndarray((3,), dtype=np.int64, buffer=self._shm_ctrl.buf) + self._ctrl = np.ndarray((3,), dtype=np.int64, buffer=self._shm_ctrl.buf) # type: ignore[var-annotated] if self._is_owner: self._ctrl[:] = 0 # initialize only once @@ -163,7 +163,7 @@ def _create_or_open(name: str, size: int): else None ) - def descriptor(self): + def descriptor(self): # type: ignore[no-untyped-def] return { "kind": "cpu", "shape": self._shape, @@ -178,19 +178,19 @@ def device(self) -> str: return "cpu" @property - def shape(self): + def shape(self): # type: ignore[no-untyped-def] return self._shape @property - def dtype(self): + def dtype(self): # type: ignore[no-untyped-def] return self._dtype - def publish(self, frame) -> None: + def publish(self, frame) -> None: # type: ignore[no-untyped-def] assert isinstance(frame, np.ndarray) assert frame.shape == self._shape and frame.dtype == self._dtype active = int(self._ctrl[2]) inactive = 1 - active - view = np.ndarray( + view = np.ndarray( # type: ignore[var-annotated] self._shape, dtype=self._dtype, buffer=self._shm_data.buf, @@ -203,12 +203,12 @@ def publish(self, frame) -> None: self._ctrl[2] = inactive self._ctrl[0] += 1 - def read(self, last_seq: int = -1, require_new: bool = True): + def read(self, last_seq: int = -1, require_new: bool = True): # type: ignore[no-untyped-def] for _ in range(3): seq1 = int(self._ctrl[0]) idx = int(self._ctrl[2]) ts = int(self._ctrl[1]) - view = np.ndarray( + view = np.ndarray( # type: ignore[var-annotated] self._shape, dtype=self._dtype, buffer=self._shm_data.buf, offset=idx * self._nbytes ) if seq1 == int(self._ctrl[0]): @@ -217,7 +217,7 @@ def read(self, last_seq: int = -1, require_new: bool = True): return seq1, ts, view return last_seq, 0, None - def descriptor(self): + def descriptor(self): # type: ignore[no-redef, no-untyped-def] return { "kind": "cpu", "shape": self._shape, @@ -228,13 +228,13 @@ def descriptor(self): } @classmethod - def attach(cls, desc: str): + def attach(cls, desc: str): # type: ignore[no-untyped-def, override] obj = object.__new__(cls) - obj._shape = tuple(desc["shape"]) - obj._dtype = np.dtype(desc["dtype"]) - obj._nbytes = int(desc["nbytes"]) - data_name = desc["data_name"] - ctrl_name = desc["ctrl_name"] + obj._shape = tuple(desc["shape"]) # type: ignore[index] + obj._dtype = np.dtype(desc["dtype"]) # type: ignore[index] + obj._nbytes = int(desc["nbytes"]) # type: ignore[index] + data_name = desc["data_name"] # type: ignore[index] + ctrl_name = desc["ctrl_name"] # type: ignore[index] try: obj._shm_data = _open_shm_with_retry(data_name) obj._shm_ctrl = _open_shm_with_retry(ctrl_name) @@ -287,13 +287,13 @@ class CPU_IPC_Factory: """Creates/attaches CPU shared-memory channels.""" @staticmethod - def create(shape, dtype=np.uint8) -> CpuShmChannel: + def create(shape, dtype=np.uint8) -> CpuShmChannel: # type: ignore[no-untyped-def] return CpuShmChannel(shape, dtype=dtype) @staticmethod - def attach(desc: dict) -> CpuShmChannel: + def attach(desc: dict) -> CpuShmChannel: # type: ignore[type-arg] assert desc.get("kind") == "cpu", "Descriptor kind mismatch" - return CpuShmChannel.attach(desc) + return CpuShmChannel.attach(desc) # type: ignore[arg-type, no-any-return] # --------------------------- @@ -301,7 +301,7 @@ def attach(desc: dict) -> CpuShmChannel: # --------------------------- -def make_frame_channel( +def make_frame_channel( # type: ignore[no-untyped-def] shape, dtype=np.uint8, prefer: str = "auto", device: int = 0 ) -> FrameChannel: """Choose CUDA IPC if available (or requested), otherwise CPU SHM.""" diff --git a/dimos/protocol/pubsub/shmpubsub.py b/dimos/protocol/pubsub/shmpubsub.py index bbbf2192d7..6224faf410 100644 --- a/dimos/protocol/pubsub/shmpubsub.py +++ b/dimos/protocol/pubsub/shmpubsub.py @@ -88,7 +88,7 @@ class _TopicState: "thread", ) - def __init__(self, channel, capacity: int, cp_mod) -> None: + def __init__(self, channel, capacity: int, cp_mod) -> None: # type: ignore[no-untyped-def] self.channel = channel self.capacity = int(capacity) self.shape = (self.capacity + 20,) # +20 for header: length(4) + uuid(16) @@ -211,7 +211,7 @@ def _unsub() -> None: # ----- Capacity mgmt ---------------------------------------------------- - def reconfigure(self, topic: str, *, capacity: int) -> dict: + def reconfigure(self, topic: str, *, capacity: int) -> dict: # type: ignore[type-arg] """Change payload capacity (bytes) for a topic; returns new descriptor.""" st = self._ensure_topic(topic) new_cap = int(capacity) @@ -221,7 +221,7 @@ def reconfigure(self, topic: str, *, capacity: int) -> dict: st.shape = new_shape st.dtype = np.uint8 st.last_seq = -1 - return desc + return desc # type: ignore[no-any-return] # ----- Internals -------------------------------------------------------- diff --git a/dimos/protocol/pubsub/spec.py b/dimos/protocol/pubsub/spec.py index ef5a4f450f..da7cd8e9a6 100644 --- a/dimos/protocol/pubsub/spec.py +++ b/dimos/protocol/pubsub/spec.py @@ -55,10 +55,10 @@ def unsubscribe(self) -> None: self._unsubscribe_fn() # context-manager helper - def __enter__(self): + def __enter__(self): # type: ignore[no-untyped-def] return self - def __exit__(self, *exc) -> None: + def __exit__(self, *exc) -> None: # type: ignore[no-untyped-def] self.unsubscribe() # public helper: returns disposable object @@ -83,7 +83,7 @@ def _cb(msg: MsgT, topic: TopicT) -> None: # async context manager returning a queue @asynccontextmanager - async def queue(self, topic: TopicT, *, max_pending: int | None = None): + async def queue(self, topic: TopicT, *, max_pending: int | None = None): # type: ignore[no-untyped-def] q: asyncio.Queue[MsgT] = asyncio.Queue(maxsize=max_pending or 0) def _queue_cb(msg: MsgT, topic: TopicT) -> None: @@ -114,13 +114,13 @@ def encode(self, msg: MsgT, topic: TopicT) -> bytes: ... @abstractmethod def decode(self, msg: bytes, topic: TopicT) -> MsgT: ... - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(*args, **kwargs) - self._encode_callback_map: dict = {} + self._encode_callback_map: dict = {} # type: ignore[type-arg] def publish(self, topic: TopicT, message: MsgT) -> None: """Encode the message and publish it.""" - if getattr(self, "_stop_event", None) is not None and self._stop_event.is_set(): + if getattr(self, "_stop_event", None) is not None and self._stop_event.is_set(): # type: ignore[attr-defined] return encoded_message = self.encode(message, topic) if encoded_message is None: @@ -136,11 +136,11 @@ def wrapper_cb(encoded_data: bytes, topic: TopicT) -> None: decoded_message = self.decode(encoded_data, topic) callback(decoded_message, topic) - return super().subscribe(topic, wrapper_cb) # type: ignore[misc] + return super().subscribe(topic, wrapper_cb) # type: ignore[misc, no-any-return] class PickleEncoderMixin(PubSubEncoderMixin[TopicT, MsgT]): - def encode(self, msg: MsgT, *_: TopicT) -> bytes: + def encode(self, msg: MsgT, *_: TopicT) -> bytes: # type: ignore[return] try: return pickle.dumps(msg) except Exception as e: @@ -151,4 +151,4 @@ def encode(self, msg: MsgT, *_: TopicT) -> bytes: print("Tried to pickle:", msg) def decode(self, msg: bytes, _: TopicT) -> MsgT: - return pickle.loads(msg) + return pickle.loads(msg) # type: ignore[no-any-return] diff --git a/dimos/protocol/rpc/lcmrpc.py b/dimos/protocol/rpc/lcmrpc.py index 7ff98b1338..9da2e5bb80 100644 --- a/dimos/protocol/rpc/lcmrpc.py +++ b/dimos/protocol/rpc/lcmrpc.py @@ -18,7 +18,7 @@ from dimos.utils.generic import short_id -class LCMRPC(PassThroughPubSubRPC, PickleLCM): +class LCMRPC(PassThroughPubSubRPC, PickleLCM): # type: ignore[type-arg] def topicgen(self, name: str, req_or_res: bool) -> Topic: suffix = "res" if req_or_res else "req" topic = f"/rpc/{name}/{suffix}" diff --git a/dimos/protocol/rpc/off_test_pubsubrpc.py b/dimos/protocol/rpc/off_test_pubsubrpc.py index 940baad2f7..0bd65429da 100644 --- a/dimos/protocol/rpc/off_test_pubsubrpc.py +++ b/dimos/protocol/rpc/off_test_pubsubrpc.py @@ -22,7 +22,7 @@ from dimos.protocol.rpc.lcmrpc import LCMRPC from dimos.protocol.service.lcmservice import autoconf -testgrid: list[Callable] = [] +testgrid: list[Callable] = [] # type: ignore[type-arg] # test module we'll use for binding RPC methods @@ -48,7 +48,7 @@ def subtract(self, a: int, b: int) -> int: # LCMRPC (mixed in PassThroughPubSubRPC into lcm pubsub) @contextmanager -def lcm_rpc_context(): +def lcm_rpc_context(): # type: ignore[no-untyped-def] server = LCMRPC(autoconf=True) client = LCMRPC(autoconf=True) server.start() @@ -66,7 +66,7 @@ def lcm_rpc_context(): from dimos.protocol.rpc.redisrpc import RedisRPC @contextmanager - def redis_rpc_context(): + def redis_rpc_context(): # type: ignore[no-untyped-def] server = RedisRPC() client = RedisRPC() server.start() @@ -82,10 +82,10 @@ def redis_rpc_context(): @pytest.mark.parametrize("rpc_context", testgrid) -def test_basics(rpc_context) -> None: +def test_basics(rpc_context) -> None: # type: ignore[no-untyped-def] with rpc_context() as (server, client): - def remote_function(a: int, b: int): + def remote_function(a: int, b: int): # type: ignore[no-untyped-def] return a + b # You can bind an arbitrary function to arbitrary name @@ -97,7 +97,7 @@ def remote_function(a: int, b: int): msgs = [] - def receive_msg(response) -> None: + def receive_msg(response) -> None: # type: ignore[no-untyped-def] msgs.append(response) print(f"Received response: {response}") @@ -108,7 +108,7 @@ def receive_msg(response) -> None: @pytest.mark.parametrize("rpc_context", testgrid) -def test_module_autobind(rpc_context) -> None: +def test_module_autobind(rpc_context) -> None: # type: ignore[no-untyped-def] with rpc_context() as (server, client): module = MyModule() print("\n") @@ -130,7 +130,7 @@ def test_module_autobind(rpc_context) -> None: msgs = [] - def receive_msg(msg) -> None: + def receive_msg(msg) -> None: # type: ignore[no-untyped-def] msgs.append(msg) client.call("MyModule/add", ([1, 2], {}), receive_msg) @@ -146,7 +146,7 @@ def receive_msg(msg) -> None: # # can do blocking calls @pytest.mark.parametrize("rpc_context", testgrid) -def test_sync(rpc_context) -> None: +def test_sync(rpc_context) -> None: # type: ignore[no-untyped-def] with rpc_context() as (server, client): module = MyModule() print("\n") @@ -160,7 +160,7 @@ def test_sync(rpc_context) -> None: # # can do blocking calls @pytest.mark.parametrize("rpc_context", testgrid) -def test_kwargs(rpc_context) -> None: +def test_kwargs(rpc_context) -> None: # type: ignore[no-untyped-def] with rpc_context() as (server, client): module = MyModule() print("\n") @@ -173,7 +173,7 @@ def test_kwargs(rpc_context) -> None: # or async calls as well @pytest.mark.parametrize("rpc_context", testgrid) @pytest.mark.asyncio -async def test_async(rpc_context) -> None: +async def test_async(rpc_context) -> None: # type: ignore[no-untyped-def] with rpc_context() as (server, client): module = MyModule() print("\n") @@ -200,8 +200,8 @@ def add(self, a: int, b: int = 30) -> int: dimos = start(2) - module = dimos.deploy(MyModule) - caller = dimos.deploy(CallerModule, module.add) + module = dimos.deploy(MyModule) # type: ignore[attr-defined] + caller = dimos.deploy(CallerModule, module.add) # type: ignore[attr-defined] print("deployed", module) print("deployed", caller) @@ -213,4 +213,4 @@ def add(self, a: int, b: int = 30) -> int: # kwargs assert caller.add(1, b=1) == 2 - dimos.shutdown() + dimos.shutdown() # type: ignore[no-untyped-call] diff --git a/dimos/protocol/rpc/pubsubrpc.py b/dimos/protocol/rpc/pubsubrpc.py index 033cb7a5e2..6146261159 100644 --- a/dimos/protocol/rpc/pubsubrpc.py +++ b/dimos/protocol/rpc/pubsubrpc.py @@ -39,7 +39,7 @@ # (name, true_if_response_topic) -> TopicT TopicGen = Callable[[str, bool], TopicT] -MsgGen = Callable[[str, list], MsgT] +MsgGen = Callable[[str, list], MsgT] # type: ignore[type-arg] class RPCReq(TypedDict): @@ -69,13 +69,13 @@ def _encodeRPCReq(self, res: RPCReq) -> MsgT: ... @abstractmethod def _encodeRPCRes(self, res: RPCRes) -> MsgT: ... - def call(self, name: str, arguments: Args, cb: Callable | None): + def call(self, name: str, arguments: Args, cb: Callable | None): # type: ignore[no-untyped-def, type-arg] if cb is None: return self.call_nowait(name, arguments) return self.call_cb(name, arguments, cb) - def call_cb(self, name: str, arguments: Args, cb: Callable) -> Any: + def call_cb(self, name: str, arguments: Args, cb: Callable) -> Any: # type: ignore[type-arg] topic_req = self.topicgen(name, False) topic_res = self.topicgen(name, True) msg_id = float(time.time()) @@ -101,7 +101,7 @@ def call_nowait(self, name: str, arguments: Args) -> None: req: RPCReq = {"name": name, "args": arguments, "id": None} self.publish(topic_req, self._encodeRPCReq(req)) - def serve_rpc(self, f: FunctionType, name: str | None = None): + def serve_rpc(self, f: FunctionType, name: str | None = None): # type: ignore[no-untyped-def, override] if not name: name = f.__name__ @@ -140,15 +140,15 @@ def execute_and_respond() -> None: # simple PUBSUB RPC implementation that doesn't encode # special request/response messages, assumes pubsub implementation # supports generic dictionary pubsub -class PassThroughPubSubRPC(PubSubRPCMixin[TopicT, dict], Generic[TopicT]): - def _encodeRPCReq(self, req: RPCReq) -> dict: +class PassThroughPubSubRPC(PubSubRPCMixin[TopicT, dict], Generic[TopicT]): # type: ignore[type-arg] + def _encodeRPCReq(self, req: RPCReq) -> dict: # type: ignore[type-arg] return dict(req) - def _decodeRPCRes(self, msg: dict) -> RPCRes: + def _decodeRPCRes(self, msg: dict) -> RPCRes: # type: ignore[type-arg] return msg # type: ignore[return-value] - def _encodeRPCRes(self, res: RPCRes) -> dict: + def _encodeRPCRes(self, res: RPCRes) -> dict: # type: ignore[type-arg] return dict(res) - def _decodeRPCReq(self, msg: dict) -> RPCReq: + def _decodeRPCReq(self, msg: dict) -> RPCReq: # type: ignore[type-arg] return msg # type: ignore[return-value] diff --git a/dimos/protocol/rpc/redisrpc.py b/dimos/protocol/rpc/redisrpc.py index b0a715fe43..1c24f7a609 100644 --- a/dimos/protocol/rpc/redisrpc.py +++ b/dimos/protocol/rpc/redisrpc.py @@ -16,6 +16,6 @@ from dimos.protocol.rpc.pubsubrpc import PassThroughPubSubRPC -class RedisRPC(PassThroughPubSubRPC, Redis): +class RedisRPC(PassThroughPubSubRPC, Redis): # type: ignore[type-arg] def topicgen(self, name: str, req_or_res: bool) -> str: return f"/rpc/{name}/{'res' if req_or_res else 'req'}" diff --git a/dimos/protocol/rpc/spec.py b/dimos/protocol/rpc/spec.py index ee9f99e16b..ae9f45f11f 100644 --- a/dimos/protocol/rpc/spec.py +++ b/dimos/protocol/rpc/spec.py @@ -21,13 +21,13 @@ class Empty: ... -Args = tuple[list, dict[str, Any]] +Args = tuple[list, dict[str, Any]] # type: ignore[type-arg] # module that we can inspect for RPCs class RPCInspectable(Protocol): @property - def rpcs(self) -> dict[str, Callable]: ... + def rpcs(self) -> dict[str, Callable]: ... # type: ignore[type-arg] class RPCClient(Protocol): @@ -39,7 +39,7 @@ def call(self, name: str, arguments: Args, cb: None) -> None: ... @overload def call(self, name: str, arguments: Args, cb: Callable[[Any], None]) -> Callable[[], Any]: ... - def call(self, name: str, arguments: Args, cb: Callable | None) -> Callable[[], Any] | None: ... + def call(self, name: str, arguments: Args, cb: Callable | None) -> Callable[[], Any] | None: ... # type: ignore[type-arg] # we expect to crash if we don't get a return value after 10 seconds # but callers can override this timeout for extra long functions @@ -48,20 +48,20 @@ def call_sync( ) -> tuple[Any, Callable[[], None]]: event = threading.Event() - def receive_value(val) -> None: - event.result = val # attach to event + def receive_value(val) -> None: # type: ignore[no-untyped-def] + event.result = val # type: ignore[attr-defined] # attach to event event.set() unsub_fn = self.call(name, arguments, receive_value) if not event.wait(rpc_timeout): raise TimeoutError(f"RPC call to '{name}' timed out after {rpc_timeout} seconds") - return event.result, unsub_fn + return event.result, unsub_fn # type: ignore[attr-defined] async def call_async(self, name: str, arguments: Args) -> Any: loop = asyncio.get_event_loop() future = loop.create_future() - def receive_value(val) -> None: + def receive_value(val) -> None: # type: ignore[no-untyped-def] try: loop.call_soon_threadsafe(future.set_result, val) except Exception as e: @@ -73,14 +73,14 @@ def receive_value(val) -> None: class RPCServer(Protocol): - def serve_rpc(self, f: Callable, name: str) -> Callable[[], None]: ... + def serve_rpc(self, f: Callable, name: str) -> Callable[[], None]: ... # type: ignore[type-arg] def serve_module_rpc(self, module: RPCInspectable, name: str | None = None) -> None: for fname in module.rpcs.keys(): if not name: name = module.__class__.__name__ - def override_f(*args, fname=fname, **kwargs): + def override_f(*args, fname=fname, **kwargs): # type: ignore[no-untyped-def] return getattr(module, fname)(*args, **kwargs) topic = name + "/" + fname diff --git a/dimos/protocol/service/lcmservice.py b/dimos/protocol/service/lcmservice.py index 1b19a5cfeb..83e26e2dc0 100644 --- a/dimos/protocol/service/lcmservice.py +++ b/dimos/protocol/service/lcmservice.py @@ -36,7 +36,7 @@ def check_root() -> bool: """Return True if the current process is running as root (UID 0).""" try: - return os.geteuid() == 0 # type: ignore[attr-defined] + return os.geteuid() == 0 except AttributeError: # Platforms without geteuid (e.g. Windows) – assume non-root. return False @@ -229,7 +229,7 @@ class LCMService(Service[LCMConfig]): _call_thread_pool: ThreadPoolExecutor | None = None _call_thread_pool_lock: threading.RLock = threading.RLock() - def __init__(self, **kwargs) -> None: + def __init__(self, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(**kwargs) # we support passing an existing LCM instance @@ -244,7 +244,7 @@ def __init__(self, **kwargs) -> None: self._stop_event = threading.Event() self._thread = None - def __getstate__(self): + def __getstate__(self): # type: ignore[no-untyped-def] """Exclude unpicklable runtime attributes when serializing.""" state = self.__dict__.copy() # Remove unpicklable attributes @@ -256,7 +256,7 @@ def __getstate__(self): state.pop("_call_thread_pool_lock", None) return state - def __setstate__(self, state) -> None: + def __setstate__(self, state) -> None: # type: ignore[no-untyped-def] """Restore object from pickled state.""" self.__dict__.update(state) # Reinitialize runtime attributes diff --git a/dimos/protocol/service/spec.py b/dimos/protocol/service/spec.py index d55c1bfacf..469e9d8e61 100644 --- a/dimos/protocol/service/spec.py +++ b/dimos/protocol/service/spec.py @@ -22,13 +22,13 @@ class Configurable(Generic[ConfigT]): default_config: type[ConfigT] - def __init__(self, **kwargs) -> None: + def __init__(self, **kwargs) -> None: # type: ignore[no-untyped-def] self.config: ConfigT = self.default_config(**kwargs) class Service(Configurable[ConfigT], ABC): def start(self) -> None: - super().start() + super().start() # type: ignore[misc] def stop(self) -> None: - super().stop() + super().stop() # type: ignore[misc] diff --git a/dimos/protocol/skill/comms.py b/dimos/protocol/skill/comms.py index b0adecf5c5..3c50070e91 100644 --- a/dimos/protocol/skill/comms.py +++ b/dimos/protocol/skill/comms.py @@ -18,7 +18,7 @@ from typing import TYPE_CHECKING, Generic, TypeVar from dimos.protocol.pubsub.lcmpubsub import PickleLCM -from dimos.protocol.service import Service +from dimos.protocol.service import Service # type: ignore[attr-defined] from dimos.protocol.skill.type import SkillMsg if TYPE_CHECKING: @@ -32,10 +32,10 @@ class SkillCommsSpec: @abstractmethod - def publish(self, msg: SkillMsg) -> None: ... + def publish(self, msg: SkillMsg) -> None: ... # type: ignore[type-arg] @abstractmethod - def subscribe(self, cb: Callable[[SkillMsg], None]) -> None: ... + def subscribe(self, cb: Callable[[SkillMsg], None]) -> None: ... # type: ignore[type-arg] @abstractmethod def start(self) -> None: ... @@ -56,10 +56,10 @@ class PubSubCommsConfig(Generic[TopicT, MsgT]): # implementation of the SkillComms using any standard PubSub mechanism -class PubSubComms(Service[PubSubCommsConfig], SkillCommsSpec): - default_config: type[PubSubCommsConfig] = PubSubCommsConfig +class PubSubComms(Service[PubSubCommsConfig], SkillCommsSpec): # type: ignore[type-arg] + default_config: type[PubSubCommsConfig] = PubSubCommsConfig # type: ignore[type-arg] - def __init__(self, **kwargs) -> None: + def __init__(self, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(**kwargs) pubsub_config = getattr(self.config, "pubsub", None) if pubsub_config is not None: @@ -79,17 +79,17 @@ def start(self) -> None: def stop(self) -> None: self.pubsub.stop() - def publish(self, msg: SkillMsg) -> None: + def publish(self, msg: SkillMsg) -> None: # type: ignore[type-arg] self.pubsub.publish(self.config.topic, msg) - def subscribe(self, cb: Callable[[SkillMsg], None]) -> None: + def subscribe(self, cb: Callable[[SkillMsg], None]) -> None: # type: ignore[type-arg] self.pubsub.subscribe(self.config.topic, lambda msg, topic: cb(msg)) @dataclass -class LCMCommsConfig(PubSubCommsConfig[str, SkillMsg]): +class LCMCommsConfig(PubSubCommsConfig[str, SkillMsg]): # type: ignore[type-arg] topic: str = "/skill" - pubsub: type[PubSub] | PubSub | None = PickleLCM + pubsub: type[PubSub] | PubSub | None = PickleLCM # type: ignore[type-arg] # lcm needs to be started only if receiving # skill comms are broadcast only in modules so we don't autostart autostart: bool = False diff --git a/dimos/protocol/skill/coordinator.py b/dimos/protocol/skill/coordinator.py index a672ceacee..45ffeee814 100644 --- a/dimos/protocol/skill/coordinator.py +++ b/dimos/protocol/skill/coordinator.py @@ -30,7 +30,7 @@ from dimos.core import rpc from dimos.core.module import Module, get_loop from dimos.protocol.skill.comms import LCMSkillComms, SkillCommsSpec -from dimos.protocol.skill.skill import SkillConfig, SkillContainer +from dimos.protocol.skill.skill import SkillConfig, SkillContainer # type: ignore[attr-defined] from dimos.protocol.skill.type import MsgType, Output, Reducer, Return, SkillMsg, Stream from dimos.protocol.skill.utils import interpret_tool_call_args from dimos.utils.logging_config import setup_logger @@ -70,11 +70,11 @@ class SkillState: msg_count: int = 0 sent_tool_msg: bool = False - start_msg: SkillMsg[Literal[MsgType.start]] = None - end_msg: SkillMsg[Literal[MsgType.ret]] = None - error_msg: SkillMsg[Literal[MsgType.error]] = None - ret_msg: SkillMsg[Literal[MsgType.ret]] = None - reduced_stream_msg: list[SkillMsg[Literal[MsgType.reduced_stream]]] = None + start_msg: SkillMsg[Literal[MsgType.start]] = None # type: ignore[assignment] + end_msg: SkillMsg[Literal[MsgType.ret]] = None # type: ignore[assignment] + error_msg: SkillMsg[Literal[MsgType.error]] = None # type: ignore[assignment] + ret_msg: SkillMsg[Literal[MsgType.ret]] = None # type: ignore[assignment] + reduced_stream_msg: list[SkillMsg[Literal[MsgType.reduced_stream]]] = None # type: ignore[assignment] def __init__(self, call_id: str, name: str, skill_config: SkillConfig | None = None) -> None: super().__init__() @@ -101,22 +101,22 @@ def duration(self) -> float: else: return 0.0 - def content(self) -> dict[str, Any] | str | int | float | None: + def content(self) -> dict[str, Any] | str | int | float | None: # type: ignore[return] if self.state == SkillStateEnum.running: if self.reduced_stream_msg: - return self.reduced_stream_msg.content + return self.reduced_stream_msg.content # type: ignore[attr-defined, no-any-return] if self.state == SkillStateEnum.completed: if self.reduced_stream_msg: # are we a streaming skill? - return self.reduced_stream_msg.content - return self.ret_msg.content + return self.reduced_stream_msg.content # type: ignore[attr-defined, no-any-return] + return self.ret_msg.content # type: ignore[return-value] if self.state == SkillStateEnum.error: print("Error msg:", self.error_msg.content) if self.reduced_stream_msg: - (self.reduced_stream_msg.content + "\n" + self.error_msg.content) + (self.reduced_stream_msg.content + "\n" + self.error_msg.content) # type: ignore[attr-defined] else: - return self.error_msg.content + return self.error_msg.content # type: ignore[return-value] def agent_encode(self) -> ToolMessage | str: # tool call can emit a single ToolMessage @@ -126,7 +126,7 @@ def agent_encode(self) -> ToolMessage | str: if not self.sent_tool_msg: self.sent_tool_msg = True return ToolMessage( - self.content() or "Querying, please wait, you will receive a response soon.", + self.content() or "Querying, please wait, you will receive a response soon.", # type: ignore[arg-type] name=self.name, tool_call_id=self.call_id, ) @@ -142,11 +142,11 @@ def agent_encode(self) -> ToolMessage | str: ) # returns True if the agent should be called for this message - def handle_msg(self, msg: SkillMsg) -> bool: + def handle_msg(self, msg: SkillMsg) -> bool: # type: ignore[type-arg] self.msg_count += 1 if msg.type == MsgType.stream: self.state = SkillStateEnum.running - self.reduced_stream_msg = self.skill_config.reducer(self.reduced_stream_msg, msg) + self.reduced_stream_msg = self.skill_config.reducer(self.reduced_stream_msg, msg) # type: ignore[arg-type, assignment] if ( self.skill_config.stream == Stream.none @@ -263,7 +263,7 @@ def __str__(self) -> str: # It aggregates skills from static and dynamic containers, manages skill states, # and decides when to notify the agent about updates. class SkillCoordinator(Module): - default_config = SkillCoordinatorConfig + default_config = SkillCoordinatorConfig # type: ignore[assignment] empty: bool = True _static_containers: list[SkillContainer] @@ -313,7 +313,7 @@ def _ensure_updates_available(self) -> asyncio.Event: else: ... # print(f"[DEBUG] Reusing _updates_available event {id(self._updates_available)}") - return self._updates_available + return self._updates_available # type: ignore[return-value] @rpc def start(self) -> None: @@ -346,9 +346,9 @@ def __len__(self) -> int: # this can be converted to non-langchain json schema output # and langchain takes this output as well # just faster for now - def get_tools(self) -> list[dict]: + def get_tools(self) -> list[dict]: # type: ignore[type-arg] return [ - langchain_tool(skill_config.f) + langchain_tool(skill_config.f) # type: ignore[arg-type, misc] for skill_config in self.skills().values() if not skill_config.hide_skill ] @@ -382,7 +382,7 @@ def call_skill( arg_list, arg_keywords = interpret_tool_call_args(args) - return skill_config.call( + return skill_config.call( # type: ignore[no-any-return] call_id, *arg_list, **arg_keywords, @@ -392,7 +392,7 @@ def call_skill( # Updates local skill state (appends to streamed data if needed etc) # # Checks if agent needs to be notified (if ToolConfig has Return=call_agent or Stream=call_agent) - def handle_message(self, msg: SkillMsg) -> None: + def handle_message(self, msg: SkillMsg) -> None: # type: ignore[type-arg] if self._closed_coord: import traceback @@ -460,7 +460,7 @@ def has_passive_skills(self) -> bool: return False return True - async def wait_for_updates(self, timeout: float | None = None) -> True: + async def wait_for_updates(self, timeout: float | None = None) -> True: # type: ignore[valid-type] """Wait for skill updates to become available. This method should be called by the agent when it's ready to receive updates. @@ -540,8 +540,8 @@ def generate_snapshot(self, clear: bool = True) -> SkillStateDict: logger.info(f"Skill {skill_run.name} (call_id={call_id}) finished") to_delete.append(call_id) if skill_run.state == SkillStateEnum.error: - error_msg = skill_run.error_msg.content.get("msg", "Unknown error") - error_traceback = skill_run.error_msg.content.get( + error_msg = skill_run.error_msg.content.get("msg", "Unknown error") # type: ignore[union-attr] + error_traceback = skill_run.error_msg.content.get( # type: ignore[union-attr] "traceback", "No traceback available" ) @@ -560,7 +560,7 @@ def generate_snapshot(self, clear: bool = True) -> SkillStateDict: logger.debug( f"Resetting accumulator for skill {skill_run.name} (call_id={call_id})" ) - skill_run.reduced_stream_msg = None + skill_run.reduced_stream_msg = None # type: ignore[assignment] for call_id in to_delete: logger.debug(f"Call {call_id} finished, removing from state") diff --git a/dimos/protocol/skill/schema.py b/dimos/protocol/skill/schema.py index 49dc1caa37..0d3a019956 100644 --- a/dimos/protocol/skill/schema.py +++ b/dimos/protocol/skill/schema.py @@ -16,7 +16,7 @@ from typing import Union, get_args, get_origin -def python_type_to_json_schema(python_type) -> dict: +def python_type_to_json_schema(python_type) -> dict: # type: ignore[no-untyped-def, type-arg] """Convert Python type annotations to JSON Schema format.""" # Handle None/NoneType if python_type is type(None) or python_type is None: @@ -60,7 +60,7 @@ def python_type_to_json_schema(python_type) -> dict: return type_map.get(python_type, {"type": "string"}) -def function_to_schema(func) -> dict: +def function_to_schema(func) -> dict: # type: ignore[no-untyped-def, type-arg] """Convert a function to OpenAI function schema format.""" try: signature = inspect.signature(func) diff --git a/dimos/protocol/skill/skill.py b/dimos/protocol/skill/skill.py index 7ad260eaa5..764f238460 100644 --- a/dimos/protocol/skill/skill.py +++ b/dimos/protocol/skill/skill.py @@ -63,14 +63,14 @@ def rpc(fn: Callable[..., Any]) -> Callable[..., Any]: def skill( - reducer: Reducer = Reducer.latest, + reducer: Reducer = Reducer.latest, # type: ignore[assignment] stream: Stream = Stream.none, ret: Return = Return.call_agent, output: Output = Output.standard, hide_skill: bool = False, -) -> Callable: +) -> Callable: # type: ignore[type-arg] def decorator(f: Callable[..., Any]) -> Any: - def wrapper(self, *args, **kwargs): + def wrapper(self, *args, **kwargs): # type: ignore[no-untyped-def] skill = f"{f.__name__}" call_id = kwargs.get("call_id", None) @@ -95,7 +95,7 @@ def wrapper(self, *args, **kwargs): skill_config = SkillConfig( name=f.__name__, - reducer=reducer, + reducer=reducer, # type: ignore[arg-type] stream=stream, # if stream is passive, ret must be passive too ret=ret.passive if stream == Stream.passive else ret, @@ -121,7 +121,7 @@ class SkillContainerConfig: def threaded(f: Callable[..., Any]) -> Callable[..., None]: """Decorator to run a function in a thread pool.""" - def wrapper(self, *args, **kwargs): + def wrapper(self, *args, **kwargs): # type: ignore[no-untyped-def] if self._skill_thread_pool is None: self._skill_thread_pool = ThreadPoolExecutor( max_workers=50, thread_name_prefix="skill_worker" @@ -170,7 +170,7 @@ def stop(self) -> None: # Continue the MRO chain if there's a parent stop() method if hasattr(super(), "stop"): - super().stop() + super().stop() # type: ignore[misc] # TODO: figure out standard args/kwargs passing format, # use same interface as skill coordinator call_skill method @@ -195,7 +195,7 @@ def call_skill( # check if the skill returned a coroutine, if it is, block until it resolves if isinstance(val, asyncio.Future): - val = asyncio.run(val) + val = asyncio.run(val) # type: ignore[arg-type] # check if the skill is a generator, if it is, we need to iterate over it if hasattr(val, "__iter__") and not isinstance(val, str): diff --git a/dimos/protocol/skill/type.py b/dimos/protocol/skill/type.py index 9b1c4ce5f5..bef6eca407 100644 --- a/dimos/protocol/skill/type.py +++ b/dimos/protocol/skill/type.py @@ -59,15 +59,15 @@ class SkillConfig: ret: Return output: Output schema: dict[str, Any] - f: Callable | None = None + f: Callable | None = None # type: ignore[type-arg] autostart: bool = False hide_skill: bool = False - def bind(self, f: Callable) -> SkillConfig: + def bind(self, f: Callable) -> SkillConfig: # type: ignore[type-arg] self.f = f return self - def call(self, call_id, *args, **kwargs) -> Any: + def call(self, call_id, *args, **kwargs) -> Any: # type: ignore[no-untyped-def] if self.f is None: raise ValueError( "Function is not bound to the SkillConfig. This should be called only within AgentListener." @@ -101,8 +101,8 @@ class MsgType(Enum): def maybe_encode(something: Any) -> str: if hasattr(something, "agent_encode"): - return something.agent_encode() - return something + return something.agent_encode() # type: ignore[no-any-return] + return something # type: ignore[no-any-return] class SkillMsg(Timestamped, Generic[M]): @@ -110,7 +110,7 @@ class SkillMsg(Timestamped, Generic[M]): type: M call_id: str skill_name: str - content: str | int | float | dict | list + content: str | int | float | dict | list # type: ignore[type-arg] def __init__( self, @@ -136,7 +136,7 @@ def end(self) -> bool: def start(self) -> bool: return self.type == MsgType.start - def __str__(self) -> str: + def __str__(self) -> str: # type: ignore[return] time_ago = time.time() - self.ts if self.type == MsgType.start: @@ -167,7 +167,7 @@ def __str__(self) -> str: SimpleReducerF = Callable[[A | None, C], A] -def make_reducer(simple_reducer: SimpleReducerF) -> ReducerF: +def make_reducer(simple_reducer: SimpleReducerF) -> ReducerF: # type: ignore[type-arg] """ Converts a naive reducer function into a standard reducer function. The naive reducer function should accept an accumulator and a message, @@ -214,7 +214,7 @@ def sum_reducer( ) -> SkillMsg[Literal[MsgType.reduced_stream]]: """Sum reducer that adds values together.""" acc_value = accumulator.content if accumulator else None - new_value = acc_value + msg.content if acc_value else msg.content + new_value = acc_value + msg.content if acc_value else msg.content # type: ignore[operator] return _make_skill_msg(msg, new_value) @@ -232,7 +232,7 @@ def all_reducer( ) -> SkillMsg[Literal[MsgType.reduced_stream]]: """All reducer that collects all values into a list.""" acc_value = accumulator.content if accumulator else None - new_value = [*acc_value, msg.content] if acc_value else [msg.content] + new_value = [*acc_value, msg.content] if acc_value else [msg.content] # type: ignore[misc] return _make_skill_msg(msg, new_value) @@ -242,7 +242,7 @@ def accumulate_list( ) -> SkillMsg[Literal[MsgType.reduced_stream]]: """All reducer that collects all values into a list.""" acc_value = accumulator.content if accumulator else [] - return _make_skill_msg(msg, acc_value + msg.content) + return _make_skill_msg(msg, acc_value + msg.content) # type: ignore[operator] def accumulate_dict( @@ -251,7 +251,7 @@ def accumulate_dict( ) -> SkillMsg[Literal[MsgType.reduced_stream]]: """All reducer that collects all values into a list.""" acc_value = accumulator.content if accumulator else {} - return _make_skill_msg(msg, {**acc_value, **msg.content}) + return _make_skill_msg(msg, {**acc_value, **msg.content}) # type: ignore[dict-item] def accumulate_string( @@ -260,7 +260,7 @@ def accumulate_string( ) -> SkillMsg[Literal[MsgType.reduced_stream]]: """All reducer that collects all values into a list.""" acc_value = accumulator.content if accumulator else "" - return _make_skill_msg(msg, acc_value + "\n" + msg.content) + return _make_skill_msg(msg, acc_value + "\n" + msg.content) # type: ignore[operator] class Reducer: diff --git a/dimos/protocol/tf/tf.py b/dimos/protocol/tf/tf.py index f60e216176..1a2cce3c4d 100644 --- a/dimos/protocol/tf/tf.py +++ b/dimos/protocol/tf/tf.py @@ -24,7 +24,7 @@ from dimos.msgs.tf2_msgs import TFMessage from dimos.protocol.pubsub.lcmpubsub import LCM, Topic from dimos.protocol.pubsub.spec import PubSub -from dimos.protocol.service.lcmservice import Service +from dimos.protocol.service.lcmservice import Service # type: ignore[attr-defined] from dimos.types.timestamped import TimestampedCollection CONFIG = TypeVar("CONFIG") @@ -39,7 +39,7 @@ class TFConfig: # generic specification for transform service class TFSpec(Service[TFConfig]): - def __init__(self, **kwargs) -> None: + def __init__(self, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(**kwargs) @abstractmethod @@ -52,7 +52,7 @@ def get_frames(self) -> set[str]: return set() @abstractmethod - def get( + def get( # type: ignore[no-untyped-def] self, parent_frame: str, child_frame: str, @@ -81,7 +81,7 @@ def add(self, transform: Transform) -> None: super().add(transform) self._prune_old_transforms(transform.ts) - def _prune_old_transforms(self, current_time) -> None: + def _prune_old_transforms(self, current_time) -> None: # type: ignore[no-untyped-def] if not self._items: return @@ -172,17 +172,17 @@ def get_transform( # Check forward direction key = (parent_frame, child_frame) if key in self.buffers: - return self.buffers[key].get(time_point, time_tolerance) + return self.buffers[key].get(time_point, time_tolerance) # type: ignore[arg-type] # Check reverse direction and return inverse reverse_key = (child_frame, parent_frame) if reverse_key in self.buffers: - transform = self.buffers[reverse_key].get(time_point, time_tolerance) + transform = self.buffers[reverse_key].get(time_point, time_tolerance) # type: ignore[arg-type] return transform.inverse() if transform else None return None - def get(self, *args, **kwargs) -> Transform | None: + def get(self, *args, **kwargs) -> Transform | None: # type: ignore[no-untyped-def] simple = self.get_transform(*args, **kwargs) if simple is not None: return simple @@ -267,14 +267,14 @@ def __str__(self) -> str: @dataclass class PubSubTFConfig(TFConfig): topic: Topic | None = None # Required field but needs default for dataclass inheritance - pubsub: type[PubSub] | PubSub | None = None + pubsub: type[PubSub] | PubSub | None = None # type: ignore[type-arg] autostart: bool = True class PubSubTF(MultiTBuffer, TFSpec): default_config: type[PubSubTFConfig] = PubSubTFConfig - def __init__(self, **kwargs) -> None: + def __init__(self, **kwargs) -> None: # type: ignore[no-untyped-def] TFSpec.__init__(self, **kwargs) MultiTBuffer.__init__(self, self.config.buffer_size) @@ -287,7 +287,7 @@ def __init__(self, **kwargs) -> None: else: raise ValueError("PubSub configuration is missing") - if self.config.autostart: + if self.config.autostart: # type: ignore[attr-defined] self.start() def start(self, sub: bool = True) -> None: @@ -341,7 +341,7 @@ def receive_msg(self, msg: TFMessage, topic: Topic) -> None: @dataclass class LCMPubsubConfig(PubSubTFConfig): topic: Topic = field(default_factory=lambda: Topic("/tf", TFMessage)) - pubsub: type[PubSub] | PubSub | None = LCM + pubsub: type[PubSub] | PubSub | None = LCM # type: ignore[type-arg] autostart: bool = True diff --git a/dimos/protocol/tf/tflcmcpp.py b/dimos/protocol/tf/tflcmcpp.py index 0d5b31b9b6..c68210e187 100644 --- a/dimos/protocol/tf/tflcmcpp.py +++ b/dimos/protocol/tf/tflcmcpp.py @@ -36,10 +36,10 @@ class TFLCM(TFSpec, LCMService): default_config = Union[TFConfig, LCMConfig] - def __init__(self, **kwargs) -> None: + def __init__(self, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(**kwargs) - import tf_lcm_py as tf + import tf_lcm_py as tf # type: ignore[import-not-found] self.l = tf.LCM() self.buffer = tf.Buffer(self.config.buffer_size) @@ -58,7 +58,7 @@ def send_static(self, *args: Transform) -> None: for t in args: self.static_broadcaster.send_static_transform(t) - def lookup( + def lookup( # type: ignore[no-untyped-def] self, parent_frame: str, child_frame: str, @@ -81,7 +81,7 @@ def can_transform( if isinstance(time_point, float): time_point = datetime.fromtimestamp(time_point) - return self.buffer.can_transform(parent_frame, child_frame, time_point) + return self.buffer.can_transform(parent_frame, child_frame, time_point) # type: ignore[no-any-return] def get_frames(self) -> set[str]: return set(self.buffer.get_all_frame_names()) diff --git a/dimos/robot/agilex/piper_arm.py b/dimos/robot/agilex/piper_arm.py index 642d39c7cb..c1363a496c 100644 --- a/dimos/robot/agilex/piper_arm.py +++ b/dimos/robot/agilex/piper_arm.py @@ -15,7 +15,7 @@ import asyncio # Import LCM message types -from dimos_lcm.sensor_msgs import CameraInfo +from dimos_lcm.sensor_msgs import CameraInfo # type: ignore[import-untyped] from dimos import core from dimos.hardware.camera.zed import ZEDModule @@ -39,7 +39,7 @@ def __init__(self, robot_capabilities: list[RobotCapability] | None = None) -> N self.dimos = None self.stereo_camera = None self.manipulation_interface = None - self.skill_library = SkillLibrary() + self.skill_library = SkillLibrary() # type: ignore[assignment] # Initialize capabilities self.capabilities = robot_capabilities or [ @@ -50,15 +50,15 @@ def __init__(self, robot_capabilities: list[RobotCapability] | None = None) -> N async def start(self) -> None: """Start the robot modules.""" # Start Dimos - self.dimos = core.start(2) # Need 2 workers for ZED and manipulation modules + self.dimos = core.start(2) # type: ignore[assignment] # Need 2 workers for ZED and manipulation modules self.foxglove_bridge = FoxgloveBridge() # Enable LCM auto-configuration - pubsub.lcm.autoconf() + pubsub.lcm.autoconf() # type: ignore[attr-defined] # Deploy ZED module logger.info("Deploying ZED module...") - self.stereo_camera = self.dimos.deploy( + self.stereo_camera = self.dimos.deploy( # type: ignore[attr-defined] ZEDModule, camera_id=0, resolution="HD720", @@ -70,43 +70,43 @@ async def start(self) -> None: ) # Configure ZED LCM transports - self.stereo_camera.color_image.transport = core.LCMTransport("/zed/color_image", Image) - self.stereo_camera.depth_image.transport = core.LCMTransport("/zed/depth_image", Image) - self.stereo_camera.camera_info.transport = core.LCMTransport("/zed/camera_info", CameraInfo) + self.stereo_camera.color_image.transport = core.LCMTransport("/zed/color_image", Image) # type: ignore[attr-defined] + self.stereo_camera.depth_image.transport = core.LCMTransport("/zed/depth_image", Image) # type: ignore[attr-defined] + self.stereo_camera.camera_info.transport = core.LCMTransport("/zed/camera_info", CameraInfo) # type: ignore[attr-defined] # Deploy manipulation module logger.info("Deploying manipulation module...") - self.manipulation_interface = self.dimos.deploy(ManipulationModule) + self.manipulation_interface = self.dimos.deploy(ManipulationModule) # type: ignore[attr-defined] # Connect manipulation inputs to ZED outputs - self.manipulation_interface.rgb_image.connect(self.stereo_camera.color_image) - self.manipulation_interface.depth_image.connect(self.stereo_camera.depth_image) - self.manipulation_interface.camera_info.connect(self.stereo_camera.camera_info) + self.manipulation_interface.rgb_image.connect(self.stereo_camera.color_image) # type: ignore[attr-defined] + self.manipulation_interface.depth_image.connect(self.stereo_camera.depth_image) # type: ignore[attr-defined] + self.manipulation_interface.camera_info.connect(self.stereo_camera.camera_info) # type: ignore[attr-defined] # Configure manipulation output - self.manipulation_interface.viz_image.transport = core.LCMTransport( + self.manipulation_interface.viz_image.transport = core.LCMTransport( # type: ignore[attr-defined] "/manipulation/viz", Image ) # Print module info logger.info("Modules configured:") print("\nZED Module:") - print(self.stereo_camera.io()) + print(self.stereo_camera.io()) # type: ignore[attr-defined] print("\nManipulation Module:") - print(self.manipulation_interface.io()) + print(self.manipulation_interface.io()) # type: ignore[attr-defined] # Start modules logger.info("Starting modules...") self.foxglove_bridge.start() - self.stereo_camera.start() - self.manipulation_interface.start() + self.stereo_camera.start() # type: ignore[attr-defined] + self.manipulation_interface.start() # type: ignore[attr-defined] # Give modules time to initialize await asyncio.sleep(2) logger.info("PiperArmRobot initialized and started") - def pick_and_place( + def pick_and_place( # type: ignore[no-untyped-def] self, pick_x: int, pick_y: int, place_x: int | None = None, place_y: int | None = None ): """Execute pick and place task. @@ -126,7 +126,7 @@ def pick_and_place( logger.error("Manipulation module not initialized") return False - def handle_keyboard_command(self, key: str): + def handle_keyboard_command(self, key: str): # type: ignore[no-untyped-def] """Pass keyboard commands to manipulation module. Args: @@ -163,7 +163,7 @@ def stop(self) -> None: async def run_piper_arm() -> None: """Run the Piper Arm robot.""" - robot = PiperArmRobot() + robot = PiperArmRobot() # type: ignore[abstract] await robot.start() @@ -174,7 +174,7 @@ async def run_piper_arm() -> None: except KeyboardInterrupt: logger.info("Keyboard interrupt received") finally: - await robot.stop() + await robot.stop() # type: ignore[func-returns-value] if __name__ == "__main__": diff --git a/dimos/robot/agilex/run.py b/dimos/robot/agilex/run.py index 90258e5d82..4958883488 100644 --- a/dimos/robot/agilex/run.py +++ b/dimos/robot/agilex/run.py @@ -71,7 +71,7 @@ Remember: You're here to assist with manipulation tasks. Be helpful, precise, and always prioritize safe operation of the robot.""" -def main(): +def main(): # type: ignore[no-untyped-def] """Main entry point.""" print("\n" + "=" * 60) print("Piper Arm Robot with Claude Agent") @@ -94,7 +94,7 @@ def main(): logger.info("Starting Piper Arm Robot with Agent") # Create robot instance - robot = PiperArmRobot() + robot = PiperArmRobot() # type: ignore[abstract] try: # Start the robot (this is async, so we need asyncio.run) @@ -103,7 +103,7 @@ def main(): logger.info("Robot initialized successfully") # Set up skill library - skills = robot.get_skills() + skills = robot.get_skills() # type: ignore[no-untyped-call] skills.add(PickAndPlace) skills.add(KillSkill) @@ -114,12 +114,12 @@ def main(): logger.info(f"Skills registered: {[skill.__name__ for skill in skills.get_class_skills()]}") # Set up streams for agent and web interface - agent_response_subject = rx.subject.Subject() + agent_response_subject = rx.subject.Subject() # type: ignore[var-annotated] agent_response_stream = agent_response_subject.pipe(ops.share()) - audio_subject = rx.subject.Subject() + audio_subject = rx.subject.Subject() # type: ignore[var-annotated] # Set up streams for web interface - streams = {} + streams = {} # type: ignore[var-annotated] text_streams = { "agent_responses": agent_response_stream, @@ -136,7 +136,7 @@ def main(): raise # Set up speech-to-text - stt_node = stt() + stt_node = stt() # type: ignore[no-untyped-call] stt_node.consume_audio(audio_subject.pipe(ops.share())) # Create Claude agent @@ -155,7 +155,7 @@ def main(): agent.get_response_observable().subscribe(lambda x: agent_response_subject.on_next(x)) # Set up text-to-speech for agent responses - tts_node = tts() + tts_node = tts() # type: ignore[no-untyped-call] tts_node.consume_text(agent.get_response_observable()) logger.info("=" * 60) @@ -187,4 +187,4 @@ def main(): if __name__ == "__main__": - main() + main() # type: ignore[no-untyped-call] diff --git a/dimos/robot/all_blueprints.py b/dimos/robot/all_blueprints.py index 8a57589cff..0838ce66fc 100644 --- a/dimos/robot/all_blueprints.py +++ b/dimos/robot/all_blueprints.py @@ -75,11 +75,11 @@ def get_blueprint_by_name(name: str) -> ModuleBlueprintSet: raise ValueError(f"Unknown blueprint set name: {name}") module_path, attr = all_blueprints[name].split(":") module = __import__(module_path, fromlist=[attr]) - return getattr(module, attr) + return getattr(module, attr) # type: ignore[no-any-return] def get_module_by_name(name: str) -> ModuleBlueprintSet: if name not in all_modules: raise ValueError(f"Unknown module name: {name}") python_module = __import__(all_modules[name], fromlist=[name]) - return getattr(python_module, name)() + return getattr(python_module, name)() # type: ignore[no-any-return] diff --git a/dimos/robot/cli/dimos.py b/dimos/robot/cli/dimos.py index 5d08f3036a..28e046d22b 100644 --- a/dimos/robot/cli/dimos.py +++ b/dimos/robot/cli/dimos.py @@ -24,7 +24,7 @@ from dimos.protocol import pubsub from dimos.robot.all_blueprints import all_blueprints, get_blueprint_by_name, get_module_by_name -RobotType = Enum("RobotType", {key.replace("-", "_").upper(): key for key in all_blueprints.keys()}) +RobotType = Enum("RobotType", {key.replace("-", "_").upper(): key for key in all_blueprints.keys()}) # type: ignore[misc] main = typer.Typer( help="Dimensional CLI", @@ -32,7 +32,7 @@ ) -def create_dynamic_callback(): +def create_dynamic_callback(): # type: ignore[no-untyped-def] fields = GlobalConfig.model_fields # Build the function signature dynamically @@ -86,34 +86,34 @@ def create_dynamic_callback(): ) params.append(param) - def callback(**kwargs) -> None: + def callback(**kwargs) -> None: # type: ignore[no-untyped-def] ctx = kwargs.pop("ctx") overrides = {k: v for k, v in kwargs.items() if v is not None} ctx.obj = GlobalConfig().model_copy(update=overrides) - callback.__signature__ = inspect.Signature(params) + callback.__signature__ = inspect.Signature(params) # type: ignore[attr-defined] return callback -main.callback()(create_dynamic_callback()) +main.callback()(create_dynamic_callback()) # type: ignore[no-untyped-call] @main.command() def run( ctx: typer.Context, robot_type: RobotType = typer.Argument(..., help="Type of robot to run"), - extra_modules: list[str] = typer.Option( + extra_modules: list[str] = typer.Option( # type: ignore[valid-type] [], "--extra-module", help="Extra modules to add to the blueprint" ), ) -> None: """Start a robot blueprint""" config: GlobalConfig = ctx.obj - pubsub.lcm.autoconf() + pubsub.lcm.autoconf() # type: ignore[attr-defined] blueprint = get_blueprint_by_name(robot_type.value) if extra_modules: - loaded_modules = [get_module_by_name(mod_name) for mod_name in extra_modules] + loaded_modules = [get_module_by_name(mod_name) for mod_name in extra_modules] # type: ignore[attr-defined] blueprint = autoconnect(blueprint, *loaded_modules) dimos = blueprint.build(global_config=config) diff --git a/dimos/robot/connection_interface.py b/dimos/robot/connection_interface.py index 6480827214..ace6557a3e 100644 --- a/dimos/robot/connection_interface.py +++ b/dimos/robot/connection_interface.py @@ -45,7 +45,7 @@ def move(self, velocity: Vector, duration: float = 0.0) -> bool: pass @abstractmethod - def get_video_stream(self, fps: int = 30) -> Observable | None: + def get_video_stream(self, fps: int = 30) -> Observable | None: # type: ignore[type-arg] """Get the video stream from the robot's camera. Args: diff --git a/dimos/robot/foxglove_bridge.py b/dimos/robot/foxglove_bridge.py index b9b1832042..063383db80 100644 --- a/dimos/robot/foxglove_bridge.py +++ b/dimos/robot/foxglove_bridge.py @@ -17,7 +17,7 @@ import threading # this is missing, I'm just trying to import lcm_foxglove_bridge.py from dimos_lcm -from dimos_lcm.foxglove_bridge import FoxgloveBridge as LCMFoxgloveBridge +from dimos_lcm.foxglove_bridge import FoxgloveBridge as LCMFoxgloveBridge # type: ignore[import-untyped] from dimos.core import DimosCluster, Module, rpc @@ -29,7 +29,7 @@ class FoxgloveBridge(Module): _thread: threading.Thread _loop: asyncio.AbstractEventLoop - def __init__(self, *args, shm_channels=None, jpeg_shm_channels=None, **kwargs) -> None: + def __init__(self, *args, shm_channels=None, jpeg_shm_channels=None, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(*args, **kwargs) self.shm_channels = shm_channels or [] self.jpeg_shm_channels = jpeg_shm_channels or [] @@ -43,9 +43,9 @@ def run_bridge() -> None: asyncio.set_event_loop(self._loop) try: for logger in ["lcm_foxglove_bridge", "FoxgloveServer"]: - logger = logging.getLogger(logger) - logger.setLevel(logging.ERROR) - for handler in logger.handlers: + logger = logging.getLogger(logger) # type: ignore[assignment] + logger.setLevel(logging.ERROR) # type: ignore[attr-defined] + for handler in logger.handlers: # type: ignore[attr-defined] handler.setLevel(logging.ERROR) bridge = LCMFoxgloveBridge( @@ -82,12 +82,12 @@ def deploy( "/lidar#sensor_msgs.PointCloud2", "/map#sensor_msgs.PointCloud2", ] - foxglove_bridge = dimos.deploy( + foxglove_bridge = dimos.deploy( # type: ignore[attr-defined] FoxgloveBridge, shm_channels=shm_channels, ) foxglove_bridge.start() - return foxglove_bridge + return foxglove_bridge # type: ignore[no-any-return] foxglove_bridge = FoxgloveBridge.blueprint diff --git a/dimos/robot/position_stream.py b/dimos/robot/position_stream.py index 8cb5966b24..b2c4979d22 100644 --- a/dimos/robot/position_stream.py +++ b/dimos/robot/position_stream.py @@ -21,8 +21,8 @@ import logging import time -from geometry_msgs.msg import PoseStamped -from nav_msgs.msg import Odometry +from geometry_msgs.msg import PoseStamped # type: ignore[attr-defined] +from nav_msgs.msg import Odometry # type: ignore[attr-defined] from rclpy.node import Node from reactivex import Observable, Subject, operators as ops @@ -60,12 +60,12 @@ def __init__( self.pose_topic = pose_topic self.use_odometry = use_odometry - self._subject = Subject() + self._subject = Subject() # type: ignore[var-annotated] self.last_position = None self.last_update_time = None - self._create_subscription() + self._create_subscription() # type: ignore[no-untyped-call] logger.info( f"PositionStreamProvider initialized with " @@ -73,7 +73,7 @@ def __init__( f"{odometry_topic if use_odometry else pose_topic}" ) - def _create_subscription(self): + def _create_subscription(self): # type: ignore[no-untyped-def] """Create the appropriate ROS subscription based on configuration.""" if self.use_odometry: self.subscription = self.ros_node.create_subscription( @@ -128,13 +128,13 @@ def _update_position(self, x: float, y: float) -> None: update_rate = 1.0 / (current_time - self.last_update_time) logger.debug(f"Position update rate: {update_rate:.1f} Hz") - self.last_position = position - self.last_update_time = current_time + self.last_position = position # type: ignore[assignment] + self.last_update_time = current_time # type: ignore[assignment] self._subject.on_next(position) logger.debug(f"Position updated: ({x:.2f}, {y:.2f})") - def get_position_stream(self) -> Observable: + def get_position_stream(self) -> Observable: # type: ignore[type-arg] """ Get an Observable stream of position updates. diff --git a/dimos/robot/robot.py b/dimos/robot/robot.py index 1b0ca7041f..508aa592e1 100644 --- a/dimos/robot/robot.py +++ b/dimos/robot/robot.py @@ -43,7 +43,7 @@ def has_capability(self, capability: RobotCapability) -> bool: """ return capability in self.capabilities - def get_skills(self): + def get_skills(self): # type: ignore[no-untyped-def] """Get the robot's skill library. Returns: diff --git a/dimos/robot/ros_bridge.py b/dimos/robot/ros_bridge.py index b067f88a22..c0740a4ca8 100644 --- a/dimos/robot/ros_bridge.py +++ b/dimos/robot/ros_bridge.py @@ -23,13 +23,13 @@ from rclpy.node import Node from rclpy.qos import QoSDurabilityPolicy, QoSHistoryPolicy, QoSProfile, QoSReliabilityPolicy except ImportError: - rclpy = None - SingleThreadedExecutor = None - Node = None - QoSProfile = None - QoSReliabilityPolicy = None - QoSHistoryPolicy = None - QoSDurabilityPolicy = None + rclpy = None # type: ignore[assignment] + SingleThreadedExecutor = None # type: ignore[assignment, misc] + Node = None # type: ignore[assignment, misc] + QoSProfile = None # type: ignore[assignment, misc] + QoSReliabilityPolicy = None # type: ignore[assignment, misc] + QoSHistoryPolicy = None # type: ignore[assignment, misc] + QoSDurabilityPolicy = None # type: ignore[assignment, misc] from dimos.core.resource import Resource from dimos.protocol.pubsub.lcmpubsub import LCM, Topic @@ -54,7 +54,7 @@ def __init__(self, node_name: str = "dimos_ros_bridge") -> None: Args: node_name: Name for the ROS node (default: "dimos_ros_bridge") """ - if not rclpy.ok(): + if not rclpy.ok(): # type: ignore[attr-defined] rclpy.init() self.node = Node(node_name) @@ -69,7 +69,7 @@ def __init__(self, node_name: str = "dimos_ros_bridge") -> None: self._bridges: dict[str, dict[str, Any]] = {} - self._qos = QoSProfile( + self._qos = QoSProfile( # type: ignore[no-untyped-call] reliability=QoSReliabilityPolicy.RELIABLE, history=QoSHistoryPolicy.KEEP_LAST, durability=QoSDurabilityPolicy.VOLATILE, @@ -84,9 +84,9 @@ def start(self) -> None: def stop(self) -> None: """Shutdown the bridge and clean up resources.""" self._executor.shutdown() - self.node.destroy_node() + self.node.destroy_node() # type: ignore[no-untyped-call] - if rclpy.ok(): + if rclpy.ok(): # type: ignore[attr-defined] rclpy.shutdown() logger.info("ROSBridge shutdown complete") @@ -138,7 +138,7 @@ def add_topic( if direction == BridgeDirection.ROS_TO_DIMOS: - def ros_callback(msg) -> None: + def ros_callback(msg) -> None: # type: ignore[no-untyped-def] self._ros_to_dimos(msg, dimos_topic, dimos_type, topic_name) ros_subscription = self.node.create_subscription( @@ -149,7 +149,7 @@ def ros_callback(msg) -> None: elif direction == BridgeDirection.DIMOS_TO_ROS: ros_publisher = self.node.create_publisher(ros_type, ros_topic_name, self._qos) - def dimos_callback(msg, _topic) -> None: + def dimos_callback(msg, _topic) -> None: # type: ignore[no-untyped-def] self._dimos_to_ros(msg, ros_publisher, topic_name) dimos_subscription = self.lcm.subscribe(dimos_topic, dimos_callback) @@ -190,10 +190,10 @@ def _ros_to_dimos( dimos_type: DIMOS message type topic_name: Name of the topic for tracking """ - dimos_msg = dimos_type.from_ros_msg(ros_msg) + dimos_msg = dimos_type.from_ros_msg(ros_msg) # type: ignore[attr-defined] self.lcm.publish(dimos_topic, dimos_msg) - def _dimos_to_ros(self, dimos_msg: Any, ros_publisher, _topic_name: str) -> None: + def _dimos_to_ros(self, dimos_msg: Any, ros_publisher, _topic_name: str) -> None: # type: ignore[no-untyped-def] """Convert DIMOS message to ROS and publish. Args: diff --git a/dimos/robot/ros_command_queue.py b/dimos/robot/ros_command_queue.py index 770f44e1a6..b6e07063dc 100644 --- a/dimos/robot/ros_command_queue.py +++ b/dimos/robot/ros_command_queue.py @@ -57,7 +57,7 @@ class ROSCommand(NamedTuple): id: str # Unique ID for tracking cmd_type: CommandType # Type of command - execute_func: Callable # Function to execute the command + execute_func: Callable # type: ignore[type-arg] # Function to execute the command params: dict[str, Any] # Parameters for the command (for debugging/logging) priority: int # Priority level (lower is higher priority) timeout: float # How long to wait for this command to complete @@ -73,7 +73,7 @@ class ROSCommandQueue: def __init__( self, - webrtc_func: Callable, + webrtc_func: Callable, # type: ignore[type-arg] is_ready_func: Callable[[], bool] | None = None, is_busy_func: Callable[[], bool] | None = None, debug: bool = True, @@ -93,7 +93,7 @@ def __init__( self._debug = debug # Queue of commands to process - self._queue = PriorityQueue() + self._queue = PriorityQueue() # type: ignore[var-annotated] self._current_command = None self._last_command_time = 0 @@ -110,7 +110,7 @@ def __init__( self._command_count = 0 self._success_count = 0 self._failure_count = 0 - self._command_history = [] + self._command_history = [] # type: ignore[var-annotated] self._max_queue_wait_time = ( 30.0 # Maximum time to wait for robot to be ready before forcing @@ -125,8 +125,8 @@ def start(self) -> None: return self._should_stop = False - self._queue_thread = threading.Thread(target=self._process_queue, daemon=True) - self._queue_thread.start() + self._queue_thread = threading.Thread(target=self._process_queue, daemon=True) # type: ignore[assignment] + self._queue_thread.start() # type: ignore[attr-defined] logger.info("Queue processing thread started") def stop(self, timeout: float = 2.0) -> None: @@ -206,7 +206,7 @@ def execute_webrtc() -> bool: time.sleep(stabilization_delay) # Wait for the robot to complete the command (timeout check) - while self._is_busy_func() and (time.time() - start_time) < timeout: + while self._is_busy_func() and (time.time() - start_time) < timeout: # type: ignore[misc] if ( self._debug and (time.time() - start_time) % 5 < 0.1 ): # Print every ~5 seconds @@ -216,7 +216,7 @@ def execute_webrtc() -> bool: time.sleep(0.1) # Check if we timed out - if self._is_busy_func() and (time.time() - start_time) >= timeout: + if self._is_busy_func() and (time.time() - start_time) >= timeout: # type: ignore[misc] logger.warning(f"WebRTC request timed out: {api_id} (ID: {request_id})") return False @@ -255,10 +255,10 @@ def execute_webrtc() -> bool: return request_id - def queue_action_client_request( + def queue_action_client_request( # type: ignore[no-untyped-def] self, action_name: str, - execute_func: Callable, + execute_func: Callable, # type: ignore[type-arg] priority: int = 0, timeout: float = 30.0, **kwargs, @@ -324,15 +324,15 @@ def _process_queue(self) -> None: logger.debug( f"Robot ready state changed: {self._last_ready_state} -> {is_ready}" ) - self._last_ready_state = is_ready + self._last_ready_state = is_ready # type: ignore[assignment] if is_busy != self._last_busy_state: logger.debug(f"Robot busy state changed: {self._last_busy_state} -> {is_busy}") - self._last_busy_state = is_busy + self._last_busy_state = is_busy # type: ignore[assignment] # If the robot has transitioned to busy, record the time if is_busy: - self._stuck_in_busy_since = current_time + self._stuck_in_busy_since = current_time # type: ignore[assignment] else: self._stuck_in_busy_since = None @@ -359,7 +359,7 @@ def _process_queue(self) -> None: # Get the next command _, _, command = self._queue.get(block=False) self._current_command = command - self._last_command_time = current_time + self._last_command_time = current_time # type: ignore[assignment] # Log the command cmd_info = f"ID: {command.id}, Type: {command.cmd_type.name}" @@ -460,7 +460,7 @@ def _print_queue_status(self) -> None: ) logger.debug(status) - self._last_command_time = current_time + self._last_command_time = current_time # type: ignore[assignment] @property def queue_size(self) -> int: diff --git a/dimos/robot/ros_control.py b/dimos/robot/ros_control.py index 2e9eb95204..2a4660c328 100644 --- a/dimos/robot/ros_control.py +++ b/dimos/robot/ros_control.py @@ -19,13 +19,13 @@ import time from typing import Any -from builtin_interfaces.msg import Duration -from cv_bridge import CvBridge -from geometry_msgs.msg import Point, Twist, Vector3 -from nav2_msgs.action import Spin -from nav_msgs.msg import OccupancyGrid, Odometry +from builtin_interfaces.msg import Duration # type: ignore[attr-defined] +from cv_bridge import CvBridge # type: ignore[attr-defined] +from geometry_msgs.msg import Point, Twist, Vector3 # type: ignore[attr-defined] +from nav2_msgs.action import Spin # type: ignore[import-not-found] +from nav_msgs.msg import OccupancyGrid, Odometry # type: ignore[attr-defined] import rclpy -from rclpy.action import ActionClient +from rclpy.action import ActionClient # type: ignore[attr-defined] from rclpy.executors import MultiThreadedExecutor from rclpy.node import Node from rclpy.qos import ( @@ -34,7 +34,7 @@ QoSProfile, QoSReliabilityPolicy, ) -from sensor_msgs.msg import CompressedImage, Image +from sensor_msgs.msg import CompressedImage, Image # type: ignore[attr-defined] import tf2_ros from dimos.robot.connection_interface import ConnectionInterface @@ -104,7 +104,7 @@ def __init__( costmap_topic: Topic for local costmap data """ # Initialize rclpy and ROS node if not already running - if not rclpy.ok(): + if not rclpy.ok(): # type: ignore[attr-defined] rclpy.init() self._state_topic = state_topic @@ -137,14 +137,14 @@ def __init__( self._mode = RobotMode.INITIALIZING # Create sensor data QoS profile - sensor_qos = QoSProfile( + sensor_qos = QoSProfile( # type: ignore[no-untyped-call] reliability=QoSReliabilityPolicy.BEST_EFFORT, history=QoSHistoryPolicy.KEEP_LAST, durability=QoSDurabilityPolicy.VOLATILE, depth=1, ) - command_qos = QoSProfile( + command_qos = QoSProfile( # type: ignore[no-untyped-call] reliability=QoSReliabilityPolicy.RELIABLE, history=QoSHistoryPolicy.KEEP_LAST, durability=QoSDurabilityPolicy.VOLATILE, @@ -167,16 +167,16 @@ def __init__( self._video_provider = None self._bridge = None if camera_topics: - self._bridge = CvBridge() + self._bridge = CvBridge() # type: ignore[no-untyped-call] self._video_provider = ROSVideoProvider(dev_name=f"{node_name}_video") # Create subscribers for each topic with sensor QoS for camera_config in camera_topics.values(): - topic = camera_config["topic"] - msg_type = camera_config["type"] + topic = camera_config["topic"] # type: ignore[index] + msg_type = camera_config["type"] # type: ignore[index] logger.info( - f"Subscribing to {topic} with BEST_EFFORT QoS using message type {msg_type.__name__}" + f"Subscribing to {topic} with BEST_EFFORT QoS using message type {msg_type.__name__}" # type: ignore[attr-defined] ) _camera_subscription = self._node.create_subscription( msg_type, topic, self._image_callback, sensor_qos @@ -227,19 +227,19 @@ def __init__( logger.warning("No costmap topic provided - costmap data tracking will be unavailable") # Nav2 Action Clients - self._spin_client = ActionClient(self._node, Spin, "spin") + self._spin_client = ActionClient(self._node, Spin, "spin") # type: ignore[no-untyped-call] # Wait for action servers if not mock_connection: - self._spin_client.wait_for_server() + self._spin_client.wait_for_server() # type: ignore[no-untyped-call] # Publishers - self._move_vel_pub = self._node.create_publisher(Twist, move_vel_topic, command_qos) - self._pose_pub = self._node.create_publisher(Vector3, pose_topic, command_qos) + self._move_vel_pub = self._node.create_publisher(Twist, move_vel_topic, command_qos) # type: ignore[arg-type] + self._pose_pub = self._node.create_publisher(Vector3, pose_topic, command_qos) # type: ignore[arg-type] if webrtc_msg_type: self._webrtc_pub = self._node.create_publisher( - webrtc_msg_type, webrtc_topic, qos_profile=command_qos + webrtc_msg_type, webrtc_topic, qos_profile=command_qos # type: ignore[arg-type] ) # Initialize command queue @@ -283,30 +283,30 @@ def get_global_costmap(self) -> OccupancyGrid | None: else: return None - def _global_costmap_callback(self, msg) -> None: + def _global_costmap_callback(self, msg) -> None: # type: ignore[no-untyped-def] """Callback for costmap data""" self._global_costmap_data = msg - def _imu_callback(self, msg) -> None: + def _imu_callback(self, msg) -> None: # type: ignore[no-untyped-def] """Callback for IMU data""" self._imu_state = msg # Log IMU state (very verbose) # logger.debug(f"IMU state updated: {self._imu_state}") - def _odom_callback(self, msg) -> None: + def _odom_callback(self, msg) -> None: # type: ignore[no-untyped-def] """Callback for odometry data""" self._odom_data = msg - def _costmap_callback(self, msg) -> None: + def _costmap_callback(self, msg) -> None: # type: ignore[no-untyped-def] """Callback for costmap data""" self._costmap_data = msg - def _state_callback(self, msg) -> None: + def _state_callback(self, msg) -> None: # type: ignore[no-untyped-def] """Callback for state messages to track mode and progress""" # Call the abstract method to update RobotMode enum based on the received state self._robot_state = msg - self._update_mode(msg) + self._update_mode(msg) # type: ignore[no-untyped-call] # Log state changes (very verbose) # logger.debug(f"Robot state updated: {self._robot_state}") @@ -328,7 +328,7 @@ def _clamp_velocity(self, velocity: float, max_velocity: float) -> float: return max(min(velocity, max_velocity), -max_velocity) @abstractmethod - def _update_mode(self, *args, **kwargs): + def _update_mode(self, *args, **kwargs): # type: ignore[no-untyped-def] """Update robot mode based on state - to be implemented by child classes""" pass @@ -389,14 +389,14 @@ def get_costmap(self) -> OccupancyGrid | None: return None return self._costmap_data - def _image_callback(self, msg) -> None: + def _image_callback(self, msg) -> None: # type: ignore[no-untyped-def] """Convert ROS image to numpy array and push to data stream""" if self._video_provider and self._bridge: try: if isinstance(msg, CompressedImage): - frame = self._bridge.compressed_imgmsg_to_cv2(msg) + frame = self._bridge.compressed_imgmsg_to_cv2(msg) # type: ignore[no-untyped-call] elif isinstance(msg, Image): - frame = self._bridge.imgmsg_to_cv2(msg, "bgr8") + frame = self._bridge.imgmsg_to_cv2(msg, "bgr8") # type: ignore[no-untyped-call] else: logger.error(f"Unsupported image message type: {type(msg)}") return @@ -410,7 +410,7 @@ def video_provider(self) -> ROSVideoProvider | None: """Data provider property for streaming data""" return self._video_provider - def get_video_stream(self, fps: int = 30) -> Observable | None: + def get_video_stream(self, fps: int = 30) -> Observable | None: # type: ignore[name-defined] """Get the video stream from the robot's camera. Args: @@ -422,9 +422,9 @@ def get_video_stream(self, fps: int = 30) -> Observable | None: if not self.video_provider: return None - return self.video_provider.get_stream(fps=fps) + return self.video_provider.get_stream(fps=fps) # type: ignore[attr-defined] - def _send_action_client_goal( + def _send_action_client_goal( # type: ignore[no-untyped-def] self, client, goal_msg, description: str | None = None, time_allowance: float = 20.0 ) -> bool: """ @@ -494,7 +494,7 @@ def move(self, velocity: Vector, duration: float = 0.0) -> bool: yaw = self._clamp_velocity(yaw, self.MAX_ANGULAR_VELOCITY) # Create and send command - cmd = Twist() + cmd = Twist() # type: ignore[no-untyped-call] cmd.linear.x = float(x) cmd.linear.y = float(y) cmd.angular.z = float(yaw) @@ -512,7 +512,7 @@ def move(self, velocity: Vector, duration: float = 0.0) -> bool: return True except Exception as e: - self._logger.error(f"Failed to send movement command: {e}") + self._logger.error(f"Failed to send movement command: {e}") # type: ignore[attr-defined] return False def reverse(self, distance: float, speed: float = 0.5, time_allowance: float = 120) -> bool: @@ -535,15 +535,15 @@ def reverse(self, distance: float, speed: float = 0.5, time_allowance: float = 1 speed = min(abs(speed), self.MAX_LINEAR_VELOCITY) # Define function to execute the reverse - def execute_reverse(): + def execute_reverse(): # type: ignore[no-untyped-def] # Create BackUp goal - goal = BackUp.Goal() - goal.target = Point() + goal = BackUp.Goal() # type: ignore[name-defined] + goal.target = Point() # type: ignore[no-untyped-call] goal.target.x = -distance # Negative for backward motion goal.target.y = 0.0 goal.target.z = 0.0 goal.speed = speed # BackUp expects positive speed - goal.time_allowance = Duration(sec=time_allowance) + goal.time_allowance = Duration(sec=time_allowance) # type: ignore[no-untyped-call] print( f"[ROSControl] execute_reverse: Creating BackUp goal with distance={distance}m, speed={speed}m/s" @@ -555,7 +555,7 @@ def execute_reverse(): logger.info(f"Moving backward: distance={distance}m, speed={speed}m/s") result = self._send_action_client_goal( - self._backup_client, + self._backup_client, # type: ignore[attr-defined] goal, f"Moving backward {distance}m at {speed}m/s", time_allowance, @@ -609,11 +609,11 @@ def spin(self, degrees: float, speed: float = 45.0, time_allowance: float = 120) ) # At least 20 seconds or double the expected time # Define function to execute the spin - def execute_spin(): + def execute_spin(): # type: ignore[no-untyped-def] # Create Spin goal goal = Spin.Goal() goal.target_yaw = angle # Nav2 Spin action expects radians - goal.time_allowance = Duration(sec=time_allowance) + goal.time_allowance = Duration(sec=time_allowance) # type: ignore[no-untyped-call] logger.info(f"Spinning: angle={degrees}deg ({angle:.2f}rad)") @@ -667,14 +667,14 @@ def cleanup(self) -> None: self._executor.shutdown() # Destroy node and shutdown rclpy - self._node.destroy_node() + self._node.destroy_node() # type: ignore[no-untyped-call] rclpy.shutdown() def disconnect(self) -> None: """Disconnect from the robot and clean up resources.""" self.cleanup() - def webrtc_req( + def webrtc_req( # type: ignore[no-untyped-def] self, api_id: int, topic: str | None = None, @@ -700,7 +700,7 @@ def webrtc_req( """ try: # Create and send command - cmd = self._webrtc_msg_type() + cmd = self._webrtc_msg_type() # type: ignore[misc] cmd.api_id = api_id cmd.topic = topic if topic is not None else self._webrtc_api_topic cmd.parameter = parameter @@ -729,7 +729,7 @@ def print_robot_mode(self) -> None: print(f"Current RobotMode: {mode.name}") print(f"Mode enum: {mode}") - def queue_webrtc_req( + def queue_webrtc_req( # type: ignore[no-untyped-def] self, api_id: int, topic: str | None = None, @@ -782,7 +782,7 @@ def move_vel_control(self, x: float, y: float, yaw: float) -> bool: yaw = self._clamp_velocity(yaw, self.MAX_ANGULAR_VELOCITY) # Create and send command - cmd = Twist() + cmd = Twist() # type: ignore[no-untyped-call] cmd.linear.x = float(x) cmd.linear.y = float(y) cmd.angular.z = float(yaw) @@ -807,7 +807,7 @@ def pose_command(self, roll: float, pitch: float, yaw: float) -> bool: bool: True if command was sent successfully """ # Create the pose command message - cmd = Vector3() + cmd = Vector3() # type: ignore[no-untyped-call] cmd.x = float(roll) # Roll cmd.y = float(pitch) # Pitch cmd.z = float(yaw) # Yaw @@ -820,7 +820,7 @@ def pose_command(self, roll: float, pitch: float, yaw: float) -> bool: logger.error(f"Failed to send pose command: {e}") return False - def get_position_stream(self): + def get_position_stream(self): # type: ignore[no-untyped-def] """ Get a stream of position updates from ROS. @@ -838,13 +838,13 @@ def get_position_stream(self): return position_provider.get_position_stream() - def _goal_response_callback(self, future) -> None: + def _goal_response_callback(self, future) -> None: # type: ignore[no-untyped-def] """Handle the goal response.""" goal_handle = future.result() if not goal_handle.accepted: logger.warn("Goal was rejected!") print("[ROSControl] Goal was REJECTED by the action server") - self._action_success = False + self._action_success = False # type: ignore[assignment] return logger.info("Goal accepted") @@ -852,14 +852,14 @@ def _goal_response_callback(self, future) -> None: result_future = goal_handle.get_result_async() result_future.add_done_callback(self._goal_result_callback) - def _goal_result_callback(self, future) -> None: + def _goal_result_callback(self, future) -> None: # type: ignore[no-untyped-def] """Handle the goal result.""" try: result = future.result().result logger.info("Goal completed") print(f"[ROSControl] Goal COMPLETED with result: {result}") - self._action_success = True + self._action_success = True # type: ignore[assignment] except Exception as e: logger.error(f"Goal failed with error: {e}") print(f"[ROSControl] Goal FAILED with error: {e}") - self._action_success = False + self._action_success = False # type: ignore[assignment] diff --git a/dimos/robot/ros_observable_topic.py b/dimos/robot/ros_observable_topic.py index 7cfc70fd8b..ff40f2ce1e 100644 --- a/dimos/robot/ros_observable_topic.py +++ b/dimos/robot/ros_observable_topic.py @@ -28,7 +28,7 @@ from reactivex import operators as ops from reactivex.disposable import Disposable from reactivex.scheduler import ThreadPoolScheduler -from rxpy_backpressure import BackPressure +from rxpy_backpressure import BackPressure # type: ignore[import-untyped] from dimos.msgs.nav_msgs import OccupancyGrid from dimos.types.vector import Vector @@ -37,7 +37,7 @@ __all__ = ["QOS", "ROSObservableTopicAbility"] -TopicType = Union[OccupancyGrid, msg.OccupancyGrid, msg.Odometry] +TopicType = Union[OccupancyGrid, msg.OccupancyGrid, msg.Odometry] # type: ignore[name-defined] class QOS(enum.Enum): @@ -46,14 +46,14 @@ class QOS(enum.Enum): def to_profile(self) -> QoSProfile: if self == QOS.SENSOR: - return QoSProfile( + return QoSProfile( # type: ignore[no-untyped-call] reliability=QoSReliabilityPolicy.BEST_EFFORT, history=QoSHistoryPolicy.KEEP_LAST, durability=QoSDurabilityPolicy.VOLATILE, depth=1, ) if self == QOS.COMMAND: - return QoSProfile( + return QoSProfile( # type: ignore[no-untyped-call] reliability=QoSReliabilityPolicy.RELIABLE, history=QoSHistoryPolicy.KEEP_LAST, durability=QoSDurabilityPolicy.VOLATILE, @@ -80,32 +80,32 @@ class ROSObservableTopicAbility: # ├──► observe_on(pool) ─► backpressure.latest ─► sub2 (slow) # └──► observe_on(pool) ─► backpressure.latest ─► sub3 (slower) # - def _maybe_conversion(self, msg_type: TopicType, callback) -> Callable[[TopicType], Any]: + def _maybe_conversion(self, msg_type: TopicType, callback) -> Callable[[TopicType], Any]: # type: ignore[no-untyped-def] if msg_type == "Costmap": - return lambda msg: callback(OccupancyGrid.from_msg(msg)) + return lambda msg: callback(OccupancyGrid.from_msg(msg)) # type: ignore[attr-defined] # just for test, not sure if this Vector auto-instantiation is used irl if msg_type == Vector: return lambda msg: callback(Vector.from_msg(msg)) - return callback + return callback # type: ignore[no-any-return] - def _sub_msg_type(self, msg_type): + def _sub_msg_type(self, msg_type): # type: ignore[no-untyped-def] if msg_type == "Costmap": - return msg.OccupancyGrid + return msg.OccupancyGrid # type: ignore[attr-defined] if msg_type == Vector: - return msg.Odometry + return msg.Odometry # type: ignore[attr-defined] return msg_type @functools.cache - def topic( + def topic( # type: ignore[no-untyped-def] self, topic_name: str, msg_type: TopicType, qos=QOS.SENSOR, scheduler: ThreadPoolScheduler | None = None, drop_unprocessed: bool = True, - ) -> rx.Observable: + ) -> rx.Observable: # type: ignore[type-arg] if scheduler is None: scheduler = get_scheduler() @@ -113,14 +113,14 @@ def topic( qos_profile = qos.to_profile() # upstream ROS callback - def _on_subscribe(obs, _): - ros_sub = self._node.create_subscription( - self._sub_msg_type(msg_type), + def _on_subscribe(obs, _): # type: ignore[no-untyped-def] + ros_sub = self._node.create_subscription( # type: ignore[attr-defined] + self._sub_msg_type(msg_type), # type: ignore[no-untyped-call] topic_name, self._maybe_conversion(msg_type, obs.on_next), qos_profile, ) - return Disposable(lambda: self._node.destroy_subscription(ros_sub)) + return Disposable(lambda: self._node.destroy_subscription(ros_sub)) # type: ignore[attr-defined] upstream = rx.create(_on_subscribe) @@ -131,7 +131,7 @@ def _on_subscribe(obs, _): ) # per-subscriber factory - def per_sub(): + def per_sub(): # type: ignore[no-untyped-def] # hop off the ROS thread into the pool base = core.pipe(ops.observe_on(scheduler)) @@ -139,13 +139,13 @@ def per_sub(): if not drop_unprocessed: return base - def _subscribe(observer, sch=None): + def _subscribe(observer, sch=None): # type: ignore[no-untyped-def] return base.subscribe(BackPressure.LATEST(observer), scheduler=sch) return rx.create(_subscribe) # each `.subscribe()` call gets its own async backpressure chain - return rx.defer(lambda *_: per_sub()) + return rx.defer(lambda *_: per_sub()) # type: ignore[no-untyped-call] # If you are not interested in processing streams, just want to fetch the latest stream # value use this function. It runs a subscription in the background. @@ -160,7 +160,7 @@ def _subscribe(observer, sch=None): # odom.dispose() # clean up the subscription # # see test_ros_observable_topic.py test_topic_latest for more details - def topic_latest( + def topic_latest( # type: ignore[no-untyped-def] self, topic_name: str, msg_type: TopicType, timeout: float | None = 100.0, qos=QOS.SENSOR ): """ @@ -181,7 +181,7 @@ def topic_latest( ops.first(), *([ops.timeout(timeout)] if timeout is not None else []) ).run() except Exception: - conn.dispose() + conn.dispose() # type: ignore[union-attr] msg = f"{topic_name} message not received after {timeout} seconds. Is robot connected?" logger.error(msg) raise Exception(msg) @@ -189,10 +189,10 @@ def topic_latest( cache = {"val": first_val} sub = core.subscribe(lambda v: cache.__setitem__("val", v)) - def reader(): + def reader(): # type: ignore[no-untyped-def] return cache["val"] - reader.dispose = lambda: (sub.dispose(), conn.dispose()) + reader.dispose = lambda: (sub.dispose(), conn.dispose()) # type: ignore[attr-defined, union-attr] return reader # If you are not interested in processing streams, just want to fetch the latest stream @@ -210,7 +210,7 @@ def reader(): # odom.dispose() # clean up the subscription # # see test_ros_observable_topic.py test_topic_latest for more details - async def topic_latest_async( + async def topic_latest_async( # type: ignore[no-untyped-def] self, topic_name: str, msg_type: TopicType, qos=QOS.SENSOR, timeout: float = 30.0 ): loop = asyncio.get_running_loop() @@ -219,7 +219,7 @@ async def topic_latest_async( core = self.topic(topic_name, msg_type, qos=qos) # single ROS callback - def _on_next(v) -> None: + def _on_next(v) -> None: # type: ignore[no-untyped-def] cache["val"] = v if not first.done(): loop.call_soon_threadsafe(first.set_result, v) @@ -232,8 +232,8 @@ def _on_next(v) -> None: subscription.dispose() raise - def reader(): + def reader(): # type: ignore[no-untyped-def] return cache["val"] - reader.dispose = subscription.dispose + reader.dispose = subscription.dispose # type: ignore[attr-defined] return reader diff --git a/dimos/robot/ros_transform.py b/dimos/robot/ros_transform.py index d54eb8cd15..aa67b3c288 100644 --- a/dimos/robot/ros_transform.py +++ b/dimos/robot/ros_transform.py @@ -13,14 +13,14 @@ # limitations under the License. -from geometry_msgs.msg import TransformStamped +from geometry_msgs.msg import TransformStamped # type: ignore[attr-defined] import rclpy from scipy.spatial.transform import Rotation as R -from tf2_geometry_msgs import PointStamped +from tf2_geometry_msgs import PointStamped # type: ignore[attr-defined] import tf2_ros from tf2_ros import Buffer -from dimos.types.path import Path +from dimos.types.path import Path # type: ignore[import-untyped] from dimos.types.vector import Vector from dimos.utils.logging_config import setup_logger @@ -29,17 +29,17 @@ __all__ = ["ROSTransformAbility"] -def to_euler_rot(msg: TransformStamped) -> [Vector, Vector]: +def to_euler_rot(msg: TransformStamped) -> [Vector, Vector]: # type: ignore[valid-type] q = msg.transform.rotation rotation = R.from_quat([q.x, q.y, q.z, q.w]) return Vector(rotation.as_euler("xyz", degrees=False)) -def to_euler_pos(msg: TransformStamped) -> [Vector, Vector]: +def to_euler_pos(msg: TransformStamped) -> [Vector, Vector]: # type: ignore[valid-type] return Vector(msg.transform.translation).to_2d() -def to_euler(msg: TransformStamped) -> [Vector, Vector]: +def to_euler(msg: TransformStamped) -> [Vector, Vector]: # type: ignore[valid-type] return [to_euler_pos(msg), to_euler_rot(msg)] @@ -50,24 +50,24 @@ class ROSTransformAbility: def tf_buffer(self) -> Buffer: if not hasattr(self, "_tf_buffer"): self._tf_buffer = tf2_ros.Buffer() - self._tf_listener = tf2_ros.TransformListener(self._tf_buffer, self._node) + self._tf_listener = tf2_ros.TransformListener(self._tf_buffer, self._node) # type: ignore[attr-defined] logger.info("Transform listener initialized") return self._tf_buffer - def transform_euler_pos( + def transform_euler_pos( # type: ignore[no-untyped-def] self, source_frame: str, target_frame: str = "map", timeout: float = 1.0 ): - return to_euler_pos(self.transform(source_frame, target_frame, timeout)) + return to_euler_pos(self.transform(source_frame, target_frame, timeout)) # type: ignore[arg-type] - def transform_euler_rot( + def transform_euler_rot( # type: ignore[no-untyped-def] self, source_frame: str, target_frame: str = "map", timeout: float = 1.0 ): - return to_euler_rot(self.transform(source_frame, target_frame, timeout)) + return to_euler_rot(self.transform(source_frame, target_frame, timeout)) # type: ignore[arg-type] - def transform_euler(self, source_frame: str, target_frame: str = "map", timeout: float = 1.0): + def transform_euler(self, source_frame: str, target_frame: str = "map", timeout: float = 1.0): # type: ignore[no-untyped-def] res = self.transform(source_frame, target_frame, timeout) - return to_euler(res) + return to_euler(res) # type: ignore[arg-type] def transform( self, source_frame: str, target_frame: str = "map", timeout: float = 1.0 @@ -81,14 +81,14 @@ def transform( ) return transform except ( - tf2_ros.LookupException, - tf2_ros.ConnectivityException, - tf2_ros.ExtrapolationException, + tf2_ros.LookupException, # type: ignore[attr-defined] + tf2_ros.ConnectivityException, # type: ignore[attr-defined] + tf2_ros.ExtrapolationException, # type: ignore[attr-defined] ) as e: logger.error(f"Transform lookup failed: {e}") return None - def transform_point( + def transform_point( # type: ignore[no-untyped-def] self, point: Vector, source_frame: str, target_frame: str = "map", timeout: float = 1.0 ): """Transform a point from source_frame to target_frame. @@ -117,7 +117,7 @@ def transform_point( ps.header.stamp = rclpy.time.Time().to_msg() # Latest available transform ps.point.x = point[0] ps.point.y = point[1] - ps.point.z = point[2] if len(point) > 2 else 0.0 + ps.point.z = point[2] if len(point) > 2 else 0.0 # type: ignore[arg-type] # Transform point transformed_ps = self.tf_buffer.transform( @@ -125,21 +125,21 @@ def transform_point( ) # Return as Vector type - if len(point) > 2: + if len(point) > 2: # type: ignore[arg-type] return Vector( - transformed_ps.point.x, transformed_ps.point.y, transformed_ps.point.z + transformed_ps.point.x, transformed_ps.point.y, transformed_ps.point.z # type: ignore[union-attr] ) else: - return Vector(transformed_ps.point.x, transformed_ps.point.y) + return Vector(transformed_ps.point.x, transformed_ps.point.y) # type: ignore[union-attr] except ( - tf2_ros.LookupException, - tf2_ros.ConnectivityException, - tf2_ros.ExtrapolationException, + tf2_ros.LookupException, # type: ignore[attr-defined] + tf2_ros.ConnectivityException, # type: ignore[attr-defined] + tf2_ros.ExtrapolationException, # type: ignore[attr-defined] ) as e: logger.error(f"Transform from {source_frame} to {target_frame} failed: {e}") return None - def transform_path( + def transform_path( # type: ignore[no-untyped-def] self, path: Path, source_frame: str, target_frame: str = "map", timeout: float = 1.0 ): """Transform a path from source_frame to target_frame. @@ -160,7 +160,7 @@ def transform_path( transformed_path.append(transformed_point) return transformed_path - def transform_rot( + def transform_rot( # type: ignore[no-untyped-def] self, rotation: Vector, source_frame: str, target_frame: str = "map", timeout: float = 1.0 ): """Transform a rotation from source_frame to target_frame. @@ -184,7 +184,7 @@ def transform_rot( ) # Create a rotation matrix from the input Euler angles - input_rotation = R.from_euler("xyz", rotation, degrees=False) + input_rotation = R.from_euler("xyz", rotation, degrees=False) # type: ignore[arg-type] # Get the transform from source to target frame transform = self.transform(source_frame, target_frame, timeout) @@ -206,14 +206,14 @@ def transform_rot( return Vector(euler_angles) except ( - tf2_ros.LookupException, - tf2_ros.ConnectivityException, - tf2_ros.ExtrapolationException, + tf2_ros.LookupException, # type: ignore[attr-defined] + tf2_ros.ConnectivityException, # type: ignore[attr-defined] + tf2_ros.ExtrapolationException, # type: ignore[attr-defined] ) as e: logger.error(f"Transform rotation from {source_frame} to {target_frame} failed: {e}") return None - def transform_pose( + def transform_pose( # type: ignore[no-untyped-def] self, position: Vector, rotation: Vector, diff --git a/dimos/robot/unitree/connection/connection.py b/dimos/robot/unitree/connection/connection.py index 0d904df7c4..352309afc1 100644 --- a/dimos/robot/unitree/connection/connection.py +++ b/dimos/robot/unitree/connection/connection.py @@ -19,9 +19,9 @@ import time from typing import TypeAlias -from aiortc import MediaStreamTrack -from go2_webrtc_driver.constants import RTC_TOPIC, SPORT_CMD, VUI_COLOR -from go2_webrtc_driver.webrtc_driver import ( # type: ignore[import-not-found] +from aiortc import MediaStreamTrack # type: ignore[import-untyped] +from go2_webrtc_driver.constants import RTC_TOPIC, SPORT_CMD, VUI_COLOR # type: ignore[import-untyped] +from go2_webrtc_driver.webrtc_driver import ( # type: ignore[import-untyped] Go2WebRTCConnection, WebRTCConnectionMethod, ) @@ -48,7 +48,7 @@ class SerializableVideoFrame: """Pickleable wrapper for av.VideoFrame with all metadata""" - data: np.ndarray + data: np.ndarray # type: ignore[type-arg] pts: int | None = None time: float | None = None dts: int | None = None @@ -57,7 +57,7 @@ class SerializableVideoFrame: format: str | None = None @classmethod - def from_av_frame(cls, frame): + def from_av_frame(cls, frame): # type: ignore[no-untyped-def] return cls( data=frame.to_ndarray(format="rgb24"), pts=frame.pts, @@ -68,7 +68,7 @@ def from_av_frame(cls, frame): format=frame.format.name if hasattr(frame, "format") and frame.format else None, ) - def to_ndarray(self, format=None): + def to_ndarray(self, format=None): # type: ignore[no-untyped-def] return self.data @@ -197,8 +197,8 @@ async def async_move_duration() -> None: return False # Generic conversion of unitree subscription to Subject (used for all subs) - def unitree_sub_stream(self, topic_name: str): - def subscribe_in_thread(cb) -> None: + def unitree_sub_stream(self, topic_name: str): # type: ignore[no-untyped-def] + def subscribe_in_thread(cb) -> None: # type: ignore[no-untyped-def] # Run the subscription in the background thread that has the event loop def run_subscription() -> None: self.conn.datachannel.pub_sub.subscribe(topic_name, cb) @@ -206,7 +206,7 @@ def run_subscription() -> None: # Use call_soon_threadsafe to run in the background thread self.loop.call_soon_threadsafe(run_subscription) - def unsubscribe_in_thread(cb) -> None: + def unsubscribe_in_thread(cb) -> None: # type: ignore[no-untyped-def] # Run the unsubscription in the background thread that has the event loop def run_unsubscription() -> None: self.conn.datachannel.pub_sub.unsubscribe(topic_name) @@ -220,7 +220,7 @@ def run_unsubscription() -> None: ) # Generic sync API call (we jump into the client thread) - def publish_request(self, topic: str, data: dict): + def publish_request(self, topic: str, data: dict): # type: ignore[no-untyped-def, type-arg] future = asyncio.run_coroutine_threadsafe( self.conn.datachannel.pub_sub.publish_request_new(topic, data), self.loop ) @@ -270,7 +270,7 @@ def video_stream(self) -> Observable[Image]: def lowstate_stream(self) -> Observable[LowStateMsg]: return backpressure(self.unitree_sub_stream(RTC_TOPIC["LOW_STATE"])) - def standup_ai(self): + def standup_ai(self): # type: ignore[no-untyped-def] return self.publish_request(RTC_TOPIC["SPORT_MOD"], {"api_id": SPORT_CMD["BalanceStand"]}) def standup_normal(self) -> bool: @@ -280,17 +280,17 @@ def standup_normal(self) -> bool: return True @rpc - def standup(self): + def standup(self): # type: ignore[no-untyped-def] if self.mode == "ai": - return self.standup_ai() + return self.standup_ai() # type: ignore[no-untyped-call] else: return self.standup_normal() @rpc - def liedown(self): + def liedown(self): # type: ignore[no-untyped-def] return self.publish_request(RTC_TOPIC["SPORT_MOD"], {"api_id": SPORT_CMD["StandDown"]}) - async def handstand(self): + async def handstand(self): # type: ignore[no-untyped-def] return self.publish_request( RTC_TOPIC["SPORT_MOD"], {"api_id": SPORT_CMD["Standup"], "parameter": {"data": True}}, @@ -298,7 +298,7 @@ async def handstand(self): @rpc def color(self, color: VUI_COLOR = VUI_COLOR.RED, colortime: int = 60) -> bool: - return self.publish_request( + return self.publish_request( # type: ignore[no-any-return] RTC_TOPIC["VUI"], { "api_id": 1001, @@ -319,7 +319,7 @@ async def accept_track(track: MediaStreamTrack) -> None: if stop_event.is_set(): return frame = await track.recv() - serializable_frame = SerializableVideoFrame.from_av_frame(frame) + serializable_frame = SerializableVideoFrame.from_av_frame(frame) # type: ignore[no-untyped-call] subject.on_next(serializable_frame) self.conn.video.add_track_callback(accept_track) @@ -356,9 +356,9 @@ def get_video_stream(self, fps: int = 30) -> Observable[VideoMessage]: """ print("Starting WebRTC video stream...") stream = self.video_stream() - return stream + return stream # type: ignore[no-any-return] - def stop(self) -> bool: + def stop(self) -> bool: # type: ignore[no-redef] """Stop the robot's movement. Returns: diff --git a/dimos/robot/unitree/connection/g1.py b/dimos/robot/unitree/connection/g1.py index 8e63cbb40a..8e226ced44 100644 --- a/dimos/robot/unitree/connection/g1.py +++ b/dimos/robot/unitree/connection/g1.py @@ -28,7 +28,7 @@ class G1Connection(Module): connection: UnitreeWebRTCConnection - def __init__(self, ip: str | None = None, **kwargs) -> None: + def __init__(self, ip: str | None = None, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(**kwargs) if ip is None: @@ -40,7 +40,7 @@ def start(self) -> None: super().start() self.connection.start() self._disposables.add( - self.cmd_vel.subscribe(self.move), + self.cmd_vel.subscribe(self.move), # type: ignore[arg-type] ) @rpc @@ -55,13 +55,13 @@ def move(self, twist_stamped: TwistStamped, duration: float = 0.0) -> None: self.connection.move(twist, duration) @rpc - def publish_request(self, topic: str, data: dict): + def publish_request(self, topic: str, data: dict): # type: ignore[no-untyped-def, type-arg] """Forward WebRTC publish requests to connection.""" return self.connection.publish_request(topic, data) def deploy(dimos: DimosCluster, ip: str, local_planner: spec.LocalPlanner) -> G1Connection: - connection = dimos.deploy(G1Connection, ip) + connection = dimos.deploy(G1Connection, ip) # type: ignore[attr-defined] connection.cmd_vel.connect(local_planner.cmd_vel) connection.start() - return connection + return connection # type: ignore[no-any-return] diff --git a/dimos/robot/unitree/connection/go2.py b/dimos/robot/unitree/connection/go2.py index c5f250a5fa..f12321bad7 100644 --- a/dimos/robot/unitree/connection/go2.py +++ b/dimos/robot/unitree/connection/go2.py @@ -17,7 +17,7 @@ import time from typing import Protocol -from dimos_lcm.sensor_msgs import CameraInfo +from dimos_lcm.sensor_msgs import CameraInfo # type: ignore[import-untyped] from reactivex.observable import Observable from dimos import spec @@ -46,13 +46,13 @@ class Go2ConnectionProtocol(Protocol): def start(self) -> None: ... def stop(self) -> None: ... - def lidar_stream(self) -> Observable: ... - def odom_stream(self) -> Observable: ... - def video_stream(self) -> Observable: ... + def lidar_stream(self) -> Observable: ... # type: ignore[type-arg] + def odom_stream(self) -> Observable: ... # type: ignore[type-arg] + def video_stream(self) -> Observable: ... # type: ignore[type-arg] def move(self, twist: TwistStamped, duration: float = 0.0) -> bool: ... def standup(self) -> None: ... def liedown(self) -> None: ... - def publish_request(self, topic: str, data: dict) -> dict: ... + def publish_request(self, topic: str, data: dict) -> dict: ... # type: ignore[type-arg] def _camera_info_static() -> CameraInfo: @@ -91,7 +91,7 @@ class ReplayConnection(UnitreeWebRTCConnection): dir_name = "unitree_go2_office_walk2" # we don't want UnitreeWebRTCConnection to init - def __init__( + def __init__( # type: ignore[no-untyped-def] self, **kwargs, ) -> None: @@ -115,29 +115,29 @@ def liedown(self) -> None: print("liedown suppressed") @simple_mcache - def lidar_stream(self): + def lidar_stream(self): # type: ignore[no-untyped-def] print("lidar stream start") - lidar_store = TimedSensorReplay(f"{self.dir_name}/lidar") - return lidar_store.stream(**self.replay_config) + lidar_store = TimedSensorReplay(f"{self.dir_name}/lidar") # type: ignore[var-annotated] + return lidar_store.stream(**self.replay_config) # type: ignore[arg-type] @simple_mcache - def odom_stream(self): + def odom_stream(self): # type: ignore[no-untyped-def] print("odom stream start") - odom_store = TimedSensorReplay(f"{self.dir_name}/odom") - return odom_store.stream(**self.replay_config) + odom_store = TimedSensorReplay(f"{self.dir_name}/odom") # type: ignore[var-annotated] + return odom_store.stream(**self.replay_config) # type: ignore[arg-type] # we don't have raw video stream in the data set @simple_mcache - def video_stream(self): + def video_stream(self): # type: ignore[no-untyped-def] print("video stream start") - video_store = TimedSensorReplay(f"{self.dir_name}/video") + video_store = TimedSensorReplay(f"{self.dir_name}/video") # type: ignore[var-annotated] - return video_store.stream(**self.replay_config) + return video_store.stream(**self.replay_config) # type: ignore[arg-type] - def move(self, twist: TwistStamped, duration: float = 0.0) -> None: + def move(self, twist: TwistStamped, duration: float = 0.0) -> None: # type: ignore[override] pass - def publish_request(self, topic: str, data: dict): + def publish_request(self, topic: str, data: dict): # type: ignore[no-untyped-def, type-arg] """Fake publish request for testing.""" return {"status": "ok", "message": "Fake publish"} @@ -155,7 +155,7 @@ class GO2Connection(Module, spec.Camera, spec.Pointcloud): camera_info_static: CameraInfo = _camera_info_static() - def __init__( + def __init__( # type: ignore[no-untyped-def] self, ip: str | None = None, *args, @@ -163,11 +163,11 @@ def __init__( ) -> None: match ip: case None | "fake" | "mock" | "replay": - self.connection = ReplayConnection() + self.connection = ReplayConnection() # type: ignore[assignment] case "mujoco": from dimos.robot.unitree_webrtc.mujoco_connection import MujocoConnection - self.connection = MujocoConnection(GlobalConfig()) + self.connection = MujocoConnection(GlobalConfig()) # type: ignore[assignment] case _: self.connection = UnitreeWebRTCConnection(ip) @@ -244,12 +244,12 @@ def _odom_to_tf(cls, odom: PoseStamped) -> list[Transform]: sensor, ] - def _publish_tf(self, msg) -> None: + def _publish_tf(self, msg) -> None: # type: ignore[no-untyped-def] self.tf.publish(*self._odom_to_tf(msg)) def publish_camera_info(self) -> None: while True: - self.camera_info.publish(_camera_info_static()) + self.camera_info.publish(_camera_info_static()) # type: ignore[no-untyped-call] time.sleep(1.0) @rpc @@ -258,17 +258,17 @@ def move(self, twist: TwistStamped, duration: float = 0.0) -> None: self.connection.move(twist, duration) @rpc - def standup(self): + def standup(self): # type: ignore[no-untyped-def] """Make the robot stand up.""" return self.connection.standup() @rpc - def liedown(self): + def liedown(self): # type: ignore[no-untyped-def] """Make the robot lie down.""" return self.connection.liedown() @rpc - def publish_request(self, topic: str, data: dict): + def publish_request(self, topic: str, data: dict): # type: ignore[no-untyped-def, type-arg] """Publish a request to the WebRTC connection. Args: topic: The RTC topic to publish to @@ -282,7 +282,7 @@ def publish_request(self, topic: str, data: dict): def deploy(dimos: DimosCluster, ip: str, prefix: str = "") -> GO2Connection: from dimos.constants import DEFAULT_CAPACITY_COLOR_IMAGE - connection = dimos.deploy(GO2Connection, ip) + connection = dimos.deploy(GO2Connection, ip) # type: ignore[attr-defined] connection.pointcloud.transport = pSHMTransport( f"{prefix}/lidar", default_capacity=DEFAULT_CAPACITY_COLOR_IMAGE @@ -296,4 +296,4 @@ def deploy(dimos: DimosCluster, ip: str, prefix: str = "") -> GO2Connection: connection.camera_info.transport = LCMTransport(f"{prefix}/camera_info", CameraInfo) connection.start() - return connection + return connection # type: ignore[no-any-return] diff --git a/dimos/robot/unitree/g1/g1agent.py b/dimos/robot/unitree/g1/g1agent.py index 826a3c4ad8..9c94445a1d 100644 --- a/dimos/robot/unitree/g1/g1agent.py +++ b/dimos/robot/unitree/g1/g1agent.py @@ -19,7 +19,7 @@ from dimos.robot.unitree.g1 import g1detector -def deploy(dimos: DimosCluster, ip: str): +def deploy(dimos: DimosCluster, ip: str): # type: ignore[no-untyped-def] g1 = g1detector.deploy(dimos, ip) nav = g1.get("nav") @@ -29,7 +29,7 @@ def deploy(dimos: DimosCluster, ip: str): spatialmem = spatial_perception.deploy(dimos, camera) - navskills = dimos.deploy( + navskills = dimos.deploy( # type: ignore[attr-defined] NavigationSkillContainer, spatialmem, nav, @@ -37,7 +37,7 @@ def deploy(dimos: DimosCluster, ip: str): ) navskills.start() - agent = agents2.deploy( + agent = agents2.deploy( # type: ignore[attr-defined] dimos, "You are controling a humanoid robot", skill_containers=[connection, nav, camera, spatialmem, navskills], diff --git a/dimos/robot/unitree/g1/g1detector.py b/dimos/robot/unitree/g1/g1detector.py index b743aaac6e..34c8fcc3b6 100644 --- a/dimos/robot/unitree/g1/g1detector.py +++ b/dimos/robot/unitree/g1/g1detector.py @@ -18,7 +18,7 @@ from dimos.robot.unitree.g1 import g1zed -def deploy(dimos: DimosCluster, ip: str): +def deploy(dimos: DimosCluster, ip: str): # type: ignore[no-untyped-def] g1 = g1zed.deploy(dimos, ip) nav = g1.get("nav") diff --git a/dimos/robot/unitree/g1/g1zed.py b/dimos/robot/unitree/g1/g1zed.py index 6b6336cd0d..215552be3c 100644 --- a/dimos/robot/unitree/g1/g1zed.py +++ b/dimos/robot/unitree/g1/g1zed.py @@ -45,7 +45,7 @@ class G1ZedDeployResult(TypedDict): def deploy_g1_monozed(dimos: DimosCluster) -> CameraModule: camera = cast( "CameraModule", - dimos.deploy( + dimos.deploy( # type: ignore[attr-defined] CameraModule, frequency=4.0, transform=Transform( @@ -69,8 +69,8 @@ def deploy_g1_monozed(dimos: DimosCluster) -> CameraModule: return camera -def deploy(dimos: DimosCluster, ip: str): - nav = rosnav.deploy( +def deploy(dimos: DimosCluster, ip: str): # type: ignore[no-untyped-def] + nav = rosnav.deploy( # type: ignore[call-arg] dimos, sensor_to_base_link_transform=Transform( frame_id="sensor", child_frame_id="base_link", translation=Vector3(0.0, 0.0, 1.5) diff --git a/dimos/robot/unitree/go2/go2.py b/dimos/robot/unitree/go2/go2.py index 0e78485adc..53614c09d0 100644 --- a/dimos/robot/unitree/go2/go2.py +++ b/dimos/robot/unitree/go2/go2.py @@ -22,7 +22,7 @@ logger = setup_logger(__name__, level=logging.INFO) -def deploy(dimos: DimosCluster, ip: str): +def deploy(dimos: DimosCluster, ip: str): # type: ignore[no-untyped-def] connection = go2.deploy(dimos, ip) foxglove_bridge.deploy(dimos) diff --git a/dimos/robot/unitree/run.py b/dimos/robot/unitree/run.py index 43338c9353..4ae3d32e6b 100644 --- a/dimos/robot/unitree/run.py +++ b/dimos/robot/unitree/run.py @@ -108,7 +108,7 @@ def main() -> None: module.deploy(dimos, args.ip) wait_exit() finally: - dimos.close_all() + dimos.close_all() # type: ignore[attr-defined] if __name__ == "__main__": diff --git a/dimos/robot/unitree_webrtc/connection.py b/dimos/robot/unitree_webrtc/connection.py index 4aee995c02..c23c65d76b 100644 --- a/dimos/robot/unitree_webrtc/connection.py +++ b/dimos/robot/unitree_webrtc/connection.py @@ -19,9 +19,9 @@ import time from typing import Literal, TypeAlias -from aiortc import MediaStreamTrack -from go2_webrtc_driver.constants import RTC_TOPIC, SPORT_CMD, VUI_COLOR -from go2_webrtc_driver.webrtc_driver import ( # type: ignore[import-not-found] +from aiortc import MediaStreamTrack # type: ignore[import-untyped] +from go2_webrtc_driver.constants import RTC_TOPIC, SPORT_CMD, VUI_COLOR # type: ignore[import-untyped] +from go2_webrtc_driver.webrtc_driver import ( # type: ignore[import-untyped] Go2WebRTCConnection, WebRTCConnectionMethod, ) @@ -40,14 +40,14 @@ from dimos.utils.decorators.decorators import simple_mcache from dimos.utils.reactive import backpressure, callback_to_observable -VideoMessage: TypeAlias = np.ndarray[tuple[int, int, Literal[3]], np.uint8] +VideoMessage: TypeAlias = np.ndarray[tuple[int, int, Literal[3]], np.uint8] # type: ignore[type-var] @dataclass class SerializableVideoFrame: """Pickleable wrapper for av.VideoFrame with all metadata""" - data: np.ndarray + data: np.ndarray # type: ignore[type-arg] pts: int | None = None time: float | None = None dts: int | None = None @@ -56,7 +56,7 @@ class SerializableVideoFrame: format: str | None = None @classmethod - def from_av_frame(cls, frame): + def from_av_frame(cls, frame): # type: ignore[no-untyped-def] return cls( data=frame.to_ndarray(format="rgb24"), pts=frame.pts, @@ -67,7 +67,7 @@ def from_av_frame(cls, frame): format=frame.format.name if hasattr(frame, "format") and frame.format else None, ) - def to_ndarray(self, format=None): + def to_ndarray(self, format=None): # type: ignore[no-untyped-def] return self.data @@ -174,9 +174,9 @@ async def async_move_duration() -> None: self.stop_timer.cancel() # Auto-stop after 0.5 seconds if no new commands - self.stop_timer = threading.Timer(self.cmd_vel_timeout, self.stop) - self.stop_timer.daemon = True - self.stop_timer.start() + self.stop_timer = threading.Timer(self.cmd_vel_timeout, self.stop) # type: ignore[assignment] + self.stop_timer.daemon = True # type: ignore[attr-defined] + self.stop_timer.start() # type: ignore[attr-defined] try: if duration > 0: @@ -195,8 +195,8 @@ async def async_move_duration() -> None: return False # Generic conversion of unitree subscription to Subject (used for all subs) - def unitree_sub_stream(self, topic_name: str): - def subscribe_in_thread(cb) -> None: + def unitree_sub_stream(self, topic_name: str): # type: ignore[no-untyped-def] + def subscribe_in_thread(cb) -> None: # type: ignore[no-untyped-def] # Run the subscription in the background thread that has the event loop def run_subscription() -> None: self.conn.datachannel.pub_sub.subscribe(topic_name, cb) @@ -204,7 +204,7 @@ def run_subscription() -> None: # Use call_soon_threadsafe to run in the background thread self.loop.call_soon_threadsafe(run_subscription) - def unsubscribe_in_thread(cb) -> None: + def unsubscribe_in_thread(cb) -> None: # type: ignore[no-untyped-def] # Run the unsubscription in the background thread that has the event loop def run_unsubscription() -> None: self.conn.datachannel.pub_sub.unsubscribe(topic_name) @@ -218,7 +218,7 @@ def run_unsubscription() -> None: ) # Generic sync API call (we jump into the client thread) - def publish_request(self, topic: str, data: dict): + def publish_request(self, topic: str, data: dict): # type: ignore[no-untyped-def, type-arg] future = asyncio.run_coroutine_threadsafe( self.conn.datachannel.pub_sub.publish_request_new(topic, data), self.loop ) @@ -226,28 +226,28 @@ def publish_request(self, topic: str, data: dict): @simple_mcache def raw_lidar_stream(self) -> Subject[LidarMessage]: - return backpressure(self.unitree_sub_stream(RTC_TOPIC["ULIDAR_ARRAY"])) + return backpressure(self.unitree_sub_stream(RTC_TOPIC["ULIDAR_ARRAY"])) # type: ignore[return-value] @simple_mcache def raw_odom_stream(self) -> Subject[Pose]: - return backpressure(self.unitree_sub_stream(RTC_TOPIC["ROBOTODOM"])) + return backpressure(self.unitree_sub_stream(RTC_TOPIC["ROBOTODOM"])) # type: ignore[return-value] @simple_mcache def lidar_stream(self) -> Subject[LidarMessage]: - return backpressure( + return backpressure( # type: ignore[return-value] self.raw_lidar_stream().pipe( - ops.map(lambda raw_frame: LidarMessage.from_msg(raw_frame, ts=time.time())) + ops.map(lambda raw_frame: LidarMessage.from_msg(raw_frame, ts=time.time())) # type: ignore[arg-type] ) ) @simple_mcache def tf_stream(self) -> Subject[Transform]: base_link = functools.partial(Transform.from_pose, "base_link") - return backpressure(self.odom_stream().pipe(ops.map(base_link))) + return backpressure(self.odom_stream().pipe(ops.map(base_link))) # type: ignore[return-value] @simple_mcache def odom_stream(self) -> Subject[Pose]: - return backpressure(self.raw_odom_stream().pipe(ops.map(Odometry.from_msg))) + return backpressure(self.raw_odom_stream().pipe(ops.map(Odometry.from_msg))) # type: ignore[return-value] @simple_mcache def video_stream(self) -> Observable[Image]: @@ -257,7 +257,7 @@ def video_stream(self) -> Observable[Image]: ops.map( lambda frame: Image.from_numpy( # np.ascontiguousarray(frame.to_ndarray("rgb24")), - frame.to_ndarray(format="rgb24"), + frame.to_ndarray(format="rgb24"), # type: ignore[attr-defined] frame_id="camera_optical", ) ), @@ -266,9 +266,9 @@ def video_stream(self) -> Observable[Image]: @simple_mcache def lowstate_stream(self) -> Subject[LowStateMsg]: - return backpressure(self.unitree_sub_stream(RTC_TOPIC["LOW_STATE"])) + return backpressure(self.unitree_sub_stream(RTC_TOPIC["LOW_STATE"])) # type: ignore[return-value] - def standup_ai(self): + def standup_ai(self): # type: ignore[no-untyped-def] return self.publish_request(RTC_TOPIC["SPORT_MOD"], {"api_id": SPORT_CMD["BalanceStand"]}) def standup_normal(self) -> bool: @@ -278,17 +278,17 @@ def standup_normal(self) -> bool: return True @rpc - def standup(self): + def standup(self): # type: ignore[no-untyped-def] if self.mode == "ai": - return self.standup_ai() + return self.standup_ai() # type: ignore[no-untyped-call] else: return self.standup_normal() @rpc - def liedown(self): + def liedown(self): # type: ignore[no-untyped-def] return self.publish_request(RTC_TOPIC["SPORT_MOD"], {"api_id": SPORT_CMD["StandDown"]}) - async def handstand(self): + async def handstand(self): # type: ignore[no-untyped-def] return self.publish_request( RTC_TOPIC["SPORT_MOD"], {"api_id": SPORT_CMD["Standup"], "parameter": {"data": True}}, @@ -296,7 +296,7 @@ async def handstand(self): @rpc def color(self, color: VUI_COLOR = VUI_COLOR.RED, colortime: int = 60) -> bool: - return self.publish_request( + return self.publish_request( # type: ignore[no-any-return] RTC_TOPIC["VUI"], { "api_id": 1001, @@ -315,9 +315,9 @@ def raw_video_stream(self) -> Observable[VideoMessage]: async def accept_track(track: MediaStreamTrack) -> VideoMessage: while True: if stop_event.is_set(): - return + return # type: ignore[return-value] frame = await track.recv() - serializable_frame = SerializableVideoFrame.from_av_frame(frame) + serializable_frame = SerializableVideoFrame.from_av_frame(frame) # type: ignore[no-untyped-call] subject.on_next(serializable_frame) self.conn.video.add_track_callback(accept_track) @@ -357,13 +357,13 @@ def get_video_stream(self, fps: int = 30) -> Observable[VideoMessage]: stream = self.video_stream() if stream is None: print("Warning: Video stream is not available") - return stream + return stream # type: ignore[no-any-return] except Exception as e: print(f"Error getting video stream: {e}") - return None + return None # type: ignore[return-value] - def stop(self) -> bool: + def stop(self) -> bool: # type: ignore[no-redef] """Stop the robot's movement. Returns: diff --git a/dimos/robot/unitree_webrtc/demo_error_on_name_conflicts.py b/dimos/robot/unitree_webrtc/demo_error_on_name_conflicts.py index d0f4093a4f..eb0f9838f5 100644 --- a/dimos/robot/unitree_webrtc/demo_error_on_name_conflicts.py +++ b/dimos/robot/unitree_webrtc/demo_error_on_name_conflicts.py @@ -27,7 +27,7 @@ class Data2: class ModuleA(Module): - shared_data: Out[Data1] = None + shared_data: Out[Data1] = None # type: ignore[assignment] @rpc def start(self) -> None: @@ -39,7 +39,7 @@ def stop(self) -> None: class ModuleB(Module): - shared_data: In[Data2] = None + shared_data: In[Data2] = None # type: ignore[assignment] @rpc def start(self) -> None: diff --git a/dimos/robot/unitree_webrtc/depth_module.py b/dimos/robot/unitree_webrtc/depth_module.py index d0542cfcb0..ccf0202098 100644 --- a/dimos/robot/unitree_webrtc/depth_module.py +++ b/dimos/robot/unitree_webrtc/depth_module.py @@ -17,7 +17,7 @@ import threading import time -from dimos_lcm.sensor_msgs import CameraInfo +from dimos_lcm.sensor_msgs import CameraInfo # type: ignore[import-untyped] import numpy as np from dimos.core import In, Module, Out, rpc @@ -41,13 +41,13 @@ class DepthModule(Module): """ # LCM inputs - color_image: In[Image] = None - camera_info: In[CameraInfo] = None + color_image: In[Image] = None # type: ignore[assignment] + camera_info: In[CameraInfo] = None # type: ignore[assignment] # LCM outputs - depth_image: Out[Image] = None + depth_image: Out[Image] = None # type: ignore[assignment] - def __init__( + def __init__( # type: ignore[no-untyped-def] self, gt_depth_scale: float = 0.5, global_config: GlobalConfig | None = None, @@ -129,12 +129,12 @@ def _on_camera_info(self, msg: CameraInfo) -> None: cx = K[2] cy = K[5] - self.camera_intrinsics = [fx, fy, cx, cy] + self.camera_intrinsics = [fx, fy, cx, cy] # type: ignore[assignment] # Initialize Metric3D with camera intrinsics from dimos.models.depth.metric3d import Metric3D - self.metric3d = Metric3D(camera_intrinsics=self.camera_intrinsics) + self.metric3d = Metric3D(camera_intrinsics=self.camera_intrinsics) # type: ignore[assignment] self._camera_info_received = True logger.info( @@ -150,7 +150,7 @@ def _on_video(self, msg: Image) -> None: return # Simply store the latest frame - processing happens in main loop - self._latest_frame = msg + self._latest_frame = msg # type: ignore[assignment] logger.debug( f"Received video frame: format={msg.format}, shape={msg.data.shape if hasattr(msg.data, 'shape') else 'unknown'}" ) @@ -186,7 +186,7 @@ def _main_processing_loop(self) -> None: logger.info("Main processing loop stopped") - def _process_depth(self, img_array: np.ndarray) -> None: + def _process_depth(self, img_array: np.ndarray) -> None: # type: ignore[type-arg] """Process depth estimation using Metric3D.""" if self._cannot_process_depth: self._last_depth = None diff --git a/dimos/robot/unitree_webrtc/g1_run.py b/dimos/robot/unitree_webrtc/g1_run.py index b8c0bc77c7..dec959f060 100644 --- a/dimos/robot/unitree_webrtc/g1_run.py +++ b/dimos/robot/unitree_webrtc/g1_run.py @@ -31,7 +31,7 @@ from dimos.robot.unitree_webrtc.unitree_g1 import UnitreeG1 from dimos.robot.unitree_webrtc.unitree_skills import MyUnitreeSkills from dimos.skills.kill_skill import KillSkill -from dimos.skills.navigation import GetPose +from dimos.skills.navigation import GetPose # type: ignore[import-untyped] from dimos.utils.logging_config import setup_logger from dimos.web.robot_web_interface import RobotWebInterface @@ -47,7 +47,7 @@ ) -def main(): +def main(): # type: ignore[no-untyped-def] """Main entry point.""" # Parse command line arguments parser = argparse.ArgumentParser(description="Unitree G1 Robot with Claude Agent") @@ -96,7 +96,7 @@ def main(): logger.info("Starting Unitree G1 Robot with Agent") # Create robot instance with recording/replay support - robot = UnitreeG1( + robot = UnitreeG1( # type: ignore[abstract] ip=robot_ip or "0.0.0.0", # Dummy IP for replay mode recording_path=args.record, replay_path=args.replay, @@ -109,19 +109,19 @@ def main(): # Set up minimal skill library for G1 with robot_type="g1" skills = MyUnitreeSkills(robot=robot, robot_type="g1") - skills.add(KillSkill) + skills.add(KillSkill) # type: ignore[arg-type] skills.add(GetPose) # Create skill instances skills.create_instance("KillSkill", robot=robot, skill_library=skills) skills.create_instance("GetPose", robot=robot) - logger.info(f"Skills registered: {[skill.__name__ for skill in skills.get_class_skills()]}") + logger.info(f"Skills registered: {[skill.__name__ for skill in skills.get_class_skills()]}") # type: ignore[attr-defined] # Set up streams for agent and web interface - agent_response_subject = rx.subject.Subject() + agent_response_subject = rx.subject.Subject() # type: ignore[var-annotated] agent_response_stream = agent_response_subject.pipe(ops.share()) - audio_subject = rx.subject.Subject() + audio_subject = rx.subject.Subject() # type: ignore[var-annotated] # Set up streams for web interface text_streams = { @@ -142,7 +142,7 @@ def main(): agent = ClaudeAgent( dev_name="unitree_g1_agent", input_query_stream=web_interface.query_stream, # Text input from web - skills=skills, + skills=skills, # type: ignore[arg-type] system_query=system_prompt, model_name="claude-3-5-haiku-latest", thinking_budget_tokens=0, @@ -178,4 +178,4 @@ def main(): if __name__ == "__main__": - main() + main() # type: ignore[no-untyped-call] diff --git a/dimos/robot/unitree_webrtc/keyboard_teleop.py b/dimos/robot/unitree_webrtc/keyboard_teleop.py index b4e9153eef..43a2ab01d9 100644 --- a/dimos/robot/unitree_webrtc/keyboard_teleop.py +++ b/dimos/robot/unitree_webrtc/keyboard_teleop.py @@ -31,7 +31,7 @@ class KeyboardTeleop(Module): Outputs standard Twist messages on /cmd_vel for velocity control. """ - cmd_vel: Out[Twist] = None # Standard velocity commands + cmd_vel: Out[Twist] = None # type: ignore[assignment] # Standard velocity commands _stop_event: threading.Event _keys_held: set[int] | None = None @@ -61,7 +61,7 @@ def stop(self) -> None: stop_twist = Twist() stop_twist.linear = Vector3(0, 0, 0) stop_twist.angular = Vector3(0, 0, 0) - self.cmd_vel.publish(stop_twist) + self.cmd_vel.publish(stop_twist) # type: ignore[no-untyped-call] self._stop_event.set() @@ -94,7 +94,7 @@ def _pygame_loop(self) -> None: stop_twist = Twist() stop_twist.linear = Vector3(0, 0, 0) stop_twist.angular = Vector3(0, 0, 0) - self.cmd_vel.publish(stop_twist) + self.cmd_vel.publish(stop_twist) # type: ignore[no-untyped-call] print("EMERGENCY STOP!") elif event.key == pygame.K_ESCAPE: # ESC quits @@ -138,7 +138,7 @@ def _pygame_loop(self) -> None: twist.angular.z *= speed_multiplier # Always publish twist at 50Hz - self.cmd_vel.publish(twist) + self.cmd_vel.publish(twist) # type: ignore[no-untyped-call] self._update_display(twist) diff --git a/dimos/robot/unitree_webrtc/modular/connection_module.py b/dimos/robot/unitree_webrtc/modular/connection_module.py index 7aa045c750..8cd9c7bf87 100644 --- a/dimos/robot/unitree_webrtc/modular/connection_module.py +++ b/dimos/robot/unitree_webrtc/modular/connection_module.py @@ -22,12 +22,12 @@ import queue import warnings -from dimos_lcm.sensor_msgs import CameraInfo +from dimos_lcm.sensor_msgs import CameraInfo # type: ignore[import-untyped] import reactivex as rx from reactivex import operators as ops from reactivex.observable import Observable -from dimos.agents2 import Output, Reducer, Stream, skill +from dimos.agents2 import Output, Reducer, Stream, skill # type: ignore[attr-defined] from dimos.constants import DEFAULT_CAPACITY_COLOR_IMAGE from dimos.core import DimosCluster, In, LCMTransport, Module, ModuleConfig, Out, pSHMTransport, rpc from dimos.core.global_config import GlobalConfig @@ -63,7 +63,7 @@ class FakeRTC(UnitreeWebRTCConnection): dir_name = "unitree_go2_office_walk2" # we don't want UnitreeWebRTCConnection to init - def __init__( + def __init__( # type: ignore[no-untyped-def] self, **kwargs, ) -> None: @@ -87,29 +87,29 @@ def liedown(self) -> None: print("liedown suppressed") @functools.cache - def lidar_stream(self): + def lidar_stream(self): # type: ignore[no-untyped-def] print("lidar stream start") - lidar_store = TimedSensorReplay(f"{self.dir_name}/lidar") - return lidar_store.stream(**self.replay_config) + lidar_store = TimedSensorReplay(f"{self.dir_name}/lidar") # type: ignore[var-annotated] + return lidar_store.stream(**self.replay_config) # type: ignore[arg-type] @functools.cache - def odom_stream(self): + def odom_stream(self): # type: ignore[no-untyped-def] print("odom stream start") - odom_store = TimedSensorReplay(f"{self.dir_name}/odom") - return odom_store.stream(**self.replay_config) + odom_store = TimedSensorReplay(f"{self.dir_name}/odom") # type: ignore[var-annotated] + return odom_store.stream(**self.replay_config) # type: ignore[arg-type] # we don't have raw video stream in the data set @functools.cache - def video_stream(self): + def video_stream(self): # type: ignore[no-untyped-def] print("video stream start") - video_store = TimedSensorReplay(f"{self.dir_name}/video") + video_store = TimedSensorReplay(f"{self.dir_name}/video") # type: ignore[var-annotated] - return video_store.stream(**self.replay_config) + return video_store.stream(**self.replay_config) # type: ignore[arg-type] - def move(self, vector: Twist, duration: float = 0.0) -> None: + def move(self, vector: Twist, duration: float = 0.0) -> None: # type: ignore[override] pass - def publish_request(self, topic: str, data: dict): + def publish_request(self, topic: str, data: dict): # type: ignore[no-untyped-def, type-arg] """Fake publish request for testing.""" return {"status": "ok", "message": "Fake publish"} @@ -123,11 +123,11 @@ class ConnectionModuleConfig(ModuleConfig): class ConnectionModule(Module): - camera_info: Out[CameraInfo] = None - odom: Out[PoseStamped] = None - lidar: Out[LidarMessage] = None - video: Out[Image] = None - movecmd: In[Twist] = None + camera_info: Out[CameraInfo] = None # type: ignore[assignment] + odom: Out[PoseStamped] = None # type: ignore[assignment] + lidar: Out[LidarMessage] = None # type: ignore[assignment] + video: Out[Image] = None # type: ignore[assignment] + movecmd: In[Twist] = None # type: ignore[assignment] connection = None @@ -137,35 +137,35 @@ class ConnectionModule(Module): # parallel calls video_running: bool = False - def __init__(self, connection_type: str = "webrtc", *args, **kwargs) -> None: + def __init__(self, connection_type: str = "webrtc", *args, **kwargs) -> None: # type: ignore[no-untyped-def] self.connection_config = kwargs self.connection_type = connection_type Module.__init__(self, *args, **kwargs) - @skill(stream=Stream.passive, output=Output.image, reducer=Reducer.latest) - def video_stream_tool(self) -> Image: + @skill(stream=Stream.passive, output=Output.image, reducer=Reducer.latest) # type: ignore[arg-type] + def video_stream_tool(self) -> Image: # type: ignore[misc] """implicit video stream skill, don't call this directly""" if self.video_running: return "video stream already running" self.video_running = True - _queue = queue.Queue(maxsize=1) - self.connection.video_stream().subscribe(_queue.put) + _queue = queue.Queue(maxsize=1) # type: ignore[var-annotated] + self.connection.video_stream().subscribe(_queue.put) # type: ignore[attr-defined] yield from iter(_queue.get, None) @rpc def record(self, recording_name: str) -> None: - lidar_store: TimedSensorStorage = TimedSensorStorage(f"{recording_name}/lidar") - lidar_store.save_stream(self.connection.lidar_stream()).subscribe(lambda x: x) + lidar_store: TimedSensorStorage = TimedSensorStorage(f"{recording_name}/lidar") # type: ignore[type-arg] + lidar_store.save_stream(self.connection.lidar_stream()).subscribe(lambda x: x) # type: ignore[arg-type, attr-defined] - odom_store: TimedSensorStorage = TimedSensorStorage(f"{recording_name}/odom") - odom_store.save_stream(self.connection.odom_stream()).subscribe(lambda x: x) + odom_store: TimedSensorStorage = TimedSensorStorage(f"{recording_name}/odom") # type: ignore[type-arg] + odom_store.save_stream(self.connection.odom_stream()).subscribe(lambda x: x) # type: ignore[arg-type, attr-defined] - video_store: TimedSensorStorage = TimedSensorStorage(f"{recording_name}/video") - video_store.save_stream(self.connection.video_stream()).subscribe(lambda x: x) + video_store: TimedSensorStorage = TimedSensorStorage(f"{recording_name}/video") # type: ignore[type-arg] + video_store.save_stream(self.connection.video_stream()).subscribe(lambda x: x) # type: ignore[arg-type, attr-defined] @rpc - def start(self): + def start(self): # type: ignore[no-untyped-def] """Start the connection and subscribe to sensor streams.""" super().start() @@ -178,18 +178,18 @@ def start(self): case "mujoco": from dimos.robot.unitree_webrtc.mujoco_connection import MujocoConnection - self.connection = MujocoConnection(GlobalConfig()) - self.connection.start() + self.connection = MujocoConnection(GlobalConfig()) # type: ignore[assignment] + self.connection.start() # type: ignore[union-attr] case _: raise ValueError(f"Unknown connection type: {self.connection_type}") - unsub = self.connection.odom_stream().subscribe( - lambda odom: self._publish_tf(odom) and self.odom.publish(odom) + unsub = self.connection.odom_stream().subscribe( # type: ignore[union-attr] + lambda odom: self._publish_tf(odom) and self.odom.publish(odom) # type: ignore[func-returns-value, no-untyped-call] ) self._disposables.add(unsub) # Connect sensor streams to outputs - unsub = self.connection.lidar_stream().subscribe(self.lidar.publish) + unsub = self.connection.lidar_stream().subscribe(self.lidar.publish) # type: ignore[union-attr] self._disposables.add(unsub) # self.connection.lidar_stream().subscribe(lambda lidar: print("LIDAR", lidar.ts)) @@ -201,12 +201,12 @@ def resize(image: Image) -> Image: int(originalwidth / image_resize_factor), int(originalheight / image_resize_factor) ) - unsub = self.connection.video_stream().subscribe(self.video.publish) + unsub = self.connection.video_stream().subscribe(self.video.publish) # type: ignore[union-attr] self._disposables.add(unsub) unsub = self.camera_info_stream().subscribe(self.camera_info.publish) self._disposables.add(unsub) - unsub = self.movecmd.subscribe(self.connection.move) - self._disposables.add(unsub) + unsub = self.movecmd.subscribe(self.connection.move) # type: ignore[union-attr] + self._disposables.add(unsub) # type: ignore[arg-type] @rpc def stop(self) -> None: @@ -248,12 +248,12 @@ def _odom_to_tf(cls, odom: PoseStamped) -> list[Transform]: sensor, ] - def _publish_tf(self, msg) -> None: - self.odom.publish(msg) + def _publish_tf(self, msg) -> None: # type: ignore[no-untyped-def] + self.odom.publish(msg) # type: ignore[no-untyped-call] self.tf.publish(*self._odom_to_tf(msg)) @rpc - def publish_request(self, topic: str, data: dict): + def publish_request(self, topic: str, data: dict): # type: ignore[no-untyped-def, type-arg] """Publish a request to the WebRTC connection. Args: topic: The RTC topic to publish to @@ -261,7 +261,7 @@ def publish_request(self, topic: str, data: dict): Returns: The result of the publish request """ - return self.connection.publish_request(topic, data) + return self.connection.publish_request(topic, data) # type: ignore[union-attr] @classmethod def _camera_info(cls) -> Out[CameraInfo]: @@ -303,18 +303,18 @@ def _camera_info(cls) -> Out[CameraInfo]: "binning_y": 0, } - return CameraInfo(**base_msg, header=Header("camera_optical")) + return CameraInfo(**base_msg, header=Header("camera_optical")) # type: ignore[no-any-return] @functools.cache def camera_info_stream(self) -> Observable[CameraInfo]: return rx.interval(1).pipe(ops.map(lambda _: self._camera_info())) -def deploy_connection(dimos: DimosCluster, **kwargs): - foxglove_bridge = dimos.deploy(FoxgloveBridge) +def deploy_connection(dimos: DimosCluster, **kwargs): # type: ignore[no-untyped-def] + foxglove_bridge = dimos.deploy(FoxgloveBridge) # type: ignore[attr-defined, name-defined] foxglove_bridge.start() - connection = dimos.deploy( + connection = dimos.deploy( # type: ignore[attr-defined] ConnectionModule, ip=os.getenv("ROBOT_IP"), connection_type=os.getenv("CONNECTION_TYPE", "fake"), diff --git a/dimos/robot/unitree_webrtc/modular/detect.py b/dimos/robot/unitree_webrtc/modular/detect.py index 46f561b109..b6e263ae15 100644 --- a/dimos/robot/unitree_webrtc/modular/detect.py +++ b/dimos/robot/unitree_webrtc/modular/detect.py @@ -14,7 +14,7 @@ import pickle -from dimos_lcm.sensor_msgs import CameraInfo +from dimos_lcm.sensor_msgs import CameraInfo # type: ignore[import-untyped] from dimos.msgs.sensor_msgs import Image from dimos.msgs.std_msgs import Header @@ -70,7 +70,7 @@ def camera_info() -> CameraInfo: ) -def transform_chain(odom_frame: Odometry) -> list: +def transform_chain(odom_frame: Odometry) -> list: # type: ignore[type-arg] from dimos.msgs.geometry_msgs import Quaternion, Transform, Vector3 from dimos.protocol.tf import TF @@ -97,10 +97,10 @@ def transform_chain(odom_frame: Odometry) -> list: camera_optical, ) - return tf + return tf # type: ignore[return-value] -def broadcast( +def broadcast( # type: ignore[no-untyped-def] timestamp: float, lidar_frame: LidarMessage, video_frame: Image, @@ -108,15 +108,15 @@ def broadcast( detections, annotations, ) -> None: - from dimos_lcm.foxglove_msgs.ImageAnnotations import ImageAnnotations + from dimos_lcm.foxglove_msgs.ImageAnnotations import ImageAnnotations # type: ignore[import-untyped] from dimos.core import LCMTransport from dimos.msgs.geometry_msgs import PoseStamped - lidar_transport = LCMTransport("/lidar", LidarMessage) - odom_transport = LCMTransport("/odom", PoseStamped) - video_transport = LCMTransport("/image", Image) - camera_info_transport = LCMTransport("/camera_info", CameraInfo) + lidar_transport = LCMTransport("/lidar", LidarMessage) # type: ignore[var-annotated] + odom_transport = LCMTransport("/odom", PoseStamped) # type: ignore[var-annotated] + video_transport = LCMTransport("/image", Image) # type: ignore[var-annotated] + camera_info_transport = LCMTransport("/camera_info", CameraInfo) # type: ignore[var-annotated] lidar_transport.broadcast(None, lidar_frame) video_transport.broadcast(None, video_frame) @@ -129,13 +129,13 @@ def broadcast( print(video_frame) print(odom_frame) video_transport = LCMTransport("/image", Image) - annotations_transport = LCMTransport("/annotations", ImageAnnotations) + annotations_transport = LCMTransport("/annotations", ImageAnnotations) # type: ignore[var-annotated] annotations_transport.broadcast(None, annotations) -def process_data(): +def process_data(): # type: ignore[no-untyped-def] from dimos.msgs.sensor_msgs import Image - from dimos.perception.detection.module2D import Detection2DModule, build_imageannotations + from dimos.perception.detection.module2D import Detection2DModule, build_imageannotations # type: ignore[attr-defined] from dimos.robot.unitree_webrtc.type.lidar import LidarMessage from dimos.robot.unitree_webrtc.type.odometry import Odometry from dimos.utils.data import get_data @@ -152,11 +152,11 @@ def attach_frame_id(image: Image) -> Image: return image lidar_frame = lidar_store.find_closest(target, tolerance=1) - video_frame = attach_frame_id(video_store.find_closest(target, tolerance=1)) + video_frame = attach_frame_id(video_store.find_closest(target, tolerance=1)) # type: ignore[arg-type] odom_frame = odom_store.find_closest(target, tolerance=1) detector = Detection2DModule() - detections = detector.detect(video_frame) + detections = detector.detect(video_frame) # type: ignore[attr-defined] annotations = build_imageannotations(detections) data = (target, lidar_frame, video_frame, odom_frame, detections, annotations) @@ -173,7 +173,7 @@ def main() -> None: data = pickle.load(file) except FileNotFoundError: print("Processing data and creating pickle file...") - data = process_data() + data = process_data() # type: ignore[no-untyped-call] broadcast(*data) diff --git a/dimos/robot/unitree_webrtc/modular/ivan_unitree.py b/dimos/robot/unitree_webrtc/modular/ivan_unitree.py index e7a2bcabc8..b3a90f2b77 100644 --- a/dimos/robot/unitree_webrtc/modular/ivan_unitree.py +++ b/dimos/robot/unitree_webrtc/modular/ivan_unitree.py @@ -24,9 +24,9 @@ from dimos.msgs.vision_msgs import Detection2DArray from dimos.perception.detection.module2D import Detection2DModule from dimos.perception.detection.reid import ReidModule -from dimos.protocol.pubsub import lcm +from dimos.protocol.pubsub import lcm # type: ignore[attr-defined] from dimos.robot.foxglove_bridge import FoxgloveBridge -from dimos.robot.unitree_webrtc.modular import deploy_connection +from dimos.robot.unitree_webrtc.modular import deploy_connection # type: ignore[attr-defined] from dimos.robot.unitree_webrtc.modular.connection_module import ConnectionModule from dimos.utils.logging_config import setup_logger @@ -37,11 +37,11 @@ def detection_unitree() -> None: dimos = start(8) connection = deploy_connection(dimos) - def goto(pose) -> bool: + def goto(pose) -> bool: # type: ignore[no-untyped-def] print("NAVIGATION REQUESTED:", pose) return True - detector = dimos.deploy( + detector = dimos.deploy( # type: ignore[attr-defined] Detection2DModule, # goto=goto, camera_info=ConnectionModule._camera_info(), @@ -76,7 +76,7 @@ def goto(pose) -> bool: # person_tracker.detections.connect(detector.detections) # person_tracker.target.transport = LCMTransport("/goal_request", PoseStamped) - reid = dimos.deploy(ReidModule) + reid = dimos.deploy(ReidModule) # type: ignore[attr-defined] reid.image.connect(connection.video) reid.detections.connect(detector.detections) @@ -87,16 +87,16 @@ def goto(pose) -> bool: connection.start() reid.start() - from dimos.agents2 import Agent + from dimos.agents2 import Agent # type: ignore[attr-defined] from dimos.agents2.cli.human import HumanInput agent = Agent( system_prompt="You are a helpful assistant for controlling a Unitree Go2 robot.", model=Model.GPT_4O, # Could add CLAUDE models to enum - provider=Provider.OPENAI, # Would need ANTHROPIC provider + provider=Provider.OPENAI, # type: ignore[attr-defined] # Would need ANTHROPIC provider ) - human_input = dimos.deploy(HumanInput) + human_input = dimos.deploy(HumanInput) # type: ignore[attr-defined] agent.register_skills(human_input) # agent.register_skills(connection) agent.register_skills(detector) diff --git a/dimos/robot/unitree_webrtc/rosnav.py b/dimos/robot/unitree_webrtc/rosnav.py index ee0c5f9c77..e7e3990328 100644 --- a/dimos/robot/unitree_webrtc/rosnav.py +++ b/dimos/robot/unitree_webrtc/rosnav.py @@ -27,12 +27,12 @@ # TODO: Remove, deprecated class NavigationModule(Module): - goal_pose: Out[PoseStamped] = None - goal_reached: In[Bool] = None - cancel_goal: Out[Bool] = None - joy: Out[Joy] = None + goal_pose: Out[PoseStamped] = None # type: ignore[assignment] + goal_reached: In[Bool] = None # type: ignore[assignment] + cancel_goal: Out[Bool] = None # type: ignore[assignment] + joy: Out[Joy] = None # type: ignore[assignment] - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def] """Initialize NavigationModule.""" Module.__init__(self, *args, **kwargs) self.goal_reach = None @@ -46,7 +46,7 @@ def start(self) -> None: def _on_goal_reached(self, msg: Bool) -> None: """Handle goal reached status messages.""" - self.goal_reach = msg.data + self.goal_reach = msg.data # type: ignore[assignment] def _set_autonomy_mode(self) -> None: """ @@ -81,7 +81,7 @@ def _set_autonomy_mode(self) -> None: ) if self.joy: - self.joy.publish(joy_msg) + self.joy.publish(joy_msg) # type: ignore[no-untyped-call] logger.info("Setting autonomy mode via Joy message") @rpc @@ -103,9 +103,9 @@ def go_to(self, pose: PoseStamped, timeout: float = 60.0) -> bool: self.goal_reach = None self._set_autonomy_mode() - self.goal_pose.publish(pose) + self.goal_pose.publish(pose) # type: ignore[no-untyped-call] time.sleep(0.2) - self.goal_pose.publish(pose) + self.goal_pose.publish(pose) # type: ignore[no-untyped-call] start_time = time.time() while time.time() - start_time < timeout: @@ -130,7 +130,7 @@ def stop(self) -> bool: if self.cancel_goal: cancel_msg = Bool(data=True) - self.cancel_goal.publish(cancel_msg) + self.cancel_goal.publish(cancel_msg) # type: ignore[no-untyped-call] return True return False diff --git a/dimos/robot/unitree_webrtc/testing/helpers.py b/dimos/robot/unitree_webrtc/testing/helpers.py index 5159deab4c..c7de7035cf 100644 --- a/dimos/robot/unitree_webrtc/testing/helpers.py +++ b/dimos/robot/unitree_webrtc/testing/helpers.py @@ -16,7 +16,7 @@ import time from typing import Any, Protocol -import open3d as o3d +import open3d as o3d # type: ignore[import-untyped] from reactivex.observable import Observable color1 = [1, 0.706, 0] @@ -50,13 +50,13 @@ def benchmark(calls: int, targetf: Callable[[], int | None]) -> float: class ReturnsDrawable(Protocol): - def o3d_geometry(self) -> O3dDrawable: ... + def o3d_geometry(self) -> O3dDrawable: ... # type: ignore[valid-type] Drawable = O3dDrawable | ReturnsDrawable -def show3d(*components: Iterable[Drawable], title: str = "open3d") -> o3d.visualization.Visualizer: +def show3d(*components: Iterable[Drawable], title: str = "open3d") -> o3d.visualization.Visualizer: # type: ignore[valid-type] vis = o3d.visualization.Visualizer() vis.create_window(window_name=title) for component in components: @@ -99,7 +99,7 @@ def show3d_stream( q: queue.Queue[Any] = queue.Queue() stop_flag = threading.Event() - def on_next(geometry: O3dDrawable) -> None: + def on_next(geometry: O3dDrawable) -> None: # type: ignore[valid-type] q.put(geometry) def on_error(e: Exception) -> None: @@ -116,9 +116,9 @@ def on_completed() -> None: on_completed=on_completed, ) - def geom(geometry: Drawable) -> O3dDrawable: + def geom(geometry: Drawable) -> O3dDrawable: # type: ignore[valid-type] """Extracts the Open3D geometry from the given object.""" - return geometry.o3d_geometry if hasattr(geometry, "o3d_geometry") else geometry + return geometry.o3d_geometry if hasattr(geometry, "o3d_geometry") else geometry # type: ignore[attr-defined, no-any-return] # Wait for the first geometry first_geometry = None diff --git a/dimos/robot/unitree_webrtc/testing/mock.py b/dimos/robot/unitree_webrtc/testing/mock.py index 20eb357cc0..5b5c3be8b8 100644 --- a/dimos/robot/unitree_webrtc/testing/mock.py +++ b/dimos/robot/unitree_webrtc/testing/mock.py @@ -59,7 +59,7 @@ def iterate(self) -> Iterator[LidarMessage]: filename = os.path.splitext(basename)[0] yield self.load_one(filename) - def stream(self, rate_hz: float = 10.0): + def stream(self, rate_hz: float = 10.0): # type: ignore[no-untyped-def] sleep_time = 1.0 / rate_hz return from_iterable(self.iterate()).pipe( @@ -67,14 +67,14 @@ def stream(self, rate_hz: float = 10.0): ops.map(lambda x: x[0] if isinstance(x, tuple) else x), ) - def save_stream(self, observable: Observable[LidarMessage]): - return observable.pipe(ops.map(lambda frame: self.save_one(frame))) + def save_stream(self, observable: Observable[LidarMessage]): # type: ignore[no-untyped-def] + return observable.pipe(ops.map(lambda frame: self.save_one(frame))) # type: ignore[no-untyped-call] - def save(self, *frames): - [self.save_one(frame) for frame in frames] + def save(self, *frames): # type: ignore[no-untyped-def] + [self.save_one(frame) for frame in frames] # type: ignore[no-untyped-call] return self.cnt - def save_one(self, frame): + def save_one(self, frame): # type: ignore[no-untyped-def] file_name = f"/lidar_data_{self.cnt:03d}.pickle" full_path = self.root + file_name diff --git a/dimos/robot/unitree_webrtc/type/lidar.py b/dimos/robot/unitree_webrtc/type/lidar.py index a6595790ad..2e5bb550cd 100644 --- a/dimos/robot/unitree_webrtc/type/lidar.py +++ b/dimos/robot/unitree_webrtc/type/lidar.py @@ -16,7 +16,7 @@ from typing import TypedDict import numpy as np -import open3d as o3d +import open3d as o3d # type: ignore[import-untyped] from dimos.msgs.geometry_msgs import Vector3 from dimos.msgs.sensor_msgs import PointCloud2 @@ -24,7 +24,7 @@ class RawLidarPoints(TypedDict): - points: np.ndarray # Shape (N, 3) array of 3D points [x, y, z] + points: np.ndarray # type: ignore[type-arg] # Shape (N, 3) array of 3D points [x, y, z] class RawLidarData(TypedDict): @@ -53,18 +53,18 @@ class LidarMessage(PointCloud2): raw_msg: RawLidarMsg | None # _costmap: Optional[Costmap] = None # TODO: Fix after costmap migration - def __init__(self, **kwargs) -> None: + def __init__(self, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__( pointcloud=kwargs.get("pointcloud"), ts=kwargs.get("ts"), frame_id="world", ) - self.origin = kwargs.get("origin") + self.origin = kwargs.get("origin") # type: ignore[assignment] self.resolution = kwargs.get("resolution", 0.05) @classmethod - def from_msg(cls: type["LidarMessage"], raw_message: RawLidarMsg, **kwargs) -> "LidarMessage": + def from_msg(cls: type["LidarMessage"], raw_message: RawLidarMsg, **kwargs) -> "LidarMessage": # type: ignore[no-untyped-def] data = raw_message["data"] points = data["data"]["points"] pointcloud = o3d.geometry.PointCloud() @@ -89,11 +89,11 @@ def from_msg(cls: type["LidarMessage"], raw_message: RawLidarMsg, **kwargs) -> " def __repr__(self) -> str: return f"LidarMessage(ts={to_human_readable(self.ts)}, origin={self.origin}, resolution={self.resolution}, {self.pointcloud})" - def __iadd__(self, other: "LidarMessage") -> "LidarMessage": + def __iadd__(self, other: "LidarMessage") -> "LidarMessage": # type: ignore[override] self.pointcloud += other.pointcloud return self - def __add__(self, other: "LidarMessage") -> "LidarMessage": + def __add__(self, other: "LidarMessage") -> "LidarMessage": # type: ignore[override] # Determine which message is more recent if self.ts >= other.ts: ts = self.ts @@ -105,7 +105,7 @@ def __add__(self, other: "LidarMessage") -> "LidarMessage": resolution = other.resolution # Return a new LidarMessage with combined data - return LidarMessage( + return LidarMessage( # type: ignore[attr-defined, no-any-return] ts=ts, origin=origin, resolution=resolution, @@ -113,7 +113,7 @@ def __add__(self, other: "LidarMessage") -> "LidarMessage": ).estimate_normals() @property - def o3d_geometry(self): + def o3d_geometry(self): # type: ignore[no-untyped-def] return self.pointcloud # TODO: Fix after costmap migration diff --git a/dimos/robot/unitree_webrtc/type/map.py b/dimos/robot/unitree_webrtc/type/map.py index 65ad6ce0d9..436afea811 100644 --- a/dimos/robot/unitree_webrtc/type/map.py +++ b/dimos/robot/unitree_webrtc/type/map.py @@ -15,7 +15,7 @@ import time import numpy as np -import open3d as o3d +import open3d as o3d # type: ignore[import-untyped] from reactivex import interval from reactivex.disposable import Disposable @@ -28,14 +28,14 @@ class Map(Module): - lidar: In[LidarMessage] = None - global_map: Out[LidarMessage] = None - global_costmap: Out[OccupancyGrid] = None - local_costmap: Out[OccupancyGrid] = None + lidar: In[LidarMessage] = None # type: ignore[assignment] + global_map: Out[LidarMessage] = None # type: ignore[assignment] + global_costmap: Out[OccupancyGrid] = None # type: ignore[assignment] + local_costmap: Out[OccupancyGrid] = None # type: ignore[assignment] pointcloud: o3d.geometry.PointCloud = o3d.geometry.PointCloud() - def __init__( + def __init__( # type: ignore[no-untyped-def] self, voxel_size: float = 0.05, cost_resolution: float = 0.05, @@ -64,8 +64,8 @@ def start(self) -> None: unsub = self.lidar.subscribe(self.add_frame) self._disposables.add(Disposable(unsub)) - def publish(_) -> None: - self.global_map.publish(self.to_lidar_message()) + def publish(_) -> None: # type: ignore[no-untyped-def] + self.global_map.publish(self.to_lidar_message()) # type: ignore[no-untyped-call] # temporary, not sure if it belogs in mapper # used only for visualizations, not for any algo @@ -76,11 +76,11 @@ def publish(_) -> None: max_height=self.max_height, ) - self.global_costmap.publish(occupancygrid) + self.global_costmap.publish(occupancygrid) # type: ignore[no-untyped-call] if self.global_publish_interval is not None: - unsub = interval(self.global_publish_interval).subscribe(publish) - self._disposables.add(unsub) + unsub = interval(self.global_publish_interval).subscribe(publish) # type: ignore[assignment] + self._disposables.add(unsub) # type: ignore[arg-type] @rpc def stop(self) -> None: @@ -101,7 +101,7 @@ def to_lidar_message(self) -> LidarMessage: ) @rpc - def add_frame(self, frame: LidarMessage) -> "Map": + def add_frame(self, frame: LidarMessage) -> "Map": # type: ignore[return] """Voxelise *frame* and splice it into the running map.""" new_pct = frame.pointcloud.voxel_down_sample(voxel_size=self.voxel_size) @@ -116,7 +116,7 @@ def add_frame(self, frame: LidarMessage) -> "Map": min_height=0.15, max_height=0.6, ).gradient(max_distance=0.25) - self.local_costmap.publish(local_costmap) + self.local_costmap.publish(local_costmap) # type: ignore[no-untyped-call] @property def o3d_geometry(self) -> o3d.geometry.PointCloud: @@ -171,12 +171,12 @@ def splice_cylinder( mapper = Map.blueprint -def deploy(dimos: DimosCluster, connection: Go2ConnectionProtocol): - mapper = dimos.deploy(Map, global_publish_interval=1.0) +def deploy(dimos: DimosCluster, connection: Go2ConnectionProtocol): # type: ignore[no-untyped-def] + mapper = dimos.deploy(Map, global_publish_interval=1.0) # type: ignore[attr-defined] mapper.global_map.transport = LCMTransport("/global_map", LidarMessage) mapper.global_costmap.transport = LCMTransport("/global_costmap", OccupancyGrid) mapper.local_costmap.transport = LCMTransport("/local_costmap", OccupancyGrid) - mapper.lidar.connect(connection.pointcloud) + mapper.lidar.connect(connection.pointcloud) # type: ignore[attr-defined] mapper.start() return mapper diff --git a/dimos/robot/unitree_webrtc/type/odometry.py b/dimos/robot/unitree_webrtc/type/odometry.py index 52a8544fbc..e939022ef9 100644 --- a/dimos/robot/unitree_webrtc/type/odometry.py +++ b/dimos/robot/unitree_webrtc/type/odometry.py @@ -71,11 +71,11 @@ class RawOdometryMessage(TypedDict): data: OdometryData -class Odometry(PoseStamped, Timestamped): +class Odometry(PoseStamped, Timestamped): # type: ignore[misc] name = "geometry_msgs.PoseStamped" - def __init__(self, frame_id: str = "base_link", *args, **kwargs) -> None: - super().__init__(frame_id=frame_id, *args, **kwargs) + def __init__(self, frame_id: str = "base_link", *args, **kwargs) -> None: # type: ignore[no-untyped-def] + super().__init__(frame_id=frame_id, *args, **kwargs) # type: ignore[misc] @classmethod def from_msg(cls, msg: RawOdometryMessage) -> "Odometry": diff --git a/dimos/robot/unitree_webrtc/type/timeseries.py b/dimos/robot/unitree_webrtc/type/timeseries.py index a85afba93f..c00d607cc2 100644 --- a/dimos/robot/unitree_webrtc/type/timeseries.py +++ b/dimos/robot/unitree_webrtc/type/timeseries.py @@ -91,17 +91,17 @@ def __iter__(self) -> Iterable[EVENT]: ... @property def start_time(self) -> datetime: """Return the timestamp of the earliest event, assuming the data is sorted.""" - return next(iter(self)).ts + return next(iter(self)).ts # type: ignore[call-overload, no-any-return, type-var] @property def end_time(self) -> datetime: """Return the timestamp of the latest event, assuming the data is sorted.""" - return next(reversed(list(self))).ts + return next(reversed(list(self))).ts # type: ignore[call-overload, no-any-return] @property def frequency(self) -> float: """Calculate the frequency of events in Hz.""" - return len(list(self)) / (self.duration().total_seconds() or 1) + return len(list(self)) / (self.duration().total_seconds() or 1) # type: ignore[call-overload] def time_range(self) -> tuple[datetime, datetime]: """Return (earliest_ts, latest_ts). Empty input ⇒ ValueError.""" @@ -121,7 +121,7 @@ def closest_to(self, timestamp: EpochLike) -> EVENT: closest = None min_dist = float("inf") - for event in self: + for event in self: # type: ignore[attr-defined] dist = abs(event.ts - target_ts) if dist > min_dist: break @@ -130,11 +130,11 @@ def closest_to(self, timestamp: EpochLike) -> EVENT: closest = event print(f"closest: {closest}") - return closest + return closest # type: ignore[return-value] def __repr__(self) -> str: """Return a string representation of the Timeseries.""" - return f"Timeseries(date={self.start_time.strftime('%Y-%m-%d')}, start={self.start_time.strftime('%H:%M:%S')}, end={self.end_time.strftime('%H:%M:%S')}, duration={self.duration()}, events={len(list(self))}, freq={self.frequency:.2f}Hz)" + return f"Timeseries(date={self.start_time.strftime('%Y-%m-%d')}, start={self.start_time.strftime('%H:%M:%S')}, end={self.end_time.strftime('%H:%M:%S')}, duration={self.duration()}, events={len(list(self))}, freq={self.frequency:.2f}Hz)" # type: ignore[call-overload] def __str__(self) -> str: """Return a string representation of the Timeseries.""" diff --git a/dimos/robot/unitree_webrtc/type/vector.py b/dimos/robot/unitree_webrtc/type/vector.py index be00e3403c..244fc0fe0b 100644 --- a/dimos/robot/unitree_webrtc/type/vector.py +++ b/dimos/robot/unitree_webrtc/type/vector.py @@ -89,7 +89,7 @@ def __getitem__(self, idx: int) -> float: return float(self._data[idx]) def __iter__(self) -> Iterable[float]: - return iter(self._data) + return iter(self._data) # type: ignore[no-any-return] def __repr__(self) -> str: components = ",".join(f"{x:.6g}" for x in self._data) @@ -114,7 +114,7 @@ def getArrow() -> str: return f"{getArrow()} Vector {self.__repr__()}" - def serialize(self) -> dict: + def serialize(self) -> dict: # type: ignore[type-arg] """Serialize the vector to a dictionary.""" return {"type": "vector", "c": self._data.tolist()} diff --git a/dimos/robot/unitree_webrtc/unitree_b1/b1_command.py b/dimos/robot/unitree_webrtc/unitree_b1/b1_command.py index 82545fa2c6..d59db4d36a 100644 --- a/dimos/robot/unitree_webrtc/unitree_b1/b1_command.py +++ b/dimos/robot/unitree_webrtc/unitree_b1/b1_command.py @@ -39,7 +39,7 @@ class B1Command(BaseModel): ) # Control mode (uint8): 0=idle, 1=stand, 2=walk, 6=recovery @classmethod - def from_twist(cls, twist, mode: int = 2): + def from_twist(cls, twist, mode: int = 2): # type: ignore[no-untyped-def] """Create B1Command from standard ROS Twist message. This is the key integration point for navigation and planning. diff --git a/dimos/robot/unitree_webrtc/unitree_b1/connection.py b/dimos/robot/unitree_webrtc/unitree_b1/connection.py index 73285b4d76..3776612ea0 100644 --- a/dimos/robot/unitree_webrtc/unitree_b1/connection.py +++ b/dimos/robot/unitree_webrtc/unitree_b1/connection.py @@ -52,13 +52,13 @@ class B1ConnectionModule(Module): internally converts to B1Command format, and sends UDP packets at 50Hz. """ - cmd_vel: In[TwistStamped] = None # Timestamped velocity commands from ROS - mode_cmd: In[Int32] = None # Mode changes - odom_in: In[Odometry] = None # External odometry from ROS SLAM/lidar + cmd_vel: In[TwistStamped] = None # type: ignore[assignment] # Timestamped velocity commands from ROS + mode_cmd: In[Int32] = None # type: ignore[assignment] # Mode changes + odom_in: In[Odometry] = None # type: ignore[assignment] # External odometry from ROS SLAM/lidar - odom_pose: Out[PoseStamped] = None # Converted pose for internal use + odom_pose: Out[PoseStamped] = None # type: ignore[assignment] # Converted pose for internal use - def __init__( + def __init__( # type: ignore[no-untyped-def] self, ip: str = "192.168.12.1", port: int = 9090, test_mode: bool = False, *args, **kwargs ) -> None: """Initialize B1 connection module. @@ -95,7 +95,7 @@ def start(self) -> None: # Setup UDP socket (unless in test mode) if not self.test_mode: - self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # type: ignore[assignment] logger.info(f"B1 Connection started - UDP to {self.ip}:{self.port} at 50Hz") else: logger.info(f"[TEST MODE] B1 Connection started - would send to {self.ip}:{self.port}") @@ -116,12 +116,12 @@ def start(self) -> None: self.watchdog_running = True # Start 50Hz sending thread - self.send_thread = threading.Thread(target=self._send_loop, daemon=True) - self.send_thread.start() + self.send_thread = threading.Thread(target=self._send_loop, daemon=True) # type: ignore[assignment] + self.send_thread.start() # type: ignore[attr-defined] # Start watchdog thread - self.watchdog_thread = threading.Thread(target=self._watchdog_loop, daemon=True) - self.watchdog_thread.start() + self.watchdog_thread = threading.Thread(target=self._watchdog_loop, daemon=True) # type: ignore[assignment] + self.watchdog_thread.start() # type: ignore[attr-defined] @rpc def stop(self) -> None: @@ -282,7 +282,7 @@ def _publish_odom_pose(self, msg: Odometry) -> None: position=msg.pose.pose.position, orientation=msg.pose.pose.orientation, ) - self.odom_pose.publish(pose_stamped) + self.odom_pose.publish(pose_stamped) # type: ignore[no-untyped-call] def _watchdog_loop(self) -> None: """Single watchdog thread that monitors command freshness.""" @@ -359,9 +359,9 @@ def move(self, twist_stamped: TwistStamped, duration: float = 0.0) -> bool: class MockB1ConnectionModule(B1ConnectionModule): """Test connection module that prints commands instead of sending UDP.""" - def __init__(self, ip: str = "127.0.0.1", port: int = 9090, *args, **kwargs) -> None: + def __init__(self, ip: str = "127.0.0.1", port: int = 9090, *args, **kwargs) -> None: # type: ignore[no-untyped-def] """Initialize test connection without creating socket.""" - super().__init__(ip, port, test_mode=True, *args, **kwargs) + super().__init__(ip, port, test_mode=True, *args, **kwargs) # type: ignore[misc] def _send_loop(self) -> None: """Override to provide better test output with timeout detection.""" diff --git a/dimos/robot/unitree_webrtc/unitree_b1/joystick_module.py b/dimos/robot/unitree_webrtc/unitree_b1/joystick_module.py index 9c3c09861c..8978857e81 100644 --- a/dimos/robot/unitree_webrtc/unitree_b1/joystick_module.py +++ b/dimos/robot/unitree_webrtc/unitree_b1/joystick_module.py @@ -37,10 +37,10 @@ class JoystickModule(Module): This allows testing the same interface that navigation will use. """ - twist_out: Out[TwistStamped] = None # Timestamped velocity commands - mode_out: Out[Int32] = None # Mode changes + twist_out: Out[TwistStamped] = None # type: ignore[assignment] # Timestamped velocity commands + mode_out: Out[Int32] = None # type: ignore[assignment] # Mode changes - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def] Module.__init__(self, *args, **kwargs) self.pygame_ready = False self.running = False @@ -58,7 +58,7 @@ def start(self) -> bool: print("ERROR: pygame not installed. Install with: pip install pygame") return False - self.keys_held = set() + self.keys_held = set() # type: ignore[var-annotated] self.pygame_ready = True self.running = True @@ -83,7 +83,7 @@ def stop(self) -> None: linear=stop_twist.linear, angular=stop_twist.angular, ) - self.twist_out.publish(stop_twist_stamped) + self.twist_out.publish(stop_twist_stamped) # type: ignore[no-untyped-call] self._thread.join(2) @@ -120,19 +120,19 @@ def _pygame_loop(self) -> None: self.current_mode = 0 mode_msg = Int32() mode_msg.data = 0 - self.mode_out.publish(mode_msg) + self.mode_out.publish(mode_msg) # type: ignore[no-untyped-call] print("Mode: IDLE") elif event.key == pygame.K_1: self.current_mode = 1 mode_msg = Int32() mode_msg.data = 1 - self.mode_out.publish(mode_msg) + self.mode_out.publish(mode_msg) # type: ignore[no-untyped-call] print("Mode: STAND") elif event.key == pygame.K_2: self.current_mode = 2 mode_msg = Int32() mode_msg.data = 2 - self.mode_out.publish(mode_msg) + self.mode_out.publish(mode_msg) # type: ignore[no-untyped-call] print("Mode: WALK") elif event.key == pygame.K_SPACE or event.key == pygame.K_q: self.keys_held.clear() @@ -140,7 +140,7 @@ def _pygame_loop(self) -> None: self.current_mode = 0 mode_msg = Int32() mode_msg.data = 0 - self.mode_out.publish(mode_msg) + self.mode_out.publish(mode_msg) # type: ignore[no-untyped-call] # Also send zero twist stop_twist = Twist() stop_twist.linear = Vector3(0, 0, 0) @@ -151,7 +151,7 @@ def _pygame_loop(self) -> None: linear=stop_twist.linear, angular=stop_twist.angular, ) - self.twist_out.publish(stop_twist_stamped) + self.twist_out.publish(stop_twist_stamped) # type: ignore[no-untyped-call] print("EMERGENCY STOP!") elif event.key == pygame.K_ESCAPE: # ESC still quits for development convenience @@ -213,7 +213,7 @@ def _pygame_loop(self) -> None: twist_stamped = TwistStamped( ts=time.time(), frame_id="base_link", linear=twist.linear, angular=twist.angular ) - self.twist_out.publish(twist_stamped) + self.twist_out.publish(twist_stamped) # type: ignore[no-untyped-call] # Update pygame display self._update_display(twist) @@ -224,7 +224,7 @@ def _pygame_loop(self) -> None: pygame.quit() print("JoystickModule stopped") - def _update_display(self, twist) -> None: + def _update_display(self, twist) -> None: # type: ignore[no-untyped-def] """Update pygame window with current status.""" import pygame diff --git a/dimos/robot/unitree_webrtc/unitree_b1/unitree_b1.py b/dimos/robot/unitree_webrtc/unitree_b1/unitree_b1.py index 04390c2e9e..185b28a7ab 100644 --- a/dimos/robot/unitree_webrtc/unitree_b1/unitree_b1.py +++ b/dimos/robot/unitree_webrtc/unitree_b1/unitree_b1.py @@ -42,15 +42,15 @@ # Handle ROS imports for environments where ROS is not available like CI try: - from geometry_msgs.msg import TwistStamped as ROSTwistStamped - from nav_msgs.msg import Odometry as ROSOdometry - from tf2_msgs.msg import TFMessage as ROSTFMessage + from geometry_msgs.msg import TwistStamped as ROSTwistStamped # type: ignore[attr-defined] + from nav_msgs.msg import Odometry as ROSOdometry # type: ignore[attr-defined] + from tf2_msgs.msg import TFMessage as ROSTFMessage # type: ignore[attr-defined] ROS_AVAILABLE = True except ImportError: - ROSTwistStamped = None - ROSOdometry = None - ROSTFMessage = None + ROSTwistStamped = None # type: ignore[assignment, misc] + ROSOdometry = None # type: ignore[assignment, misc] + ROSTFMessage = None # type: ignore[assignment, misc] ROS_AVAILABLE = False logger = setup_logger("dimos.robot.unitree_webrtc.unitree_b1", level=logging.INFO) @@ -110,28 +110,28 @@ def start(self) -> None: logger.info("Deploying connection module...") if self.test_mode: - self.connection = self._dimos.deploy(MockB1ConnectionModule, self.ip, self.port) + self.connection = self._dimos.deploy(MockB1ConnectionModule, self.ip, self.port) # type: ignore[assignment] else: - self.connection = self._dimos.deploy(B1ConnectionModule, self.ip, self.port) + self.connection = self._dimos.deploy(B1ConnectionModule, self.ip, self.port) # type: ignore[assignment] # Configure LCM transports for connection (matching G1 pattern) - self.connection.cmd_vel.transport = core.LCMTransport("/cmd_vel", TwistStamped) - self.connection.mode_cmd.transport = core.LCMTransport("/b1/mode", Int32) - self.connection.odom_in.transport = core.LCMTransport("/state_estimation", Odometry) - self.connection.odom_pose.transport = core.LCMTransport("/odom", PoseStamped) + self.connection.cmd_vel.transport = core.LCMTransport("/cmd_vel", TwistStamped) # type: ignore[attr-defined] + self.connection.mode_cmd.transport = core.LCMTransport("/b1/mode", Int32) # type: ignore[attr-defined] + self.connection.odom_in.transport = core.LCMTransport("/state_estimation", Odometry) # type: ignore[attr-defined] + self.connection.odom_pose.transport = core.LCMTransport("/odom", PoseStamped) # type: ignore[attr-defined] # Deploy joystick move_vel control if self.enable_joystick: from dimos.robot.unitree_webrtc.unitree_b1.joystick_module import JoystickModule - self.joystick = self._dimos.deploy(JoystickModule) - self.joystick.twist_out.transport = core.LCMTransport("/cmd_vel", TwistStamped) - self.joystick.mode_out.transport = core.LCMTransport("/b1/mode", Int32) + self.joystick = self._dimos.deploy(JoystickModule) # type: ignore[assignment] + self.joystick.twist_out.transport = core.LCMTransport("/cmd_vel", TwistStamped) # type: ignore[attr-defined] + self.joystick.mode_out.transport = core.LCMTransport("/b1/mode", Int32) # type: ignore[attr-defined] logger.info("Joystick module deployed - pygame window will open") self._dimos.start_all_modules() - self.connection.idle() # Start in IDLE mode for safety + self.connection.idle() # type: ignore[attr-defined] # Start in IDLE mode for safety logger.info("B1 started in IDLE mode (safety)") # Deploy ROS bridge if enabled (matching G1 pattern) @@ -151,24 +151,24 @@ def stop(self) -> None: def _deploy_ros_bridge(self) -> None: """Deploy and configure ROS bridge (matching G1 implementation).""" - self.ros_bridge = ROSBridge("b1_ros_bridge") + self.ros_bridge = ROSBridge("b1_ros_bridge") # type: ignore[assignment] # Add /cmd_vel topic from ROS to DIMOS - self.ros_bridge.add_topic( + self.ros_bridge.add_topic( # type: ignore[attr-defined] "/cmd_vel", TwistStamped, ROSTwistStamped, direction=BridgeDirection.ROS_TO_DIMOS ) # Add /state_estimation topic from ROS to DIMOS (external odometry) - self.ros_bridge.add_topic( + self.ros_bridge.add_topic( # type: ignore[attr-defined] "/state_estimation", Odometry, ROSOdometry, direction=BridgeDirection.ROS_TO_DIMOS ) # Add /tf topic from ROS to DIMOS - self.ros_bridge.add_topic( + self.ros_bridge.add_topic( # type: ignore[attr-defined] "/tf", TFMessage, ROSTFMessage, direction=BridgeDirection.ROS_TO_DIMOS ) - self.ros_bridge.start() + self.ros_bridge.start() # type: ignore[attr-defined] logger.info("ROS bridge deployed: /cmd_vel, /state_estimation, /tf (ROS → DIMOS)") @@ -221,7 +221,7 @@ def main() -> None: args = parser.parse_args() - robot = UnitreeB1( + robot = UnitreeB1( # type: ignore[abstract] ip=args.ip, port=args.port, output_dir=args.output_dir, diff --git a/dimos/robot/unitree_webrtc/unitree_g1.py b/dimos/robot/unitree_webrtc/unitree_g1.py index 50f7504d31..0610d8b7f9 100644 --- a/dimos/robot/unitree_webrtc/unitree_g1.py +++ b/dimos/robot/unitree_webrtc/unitree_g1.py @@ -22,15 +22,15 @@ import os import time -from dimos_lcm.foxglove_msgs import SceneUpdate -from geometry_msgs.msg import PoseStamped as ROSPoseStamped, TwistStamped as ROSTwistStamped -from nav_msgs.msg import Odometry as ROSOdometry +from dimos_lcm.foxglove_msgs import SceneUpdate # type: ignore[import-untyped] +from geometry_msgs.msg import PoseStamped as ROSPoseStamped, TwistStamped as ROSTwistStamped # type: ignore[attr-defined] +from nav_msgs.msg import Odometry as ROSOdometry # type: ignore[attr-defined] from reactivex.disposable import Disposable -from sensor_msgs.msg import Joy as ROSJoy, PointCloud2 as ROSPointCloud2 -from tf2_msgs.msg import TFMessage as ROSTFMessage +from sensor_msgs.msg import Joy as ROSJoy, PointCloud2 as ROSPointCloud2 # type: ignore[attr-defined] +from tf2_msgs.msg import TFMessage as ROSTFMessage # type: ignore[attr-defined] from dimos import core -from dimos.agents2 import Agent +from dimos.agents2 import Agent # type: ignore[attr-defined] from dimos.agents2.cli.human import HumanInput from dimos.agents2.skills.ros_navigation import RosNavigation from dimos.agents2.spec import Model, Provider @@ -86,15 +86,15 @@ class G1ConnectionModule(Module): """Simplified connection module for G1 - uses WebRTC for control.""" - cmd_vel: In[Twist] = None - odom_in: In[Odometry] = None - lidar: Out[LidarMessage] = None - odom: Out[PoseStamped] = None + cmd_vel: In[Twist] = None # type: ignore[assignment] + odom_in: In[Odometry] = None # type: ignore[assignment] + lidar: Out[LidarMessage] = None # type: ignore[assignment] + odom: Out[PoseStamped] = None # type: ignore[assignment] ip: str connection_type: str | None = None _global_config: GlobalConfig - def __init__( + def __init__( # type: ignore[no-untyped-def] self, ip: str | None = None, connection_type: str | None = None, @@ -103,7 +103,7 @@ def __init__( **kwargs, ) -> None: self._global_config = global_config or GlobalConfig() - self.ip = ip if ip is not None else self._global_config.robot_ip + self.ip = ip if ip is not None else self._global_config.robot_ip # type: ignore[assignment] self.connection_type = connection_type or self._global_config.unitree_connection_type self.connection = None Module.__init__(self, *args, **kwargs) @@ -114,26 +114,26 @@ def start(self) -> None: match self.connection_type: case "webrtc": - self.connection = UnitreeWebRTCConnection(self.ip) + self.connection = UnitreeWebRTCConnection(self.ip) # type: ignore[assignment] case "replay": raise ValueError("Replay connection not implemented for G1 robot") case "mujoco": from dimos.robot.unitree_webrtc.mujoco_connection import MujocoConnection - self.connection = MujocoConnection(self._global_config) + self.connection = MujocoConnection(self._global_config) # type: ignore[assignment] case _: raise ValueError(f"Unknown connection type: {self.connection_type}") - self.connection.start() + self.connection.start() # type: ignore[attr-defined] unsub = self.cmd_vel.subscribe(self.move) self._disposables.add(Disposable(unsub)) if self.connection_type == "mujoco": - unsub = self.connection.odom_stream().subscribe(self._publish_sim_odom) + unsub = self.connection.odom_stream().subscribe(self._publish_sim_odom) # type: ignore[attr-defined] self._disposables.add(unsub) - unsub = self.connection.lidar_stream().subscribe(self._on_lidar) + unsub = self.connection.lidar_stream().subscribe(self._on_lidar) # type: ignore[attr-defined] self._disposables.add(unsub) else: unsub = self.odom_in.subscribe(self._publish_odom) @@ -141,12 +141,12 @@ def start(self) -> None: @rpc def stop(self) -> None: - self.connection.stop() + self.connection.stop() # type: ignore[attr-defined] super().stop() def _publish_tf(self, msg: PoseStamped) -> None: if self.odom.transport: - self.odom.publish(msg) + self.odom.publish(msg) # type: ignore[no-untyped-call] self.tf.publish(Transform.from_pose("base_link", msg)) @@ -191,18 +191,18 @@ def _publish_sim_odom(self, msg: SimOdometry) -> None: def _on_lidar(self, msg: LidarMessage) -> None: if self.lidar.transport: - self.lidar.publish(msg) + self.lidar.publish(msg) # type: ignore[no-untyped-call] @rpc def move(self, twist: Twist, duration: float = 0.0) -> None: """Send movement command to robot.""" - self.connection.move(twist, duration) + self.connection.move(twist, duration) # type: ignore[attr-defined] @rpc - def publish_request(self, topic: str, data: dict): + def publish_request(self, topic: str, data: dict): # type: ignore[no-untyped-def, type-arg] """Forward WebRTC publish requests to connection.""" logger.info(f"Publishing request to topic: {topic} with data: {data}") - return self.connection.publish_request(topic, data) + return self.connection.publish_request(topic, data) # type: ignore[attr-defined] g1_connection = G1ConnectionModule.blueprint @@ -257,7 +257,7 @@ def __init__( from dimos.robot.unitree_webrtc.unitree_skills import MyUnitreeSkills skill_library = MyUnitreeSkills(robot_type="g1") - self.skill_library = skill_library + self.skill_library = skill_library # type: ignore[assignment] # Set robot capabilities self.capabilities = [RobotCapability.LOCOMOTION] @@ -293,12 +293,12 @@ def _setup_directories(self) -> None: os.makedirs(self.spatial_memory_dir, exist_ok=True) os.makedirs(self.db_path, exist_ok=True) - def _deploy_detection(self, goto) -> None: + def _deploy_detection(self, goto) -> None: # type: ignore[no-untyped-def] detection = self._dimos.deploy( ObjectDBModule, goto=goto, camera_info=zed.CameraInfo.SingleWebcam ) - detection.image.connect(self.camera.image) + detection.image.connect(self.camera.image) # type: ignore[attr-defined] detection.pointcloud.transport = core.LCMTransport("/map", PointCloud2) detection.annotations.transport = core.LCMTransport("/annotations", ImageAnnotations) @@ -358,7 +358,7 @@ def start(self) -> None: agent = Agent( system_prompt="You are a helpful assistant controlling a Unitree G1 humanoid robot. You can control the robot's arms, movement modes, and navigation.", model=Model.GPT_4O, - provider=Provider.OPENAI, + provider=Provider.OPENAI, # type: ignore[attr-defined] ) # Register G1-specific skill container @@ -372,15 +372,15 @@ def start(self) -> None: agent.register_skills(self.detection) # Register ROS navigation - self._ros_nav = RosNavigation(self) - self._ros_nav.start() + self._ros_nav = RosNavigation(self) # type: ignore[assignment] + self._ros_nav.start() # type: ignore[attr-defined] agent.register_skills(self._ros_nav) agent.run_implicit_skill("human") agent.start() # For logging - skills = [tool.name for tool in agent.get_tools()] + skills = [tool.name for tool in agent.get_tools()] # type: ignore[no-untyped-call] logger.info(f"Agent configured with {len(skills)} skills: {', '.join(skills)}") agent.loop_thread() @@ -397,18 +397,18 @@ def stop(self) -> None: def _deploy_connection(self) -> None: """Deploy and configure the connection module.""" - self.connection = self._dimos.deploy(G1ConnectionModule, self.ip) + self.connection = self._dimos.deploy(G1ConnectionModule, self.ip) # type: ignore[assignment] # Configure LCM transports - self.connection.cmd_vel.transport = core.LCMTransport("/cmd_vel", Twist) - self.connection.odom_in.transport = core.LCMTransport("/state_estimation", Odometry) - self.connection.odom.transport = core.LCMTransport("/odom", PoseStamped) + self.connection.cmd_vel.transport = core.LCMTransport("/cmd_vel", Twist) # type: ignore[attr-defined] + self.connection.odom_in.transport = core.LCMTransport("/state_estimation", Odometry) # type: ignore[attr-defined] + self.connection.odom.transport = core.LCMTransport("/odom", PoseStamped) # type: ignore[attr-defined] def _deploy_camera(self) -> None: """Deploy and configure a standard webcam module.""" logger.info("Deploying standard webcam module...") - self.camera = self._dimos.deploy( + self.camera = self._dimos.deploy( # type: ignore[assignment] CameraModule, transform=Transform( translation=Vector3(0.05, 0.0, 0.0), @@ -424,8 +424,8 @@ def _deploy_camera(self) -> None: ), ) - self.camera.image.transport = core.LCMTransport("/image", Image) - self.camera.camera_info.transport = core.LCMTransport("/camera_info", CameraInfo) + self.camera.image.transport = core.LCMTransport("/image", Image) # type: ignore[attr-defined] + self.camera.camera_info.transport = core.LCMTransport("/camera_info", CameraInfo) # type: ignore[attr-defined] logger.info("Webcam module configured") def _deploy_visualization(self) -> None: @@ -438,16 +438,16 @@ def _deploy_visualization(self) -> None: # self.websocket_vis.odom.transport = core.LCMTransport("/odom", PoseStamped) # Deploy Foxglove bridge - self.foxglove_bridge = FoxgloveBridge( + self.foxglove_bridge = FoxgloveBridge( # type: ignore[assignment] shm_channels=[ "/zed/color_image#sensor_msgs.Image", "/zed/depth_image#sensor_msgs.Image", ] ) - self.foxglove_bridge.start() + self.foxglove_bridge.start() # type: ignore[attr-defined] def _deploy_perception(self) -> None: - self.spatial_memory_module = self._dimos.deploy( + self.spatial_memory_module = self._dimos.deploy( # type: ignore[assignment] SpatialMemory, collection_name=self.spatial_memory_collection, db_path=self.db_path, @@ -455,55 +455,55 @@ def _deploy_perception(self) -> None: output_dir=self.spatial_memory_dir, ) - self.spatial_memory_module.color_image.connect(self.camera.image) - self.spatial_memory_module.odom.transport = core.LCMTransport("/odom", PoseStamped) + self.spatial_memory_module.color_image.connect(self.camera.image) # type: ignore[attr-defined] + self.spatial_memory_module.odom.transport = core.LCMTransport("/odom", PoseStamped) # type: ignore[attr-defined] logger.info("Spatial memory module deployed and connected") def _deploy_joystick(self) -> None: """Deploy joystick control module.""" logger.info("Deploying G1 joystick module...") - self.joystick = self._dimos.deploy(KeyboardTeleop) - self.joystick.cmd_vel.transport = core.LCMTransport("/cmd_vel", Twist) + self.joystick = self._dimos.deploy(KeyboardTeleop) # type: ignore[assignment] + self.joystick.cmd_vel.transport = core.LCMTransport("/cmd_vel", Twist) # type: ignore[attr-defined] logger.info("Joystick module deployed - pygame window will open") def _deploy_ros_bridge(self) -> None: """Deploy and configure ROS bridge.""" - self.ros_bridge = ROSBridge("g1_ros_bridge") + self.ros_bridge = ROSBridge("g1_ros_bridge") # type: ignore[assignment] # Add /cmd_vel topic from ROS to DIMOS - self.ros_bridge.add_topic( + self.ros_bridge.add_topic( # type: ignore[attr-defined] "/cmd_vel", TwistStamped, ROSTwistStamped, direction=BridgeDirection.ROS_TO_DIMOS ) # Add /state_estimation topic from ROS to DIMOS - self.ros_bridge.add_topic( + self.ros_bridge.add_topic( # type: ignore[attr-defined] "/state_estimation", Odometry, ROSOdometry, direction=BridgeDirection.ROS_TO_DIMOS ) # Add /tf topic from ROS to DIMOS - self.ros_bridge.add_topic( + self.ros_bridge.add_topic( # type: ignore[attr-defined] "/tf", TFMessage, ROSTFMessage, direction=BridgeDirection.ROS_TO_DIMOS ) - from std_msgs.msg import Bool as ROSBool + from std_msgs.msg import Bool as ROSBool # type: ignore[attr-defined] from dimos.msgs.std_msgs import Bool # Navigation control topics from autonomy stack - self.ros_bridge.add_topic( + self.ros_bridge.add_topic( # type: ignore[attr-defined] "/goal_pose", PoseStamped, ROSPoseStamped, direction=BridgeDirection.DIMOS_TO_ROS ) - self.ros_bridge.add_topic( + self.ros_bridge.add_topic( # type: ignore[attr-defined] "/cancel_goal", Bool, ROSBool, direction=BridgeDirection.DIMOS_TO_ROS ) - self.ros_bridge.add_topic( + self.ros_bridge.add_topic( # type: ignore[attr-defined] "/goal_reached", Bool, ROSBool, direction=BridgeDirection.ROS_TO_DIMOS ) - self.ros_bridge.add_topic("/joy", Joy, ROSJoy, direction=BridgeDirection.DIMOS_TO_ROS) + self.ros_bridge.add_topic("/joy", Joy, ROSJoy, direction=BridgeDirection.DIMOS_TO_ROS) # type: ignore[attr-defined] - self.ros_bridge.add_topic( + self.ros_bridge.add_topic( # type: ignore[attr-defined] "/registered_scan", PointCloud2, ROSPointCloud2, @@ -511,7 +511,7 @@ def _deploy_ros_bridge(self) -> None: remap_topic="/map", ) - self.ros_bridge.start() + self.ros_bridge.start() # type: ignore[attr-defined] logger.info( "ROS bridge deployed: /cmd_vel, /state_estimation, /tf, /registered_scan (ROS → DIMOS)" @@ -533,12 +533,12 @@ def _start_modules(self) -> None: def move(self, twist_stamped: TwistStamped, duration: float = 0.0) -> None: """Send movement command to robot.""" - self.connection.move(twist_stamped, duration) + self.connection.move(twist_stamped, duration) # type: ignore[attr-defined] def get_odom(self) -> PoseStamped: """Get the robot's odometry.""" # Note: odom functionality removed from G1ConnectionModule - return None + return None # type: ignore[return-value] @property def spatial_memory(self) -> SpatialMemory | None: @@ -564,9 +564,9 @@ def main() -> None: args = parser.parse_args() - pubsub.lcm.autoconf() + pubsub.lcm.autoconf() # type: ignore[attr-defined] - robot = UnitreeG1( + robot = UnitreeG1( # type: ignore[abstract] ip=args.ip, output_dir=args.output_dir, recording_path=args.record, diff --git a/dimos/robot/unitree_webrtc/unitree_g1_blueprints.py b/dimos/robot/unitree_webrtc/unitree_g1_blueprints.py index 8e71173c30..917658d403 100644 --- a/dimos/robot/unitree_webrtc/unitree_g1_blueprints.py +++ b/dimos/robot/unitree_webrtc/unitree_g1_blueprints.py @@ -19,7 +19,7 @@ from basic teleoperation to full autonomous agent configurations. """ -from dimos_lcm.sensor_msgs import CameraInfo +from dimos_lcm.sensor_msgs import CameraInfo # type: ignore[import-untyped] from dimos.agents2.agent import llm_agent from dimos.agents2.cli.human import human_input diff --git a/dimos/robot/unitree_webrtc/unitree_go2.py b/dimos/robot/unitree_webrtc/unitree_go2.py index 63b6034619..b071d12f19 100644 --- a/dimos/robot/unitree_webrtc/unitree_go2.py +++ b/dimos/robot/unitree_webrtc/unitree_go2.py @@ -21,8 +21,8 @@ import time import warnings -from dimos_lcm.sensor_msgs import CameraInfo -from dimos_lcm.std_msgs import Bool, String +from dimos_lcm.sensor_msgs import CameraInfo # type: ignore[import-untyped] +from dimos_lcm.std_msgs import Bool, String # type: ignore[import-untyped] from reactivex import Observable from reactivex.disposable import CompositeDisposable @@ -86,7 +86,7 @@ class ReplayRTC(Resource): """Replay WebRTC connection for testing with recorded data.""" - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def] get_data("unitree_office_walk") # Preload data for testing def start(self) -> None: @@ -102,19 +102,19 @@ def liedown(self) -> None: print("liedown suppressed") @functools.cache - def lidar_stream(self): + def lidar_stream(self): # type: ignore[no-untyped-def] print("lidar stream start") lidar_store = TimedSensorReplay("unitree_office_walk/lidar", autocast=LidarMessage.from_msg) return lidar_store.stream() @functools.cache - def odom_stream(self): + def odom_stream(self): # type: ignore[no-untyped-def] print("odom stream start") odom_store = TimedSensorReplay("unitree_office_walk/odom", autocast=Odometry.from_msg) return odom_store.stream() @functools.cache - def video_stream(self): + def video_stream(self): # type: ignore[no-untyped-def] print("video stream start") video_store = TimedSensorReplay( "unitree_office_walk/video", autocast=lambda x: Image.from_numpy(x).to_rgb() @@ -124,7 +124,7 @@ def video_stream(self): def move(self, twist: Twist, duration: float = 0.0) -> None: pass - def publish_request(self, topic: str, data: dict): + def publish_request(self, topic: str, data: dict): # type: ignore[no-untyped-def, type-arg] """Fake publish request for testing.""" return {"status": "ok", "message": "Fake publish"} @@ -132,22 +132,22 @@ def publish_request(self, topic: str, data: dict): class ConnectionModule(Module): """Module that handles robot sensor data, movement commands, and camera information.""" - cmd_vel: In[Twist] = None - odom: Out[PoseStamped] = None - gps_location: Out[LatLon] = None - lidar: Out[LidarMessage] = None - color_image: Out[Image] = None - camera_info: Out[CameraInfo] = None - camera_pose: Out[PoseStamped] = None + cmd_vel: In[Twist] = None # type: ignore[assignment] + odom: Out[PoseStamped] = None # type: ignore[assignment] + gps_location: Out[LatLon] = None # type: ignore[assignment] + lidar: Out[LidarMessage] = None # type: ignore[assignment] + color_image: Out[Image] = None # type: ignore[assignment] + camera_info: Out[CameraInfo] = None # type: ignore[assignment] + camera_pose: Out[PoseStamped] = None # type: ignore[assignment] ip: str connection_type: str = "webrtc" - _odom: PoseStamped = None - _lidar: LidarMessage = None - _last_image: Image = None + _odom: PoseStamped = None # type: ignore[assignment] + _lidar: LidarMessage = None # type: ignore[assignment] + _last_image: Image = None # type: ignore[assignment] _global_config: GlobalConfig - def __init__( + def __init__( # type: ignore[no-untyped-def] self, ip: str | None = None, connection_type: str | None = None, @@ -157,7 +157,7 @@ def __init__( **kwargs, ) -> None: self._global_config = global_config or GlobalConfig() - self.ip = ip if ip is not None else self._global_config.robot_ip + self.ip = ip if ip is not None else self._global_config.robot_ip # type: ignore[assignment] self.connection_type = connection_type or self._global_config.unitree_connection_type self.rectify_image = not self._global_config.simulation self.tf = TF() @@ -181,8 +181,8 @@ def __init__( self.lcm_camera_info.D ) # zero out distortion coefficients for rectification else: - self.camera_matrix = None - self.dist_coeffs = None + self.camera_matrix = None # type: ignore[assignment] + self.dist_coeffs = None # type: ignore[assignment] Module.__init__(self, *args, **kwargs) @@ -193,30 +193,30 @@ def start(self) -> None: match self.connection_type: case "webrtc": - self.connection = UnitreeWebRTCConnection(self.ip) + self.connection = UnitreeWebRTCConnection(self.ip) # type: ignore[assignment] case "replay": - self.connection = ReplayRTC(self.ip) + self.connection = ReplayRTC(self.ip) # type: ignore[assignment] case "mujoco": from dimos.robot.unitree_webrtc.mujoco_connection import MujocoConnection - self.connection = MujocoConnection(self._global_config) + self.connection = MujocoConnection(self._global_config) # type: ignore[assignment] case _: raise ValueError(f"Unknown connection type: {self.connection_type}") - self.connection.start() + self.connection.start() # type: ignore[attr-defined] # Connect sensor streams to outputs - unsub = self.connection.lidar_stream().subscribe(self._on_lidar) + unsub = self.connection.lidar_stream().subscribe(self._on_lidar) # type: ignore[attr-defined] self._disposables.add(unsub) - unsub = self.connection.odom_stream().subscribe(self._publish_tf) + unsub = self.connection.odom_stream().subscribe(self._publish_tf) # type: ignore[attr-defined] self._disposables.add(unsub) - unsub = self.connection.video_stream().subscribe(self._on_video) + unsub = self.connection.video_stream().subscribe(self._on_video) # type: ignore[attr-defined] self._disposables.add(unsub) unsub = self.cmd_vel.subscribe(self.move) - self._disposables.add(unsub) + self._disposables.add(unsub) # type: ignore[arg-type] @rpc def stop(self) -> None: @@ -226,7 +226,7 @@ def stop(self) -> None: def _on_lidar(self, msg: LidarMessage) -> None: if self.lidar.transport: - self.lidar.publish(msg) + self.lidar.publish(msg) # type: ignore[no-untyped-call] def _on_video(self, msg: Image) -> None: """Handle incoming video frames and publish synchronized camera data.""" @@ -235,21 +235,21 @@ def _on_video(self, msg: Image) -> None: rectified_msg = rectify_image(msg, self.camera_matrix, self.dist_coeffs) self._last_image = rectified_msg if self.color_image.transport: - self.color_image.publish(rectified_msg) + self.color_image.publish(rectified_msg) # type: ignore[no-untyped-call] else: self._last_image = msg if self.color_image.transport: - self.color_image.publish(msg) + self.color_image.publish(msg) # type: ignore[no-untyped-call] # Publish camera info and pose synchronized with video timestamp = msg.ts if msg.ts else time.time() self._publish_camera_info(timestamp) self._publish_camera_pose(timestamp) - def _publish_tf(self, msg) -> None: + def _publish_tf(self, msg) -> None: # type: ignore[no-untyped-def] self._odom = msg if self.odom.transport: - self.odom.publish(msg) + self.odom.publish(msg) # type: ignore[no-untyped-call] self.tf.publish(Transform.from_pose("base_link", msg)) # Publish camera_link transform @@ -275,7 +275,7 @@ def _publish_camera_info(self, timestamp: float) -> None: header = Header(timestamp, "camera_link") self.lcm_camera_info.header = header if self.camera_info.transport: - self.camera_info.publish(self.lcm_camera_info) + self.camera_info.publish(self.lcm_camera_info) # type: ignore[no-untyped-call] def _publish_camera_pose(self, timestamp: float) -> None: """Publish camera pose from TF lookup.""" @@ -296,7 +296,7 @@ def _publish_camera_pose(self, timestamp: float) -> None: orientation=transform.rotation, ) if self.camera_pose.transport: - self.camera_pose.publish(pose_msg) + self.camera_pose.publish(pose_msg) # type: ignore[no-untyped-call] else: logger.debug("Could not find transform from world to camera_link") @@ -315,20 +315,20 @@ def get_odom(self) -> PoseStamped | None: @rpc def move(self, twist: Twist, duration: float = 0.0) -> None: """Send movement command to robot.""" - self.connection.move(twist, duration) + self.connection.move(twist, duration) # type: ignore[attr-defined] @rpc - def standup(self): + def standup(self): # type: ignore[no-untyped-def] """Make the robot stand up.""" - return self.connection.standup() + return self.connection.standup() # type: ignore[attr-defined] @rpc - def liedown(self): + def liedown(self): # type: ignore[no-untyped-def] """Make the robot lie down.""" - return self.connection.liedown() + return self.connection.liedown() # type: ignore[attr-defined] @rpc - def publish_request(self, topic: str, data: dict): + def publish_request(self, topic: str, data: dict): # type: ignore[no-untyped-def, type-arg] """Publish a request to the WebRTC connection. Args: topic: The RTC topic to publish to @@ -336,7 +336,7 @@ def publish_request(self, topic: str, data: dict): Returns: The result of the publish request """ - return self.connection.publish_request(topic, data) + return self.connection.publish_request(topic, data) # type: ignore[attr-defined] connection = ConnectionModule.blueprint @@ -440,105 +440,105 @@ def stop(self) -> None: def _deploy_connection(self) -> None: """Deploy and configure the connection module.""" - self.connection = self._dimos.deploy( + self.connection = self._dimos.deploy( # type: ignore[assignment] ConnectionModule, self.ip, connection_type=self.connection_type ) - self.connection.lidar.transport = core.LCMTransport("/lidar", LidarMessage) - self.connection.odom.transport = core.LCMTransport("/odom", PoseStamped) - self.connection.gps_location.transport = core.pLCMTransport("/gps_location") - self.connection.color_image.transport = core.pSHMTransport( + self.connection.lidar.transport = core.LCMTransport("/lidar", LidarMessage) # type: ignore[attr-defined] + self.connection.odom.transport = core.LCMTransport("/odom", PoseStamped) # type: ignore[attr-defined] + self.connection.gps_location.transport = core.pLCMTransport("/gps_location") # type: ignore[attr-defined] + self.connection.color_image.transport = core.pSHMTransport( # type: ignore[attr-defined] "/go2/color_image", default_capacity=DEFAULT_CAPACITY_COLOR_IMAGE ) - self.connection.cmd_vel.transport = core.LCMTransport("/cmd_vel", Twist) - self.connection.camera_info.transport = core.LCMTransport("/go2/camera_info", CameraInfo) - self.connection.camera_pose.transport = core.LCMTransport("/go2/camera_pose", PoseStamped) + self.connection.cmd_vel.transport = core.LCMTransport("/cmd_vel", Twist) # type: ignore[attr-defined] + self.connection.camera_info.transport = core.LCMTransport("/go2/camera_info", CameraInfo) # type: ignore[attr-defined] + self.connection.camera_pose.transport = core.LCMTransport("/go2/camera_pose", PoseStamped) # type: ignore[attr-defined] def _deploy_mapping(self) -> None: """Deploy and configure the mapping module.""" min_height = 0.3 if self.connection_type == "mujoco" else 0.15 - self.mapper = self._dimos.deploy( + self.mapper = self._dimos.deploy( # type: ignore[assignment] Map, voxel_size=0.5, global_publish_interval=2.5, min_height=min_height ) - self.mapper.global_map.transport = core.LCMTransport("/global_map", LidarMessage) - self.mapper.global_costmap.transport = core.LCMTransport("/global_costmap", OccupancyGrid) - self.mapper.local_costmap.transport = core.LCMTransport("/local_costmap", OccupancyGrid) + self.mapper.global_map.transport = core.LCMTransport("/global_map", LidarMessage) # type: ignore[attr-defined] + self.mapper.global_costmap.transport = core.LCMTransport("/global_costmap", OccupancyGrid) # type: ignore[attr-defined] + self.mapper.local_costmap.transport = core.LCMTransport("/local_costmap", OccupancyGrid) # type: ignore[attr-defined] - self.mapper.lidar.connect(self.connection.lidar) + self.mapper.lidar.connect(self.connection.lidar) # type: ignore[attr-defined] def _deploy_navigation(self) -> None: """Deploy and configure navigation modules.""" - self.global_planner = self._dimos.deploy(AstarPlanner) - self.local_planner = self._dimos.deploy(HolonomicLocalPlanner) - self.navigator = self._dimos.deploy( + self.global_planner = self._dimos.deploy(AstarPlanner) # type: ignore[assignment] + self.local_planner = self._dimos.deploy(HolonomicLocalPlanner) # type: ignore[assignment] + self.navigator = self._dimos.deploy( # type: ignore[assignment] BehaviorTreeNavigator, - reset_local_planner=self.local_planner.reset, - check_goal_reached=self.local_planner.is_goal_reached, + reset_local_planner=self.local_planner.reset, # type: ignore[attr-defined] + check_goal_reached=self.local_planner.is_goal_reached, # type: ignore[attr-defined] ) - self.frontier_explorer = self._dimos.deploy(WavefrontFrontierExplorer) + self.frontier_explorer = self._dimos.deploy(WavefrontFrontierExplorer) # type: ignore[assignment] - self.navigator.target.transport = core.LCMTransport("/navigation_goal", PoseStamped) - self.navigator.goal_request.transport = core.LCMTransport("/goal_request", PoseStamped) - self.navigator.goal_reached.transport = core.LCMTransport("/goal_reached", Bool) - self.navigator.navigation_state.transport = core.LCMTransport("/navigation_state", String) - self.navigator.global_costmap.transport = core.LCMTransport( + self.navigator.target.transport = core.LCMTransport("/navigation_goal", PoseStamped) # type: ignore[attr-defined] + self.navigator.goal_request.transport = core.LCMTransport("/goal_request", PoseStamped) # type: ignore[attr-defined] + self.navigator.goal_reached.transport = core.LCMTransport("/goal_reached", Bool) # type: ignore[attr-defined] + self.navigator.navigation_state.transport = core.LCMTransport("/navigation_state", String) # type: ignore[attr-defined] + self.navigator.global_costmap.transport = core.LCMTransport( # type: ignore[attr-defined] "/global_costmap", OccupancyGrid ) - self.global_planner.path.transport = core.LCMTransport("/global_path", Path) - self.local_planner.cmd_vel.transport = core.LCMTransport("/cmd_vel", Twist) - self.frontier_explorer.goal_request.transport = core.LCMTransport( + self.global_planner.path.transport = core.LCMTransport("/global_path", Path) # type: ignore[attr-defined] + self.local_planner.cmd_vel.transport = core.LCMTransport("/cmd_vel", Twist) # type: ignore[attr-defined] + self.frontier_explorer.goal_request.transport = core.LCMTransport( # type: ignore[attr-defined] "/goal_request", PoseStamped ) - self.frontier_explorer.goal_reached.transport = core.LCMTransport("/goal_reached", Bool) - self.frontier_explorer.explore_cmd.transport = core.LCMTransport("/explore_cmd", Bool) - self.frontier_explorer.stop_explore_cmd.transport = core.LCMTransport( + self.frontier_explorer.goal_reached.transport = core.LCMTransport("/goal_reached", Bool) # type: ignore[attr-defined] + self.frontier_explorer.explore_cmd.transport = core.LCMTransport("/explore_cmd", Bool) # type: ignore[attr-defined] + self.frontier_explorer.stop_explore_cmd.transport = core.LCMTransport( # type: ignore[attr-defined] "/stop_explore_cmd", Bool ) - self.global_planner.target.connect(self.navigator.target) + self.global_planner.target.connect(self.navigator.target) # type: ignore[attr-defined] - self.global_planner.global_costmap.connect(self.mapper.global_costmap) - self.global_planner.odom.connect(self.connection.odom) + self.global_planner.global_costmap.connect(self.mapper.global_costmap) # type: ignore[attr-defined] + self.global_planner.odom.connect(self.connection.odom) # type: ignore[attr-defined] - self.local_planner.path.connect(self.global_planner.path) - self.local_planner.local_costmap.connect(self.mapper.local_costmap) - self.local_planner.odom.connect(self.connection.odom) + self.local_planner.path.connect(self.global_planner.path) # type: ignore[attr-defined] + self.local_planner.local_costmap.connect(self.mapper.local_costmap) # type: ignore[attr-defined] + self.local_planner.odom.connect(self.connection.odom) # type: ignore[attr-defined] - self.connection.cmd_vel.connect(self.local_planner.cmd_vel) + self.connection.cmd_vel.connect(self.local_planner.cmd_vel) # type: ignore[attr-defined] - self.navigator.odom.connect(self.connection.odom) + self.navigator.odom.connect(self.connection.odom) # type: ignore[attr-defined] - self.frontier_explorer.global_costmap.connect(self.mapper.global_costmap) - self.frontier_explorer.odom.connect(self.connection.odom) + self.frontier_explorer.global_costmap.connect(self.mapper.global_costmap) # type: ignore[attr-defined] + self.frontier_explorer.odom.connect(self.connection.odom) # type: ignore[attr-defined] def _deploy_visualization(self) -> None: """Deploy and configure visualization modules.""" - self.websocket_vis = self._dimos.deploy(WebsocketVisModule, port=self.websocket_port) - self.websocket_vis.goal_request.transport = core.LCMTransport("/goal_request", PoseStamped) - self.websocket_vis.gps_goal.transport = core.pLCMTransport("/gps_goal") - self.websocket_vis.explore_cmd.transport = core.LCMTransport("/explore_cmd", Bool) - self.websocket_vis.stop_explore_cmd.transport = core.LCMTransport("/stop_explore_cmd", Bool) - self.websocket_vis.cmd_vel.transport = core.LCMTransport("/cmd_vel", Twist) - - self.websocket_vis.odom.connect(self.connection.odom) - self.websocket_vis.gps_location.connect(self.connection.gps_location) - self.websocket_vis.path.connect(self.global_planner.path) - self.websocket_vis.global_costmap.connect(self.mapper.global_costmap) + self.websocket_vis = self._dimos.deploy(WebsocketVisModule, port=self.websocket_port) # type: ignore[assignment] + self.websocket_vis.goal_request.transport = core.LCMTransport("/goal_request", PoseStamped) # type: ignore[attr-defined] + self.websocket_vis.gps_goal.transport = core.pLCMTransport("/gps_goal") # type: ignore[attr-defined] + self.websocket_vis.explore_cmd.transport = core.LCMTransport("/explore_cmd", Bool) # type: ignore[attr-defined] + self.websocket_vis.stop_explore_cmd.transport = core.LCMTransport("/stop_explore_cmd", Bool) # type: ignore[attr-defined] + self.websocket_vis.cmd_vel.transport = core.LCMTransport("/cmd_vel", Twist) # type: ignore[attr-defined] + + self.websocket_vis.odom.connect(self.connection.odom) # type: ignore[attr-defined] + self.websocket_vis.gps_location.connect(self.connection.gps_location) # type: ignore[attr-defined] + self.websocket_vis.path.connect(self.global_planner.path) # type: ignore[attr-defined] + self.websocket_vis.global_costmap.connect(self.mapper.global_costmap) # type: ignore[attr-defined] def _deploy_foxglove_bridge(self) -> None: - self.foxglove_bridge = FoxgloveBridge( + self.foxglove_bridge = FoxgloveBridge( # type: ignore[assignment] shm_channels=[ "/go2/color_image#sensor_msgs.Image", "/go2/tracked_overlay#sensor_msgs.Image", ] ) - self.foxglove_bridge.start() + self.foxglove_bridge.start() # type: ignore[attr-defined] def _deploy_perception(self) -> None: """Deploy and configure perception modules.""" # Deploy spatial memory - self.spatial_memory_module = self._dimos.deploy( + self.spatial_memory_module = self._dimos.deploy( # type: ignore[assignment] SpatialMemory, collection_name=self.spatial_memory_collection, db_path=self.db_path, @@ -546,14 +546,14 @@ def _deploy_perception(self) -> None: output_dir=self.spatial_memory_dir, ) - self.spatial_memory_module.color_image.transport = core.pSHMTransport( + self.spatial_memory_module.color_image.transport = core.pSHMTransport( # type: ignore[attr-defined] "/go2/color_image", default_capacity=DEFAULT_CAPACITY_COLOR_IMAGE ) logger.info("Spatial memory module deployed and connected") # Deploy 2D object tracker - self.object_tracker = self._dimos.deploy( + self.object_tracker = self._dimos.deploy( # type: ignore[assignment] ObjectTracker2D, frame_id="camera_link", ) @@ -561,13 +561,13 @@ def _deploy_perception(self) -> None: # Deploy bbox navigation module self.bbox_navigator = self._dimos.deploy(BBoxNavigationModule, goal_distance=1.0) - self.utilization_module = self._dimos.deploy(UtilizationModule) + self.utilization_module = self._dimos.deploy(UtilizationModule) # type: ignore[assignment] # Set up transports for object tracker - self.object_tracker.detection2darray.transport = core.LCMTransport( + self.object_tracker.detection2darray.transport = core.LCMTransport( # type: ignore[attr-defined] "/go2/detection2d", Detection2DArray ) - self.object_tracker.tracked_overlay.transport = core.pSHMTransport( + self.object_tracker.tracked_overlay.transport = core.pSHMTransport( # type: ignore[attr-defined] "/go2/tracked_overlay", default_capacity=DEFAULT_CAPACITY_COLOR_IMAGE ) @@ -585,9 +585,9 @@ def _deploy_camera(self) -> None: # Connect bbox navigator inputs if self.bbox_navigator: - self.bbox_navigator.detection2d.connect(self.object_tracker.detection2darray) - self.bbox_navigator.camera_info.connect(self.connection.camera_info) - self.bbox_navigator.goal_request.connect(self.navigator.goal_request) + self.bbox_navigator.detection2d.connect(self.object_tracker.detection2darray) # type: ignore[attr-defined] + self.bbox_navigator.camera_info.connect(self.connection.camera_info) # type: ignore[attr-defined] + self.bbox_navigator.goal_request.connect(self.navigator.goal_request) # type: ignore[attr-defined] logger.info("BBox navigator connected") def _start_modules(self) -> None: @@ -598,15 +598,15 @@ def _start_modules(self) -> None: if self.skill_library is not None: for skill in self.skill_library: if isinstance(skill, AbstractRobotSkill): - self.skill_library.create_instance(skill.__name__, robot=self) + self.skill_library.create_instance(skill.__name__, robot=self) # type: ignore[attr-defined] if isinstance(self.skill_library, MyUnitreeSkills): - self.skill_library._robot = self + self.skill_library._robot = self # type: ignore[assignment] self.skill_library.init() self.skill_library.initialize_skills() def move(self, twist: Twist, duration: float = 0.0) -> None: """Send movement command to robot.""" - self.connection.move(twist, duration) + self.connection.move(twist, duration) # type: ignore[attr-defined] def explore(self) -> bool: """Start autonomous frontier exploration. @@ -614,7 +614,7 @@ def explore(self) -> bool: Returns: True if exploration started successfully """ - return self.frontier_explorer.explore() + return self.frontier_explorer.explore() # type: ignore[attr-defined, no-any-return] def navigate_to(self, pose: PoseStamped, blocking: bool = True) -> bool: """Navigate to a target pose. @@ -631,15 +631,15 @@ def navigate_to(self, pose: PoseStamped, blocking: bool = True) -> bool: logger.info( f"Navigating to pose: ({pose.position.x:.2f}, {pose.position.y:.2f}, {pose.position.z:.2f})" ) - self.navigator.set_goal(pose) + self.navigator.set_goal(pose) # type: ignore[attr-defined] time.sleep(1.0) if blocking: - while self.navigator.get_state() == NavigationState.FOLLOWING_PATH: + while self.navigator.get_state() == NavigationState.FOLLOWING_PATH: # type: ignore[attr-defined] time.sleep(0.25) time.sleep(1.0) - if not self.navigator.is_goal_reached(): + if not self.navigator.is_goal_reached(): # type: ignore[attr-defined] logger.info("Navigation was cancelled or failed") return False else: @@ -654,11 +654,11 @@ def stop_exploration(self) -> bool: Returns: True if exploration was stopped """ - self.navigator.cancel_goal() - return self.frontier_explorer.stop_exploration() + self.navigator.cancel_goal() # type: ignore[attr-defined] + return self.frontier_explorer.stop_exploration() # type: ignore[attr-defined, no-any-return] def is_exploration_active(self) -> bool: - return self.frontier_explorer.is_exploration_active() + return self.frontier_explorer.is_exploration_active() # type: ignore[attr-defined, no-any-return] def cancel_navigation(self) -> bool: """Cancel the current navigation goal. @@ -666,7 +666,7 @@ def cancel_navigation(self) -> bool: Returns: True if goal was cancelled """ - return self.navigator.cancel_goal() + return self.navigator.cancel_goal() # type: ignore[attr-defined, no-any-return] @property def spatial_memory(self) -> SpatialMemory | None: @@ -679,7 +679,7 @@ def spatial_memory(self) -> SpatialMemory | None: @functools.cached_property def gps_position_stream(self) -> Observable[LatLon]: - return self.connection.gps_location.transport.pure_observable() + return self.connection.gps_location.transport.pure_observable() # type: ignore[attr-defined, no-any-return] def get_odom(self) -> PoseStamped: """Get the robot's odometry. @@ -687,7 +687,7 @@ def get_odom(self) -> PoseStamped: Returns: The robot's odometry """ - return self.connection.get_odom() + return self.connection.get_odom() # type: ignore[attr-defined, no-any-return] def main() -> None: @@ -695,7 +695,7 @@ def main() -> None: ip = os.getenv("ROBOT_IP") connection_type = os.getenv("CONNECTION_TYPE", "webrtc") - pubsub.lcm.autoconf() + pubsub.lcm.autoconf() # type: ignore[attr-defined] robot = UnitreeGo2(ip=ip, websocket_port=7779, connection_type=connection_type) robot.start() diff --git a/dimos/robot/unitree_webrtc/unitree_go2_blueprints.py b/dimos/robot/unitree_webrtc/unitree_go2_blueprints.py index 8973f6cd68..d2f006848a 100644 --- a/dimos/robot/unitree_webrtc/unitree_go2_blueprints.py +++ b/dimos/robot/unitree_webrtc/unitree_go2_blueprints.py @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from dimos_lcm.sensor_msgs import CameraInfo +from dimos_lcm.sensor_msgs import CameraInfo # type: ignore[import-untyped] from dimos.agents2.agent import llm_agent from dimos.agents2.cli.human import human_input diff --git a/dimos/robot/unitree_webrtc/unitree_skill_container.py b/dimos/robot/unitree_webrtc/unitree_skill_container.py index 8fca216d04..5bb196d028 100644 --- a/dimos/robot/unitree_webrtc/unitree_skill_container.py +++ b/dimos/robot/unitree_webrtc/unitree_skill_container.py @@ -19,7 +19,7 @@ import time from typing import TYPE_CHECKING -from go2_webrtc_driver.constants import RTC_TOPIC +from go2_webrtc_driver.constants import RTC_TOPIC # type: ignore[import-untyped] from dimos.core.core import rpc from dimos.core.skill_module import SkillModule @@ -59,12 +59,12 @@ def stop(self) -> None: @rpc def set_ConnectionModule_move(self, callable: RpcCall) -> None: self._move = callable - self._move.set_rpc(self.rpc) + self._move.set_rpc(self.rpc) # type: ignore[arg-type] @rpc def set_ConnectionModule_publish_request(self, callable: RpcCall) -> None: self._publish_request = callable - self._publish_request.set_rpc(self.rpc) + self._publish_request.set_rpc(self.rpc) # type: ignore[arg-type] @skill() def move(self, x: float, y: float = 0.0, yaw: float = 0.0, duration: float = 0.0) -> str: @@ -97,8 +97,8 @@ def wait(self, seconds: float) -> str: time.sleep(seconds) return f"Wait completed with length={seconds}s" - @skill(stream=Stream.passive, reducer=Reducer.latest, hide_skill=True) - def current_time(self): + @skill(stream=Stream.passive, reducer=Reducer.latest, hide_skill=True) # type: ignore[arg-type] + def current_time(self): # type: ignore[no-untyped-def] """Provides current time implicitly, don't call this skill directly.""" print("Starting current_time skill") while True: diff --git a/dimos/robot/unitree_webrtc/unitree_skills.py b/dimos/robot/unitree_webrtc/unitree_skills.py index 2bba4caa53..c9a525ede7 100644 --- a/dimos/robot/unitree_webrtc/unitree_skills.py +++ b/dimos/robot/unitree_webrtc/unitree_skills.py @@ -20,12 +20,12 @@ from pydantic import Field if TYPE_CHECKING: - from dimos.robot.robot import MockRobot, Robot + from dimos.robot.robot import MockRobot, Robot # type: ignore[attr-defined] else: Robot = "Robot" MockRobot = "MockRobot" -from go2_webrtc_driver.constants import RTC_TOPIC +from go2_webrtc_driver.constants import RTC_TOPIC # type: ignore[import-untyped] from dimos.msgs.geometry_msgs import Twist, Vector3 from dimos.skills.skills import AbstractRobotSkill, AbstractSkill, SkillLibrary @@ -220,7 +220,7 @@ def __init__(self, robot: Robot | None = None, robot_type: str = "go2") -> None: robot_type: Type of robot ("go2" or "g1"), defaults to "go2" """ super().__init__() - self._robot: Robot = None + self._robot: Robot = None # type: ignore[assignment] self.robot_type = robot_type.lower() if self.robot_type not in ["go2", "g1"]: @@ -228,7 +228,7 @@ def __init__(self, robot: Robot | None = None, robot_type: str = "go2") -> None: # Add dynamic skills to this class based on robot type dynamic_skills = self.create_skills_live() - self.register_skills(dynamic_skills) + self.register_skills(dynamic_skills) # type: ignore[arg-type] @classmethod def register_skills(cls, skill_classes: AbstractSkill | list[AbstractSkill]) -> None: @@ -242,11 +242,11 @@ def register_skills(cls, skill_classes: AbstractSkill | list[AbstractSkill]) -> for skill_class in skill_classes: # Add to the class as a skill - setattr(cls, skill_class.__name__, skill_class) + setattr(cls, skill_class.__name__, skill_class) # type: ignore[attr-defined] def initialize_skills(self) -> None: for skill_class in self.get_class_skills(): - self.create_instance(skill_class.__name__, robot=self._robot) + self.create_instance(skill_class.__name__, robot=self._robot) # type: ignore[attr-defined] # Refresh the class skills self.refresh_class_skills() @@ -259,13 +259,13 @@ class BaseUnitreeSkill(AbstractRobotSkill): """Base skill for dynamic skill creation.""" def __call__(self) -> str: - super().__call__() + super().__call__() # type: ignore[no-untyped-call] # For Go2: Simple api_id based call if hasattr(self, "_app_id"): string = f"{Colors.GREEN_PRINT_COLOR}Executing Go2 skill: {self.__class__.__name__} with api_id={self._app_id}{Colors.RESET_COLOR}" print(string) - self._robot.connection.publish_request( + self._robot.connection.publish_request( # type: ignore[attr-defined] RTC_TOPIC["SPORT_MOD"], {"api_id": self._app_id} ) return f"{self.__class__.__name__} executed successfully" @@ -274,9 +274,9 @@ def __call__(self) -> str: elif hasattr(self, "_data_value"): string = f"{Colors.GREEN_PRINT_COLOR}Executing G1 skill: {self.__class__.__name__} with data={self._data_value}{Colors.RESET_COLOR}" print(string) - self._robot.connection.publish_request( - self._topic, - {"api_id": self._api_id, "parameter": {"data": self._data_value}}, + self._robot.connection.publish_request( # type: ignore[attr-defined] + self._topic, # type: ignore[attr-defined] + {"api_id": self._api_id, "parameter": {"data": self._data_value}}, # type: ignore[attr-defined] ) return f"{self.__class__.__name__} executed successfully" else: @@ -323,7 +323,7 @@ def __call__(self) -> str: ) skills_classes.append(skill_class) - return skills_classes + return skills_classes # type: ignore[return-value] # region Class-based Skills @@ -336,7 +336,7 @@ class Move(AbstractRobotSkill): duration: float = Field(default=0.0, description="How long to move (seconds).") def __call__(self) -> str: - self._robot.move( + self._robot.move( # type: ignore[attr-defined] Twist(linear=Vector3(self.x, self.y, 0.0), angular=Vector3(0.0, 0.0, self.yaw)), duration=self.duration, ) diff --git a/dimos/robot/utils/robot_debugger.py b/dimos/robot/utils/robot_debugger.py index b3cfb195ce..3d189abb1e 100644 --- a/dimos/robot/utils/robot_debugger.py +++ b/dimos/robot/utils/robot_debugger.py @@ -21,7 +21,7 @@ class RobotDebugger(Resource): - def __init__(self, robot) -> None: + def __init__(self, robot) -> None: # type: ignore[no-untyped-def] self._robot = robot self._threaded_server = None @@ -30,8 +30,8 @@ def start(self) -> None: return try: - import rpyc - from rpyc.utils.server import ThreadedServer + import rpyc # type: ignore[import-not-found] + from rpyc.utils.server import ThreadedServer # type: ignore[import-not-found] except ImportError: return @@ -41,8 +41,8 @@ def start(self) -> None: robot = self._robot - class RobotService(rpyc.Service): - def exposed_robot(self): + class RobotService(rpyc.Service): # type: ignore[misc] + def exposed_robot(self): # type: ignore[no-untyped-def] return robot self._threaded_server = ThreadedServer( @@ -52,7 +52,7 @@ def exposed_robot(self): "allow_all_attrs": True, }, ) - self._threaded_server.start() + self._threaded_server.start() # type: ignore[attr-defined] def stop(self) -> None: if self._threaded_server: diff --git a/dimos/simulation/base/simulator_base.py b/dimos/simulation/base/simulator_base.py index 777893d74c..c8fface73c 100644 --- a/dimos/simulation/base/simulator_base.py +++ b/dimos/simulation/base/simulator_base.py @@ -23,7 +23,7 @@ def __init__( self, headless: bool = True, open_usd: str | None = None, # Keep for Isaac compatibility - entities: list[dict[str, str | dict]] | None = None, # Add for Genesis + entities: list[dict[str, str | dict]] | None = None, # type: ignore[type-arg] # Add for Genesis ) -> None: """Initialize the simulator. @@ -37,11 +37,11 @@ def __init__( self.stage = None @abstractmethod - def get_stage(self): + def get_stage(self): # type: ignore[no-untyped-def] """Get the current stage/scene.""" pass @abstractmethod - def close(self): + def close(self): # type: ignore[no-untyped-def] """Close the simulation.""" pass diff --git a/dimos/simulation/base/stream_base.py b/dimos/simulation/base/stream_base.py index 1fb0e86add..c529943bac 100644 --- a/dimos/simulation/base/stream_base.py +++ b/dimos/simulation/base/stream_base.py @@ -25,7 +25,7 @@ class StreamBase(ABC): """Base class for simulation streaming.""" @abstractmethod - def __init__( + def __init__( # type: ignore[no-untyped-def] self, simulator, width: int = 1920, @@ -61,12 +61,12 @@ def __init__( self.proc = None @abstractmethod - def _load_stage(self, usd_path: str | Path): + def _load_stage(self, usd_path: str | Path): # type: ignore[no-untyped-def] """Load stage from file.""" pass @abstractmethod - def _setup_camera(self): + def _setup_camera(self): # type: ignore[no-untyped-def] """Setup and validate camera.""" pass @@ -98,19 +98,19 @@ def _setup_ffmpeg(self) -> None: self.transport, self.rtsp_url, ] - self.proc = subprocess.Popen(command, stdin=subprocess.PIPE) + self.proc = subprocess.Popen(command, stdin=subprocess.PIPE) # type: ignore[assignment] @abstractmethod - def _setup_annotator(self): + def _setup_annotator(self): # type: ignore[no-untyped-def] """Setup annotator.""" pass @abstractmethod - def stream(self): + def stream(self): # type: ignore[no-untyped-def] """Start streaming.""" pass @abstractmethod - def cleanup(self): + def cleanup(self): # type: ignore[no-untyped-def] """Cleanup resources.""" pass diff --git a/dimos/simulation/genesis/simulator.py b/dimos/simulation/genesis/simulator.py index f3a73be08b..8a452a18f2 100644 --- a/dimos/simulation/genesis/simulator.py +++ b/dimos/simulation/genesis/simulator.py @@ -25,7 +25,7 @@ def __init__( self, headless: bool = True, open_usd: str | None = None, # Keep for compatibility - entities: list[dict[str, str | dict]] | None = None, + entities: list[dict[str, str | dict]] | None = None, # type: ignore[type-arg] ) -> None: """Initialize the Genesis simulation. @@ -74,10 +74,10 @@ def __init__( # Don't build scene yet - let stream add camera first self.is_built = False - def _load_entities(self, entities: list[dict[str, str | dict]]): + def _load_entities(self, entities: list[dict[str, str | dict]]): # type: ignore[no-untyped-def, type-arg] """Load multiple entities into the scene.""" for entity in entities: - entity_type = entity.get("type", "").lower() + entity_type = entity.get("type", "").lower() # type: ignore[union-attr] path = entity.get("path", "") params = entity.get("params", {}) @@ -107,7 +107,7 @@ def _load_entities(self, entities: list[dict[str, str | dict]]): print(f"[Genesis] Added MJCF model from {path}") elif entity_type == "primitive": - shape_type = params.pop("shape", "plane") + shape_type = params.pop("shape", "plane") # type: ignore[union-attr] if shape_type == "plane": morph = gs.morphs.Plane(**params) elif shape_type == "box": @@ -133,7 +133,7 @@ def _load_entities(self, entities: list[dict[str, str | dict]]): except Exception as e: print(f"[Warning] Failed to load entity {entity}: {e!s}") - def add_entity(self, entity_type: str, path: str = "", **params) -> None: + def add_entity(self, entity_type: str, path: str = "", **params) -> None: # type: ignore[no-untyped-def] """Add a single entity to the scene. Args: @@ -143,7 +143,7 @@ def add_entity(self, entity_type: str, path: str = "", **params) -> None: """ self._load_entities([{"type": entity_type, "path": path, "params": params}]) - def get_stage(self): + def get_stage(self): # type: ignore[no-untyped-def] """Get the current stage/scene.""" return self.scene diff --git a/dimos/simulation/genesis/stream.py b/dimos/simulation/genesis/stream.py index d24b254b38..903c764305 100644 --- a/dimos/simulation/genesis/stream.py +++ b/dimos/simulation/genesis/stream.py @@ -24,7 +24,7 @@ class GenesisStream(StreamBase): """Genesis stream implementation.""" - def __init__( + def __init__( # type: ignore[no-untyped-def] self, simulator, width: int = 1920, @@ -110,8 +110,8 @@ def stream(self) -> None: frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) # Write to FFmpeg - self.proc.stdin.write(frame.tobytes()) - self.proc.stdin.flush() + self.proc.stdin.write(frame.tobytes()) # type: ignore[attr-defined] + self.proc.stdin.flush() # type: ignore[attr-defined] # Log metrics frame_time = time.time() - frame_start @@ -134,8 +134,8 @@ def cleanup(self) -> None: """Cleanup resources.""" print("[Cleanup] Stopping FFmpeg process...") if hasattr(self, "proc"): - self.proc.stdin.close() - self.proc.wait() + self.proc.stdin.close() # type: ignore[attr-defined] + self.proc.wait() # type: ignore[attr-defined] print("[Cleanup] Closing simulation...") try: self.simulator.close() diff --git a/dimos/simulation/isaac/simulator.py b/dimos/simulation/isaac/simulator.py index 0d49b9145e..8fcfacd920 100644 --- a/dimos/simulation/isaac/simulator.py +++ b/dimos/simulation/isaac/simulator.py @@ -13,7 +13,7 @@ # limitations under the License. -from isaacsim import SimulationApp +from isaacsim import SimulationApp # type: ignore[import-not-found] from ..base.simulator_base import SimulatorBase @@ -25,15 +25,15 @@ def __init__( self, headless: bool = True, open_usd: str | None = None, - entities: list[dict[str, str | dict]] | None = None, # Add but ignore + entities: list[dict[str, str | dict]] | None = None, # type: ignore[type-arg] # Add but ignore ) -> None: """Initialize the Isaac Sim simulation.""" super().__init__(headless, open_usd) self.app = SimulationApp({"headless": headless, "open_usd": open_usd}) - def get_stage(self): + def get_stage(self): # type: ignore[no-untyped-def] """Get the current USD stage.""" - import omni.usd + import omni.usd # type: ignore[import-not-found] self.stage = omni.usd.get_context().get_stage() return self.stage diff --git a/dimos/simulation/isaac/stream.py b/dimos/simulation/isaac/stream.py index eb85ba8815..7b765b4d76 100644 --- a/dimos/simulation/isaac/stream.py +++ b/dimos/simulation/isaac/stream.py @@ -23,7 +23,7 @@ class IsaacStream(StreamBase): """Isaac Sim stream implementation.""" - def __init__( + def __init__( # type: ignore[no-untyped-def] self, simulator, width: int = 1920, @@ -49,20 +49,20 @@ def __init__( ) # Import omni.replicator after SimulationApp initialization - import omni.replicator.core as rep + import omni.replicator.core as rep # type: ignore[import-not-found] self.rep = rep # Initialize components if usd_path: self._load_stage(usd_path) - self._setup_camera() + self._setup_camera() # type: ignore[no-untyped-call] self._setup_ffmpeg() self._setup_annotator() - def _load_stage(self, usd_path: str | Path): + def _load_stage(self, usd_path: str | Path): # type: ignore[no-untyped-def] """Load USD stage from file.""" - import omni.usd + import omni.usd # type: ignore[import-not-found] abs_path = str(Path(usd_path).resolve()) omni.usd.get_context().open_stage(abs_path) @@ -70,7 +70,7 @@ def _load_stage(self, usd_path: str | Path): if not self.stage: raise RuntimeError(f"Failed to load stage: {abs_path}") - def _setup_camera(self): + def _setup_camera(self): # type: ignore[no-untyped-def] """Setup and validate camera.""" self.stage = self.simulator.get_stage() camera_prim = self.stage.GetPrimAtPath(self.camera_path) @@ -106,8 +106,8 @@ def stream(self) -> None: frame = cv2.cvtColor(frame, cv2.COLOR_RGBA2BGR) # Write to FFmpeg - self.proc.stdin.write(frame.tobytes()) - self.proc.stdin.flush() + self.proc.stdin.write(frame.tobytes()) # type: ignore[attr-defined] + self.proc.stdin.flush() # type: ignore[attr-defined] # Log metrics frame_time = time.time() - frame_start @@ -130,8 +130,8 @@ def cleanup(self) -> None: """Cleanup resources.""" print("[Cleanup] Stopping FFmpeg process...") if hasattr(self, "proc"): - self.proc.stdin.close() - self.proc.wait() + self.proc.stdin.close() # type: ignore[attr-defined] + self.proc.wait() # type: ignore[attr-defined] print("[Cleanup] Closing simulation...") self.simulator.close() print("[Cleanup] Successfully cleaned up resources") diff --git a/dimos/skills/kill_skill.py b/dimos/skills/kill_skill.py index b9d02729f5..f3f7687c43 100644 --- a/dimos/skills/kill_skill.py +++ b/dimos/skills/kill_skill.py @@ -38,7 +38,7 @@ class KillSkill(AbstractSkill): skill_name: str = Field(..., description="Name of the skill to terminate") - def __init__(self, skill_library: SkillLibrary | None = None, **data) -> None: + def __init__(self, skill_library: SkillLibrary | None = None, **data) -> None: # type: ignore[no-untyped-def] """ Initialize the kill skill. @@ -49,13 +49,13 @@ def __init__(self, skill_library: SkillLibrary | None = None, **data) -> None: super().__init__(**data) self._skill_library = skill_library - def __call__(self): + def __call__(self): # type: ignore[no-untyped-def] """ Terminate the specified skill. Returns: A message indicating whether the skill was successfully terminated """ - print("running skills", self._skill_library.get_running_skills()) + print("running skills", self._skill_library.get_running_skills()) # type: ignore[union-attr] # Terminate the skill using the skill library - return self._skill_library.terminate_skill(self.skill_name) + return self._skill_library.terminate_skill(self.skill_name) # type: ignore[union-attr] diff --git a/dimos/skills/manipulation/abstract_manipulation_skill.py b/dimos/skills/manipulation/abstract_manipulation_skill.py index e3f6e719fa..66eea99671 100644 --- a/dimos/skills/manipulation/abstract_manipulation_skill.py +++ b/dimos/skills/manipulation/abstract_manipulation_skill.py @@ -26,7 +26,7 @@ class AbstractManipulationSkill(AbstractRobotSkill): This abstract class provides access to the robot's manipulation memory system. """ - def __init__(self, *args, robot: Robot | None = None, **kwargs) -> None: + def __init__(self, *args, robot: Robot | None = None, **kwargs) -> None: # type: ignore[no-untyped-def] """Initialize the manipulation skill. Args: @@ -34,7 +34,7 @@ def __init__(self, *args, robot: Robot | None = None, **kwargs) -> None: """ super().__init__(*args, robot=robot, **kwargs) - if self._robot and not self._robot.manipulation_interface: + if self._robot and not self._robot.manipulation_interface: # type: ignore[attr-defined] raise NotImplementedError( "This robot does not have a manipulation interface implemented" ) @@ -55,4 +55,4 @@ def manipulation_interface(self) -> ManipulationInterface | None: if not self._robot.has_capability(RobotCapability.MANIPULATION): raise RuntimeError("This robot does not have manipulation capabilities") - return self._robot.manipulation_interface + return self._robot.manipulation_interface # type: ignore[attr-defined, no-any-return] diff --git a/dimos/skills/manipulation/force_constraint_skill.py b/dimos/skills/manipulation/force_constraint_skill.py index 72616c32a3..294be6782d 100644 --- a/dimos/skills/manipulation/force_constraint_skill.py +++ b/dimos/skills/manipulation/force_constraint_skill.py @@ -16,7 +16,7 @@ from pydantic import Field from dimos.skills.manipulation.abstract_manipulation_skill import AbstractManipulationSkill -from dimos.types.manipulation import ForceConstraint, Vector +from dimos.types.manipulation import ForceConstraint, Vector # type: ignore[attr-defined] from dimos.utils.logging_config import setup_logger # Initialize logger @@ -53,7 +53,7 @@ def __call__(self) -> ForceConstraint: # Create force direction vector if provided (convert 2D point to 3D vector with z=0) force_direction_vector = None if self.force_direction: - force_direction_vector = Vector(self.force_direction[0], self.force_direction[1], 0.0) + force_direction_vector = Vector(self.force_direction[0], self.force_direction[1], 0.0) # type: ignore[arg-type] # Create and return the constraint constraint = ForceConstraint( @@ -64,7 +64,7 @@ def __call__(self) -> ForceConstraint: ) # Add constraint to manipulation interface for Agent recall - self.manipulation_interface.add_constraint(constraint) + self.manipulation_interface.add_constraint(constraint) # type: ignore[union-attr] # Log the constraint creation logger.info(f"Generated force constraint: {self.description}") diff --git a/dimos/skills/manipulation/manipulate_skill.py b/dimos/skills/manipulation/manipulate_skill.py index 7905d4f76c..896ba7c584 100644 --- a/dimos/skills/manipulation/manipulate_skill.py +++ b/dimos/skills/manipulation/manipulate_skill.py @@ -80,7 +80,7 @@ def __call__(self) -> dict[str, Any]: task = ManipulationTask( description=self.description, target_object=self.target_object, - target_point=tuple(map(int, self.target_point.strip("()").split(","))), + target_point=tuple(map(int, self.target_point.strip("()").split(","))), # type: ignore[arg-type] constraints=constraint, metadata=metadata, timestamp=timestamp, @@ -89,7 +89,7 @@ def __call__(self) -> dict[str, Any]: ) # Add task to manipulation interface - self.manipulation_interface.add_manipulation_task(task) + self.manipulation_interface.add_manipulation_task(task) # type: ignore[union-attr] # Execute the manipulation result = self._execute_manipulation(task) @@ -106,9 +106,9 @@ def _build_manipulation_metadata(self) -> ManipulationMetadata: Build metadata for the current environment state, including object data and movement tolerances. """ # Get detected objects from the manipulation interface - detected_objects = [] + detected_objects = [] # type: ignore[var-annotated] try: - detected_objects = self.manipulation_interface.get_latest_objects() or [] + detected_objects = self.manipulation_interface.get_latest_objects() or [] # type: ignore[union-attr] except Exception as e: logger.warning(f"Failed to get detected objects: {e}") diff --git a/dimos/skills/manipulation/pick_and_place.py b/dimos/skills/manipulation/pick_and_place.py index 1143ce073a..386bd80395 100644 --- a/dimos/skills/manipulation/pick_and_place.py +++ b/dimos/skills/manipulation/pick_and_place.py @@ -75,7 +75,7 @@ def parse_qwen_points_response(response: str) -> tuple[tuple[int, int], tuple[in def save_debug_image_with_points( - image: np.ndarray, + image: np.ndarray, # type: ignore[type-arg] pick_point: tuple[int, int] | None = None, place_point: tuple[int, int] | None = None, filename_prefix: str = "qwen_debug", @@ -205,7 +205,7 @@ class PickAndPlace(AbstractRobotSkill): "qwen2.5-vl-72b-instruct", description="Qwen model to use for visual queries" ) - def __init__(self, robot=None, **data) -> None: + def __init__(self, robot=None, **data) -> None: # type: ignore[no-untyped-def] """ Initialize the PickAndPlace skill. @@ -215,29 +215,29 @@ def __init__(self, robot=None, **data) -> None: """ super().__init__(robot=robot, **data) - def _get_camera_frame(self) -> np.ndarray | None: + def _get_camera_frame(self) -> np.ndarray | None: # type: ignore[type-arg] """ Get a single RGB frame from the robot's camera. Returns: RGB image as numpy array or None if capture fails """ - if not self._robot or not self._robot.manipulation_interface: + if not self._robot or not self._robot.manipulation_interface: # type: ignore[attr-defined] logger.error("Robot or stereo camera not available") return None try: # Use the RPC call to get a single RGB frame - rgb_frame = self._robot.manipulation_interface.get_single_rgb_frame() + rgb_frame = self._robot.manipulation_interface.get_single_rgb_frame() # type: ignore[attr-defined] if rgb_frame is None: logger.error("Failed to capture RGB frame from camera") - return rgb_frame + return rgb_frame # type: ignore[no-any-return] except Exception as e: logger.error(f"Error getting camera frame: {e}") return None def _query_pick_and_place_points( - self, frame: np.ndarray + self, frame: np.ndarray # type: ignore[type-arg] ) -> tuple[tuple[int, int], tuple[int, int]] | None: """ Query Qwen to get both pick and place points in a single query. @@ -270,7 +270,7 @@ def _query_pick_and_place_points( return None def _query_single_point( - self, frame: np.ndarray, query: str, point_type: str + self, frame: np.ndarray, query: str, point_type: str # type: ignore[type-arg] ) -> tuple[int, int] | None: """ Query Qwen to get a single point location. @@ -323,7 +323,7 @@ def __call__(self) -> dict[str, Any]: Returns: Dictionary with operation results """ - super().__call__() + super().__call__() # type: ignore[no-untyped-call] if not self._robot: error_msg = "No robot instance provided to PickAndPlace skill" @@ -331,7 +331,7 @@ def __call__(self) -> dict[str, Any]: return {"success": False, "error": error_msg} # Register skill as running - skill_library = self._robot.get_skills() + skill_library = self._robot.get_skills() # type: ignore[no-untyped-call] self.register_as_running("PickAndPlace", skill_library) # Get camera frame @@ -365,7 +365,7 @@ def __call__(self) -> dict[str, Any]: # Try single query first for efficiency points = self._query_pick_and_place_points(frame) - pick_point, place_point = points + pick_point, place_point = points # type: ignore[misc] logger.info(f"Pick point: {pick_point}, Place point: {place_point}") @@ -377,7 +377,7 @@ def __call__(self) -> dict[str, Any]: try: if place_point: # Pick and place - result = self._robot.pick_and_place( + result = self._robot.pick_and_place( # type: ignore[attr-defined] pick_x=pick_point[0], pick_y=pick_point[1], place_x=place_point[0], @@ -385,7 +385,7 @@ def __call__(self) -> dict[str, Any]: ) else: # Pick only - result = self._robot.pick_and_place( + result = self._robot.pick_and_place( # type: ignore[attr-defined] pick_x=pick_point[0], pick_y=pick_point[1], place_x=None, place_y=None ) @@ -434,7 +434,7 @@ def stop(self) -> None: # Unregister skill from skill library if self._robot: - skill_library = self._robot.get_skills() + skill_library = self._robot.get_skills() # type: ignore[no-untyped-call] self.unregister_as_running("PickAndPlace", skill_library) logger.info("PickAndPlace skill stopped successfully") diff --git a/dimos/skills/manipulation/rotation_constraint_skill.py b/dimos/skills/manipulation/rotation_constraint_skill.py index ae1bdbb57d..90672e7632 100644 --- a/dimos/skills/manipulation/rotation_constraint_skill.py +++ b/dimos/skills/manipulation/rotation_constraint_skill.py @@ -71,25 +71,25 @@ def __call__(self) -> RotationConstraint: values = [0.0, 0.0, 0.0] axis_index = {"roll": 0, "pitch": 1, "yaw": 2}[self.rotation_axis] values[axis_index] = self.start_angle - start_angle_vector = Vector(*values) + start_angle_vector = Vector(*values) # type: ignore[arg-type] end_angle_vector = None if self.end_angle is not None: values = [0.0, 0.0, 0.0] axis_index = {"roll": 0, "pitch": 1, "yaw": 2}[self.rotation_axis] values[axis_index] = self.end_angle - end_angle_vector = Vector(*values) + end_angle_vector = Vector(*values) # type: ignore[arg-type] # Create pivot point vector if provided (convert 2D point to 3D vector with z=0) pivot_point_vector = None if self.pivot_point: - pivot_point_vector = Vector(self.pivot_point[0], self.pivot_point[1], 0.0) + pivot_point_vector = Vector(self.pivot_point[0], self.pivot_point[1], 0.0) # type: ignore[arg-type] # Create secondary pivot point vector if provided secondary_pivot_vector = None if self.secondary_pivot_point: secondary_pivot_vector = Vector( - self.secondary_pivot_point[0], self.secondary_pivot_point[1], 0.0 + self.secondary_pivot_point[0], self.secondary_pivot_point[1], 0.0 # type: ignore[arg-type] ) constraint = RotationConstraint( @@ -101,7 +101,7 @@ def __call__(self) -> RotationConstraint: ) # Add constraint to manipulation interface - self.manipulation_interface.add_constraint(constraint) + self.manipulation_interface.add_constraint(constraint) # type: ignore[union-attr] # Log the constraint creation logger.info(f"Generated rotation constraint around {self.rotation_axis} axis") diff --git a/dimos/skills/manipulation/translation_constraint_skill.py b/dimos/skills/manipulation/translation_constraint_skill.py index 6e1808744f..f88fc519b4 100644 --- a/dimos/skills/manipulation/translation_constraint_skill.py +++ b/dimos/skills/manipulation/translation_constraint_skill.py @@ -17,7 +17,7 @@ from pydantic import Field from dimos.skills.manipulation.abstract_manipulation_skill import AbstractManipulationSkill -from dimos.types.manipulation import TranslationConstraint, Vector +from dimos.types.manipulation import TranslationConstraint, Vector # type: ignore[attr-defined] from dimos.utils.logging_config import setup_logger # Initialize logger @@ -66,22 +66,22 @@ def __call__(self) -> TranslationConstraint: # Create reference point vector if provided (convert 2D point to 3D vector with z=0) reference_point = None if self.reference_point: - reference_point = Vector(self.reference_point[0], self.reference_point[1], 0.0) + reference_point = Vector(self.reference_point[0], self.reference_point[1], 0.0) # type: ignore[arg-type] # Create bounds minimum vector if provided bounds_min = None if self.bounds_min: - bounds_min = Vector(self.bounds_min[0], self.bounds_min[1], 0.0) + bounds_min = Vector(self.bounds_min[0], self.bounds_min[1], 0.0) # type: ignore[arg-type] # Create bounds maximum vector if provided bounds_max = None if self.bounds_max: - bounds_max = Vector(self.bounds_max[0], self.bounds_max[1], 0.0) + bounds_max = Vector(self.bounds_max[0], self.bounds_max[1], 0.0) # type: ignore[arg-type] # Create relative target vector if provided target_point = None if self.target_point: - target_point = Vector(self.target_point[0], self.target_point[1], 0.0) + target_point = Vector(self.target_point[0], self.target_point[1], 0.0) # type: ignore[arg-type] constraint = TranslationConstraint( translation_axis=self.translation_axis, @@ -92,9 +92,9 @@ def __call__(self) -> TranslationConstraint: ) # Add constraint to manipulation interface - self.manipulation_interface.add_constraint(constraint) + self.manipulation_interface.add_constraint(constraint) # type: ignore[union-attr] # Log the constraint creation logger.info(f"Generated translation constraint along {self.translation_axis} axis") - return {"success": True} + return {"success": True} # type: ignore[return-value] diff --git a/dimos/skills/skills.py b/dimos/skills/skills.py index 196fcf07b5..34b13d8e95 100644 --- a/dimos/skills/skills.py +++ b/dimos/skills/skills.py @@ -39,7 +39,7 @@ class SkillLibrary: def __init__(self) -> None: self.registered_skills: list[AbstractSkill] = [] self.class_skills: list[AbstractSkill] = [] - self._running_skills = {} # {skill_name: (instance, subscription)} + self._running_skills = {} # type: ignore[var-annotated] # {skill_name: (instance, subscription)} self.init() @@ -78,7 +78,7 @@ def get_class_skills(self) -> list[AbstractSkill]: # Skip attributes that can't be accessed or aren't classes continue - return skills + return skills # type: ignore[return-value] def refresh_class_skills(self) -> None: self.class_skills = self.get_class_skills() @@ -99,7 +99,7 @@ def remove(self, skill: AbstractSkill) -> None: def clear(self) -> None: self.registered_skills.clear() - def __iter__(self) -> Iterator: + def __iter__(self) -> Iterator: # type: ignore[type-arg] return iter(self.registered_skills) def __len__(self) -> int: @@ -108,14 +108,14 @@ def __len__(self) -> int: def __contains__(self, skill: AbstractSkill) -> bool: return skill in self.registered_skills - def __getitem__(self, index): + def __getitem__(self, index): # type: ignore[no-untyped-def] return self.registered_skills[index] # ==== Calling a Function ==== - _instances: dict[str, dict] = {} + _instances: dict[str, dict] = {} # type: ignore[type-arg] - def create_instance(self, name: str, **kwargs) -> None: + def create_instance(self, name: str, **kwargs) -> None: # type: ignore[no-untyped-def] # Key based only on the name key = name @@ -123,7 +123,7 @@ def create_instance(self, name: str, **kwargs) -> None: # Instead of creating an instance, store the args for later use self._instances[key] = kwargs - def call(self, name: str, **args): + def call(self, name: str, **args): # type: ignore[no-untyped-def] try: # Get the stored args if available; otherwise, use an empty dict stored_args = self._instances.get(name, {}) @@ -135,7 +135,7 @@ def call(self, name: str, **args): skill_class = getattr(self, name, None) if skill_class is None: for skill in self.get(): - if name == skill.__name__: + if name == skill.__name__: # type: ignore[attr-defined] skill_class = skill break if skill_class is None: @@ -144,7 +144,7 @@ def call(self, name: str, **args): return error_msg # Initialize the instance with the merged arguments - instance = skill_class(**complete_args) + instance = skill_class(**complete_args) # type: ignore[operator] print(f"Instance created and function called for: {name} with args: {complete_args}") # Call the instance directly @@ -162,9 +162,9 @@ def get_tools(self) -> Any: return tools_json def get_list_of_skills_as_json(self, list_of_skills: list[AbstractSkill]) -> list[str]: - return list(map(pydantic_function_tool, list_of_skills)) + return list(map(pydantic_function_tool, list_of_skills)) # type: ignore[arg-type] - def register_running_skill(self, name: str, instance: Any, subscription=None) -> None: + def register_running_skill(self, name: str, instance: Any, subscription=None) -> None: # type: ignore[no-untyped-def] """ Register a running skill with its subscription. @@ -194,7 +194,7 @@ def unregister_running_skill(self, name: str) -> bool: return True return False - def get_running_skills(self): + def get_running_skills(self): # type: ignore[no-untyped-def] """ Get all running skills. @@ -203,7 +203,7 @@ def get_running_skills(self): """ return self._running_skills.copy() - def terminate_skill(self, name: str): + def terminate_skill(self, name: str): # type: ignore[no-untyped-def] """ Terminate a running skill. @@ -256,17 +256,17 @@ def terminate_skill(self, name: str): class AbstractSkill(BaseModel): - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def] print("Initializing AbstractSkill Class") super().__init__(*args, **kwargs) - self._instances = {} - self._list_of_skills = [] # Initialize the list of skills + self._instances = {} # type: ignore[var-annotated] + self._list_of_skills = [] # type: ignore[var-annotated] # Initialize the list of skills print(f"Instances: {self._instances}") def clone(self) -> AbstractSkill: return AbstractSkill() - def register_as_running( + def register_as_running( # type: ignore[no-untyped-def] self, name: str, skill_library: SkillLibrary, subscription=None ) -> None: """ @@ -296,7 +296,7 @@ def get_tools(self) -> Any: return tools_json def get_list_of_skills_as_json(self, list_of_skills: list[AbstractSkill]) -> list[str]: - return list(map(pydantic_function_tool, list_of_skills)) + return list(map(pydantic_function_tool, list_of_skills)) # type: ignore[arg-type] # endregion AbstractSkill @@ -310,11 +310,11 @@ def get_list_of_skills_as_json(self, list_of_skills: list[AbstractSkill]) -> lis class AbstractRobotSkill(AbstractSkill): - _robot: Robot = None + _robot: Robot = None # type: ignore[assignment] - def __init__(self, *args, robot: Robot | None = None, **kwargs) -> None: + def __init__(self, *args, robot: Robot | None = None, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(*args, **kwargs) - self._robot = robot + self._robot = robot # type: ignore[assignment] print( f"{Colors.BLUE_PRINT_COLOR}Robot Skill Initialized with Robot: {robot}{Colors.RESET_COLOR}" ) @@ -327,7 +327,7 @@ def set_robot(self, robot: Robot) -> None: """ self._robot = robot - def __call__(self): + def __call__(self): # type: ignore[no-untyped-def] if self._robot is None: raise RuntimeError( f"{Colors.RED_PRINT_COLOR}" diff --git a/dimos/skills/speak.py b/dimos/skills/speak.py index a1e3abb078..d1a774795e 100644 --- a/dimos/skills/speak.py +++ b/dimos/skills/speak.py @@ -29,7 +29,7 @@ _audio_device_lock = threading.RLock() # Global queue for sequential audio processing -_audio_queue = queue.Queue() +_audio_queue = queue.Queue() # type: ignore[var-annotated] _queue_processor_thread = None _queue_running = False @@ -79,27 +79,27 @@ class Speak(AbstractSkill): text: str = Field(..., description="Text to speak") - def __init__(self, tts_node: Any | None = None, **data) -> None: + def __init__(self, tts_node: Any | None = None, **data) -> None: # type: ignore[no-untyped-def] super().__init__(**data) self._tts_node = tts_node self._audio_complete = threading.Event() self._subscription = None - self._subscriptions: list = [] # Track all subscriptions + self._subscriptions: list = [] # type: ignore[type-arg] # Track all subscriptions - def __call__(self): + def __call__(self): # type: ignore[no-untyped-def] if not self._tts_node: logger.error("No TTS node provided to Speak skill") return "Error: No TTS node available" # Create a result queue to get the result back from the audio thread - result_queue = queue.Queue(1) + result_queue = queue.Queue(1) # type: ignore[var-annotated] # Define the speech task to run in the audio queue def speak_task() -> None: try: # Using a lock to ensure exclusive access to audio device with _audio_device_lock: - text_subject = Subject() + text_subject = Subject() # type: ignore[var-annotated] self._audio_complete.clear() self._subscriptions = [] @@ -109,15 +109,15 @@ def on_complete() -> None: self._audio_complete.set() # This function will be called if there's an error - def on_error(error) -> None: + def on_error(error) -> None: # type: ignore[no-untyped-def] logger.error(f"Error in TTS processing: {error}") self._audio_complete.set() # Connect the Subject to the TTS node and keep the subscription - self._tts_node.consume_text(text_subject) + self._tts_node.consume_text(text_subject) # type: ignore[union-attr] # Subscribe to the audio output to know when it's done - self._subscription = self._tts_node.emit_text().subscribe( + self._subscription = self._tts_node.emit_text().subscribe( # type: ignore[union-attr] on_next=lambda text: logger.debug(f"TTS processing: {text}"), on_completed=on_complete, on_error=on_error, diff --git a/dimos/skills/unitree/unitree_speak.py b/dimos/skills/unitree/unitree_speak.py index 539ca0cd29..dbf936efa3 100644 --- a/dimos/skills/unitree/unitree_speak.py +++ b/dimos/skills/unitree/unitree_speak.py @@ -19,11 +19,11 @@ import tempfile import time -from go2_webrtc_driver.constants import RTC_TOPIC +from go2_webrtc_driver.constants import RTC_TOPIC # type: ignore[import-untyped] import numpy as np from openai import OpenAI from pydantic import Field -import soundfile as sf +import soundfile as sf # type: ignore[import-untyped] from dimos.skills.skills import AbstractRobotSkill from dimos.utils.logging_config import setup_logger @@ -58,33 +58,33 @@ class UnitreeSpeak(AbstractRobotSkill): default=False, description="Use megaphone mode for lower latency (experimental)" ) - def __init__(self, **data) -> None: + def __init__(self, **data) -> None: # type: ignore[no-untyped-def] super().__init__(**data) self._openai_client = None - def _get_openai_client(self): + def _get_openai_client(self): # type: ignore[no-untyped-def] if self._openai_client is None: - self._openai_client = OpenAI() + self._openai_client = OpenAI() # type: ignore[assignment] return self._openai_client def _generate_audio(self, text: str) -> bytes: try: - client = self._get_openai_client() + client = self._get_openai_client() # type: ignore[no-untyped-call] response = client.audio.speech.create( model="tts-1", voice=self.voice, input=text, speed=self.speed, response_format="mp3" ) - return response.content + return response.content # type: ignore[no-any-return] except Exception as e: logger.error(f"Error generating audio: {e}") raise - def _webrtc_request(self, api_id: int, parameter: dict | None = None): + def _webrtc_request(self, api_id: int, parameter: dict | None = None): # type: ignore[no-untyped-def, type-arg] if parameter is None: parameter = {} request_data = {"api_id": api_id, "parameter": json.dumps(parameter) if parameter else "{}"} - return self._robot.connection.publish_request(RTC_TOPIC["AUDIO_HUB_REQ"], request_data) + return self._robot.connection.publish_request(RTC_TOPIC["AUDIO_HUB_REQ"], request_data) # type: ignore[attr-defined] def _upload_audio_to_robot(self, audio_data: bytes, filename: str) -> str: try: @@ -123,7 +123,7 @@ def _upload_audio_to_robot(self, audio_data: bytes, filename: str) -> str: for audio in audio_list: if audio.get("CUSTOM_NAME") == filename: - return audio.get("UNIQUE_ID") + return audio.get("UNIQUE_ID") # type: ignore[no-any-return] logger.warning( f"Could not find uploaded audio '{filename}' in list, using filename as UUID" @@ -134,7 +134,7 @@ def _upload_audio_to_robot(self, audio_data: bytes, filename: str) -> str: logger.error(f"Error uploading audio to robot: {e}") raise - def _play_audio_on_robot(self, uuid: str): + def _play_audio_on_robot(self, uuid: str): # type: ignore[no-untyped-def] try: self._webrtc_request(AUDIO_API["SET_PLAY_MODE"], {"play_mode": PLAY_MODES["NO_CYCLE"]}) time.sleep(0.1) @@ -155,7 +155,7 @@ def _stop_audio_playback(self) -> None: except Exception as e: logger.warning(f"Error stopping audio playback: {e}") - def _upload_and_play_megaphone(self, audio_data: bytes, duration: float): + def _upload_and_play_megaphone(self, audio_data: bytes, duration: float): # type: ignore[no-untyped-def] try: logger.debug("Entering megaphone mode") self._webrtc_request(AUDIO_API["ENTER_MEGAPHONE"], {}) @@ -204,7 +204,7 @@ def _upload_and_play_megaphone(self, audio_data: bytes, duration: float): logger.warning(f"Error exiting megaphone mode: {e}") def __call__(self) -> str: - super().__call__() + super().__call__() # type: ignore[no-untyped-call] if not self._robot: logger.error("No robot instance provided to UnitreeSpeak skill") diff --git a/dimos/skills/visual_navigation_skills.py b/dimos/skills/visual_navigation_skills.py index 8064f28cc9..22fbe9c40e 100644 --- a/dimos/skills/visual_navigation_skills.py +++ b/dimos/skills/visual_navigation_skills.py @@ -25,7 +25,7 @@ from pydantic import Field -from dimos.perception.visual_servoing import VisualServoing +from dimos.perception.visual_servoing import VisualServoing # type: ignore[import-untyped] from dimos.skills.skills import AbstractRobotSkill from dimos.types.vector import Vector from dimos.utils.logging_config import setup_logger @@ -51,19 +51,19 @@ class FollowHuman(AbstractRobotSkill): None, description="Optional point to start tracking (x,y pixel coordinates)" ) - def __init__(self, robot=None, **data) -> None: + def __init__(self, robot=None, **data) -> None: # type: ignore[no-untyped-def] super().__init__(robot=robot, **data) self._stop_event = threading.Event() self._visual_servoing = None - def __call__(self): + def __call__(self): # type: ignore[no-untyped-def] """ Start following a human using visual servoing. Returns: bool: True if successful, False otherwise """ - super().__call__() + super().__call__() # type: ignore[no-untyped-call] if ( not hasattr(self._robot, "person_tracking_stream") @@ -88,7 +88,7 @@ def __call__(self): start_time = time.time() # Start tracking - track_success = self._visual_servoing.start_tracking( + track_success = self._visual_servoing.start_tracking( # type: ignore[attr-defined] point=self.point, desired_distance=self.distance ) @@ -98,15 +98,15 @@ def __call__(self): # Main follow loop while ( - self._visual_servoing.running + self._visual_servoing.running # type: ignore[attr-defined] and time.time() - start_time < self.timeout and not self._stop_event.is_set() ): - output = self._visual_servoing.updateTracking() + output = self._visual_servoing.updateTracking() # type: ignore[attr-defined] x_vel = output.get("linear_vel") z_vel = output.get("angular_vel") logger.debug(f"Following human: x_vel: {x_vel}, z_vel: {z_vel}") - self._robot.move(Vector(x_vel, 0, z_vel)) + self._robot.move(Vector(x_vel, 0, z_vel)) # type: ignore[arg-type, attr-defined] time.sleep(0.05) # If we completed the full timeout duration, consider it success diff --git a/dimos/stream/audio/base.py b/dimos/stream/audio/base.py index 43c3c13dec..178cc8bc0b 100644 --- a/dimos/stream/audio/base.py +++ b/dimos/stream/audio/base.py @@ -22,7 +22,7 @@ class AbstractAudioEmitter(ABC): """Base class for components that emit audio.""" @abstractmethod - def emit_audio(self) -> Observable: + def emit_audio(self) -> Observable: # type: ignore[type-arg] """Create an observable that emits audio frames. Returns: @@ -35,7 +35,7 @@ class AbstractAudioConsumer(ABC): """Base class for components that consume audio.""" @abstractmethod - def consume_audio(self, audio_observable: Observable) -> "AbstractAudioConsumer": + def consume_audio(self, audio_observable: Observable) -> "AbstractAudioConsumer": # type: ignore[type-arg] """Set the audio observable to consume. Args: @@ -60,7 +60,7 @@ class AudioEvent: """Class to represent an audio frame event with metadata.""" def __init__( - self, data: np.ndarray, sample_rate: int, timestamp: float, channels: int = 1 + self, data: np.ndarray, sample_rate: int, timestamp: float, channels: int = 1 # type: ignore[type-arg] ) -> None: """ Initialize an AudioEvent. diff --git a/dimos/stream/audio/node_key_recorder.py b/dimos/stream/audio/node_key_recorder.py index 5e918bae5c..8d1f1864f2 100644 --- a/dimos/stream/audio/node_key_recorder.py +++ b/dimos/stream/audio/node_key_recorder.py @@ -51,7 +51,7 @@ def __init__( self.max_recording_time = max_recording_time self.always_subscribe = always_subscribe - self._audio_buffer = [] + self._audio_buffer = [] # type: ignore[var-annotated] self._is_recording = False self._recording_start_time = 0 self._sample_rate = None # Will be updated from incoming audio @@ -59,8 +59,8 @@ def __init__( self._audio_observable = None self._subscription = None - self._output_subject = Subject() # For record-time passthrough - self._recording_subject = ReplaySubject(1) # For full completed recordings + self._output_subject = Subject() # type: ignore[var-annotated] # For record-time passthrough + self._recording_subject = ReplaySubject(1) # type: ignore[var-annotated] # For full completed recordings # Start a thread to monitor for input self._running = True @@ -69,7 +69,7 @@ def __init__( logger.info("Started audio recorder (press any key to start/stop recording)") - def consume_audio(self, audio_observable: Observable) -> "KeyRecorder": + def consume_audio(self, audio_observable: Observable) -> "KeyRecorder": # type: ignore[type-arg] """ Set the audio observable to use when recording. If always_subscribe is True, subscribes immediately. @@ -81,11 +81,11 @@ def consume_audio(self, audio_observable: Observable) -> "KeyRecorder": Returns: Self for method chaining """ - self._audio_observable = audio_observable + self._audio_observable = audio_observable # type: ignore[assignment] # If configured to always subscribe, do it now if self.always_subscribe and not self._subscription: - self._subscription = audio_observable.subscribe( + self._subscription = audio_observable.subscribe( # type: ignore[assignment] on_next=self._process_audio_event, on_error=self._handle_error, on_completed=self._handle_completion, @@ -94,7 +94,7 @@ def consume_audio(self, audio_observable: Observable) -> "KeyRecorder": return self - def emit_audio(self) -> Observable: + def emit_audio(self) -> Observable: # type: ignore[type-arg] """ Create an observable that emits audio events in real-time (pass-through). @@ -103,7 +103,7 @@ def emit_audio(self) -> Observable: """ return self._output_subject - def emit_recording(self) -> Observable: + def emit_recording(self) -> Observable: # type: ignore[type-arg] """ Create an observable that emits combined audio recordings when recording stops. @@ -187,7 +187,7 @@ def _stop_recording(self) -> None: else: logger.warning("No audio was recorded") - def _process_audio_event(self, audio_event) -> None: + def _process_audio_event(self, audio_event) -> None: # type: ignore[no-untyped-def] """Process incoming audio events.""" # Only buffer if recording @@ -215,7 +215,7 @@ def _combine_audio_events(self, audio_events: list[AudioEvent]) -> AudioEvent: """Combine multiple audio events into a single event.""" if not audio_events: logger.warning("Attempted to combine empty audio events list") - return None + return None # type: ignore[return-value] # Filter out any empty events that might cause broadcasting errors valid_events = [ @@ -227,7 +227,7 @@ def _combine_audio_events(self, audio_events: list[AudioEvent]) -> AudioEvent: if not valid_events: logger.warning("No valid audio events to combine") - return None + return None # type: ignore[return-value] first_event = valid_events[0] channels = first_event.channels @@ -239,7 +239,7 @@ def _combine_audio_events(self, audio_events: list[AudioEvent]) -> AudioEvent: # Safety check - if somehow we got no samples if total_samples <= 0: logger.warning(f"Combined audio would have {total_samples} samples - aborting") - return None + return None # type: ignore[return-value] # For multichannel audio, data shape could be (samples,) or (samples, channels) if len(first_event.data.shape) == 1: @@ -278,15 +278,15 @@ def _combine_audio_events(self, audio_events: list[AudioEvent]) -> AudioEvent: if combined_data.size > 0: return AudioEvent( data=combined_data, - sample_rate=self._sample_rate, + sample_rate=self._sample_rate, # type: ignore[arg-type] timestamp=valid_events[0].timestamp, channels=channels, ) else: logger.warning("Failed to create valid combined audio event") - return None + return None # type: ignore[return-value] - def _handle_error(self, error) -> None: + def _handle_error(self, error) -> None: # type: ignore[no-untyped-def] """Handle errors from the observable.""" logger.error(f"Error in audio observable: {error}") diff --git a/dimos/stream/audio/node_microphone.py b/dimos/stream/audio/node_microphone.py index 1f4bf13499..144d809db1 100644 --- a/dimos/stream/audio/node_microphone.py +++ b/dimos/stream/audio/node_microphone.py @@ -18,7 +18,7 @@ import numpy as np from reactivex import Observable, create, disposable -import sounddevice as sd +import sounddevice as sd # type: ignore[import-untyped] from dimos.stream.audio.base import ( AbstractAudioEmitter, @@ -38,7 +38,7 @@ def __init__( sample_rate: int = 16000, channels: int = 1, block_size: int = 1024, - dtype: np.dtype = np.float32, + dtype: np.dtype = np.float32, # type: ignore[assignment, type-arg] ) -> None: """ Initialize SounddeviceAudioSource. @@ -59,7 +59,7 @@ def __init__( self._stream = None self._running = False - def emit_audio(self) -> Observable: + def emit_audio(self) -> Observable: # type: ignore[type-arg] """ Create an observable that emits audio frames. @@ -67,9 +67,9 @@ def emit_audio(self) -> Observable: Observable emitting AudioEvent objects """ - def on_subscribe(observer, scheduler): + def on_subscribe(observer, scheduler): # type: ignore[no-untyped-def] # Callback function to process audio data - def audio_callback(indata, frames, time_info, status) -> None: + def audio_callback(indata, frames, time_info, status) -> None: # type: ignore[no-untyped-def] if status: logger.warning(f"Audio callback status: {status}") @@ -93,7 +93,7 @@ def audio_callback(indata, frames, time_info, status) -> None: dtype=self.dtype, callback=audio_callback, ) - self._stream.start() + self._stream.start() # type: ignore[attr-defined] self._running = True logger.info( @@ -120,7 +120,7 @@ def dispose() -> None: def get_available_devices(self) -> list[dict[str, Any]]: """Get a list of available audio input devices.""" - return sd.query_devices() + return sd.query_devices() # type: ignore[no-any-return] if __name__ == "__main__": diff --git a/dimos/stream/audio/node_normalizer.py b/dimos/stream/audio/node_normalizer.py index 064fc3cf6c..5de83ddb21 100644 --- a/dimos/stream/audio/node_normalizer.py +++ b/dimos/stream/audio/node_normalizer.py @@ -46,7 +46,7 @@ def __init__( max_gain: float = 10.0, decay_factor: float = 0.999, adapt_speed: float = 0.05, - volume_func: Callable[[np.ndarray], float] = calculate_peak_volume, + volume_func: Callable[[np.ndarray], float] = calculate_peak_volume, # type: ignore[type-arg] ) -> None: """ Initialize AudioNormalizer. @@ -119,7 +119,7 @@ def _normalize_audio(self, audio_event: AudioEvent) -> AudioEvent: channels=audio_event.channels, ) - def consume_audio(self, audio_observable: Observable) -> "AudioNormalizer": + def consume_audio(self, audio_observable: Observable) -> "AudioNormalizer": # type: ignore[type-arg] """ Set the audio source observable to consume. @@ -129,10 +129,10 @@ def consume_audio(self, audio_observable: Observable) -> "AudioNormalizer": Returns: Self for method chaining """ - self.audio_observable = audio_observable + self.audio_observable = audio_observable # type: ignore[assignment] return self - def emit_audio(self) -> Observable: + def emit_audio(self) -> Observable: # type: ignore[type-arg] """ Create an observable that emits normalized audio frames. @@ -190,7 +190,7 @@ def dispose() -> None: use_mic = True elif arg.startswith("level="): try: - target_level = float(arg.split("=")[1]) + target_level = float(arg.split("=")[1]) # type: ignore[assignment] except ValueError: print(f"Invalid target level: {arg}") sys.exit(1) diff --git a/dimos/stream/audio/node_output.py b/dimos/stream/audio/node_output.py index 3dc93d3757..430490671f 100644 --- a/dimos/stream/audio/node_output.py +++ b/dimos/stream/audio/node_output.py @@ -17,7 +17,7 @@ import numpy as np from reactivex import Observable -import sounddevice as sd +import sounddevice as sd # type: ignore[import-untyped] from dimos.stream.audio.base import ( AbstractAudioTransform, @@ -42,7 +42,7 @@ def __init__( sample_rate: int = 16000, channels: int = 1, block_size: int = 1024, - dtype: np.dtype = np.float32, + dtype: np.dtype = np.float32, # type: ignore[assignment, type-arg] ) -> None: """ Initialize SounddeviceAudioOutput. @@ -65,7 +65,7 @@ def __init__( self._subscription = None self.audio_observable = None - def consume_audio(self, audio_observable: Observable) -> "SounddeviceAudioOutput": + def consume_audio(self, audio_observable: Observable) -> "SounddeviceAudioOutput": # type: ignore[type-arg] """ Subscribe to an audio observable and play the audio through the speakers. @@ -75,7 +75,7 @@ def consume_audio(self, audio_observable: Observable) -> "SounddeviceAudioOutput Returns: Self for method chaining """ - self.audio_observable = audio_observable + self.audio_observable = audio_observable # type: ignore[assignment] # Create and start the output stream try: @@ -86,7 +86,7 @@ def consume_audio(self, audio_observable: Observable) -> "SounddeviceAudioOutput blocksize=self.block_size, dtype=self.dtype, ) - self._stream.start() + self._stream.start() # type: ignore[attr-defined] self._running = True logger.info( @@ -99,7 +99,7 @@ def consume_audio(self, audio_observable: Observable) -> "SounddeviceAudioOutput raise e # Subscribe to the observable - self._subscription = audio_observable.subscribe( + self._subscription = audio_observable.subscribe( # type: ignore[assignment] on_next=self._play_audio_event, on_error=self._handle_error, on_completed=self._handle_completion, @@ -107,7 +107,7 @@ def consume_audio(self, audio_observable: Observable) -> "SounddeviceAudioOutput return self - def emit_audio(self) -> Observable: + def emit_audio(self) -> Observable: # type: ignore[type-arg] """ Pass through the audio observable to allow chaining with other components. @@ -133,7 +133,7 @@ def stop(self) -> None: self._stream.close() self._stream = None - def _play_audio_event(self, audio_event) -> None: + def _play_audio_event(self, audio_event) -> None: # type: ignore[no-untyped-def] """Play audio from an AudioEvent.""" if not self._running or not self._stream: return @@ -151,7 +151,7 @@ def _play_audio_event(self, audio_event) -> None: except Exception as e: logger.error(f"Error playing audio: {e}") - def _handle_error(self, error) -> None: + def _handle_error(self, error) -> None: # type: ignore[no-untyped-def] """Handle errors from the observable.""" logger.error(f"Error in audio observable: {error}") @@ -166,7 +166,7 @@ def _handle_completion(self) -> None: def get_available_devices(self) -> list[dict[str, Any]]: """Get a list of available audio output devices.""" - return sd.query_devices() + return sd.query_devices() # type: ignore[no-any-return] if __name__ == "__main__": diff --git a/dimos/stream/audio/node_simulated.py b/dimos/stream/audio/node_simulated.py index 82de718ced..63cd5a43ea 100644 --- a/dimos/stream/audio/node_simulated.py +++ b/dimos/stream/audio/node_simulated.py @@ -18,7 +18,7 @@ import numpy as np from reactivex import Observable, create, disposable -from dimos.stream.audio.abstract import ( +from dimos.stream.audio.abstract import ( # type: ignore[import-untyped] AbstractAudioEmitter, AudioEvent, ) @@ -27,7 +27,7 @@ logger = setup_logger("dimos.stream.audio.node_simulated") -class SimulatedAudioSource(AbstractAudioEmitter): +class SimulatedAudioSource(AbstractAudioEmitter): # type: ignore[misc] """Audio source that generates simulated audio for testing.""" def __init__( @@ -35,7 +35,7 @@ def __init__( sample_rate: int = 16000, frame_length: int = 1024, channels: int = 1, - dtype: np.dtype = np.float32, + dtype: np.dtype = np.float32, # type: ignore[assignment, type-arg] frequency: float = 440.0, # A4 note waveform: str = "sine", # Type of waveform modulation_rate: float = 0.5, # Modulation rate in Hz @@ -71,7 +71,7 @@ def __init__( self._running = False self._thread = None - def _generate_sine_wave(self, time_points: np.ndarray) -> np.ndarray: + def _generate_sine_wave(self, time_points: np.ndarray) -> np.ndarray: # type: ignore[type-arg] """Generate a waveform based on selected type.""" # Generate base time points with phase t = time_points + self.phase @@ -131,9 +131,9 @@ def _generate_sine_wave(self, time_points: np.ndarray) -> np.ndarray: if self.dtype == np.int16: wave = (wave * 32767).astype(np.int16) - return wave + return wave # type: ignore[no-any-return] - def _audio_thread(self, observer, interval: float) -> None: + def _audio_thread(self, observer, interval: float) -> None: # type: ignore[no-untyped-def] """Thread function for simulated audio generation.""" try: sample_index = 0 @@ -171,7 +171,7 @@ def _audio_thread(self, observer, interval: float) -> None: self._running = False observer.on_completed() - def emit_audio(self, fps: int = 30) -> Observable: + def emit_audio(self, fps: int = 30) -> Observable: # type: ignore[type-arg] """ Create an observable that emits simulated audio frames. @@ -182,15 +182,15 @@ def emit_audio(self, fps: int = 30) -> Observable: Observable emitting AudioEvent objects """ - def on_subscribe(observer, scheduler): + def on_subscribe(observer, scheduler): # type: ignore[no-untyped-def] # Calculate interval based on fps interval = 1.0 / fps # Start the audio generation thread - self._thread = threading.Thread( + self._thread = threading.Thread( # type: ignore[assignment] target=self._audio_thread, args=(observer, interval), daemon=True ) - self._thread.start() + self._thread.start() # type: ignore[attr-defined] logger.info( f"Started simulated audio source: {self.sample_rate}Hz, " diff --git a/dimos/stream/audio/node_volume_monitor.py b/dimos/stream/audio/node_volume_monitor.py index e1c5b226a4..0c15492cdb 100644 --- a/dimos/stream/audio/node_volume_monitor.py +++ b/dimos/stream/audio/node_volume_monitor.py @@ -35,7 +35,7 @@ def __init__( self, threshold: float = 0.01, bar_length: int = 50, - volume_func: Callable = calculate_peak_volume, + volume_func: Callable = calculate_peak_volume, # type: ignore[type-arg] ) -> None: """ Initialize VolumeMonitorNode. @@ -75,7 +75,7 @@ def create_volume_text(self, volume: float) -> str: activity = "active" if active else "silent" return f"{bar} {percentage:3d}% {activity}" - def consume_audio(self, audio_observable: Observable) -> "VolumeMonitorNode": + def consume_audio(self, audio_observable: Observable) -> "VolumeMonitorNode": # type: ignore[type-arg] """ Set the audio source observable to consume. @@ -85,10 +85,10 @@ def consume_audio(self, audio_observable: Observable) -> "VolumeMonitorNode": Returns: Self for method chaining """ - self.audio_observable = audio_observable + self.audio_observable = audio_observable # type: ignore[assignment] return self - def emit_text(self) -> Observable: + def emit_text(self) -> Observable: # type: ignore[type-arg] """ Create an observable that emits volume text descriptions. @@ -134,10 +134,10 @@ def dispose() -> None: def monitor( - audio_source: Observable, + audio_source: Observable, # type: ignore[type-arg] threshold: float = 0.01, bar_length: int = 50, - volume_func: Callable = calculate_peak_volume, + volume_func: Callable = calculate_peak_volume, # type: ignore[type-arg] ) -> VolumeMonitorNode: """ Create a volume monitor node connected to a text output node. @@ -168,8 +168,8 @@ def monitor( if __name__ == "__main__": - from audio.node_simulated import SimulatedAudioSource - from utils import keepalive + from audio.node_simulated import SimulatedAudioSource # type: ignore[import-not-found] + from utils import keepalive # type: ignore[import-untyped] # Use the monitor function to create and connect the nodes volume_monitor = monitor(SimulatedAudioSource().emit_audio()) diff --git a/dimos/stream/audio/pipelines.py b/dimos/stream/audio/pipelines.py index ceaeb80fac..91b40e2dca 100644 --- a/dimos/stream/audio/pipelines.py +++ b/dimos/stream/audio/pipelines.py @@ -22,7 +22,7 @@ from dimos.stream.audio.tts.node_openai import OpenAITTSNode, Voice -def stt(): +def stt(): # type: ignore[no-untyped-def] # Create microphone source, recorder, and audio output mic = SounddeviceAudioSource() normalizer = AudioNormalizer() @@ -41,7 +41,7 @@ def stt(): return whisper_node -def tts(): +def tts(): # type: ignore[no-untyped-def] tts_node = OpenAITTSNode(speed=1.2, voice=Voice.ONYX) agent_text_printer = TextPrinterNode(prefix="AGENT: ") agent_text_printer.consume_text(tts_node.emit_text()) diff --git a/dimos/stream/audio/stt/node_whisper.py b/dimos/stream/audio/stt/node_whisper.py index 05ec5274c8..56230329e1 100644 --- a/dimos/stream/audio/stt/node_whisper.py +++ b/dimos/stream/audio/stt/node_whisper.py @@ -16,7 +16,7 @@ from typing import Any from reactivex import Observable, create, disposable -import whisper +import whisper # type: ignore[import-untyped] from dimos.stream.audio.base import ( AbstractAudioConsumer, @@ -44,7 +44,7 @@ def __init__( self.modelopts = modelopts self.model = whisper.load_model(model) - def consume_audio(self, audio_observable: Observable) -> "WhisperNode": + def consume_audio(self, audio_observable: Observable) -> "WhisperNode": # type: ignore[type-arg] """ Set the audio source observable to consume. @@ -54,10 +54,10 @@ def consume_audio(self, audio_observable: Observable) -> "WhisperNode": Returns: Self for method chaining """ - self.audio_observable = audio_observable + self.audio_observable = audio_observable # type: ignore[assignment] return self - def emit_text(self) -> Observable: + def emit_text(self) -> Observable: # type: ignore[type-arg] """ Create an observable that emits transcribed text from audio. diff --git a/dimos/stream/audio/text/base.py b/dimos/stream/audio/text/base.py index b7305c0bcc..9f17492407 100644 --- a/dimos/stream/audio/text/base.py +++ b/dimos/stream/audio/text/base.py @@ -21,7 +21,7 @@ class AbstractTextEmitter(ABC): """Base class for components that emit audio.""" @abstractmethod - def emit_text(self) -> Observable: + def emit_text(self) -> Observable: # type: ignore[type-arg] """Create an observable that emits audio frames. Returns: @@ -34,7 +34,7 @@ class AbstractTextConsumer(ABC): """Base class for components that consume audio.""" @abstractmethod - def consume_text(self, text_observable: Observable) -> "AbstractTextConsumer": + def consume_text(self, text_observable: Observable) -> "AbstractTextConsumer": # type: ignore[type-arg] """Set the audio observable to consume. Args: diff --git a/dimos/stream/audio/text/node_stdout.py b/dimos/stream/audio/text/node_stdout.py index b0a5fd4ac8..0f39eb2b8d 100644 --- a/dimos/stream/audio/text/node_stdout.py +++ b/dimos/stream/audio/text/node_stdout.py @@ -49,7 +49,7 @@ def print_text(self, text: str) -> None: """ print(f"{self.prefix}{text}{self.suffix}", end=self.end, flush=True) - def consume_text(self, text_observable: Observable) -> "AbstractTextConsumer": + def consume_text(self, text_observable: Observable) -> "AbstractTextConsumer": # type: ignore[type-arg] """ Start processing text from the observable source. @@ -62,7 +62,7 @@ def consume_text(self, text_observable: Observable) -> "AbstractTextConsumer": logger.info("Starting text printer") # Subscribe to the text observable - self.subscription = text_observable.subscribe( + self.subscription = text_observable.subscribe( # type: ignore[assignment] on_next=self.print_text, on_error=lambda e: logger.error(f"Error: {e}"), on_completed=lambda: logger.info("Text printer completed"), @@ -77,7 +77,7 @@ def consume_text(self, text_observable: Observable) -> "AbstractTextConsumer": from reactivex import Subject # Create a simple text subject that we can push values to - text_subject = Subject() + text_subject = Subject() # type: ignore[var-annotated] # Create and connect the text printer text_printer = TextPrinterNode(prefix="Text: ") diff --git a/dimos/stream/audio/tts/node_openai.py b/dimos/stream/audio/tts/node_openai.py index 211b2b0246..42b7e3b1ea 100644 --- a/dimos/stream/audio/tts/node_openai.py +++ b/dimos/stream/audio/tts/node_openai.py @@ -20,7 +20,7 @@ from openai import OpenAI from reactivex import Observable, Subject -import soundfile as sf +import soundfile as sf # type: ignore[import-untyped] from dimos.stream.audio.base import ( AbstractAudioEmitter, @@ -78,15 +78,15 @@ def __init__( self.client = OpenAI(api_key=api_key) # Initialize state - self.audio_subject = Subject() - self.text_subject = Subject() + self.audio_subject = Subject() # type: ignore[var-annotated] + self.text_subject = Subject() # type: ignore[var-annotated] self.subscription = None self.processing_thread = None self.is_running = True - self.text_queue = [] + self.text_queue = [] # type: ignore[var-annotated] self.queue_lock = threading.Lock() - def emit_audio(self) -> Observable: + def emit_audio(self) -> Observable: # type: ignore[type-arg] """ Returns an observable that emits audio frames. @@ -95,7 +95,7 @@ def emit_audio(self) -> Observable: """ return self.audio_subject - def emit_text(self) -> Observable: + def emit_text(self) -> Observable: # type: ignore[type-arg] """ Returns an observable that emits the text being spoken. @@ -104,7 +104,7 @@ def emit_text(self) -> Observable: """ return self.text_subject - def consume_text(self, text_observable: Observable) -> "AbstractTextConsumer": + def consume_text(self, text_observable: Observable) -> "AbstractTextConsumer": # type: ignore[type-arg] """ Start consuming text from the observable source. @@ -117,11 +117,11 @@ def consume_text(self, text_observable: Observable) -> "AbstractTextConsumer": logger.info("Starting OpenAITTSNode") # Start the processing thread - self.processing_thread = threading.Thread(target=self._process_queue, daemon=True) - self.processing_thread.start() + self.processing_thread = threading.Thread(target=self._process_queue, daemon=True) # type: ignore[assignment] + self.processing_thread.start() # type: ignore[attr-defined] # Subscribe to the text observable - self.subscription = text_observable.subscribe( + self.subscription = text_observable.subscribe( # type: ignore[assignment] on_next=self._queue_text, on_error=lambda e: logger.error(f"Error in OpenAITTSNode: {e}"), ) @@ -226,7 +226,7 @@ def dispose(self) -> None: from dimos.stream.audio.utils import keepalive # Create a simple text subject that we can push values to - text_subject = Subject() + text_subject = Subject() # type: ignore[var-annotated] tts_node = OpenAITTSNode(voice=Voice.ALLOY) tts_node.consume_text(text_subject) diff --git a/dimos/stream/audio/tts/node_pytts.py b/dimos/stream/audio/tts/node_pytts.py index f1543331ef..aa4dc387bc 100644 --- a/dimos/stream/audio/tts/node_pytts.py +++ b/dimos/stream/audio/tts/node_pytts.py @@ -13,16 +13,16 @@ # See the License for the specific language governing permissions and # limitations under the License. -import pyttsx3 +import pyttsx3 # type: ignore[import-not-found] from reactivex import Observable, Subject -from dimos.stream.audio.text.abstract import AbstractTextTransform +from dimos.stream.audio.text.abstract import AbstractTextTransform # type: ignore[import-untyped] from dimos.utils.logging_config import setup_logger logger = setup_logger(__name__) -class PyTTSNode(AbstractTextTransform): +class PyTTSNode(AbstractTextTransform): # type: ignore[misc] """ A transform node that passes through text but also speaks it using pyttsx3. @@ -42,10 +42,10 @@ def __init__(self, rate: int = 200, volume: float = 1.0) -> None: self.engine.setProperty("rate", rate) self.engine.setProperty("volume", volume) - self.text_subject = Subject() + self.text_subject = Subject() # type: ignore[var-annotated] self.subscription = None - def emit_text(self) -> Observable: + def emit_text(self) -> Observable: # type: ignore[type-arg] """ Returns an observable that emits text strings passed through this node. @@ -54,7 +54,7 @@ def emit_text(self) -> Observable: """ return self.text_subject - def consume_text(self, text_observable: Observable) -> "AbstractTextTransform": + def consume_text(self, text_observable: Observable) -> "AbstractTextTransform": # type: ignore[type-arg] """ Start processing text from the observable source. @@ -67,7 +67,7 @@ def consume_text(self, text_observable: Observable) -> "AbstractTextTransform": logger.info("Starting PyTTSNode") # Subscribe to the text observable - self.subscription = text_observable.subscribe( + self.subscription = text_observable.subscribe( # type: ignore[assignment] on_next=self.process_text, on_error=lambda e: logger.error(f"Error in PyTTSNode: {e}"), on_completed=lambda: self.on_text_completed(), @@ -108,7 +108,7 @@ def dispose(self) -> None: import time # Create a simple text subject that we can push values to - text_subject = Subject() + text_subject = Subject() # type: ignore[var-annotated] # Create and connect the TTS node tts_node = PyTTSNode(rate=150) diff --git a/dimos/stream/audio/volume.py b/dimos/stream/audio/volume.py index bd137172b3..64afda1185 100644 --- a/dimos/stream/audio/volume.py +++ b/dimos/stream/audio/volume.py @@ -16,7 +16,7 @@ import numpy as np -def calculate_rms_volume(audio_data: np.ndarray) -> float: +def calculate_rms_volume(audio_data: np.ndarray) -> float: # type: ignore[type-arg] """ Calculate RMS (Root Mean Square) volume of audio data. @@ -38,10 +38,10 @@ def calculate_rms_volume(audio_data: np.ndarray) -> float: if audio_data.dtype == np.int16: rms = rms / 32768.0 - return rms + return rms # type: ignore[no-any-return] -def calculate_peak_volume(audio_data: np.ndarray) -> float: +def calculate_peak_volume(audio_data: np.ndarray) -> float: # type: ignore[type-arg] """ Calculate peak volume of audio data. @@ -63,7 +63,7 @@ def calculate_peak_volume(audio_data: np.ndarray) -> float: if audio_data.dtype == np.int16: peak = peak / 32768.0 - return peak + return peak # type: ignore[no-any-return] if __name__ == "__main__": @@ -78,7 +78,7 @@ def calculate_peak_volume(audio_data: np.ndarray) -> float: # Create observable and subscribe to get a single frame audio_observable = audio_source.capture_audio_as_observable() - def process_frame(frame) -> None: + def process_frame(frame) -> None: # type: ignore[no-untyped-def] # Calculate and print both RMS and peak volumes rms_vol = calculate_rms_volume(frame.data) peak_vol = calculate_peak_volume(frame.data) @@ -90,7 +90,7 @@ def process_frame(frame) -> None: # Set a flag to track when processing is complete processed = {"done": False} - def process_frame_wrapper(frame) -> None: + def process_frame_wrapper(frame) -> None: # type: ignore[no-untyped-def] # Process the frame process_frame(frame) # Mark as processed diff --git a/dimos/stream/data_provider.py b/dimos/stream/data_provider.py index f931857fda..6663047cc3 100644 --- a/dimos/stream/data_provider.py +++ b/dimos/stream/data_provider.py @@ -32,14 +32,14 @@ class AbstractDataProvider(ABC): def __init__(self, dev_name: str = "NA") -> None: self.dev_name = dev_name - self._data_subject = Subject() # Regular Subject, no initial None value + self._data_subject = Subject() # type: ignore[var-annotated] # Regular Subject, no initial None value @property - def data_stream(self) -> Observable: + def data_stream(self) -> Observable: # type: ignore[type-arg] """Get the data stream observable.""" return self._data_subject - def push_data(self, data) -> None: + def push_data(self, data) -> None: # type: ignore[no-untyped-def] """Push new data to the stream.""" self._data_subject.on_next(data) @@ -55,13 +55,13 @@ def __init__(self, dev_name: str = "ros_provider") -> None: super().__init__(dev_name) self.logger = logging.getLogger(dev_name) - def push_data(self, data) -> None: + def push_data(self, data) -> None: # type: ignore[no-untyped-def] """Push new data to the stream.""" print(f"ROSDataProvider pushing data of type: {type(data)}") super().push_data(data) print("Data pushed to subject") - def capture_data_as_observable(self, fps: int | None = None) -> Observable: + def capture_data_as_observable(self, fps: int | None = None) -> Observable: # type: ignore[type-arg] """Get the data stream as an observable. Args: @@ -168,7 +168,7 @@ def start_query_stream( # Zip the timer with the query source so each timer tick emits the next query. query_stream = timer.pipe( ops.zip(query_source), - ops.map(lambda pair: query_template.format(query=pair[1])), + ops.map(lambda pair: query_template.format(query=pair[1])), # type: ignore[index] ops.observe_on(pool_scheduler), # ops.do_action( # on_next=lambda q: self.logger.info(f"Emitting query: {q}"), diff --git a/dimos/stream/frame_processor.py b/dimos/stream/frame_processor.py index fda13ece61..3d53685c4c 100644 --- a/dimos/stream/frame_processor.py +++ b/dimos/stream/frame_processor.py @@ -57,19 +57,19 @@ def __init__( # TODO: Add randomness to jpg folder storage naming. # Will overwrite between sessions. - def to_grayscale(self, frame): + def to_grayscale(self, frame): # type: ignore[no-untyped-def] if frame is None: print("Received None frame for grayscale conversion.") return None return cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) - def edge_detection(self, frame): + def edge_detection(self, frame): # type: ignore[no-untyped-def] return cv2.Canny(frame, 100, 200) - def resize(self, frame, scale: float = 0.5): + def resize(self, frame, scale: float = 0.5): # type: ignore[no-untyped-def] return cv2.resize(frame, None, fx=scale, fy=scale, interpolation=cv2.INTER_AREA) - def export_to_jpeg(self, frame, save_limit: int = 100, loop: bool = False, suffix: str = ""): + def export_to_jpeg(self, frame, save_limit: int = 100, loop: bool = False, suffix: str = ""): # type: ignore[no-untyped-def] if frame is None: print("Error: Attempted to save a None image.") return None @@ -93,10 +93,10 @@ def export_to_jpeg(self, frame, save_limit: int = 100, loop: bool = False, suffi def compute_optical_flow( self, - acc: tuple[np.ndarray, np.ndarray, float | None], - current_frame: np.ndarray, + acc: tuple[np.ndarray, np.ndarray, float | None], # type: ignore[type-arg] + current_frame: np.ndarray, # type: ignore[type-arg] compute_relevancy: bool = True, - ) -> tuple[np.ndarray, np.ndarray, float | None]: + ) -> tuple[np.ndarray, np.ndarray, float | None]: # type: ignore[type-arg] """Computes optical flow between consecutive frames. Uses the Farneback algorithm to compute dense optical flow between the @@ -128,11 +128,11 @@ def compute_optical_flow( return (current_frame, None, None) # Convert frames to grayscale - gray_current = self.to_grayscale(current_frame) - gray_prev = self.to_grayscale(prev_frame) + gray_current = self.to_grayscale(current_frame) # type: ignore[no-untyped-call] + gray_prev = self.to_grayscale(prev_frame) # type: ignore[no-untyped-call] # Compute optical flow - flow = cv2.calcOpticalFlowFarneback(gray_prev, gray_current, None, 0.5, 3, 15, 3, 5, 1.2, 0) + flow = cv2.calcOpticalFlowFarneback(gray_prev, gray_current, None, 0.5, 3, 15, 3, 5, 1.2, 0) # type: ignore[call-overload] # Relevancy calulation (average magnitude of flow vectors) relevancy = None @@ -141,37 +141,37 @@ def compute_optical_flow( relevancy = np.mean(mag) # Return the current frame as the new previous frame and the processed optical flow, with relevancy score - return (current_frame, flow, relevancy) + return (current_frame, flow, relevancy) # type: ignore[return-value] - def visualize_flow(self, flow): + def visualize_flow(self, flow): # type: ignore[no-untyped-def] if flow is None: return None hsv = np.zeros((flow.shape[0], flow.shape[1], 3), dtype=np.uint8) hsv[..., 1] = 255 mag, ang = cv2.cartToPolar(flow[..., 0], flow[..., 1]) hsv[..., 0] = ang * 180 / np.pi / 2 - hsv[..., 2] = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX) + hsv[..., 2] = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX) # type: ignore[call-overload] rgb = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR) return rgb # ============================== - def process_stream_edge_detection(self, frame_stream): + def process_stream_edge_detection(self, frame_stream): # type: ignore[no-untyped-def] return frame_stream.pipe( ops.map(self.edge_detection), ) - def process_stream_resize(self, frame_stream): + def process_stream_resize(self, frame_stream): # type: ignore[no-untyped-def] return frame_stream.pipe( ops.map(self.resize), ) - def process_stream_to_greyscale(self, frame_stream): + def process_stream_to_greyscale(self, frame_stream): # type: ignore[no-untyped-def] return frame_stream.pipe( ops.map(self.to_grayscale), ) - def process_stream_optical_flow(self, frame_stream: Observable) -> Observable: + def process_stream_optical_flow(self, frame_stream: Observable) -> Observable: # type: ignore[type-arg] """Processes video stream to compute and visualize optical flow. Computes optical flow between consecutive frames and generates a color-coded @@ -203,15 +203,15 @@ def process_stream_optical_flow(self, frame_stream: Observable) -> Observable: """ return frame_stream.pipe( ops.scan( - lambda acc, frame: self.compute_optical_flow(acc, frame, compute_relevancy=False), + lambda acc, frame: self.compute_optical_flow(acc, frame, compute_relevancy=False), # type: ignore[arg-type, return-value] (None, None, None), ), - ops.map(lambda result: result[1]), # Extract flow component + ops.map(lambda result: result[1]), # type: ignore[index] # Extract flow component ops.filter(lambda flow: flow is not None), ops.map(self.visualize_flow), ) - def process_stream_optical_flow_with_relevancy(self, frame_stream: Observable) -> Observable: + def process_stream_optical_flow_with_relevancy(self, frame_stream: Observable) -> Observable: # type: ignore[type-arg] """Processes video stream to compute optical flow with movement relevancy. Applies optical flow computation to each frame and returns both the @@ -247,23 +247,23 @@ def process_stream_optical_flow_with_relevancy(self, frame_stream: Observable) - """ return frame_stream.pipe( ops.scan( - lambda acc, frame: self.compute_optical_flow(acc, frame, compute_relevancy=True), + lambda acc, frame: self.compute_optical_flow(acc, frame, compute_relevancy=True), # type: ignore[arg-type, return-value] (None, None, None), ), # Result is (current_frame, flow, relevancy) - ops.filter(lambda result: result[1] is not None), # Filter out None flows + ops.filter(lambda result: result[1] is not None), # type: ignore[index] # Filter out None flows ops.map( lambda result: ( - self.visualize_flow(result[1]), # Visualized flow - result[2], # Relevancy score + self.visualize_flow(result[1]), # type: ignore[index, no-untyped-call] # Visualized flow + result[2], # type: ignore[index] # Relevancy score ) ), - ops.filter(lambda result: result[0] is not None), # Ensure valid visualization + ops.filter(lambda result: result[0] is not None), # type: ignore[index] # Ensure valid visualization ) def process_stream_with_jpeg_export( - self, frame_stream: Observable, suffix: str = "", loop: bool = False - ) -> Observable: + self, frame_stream: Observable, suffix: str = "", loop: bool = False # type: ignore[type-arg] + ) -> Observable: # type: ignore[type-arg] """Processes stream by saving frames as JPEGs while passing them through. Saves each frame from the stream as a JPEG file and passes the frame diff --git a/dimos/stream/ros_video_provider.py b/dimos/stream/ros_video_provider.py index 5182ca79f8..0d29d88340 100644 --- a/dimos/stream/ros_video_provider.py +++ b/dimos/stream/ros_video_provider.py @@ -53,11 +53,11 @@ def __init__( """ super().__init__(dev_name, pool_scheduler) self.logger = logging.getLogger(dev_name) - self._subject = Subject() + self._subject = Subject() # type: ignore[var-annotated] self._last_frame_time = None self.logger.info("ROSVideoProvider initialized") - def push_data(self, frame: np.ndarray) -> None: + def push_data(self, frame: np.ndarray) -> None: # type: ignore[type-arg] """Push a new frame into the provider. Args: @@ -74,7 +74,7 @@ def push_data(self, frame: np.ndarray) -> None: self.logger.debug( f"Frame interval: {frame_interval:.3f}s ({1 / frame_interval:.1f} FPS)" ) - self._last_frame_time = current_time + self._last_frame_time = current_time # type: ignore[assignment] self.logger.debug(f"Pushing frame type: {type(frame)}") self._subject.on_next(frame) @@ -83,7 +83,7 @@ def push_data(self, frame: np.ndarray) -> None: self.logger.error(f"Push error: {e}") raise - def capture_video_as_observable(self, fps: int = 30) -> Observable: + def capture_video_as_observable(self, fps: int = 30) -> Observable: # type: ignore[type-arg] """Return an observable of video frames. Args: diff --git a/dimos/stream/rtsp_video_provider.py b/dimos/stream/rtsp_video_provider.py index 3aeb651a4d..c2952b6d80 100644 --- a/dimos/stream/rtsp_video_provider.py +++ b/dimos/stream/rtsp_video_provider.py @@ -18,7 +18,7 @@ import threading import time -import ffmpeg # ffmpeg-python wrapper +import ffmpeg # type: ignore[import-untyped] # ffmpeg-python wrapper import numpy as np import reactivex as rx from reactivex import operators as ops @@ -55,11 +55,11 @@ def __init__( super().__init__(dev_name, pool_scheduler) self.rtsp_url = rtsp_url # Holds the currently active ffmpeg process Popen object - self._ffmpeg_process: subprocess.Popen | None = None + self._ffmpeg_process: subprocess.Popen | None = None # type: ignore[type-arg] # Lock to protect access to the ffmpeg process object self._lock = threading.Lock() - def _get_stream_info(self) -> dict: + def _get_stream_info(self) -> dict: # type: ignore[type-arg] """Probes the RTSP stream to get video dimensions and FPS using ffprobe.""" logger.info(f"({self.dev_name}) Probing RTSP stream.") try: @@ -114,7 +114,7 @@ def _get_stream_info(self) -> dict: logger.info(f"({self.dev_name}) Stream info: {width}x{height} @ {fps:.2f} FPS") return {"width": width, "height": height, "fps": fps} - def _start_ffmpeg_process(self, width: int, height: int) -> subprocess.Popen: + def _start_ffmpeg_process(self, width: int, height: int) -> subprocess.Popen: # type: ignore[type-arg] """Starts the ffmpeg process to capture and decode the stream.""" logger.info(f"({self.dev_name}) Starting ffmpeg process for rtsp stream.") try: @@ -133,7 +133,7 @@ def _start_ffmpeg_process(self, width: int, height: int) -> subprocess.Popen: .run_async(pipe_stdout=True, pipe_stderr=True) # Capture stdout and stderr ) logger.info(f"({self.dev_name}) ffmpeg process started (PID: {process.pid})") - return process + return process # type: ignore[no-any-return] except ffmpeg.Error as e: stderr = e.stderr.decode("utf8", errors="ignore") if e.stderr else "No stderr" msg = f"({self.dev_name}) Failed to start ffmpeg for {self.rtsp_url}: {stderr}" @@ -144,7 +144,7 @@ def _start_ffmpeg_process(self, width: int, height: int) -> subprocess.Popen: logger.error(msg) raise VideoSourceError(msg) from e - def capture_video_as_observable(self, fps: int = 0) -> Observable: + def capture_video_as_observable(self, fps: int = 0) -> Observable: # type: ignore[type-arg] """Creates an observable from the RTSP stream using ffmpeg. The observable attempts to reconnect if the stream drops. @@ -167,9 +167,9 @@ def capture_video_as_observable(self, fps: int = 0) -> Observable: f"({self.dev_name}) The 'fps' argument ({fps}) is currently ignored. Using stream native FPS." ) - def emit_frames(observer, scheduler): + def emit_frames(observer, scheduler): # type: ignore[no-untyped-def] """Function executed by rx.create to emit frames.""" - process: subprocess.Popen | None = None + process: subprocess.Popen | None = None # type: ignore[type-arg] # Event to signal the processing loop should stop (e.g., on dispose) should_stop = threading.Event() @@ -198,7 +198,7 @@ def cleanup_process() -> None: # Ensure we clear the process variable even if wait/kill fails process = None # Also clear the shared class attribute if this was the active process - if self._ffmpeg_process and self._ffmpeg_process.pid == process.pid: + if self._ffmpeg_process and self._ffmpeg_process.pid == process.pid: # type: ignore[attr-defined] self._ffmpeg_process = None elif process and process.poll() is not None: # Process exists but already terminated @@ -207,7 +207,7 @@ def cleanup_process() -> None: ) process = None # Clear the variable # Clear shared attribute if it matches - if self._ffmpeg_process and self._ffmpeg_process.pid == process.pid: + if self._ffmpeg_process and self._ffmpeg_process.pid == process.pid: # type: ignore[attr-defined] self._ffmpeg_process = None else: # Process variable is already None or doesn't match _ffmpeg_process @@ -243,7 +243,7 @@ def cleanup_process() -> None: # 3. Frame reading loop while not should_stop.is_set(): # Read exactly one frame's worth of bytes - in_bytes = process.stdout.read(frame_size) + in_bytes = process.stdout.read(frame_size) # type: ignore[union-attr] if len(in_bytes) == 0: # End of stream or process terminated unexpectedly @@ -251,7 +251,7 @@ def cleanup_process() -> None: f"({self.dev_name}) ffmpeg stdout returned 0 bytes. EOF or process terminated." ) process.wait(timeout=0.5) # Allow stderr to flush - stderr_data = process.stderr.read().decode("utf8", errors="ignore") + stderr_data = process.stderr.read().decode("utf8", errors="ignore") # type: ignore[union-attr] exit_code = process.poll() logger.warning( f"({self.dev_name}) ffmpeg process (PID: {process.pid}) exited with code {exit_code}. Stderr: {stderr_data}" diff --git a/dimos/stream/video_operators.py b/dimos/stream/video_operators.py index d7299f3dce..221cf04ae9 100644 --- a/dimos/stream/video_operators.py +++ b/dimos/stream/video_operators.py @@ -33,7 +33,7 @@ class VideoOperators: @staticmethod def with_fps_sampling( fps: int = 25, *, sample_interval: timedelta | None = None, use_latest: bool = True - ) -> Callable[[Observable], Observable]: + ) -> Callable[[Observable], Observable]: # type: ignore[type-arg] """Creates an operator that samples frames at a specified rate. Creates a transformation operator that samples frames either by taking @@ -95,7 +95,7 @@ def with_fps_sampling( raise ValueError("FPS must be positive") sample_interval = timedelta(microseconds=int(1_000_000 / fps)) - def _operator(source: Observable) -> Observable: + def _operator(source: Observable) -> Observable: # type: ignore[type-arg] return source.pipe( ops.sample(sample_interval) if use_latest else ops.throttle_first(sample_interval) ) @@ -108,7 +108,7 @@ def with_jpeg_export( save_limit: int = 100, suffix: str = "", loop: bool = False, - ) -> Callable[[Observable], Observable]: + ) -> Callable[[Observable], Observable]: # type: ignore[type-arg] """Creates an operator that saves video frames as JPEG files. Creates a transformation operator that saves each frame from the video @@ -139,7 +139,7 @@ def with_jpeg_export( ... ) """ - def _operator(source: Observable) -> Observable: + def _operator(source: Observable) -> Observable: # type: ignore[type-arg] return source.pipe( ops.map( lambda frame: frame_processor.export_to_jpeg(frame, save_limit, loop, suffix) @@ -149,7 +149,7 @@ def _operator(source: Observable) -> Observable: return _operator @staticmethod - def with_optical_flow_filtering(threshold: float = 1.0) -> Callable[[Observable], Observable]: + def with_optical_flow_filtering(threshold: float = 1.0) -> Callable[[Observable], Observable]: # type: ignore[type-arg] """Creates an operator that filters optical flow frames by relevancy score. Filters a stream of optical flow results (frame, relevancy_score) tuples, @@ -184,40 +184,40 @@ def with_optical_flow_filtering(threshold: float = 1.0) -> Callable[[Observable] None scores are filtered out. """ return lambda source: source.pipe( - ops.filter(lambda result: result[1] is not None), - ops.filter(lambda result: result[1] > threshold), - ops.map(lambda result: result[0]), + ops.filter(lambda result: result[1] is not None), # type: ignore[index] + ops.filter(lambda result: result[1] > threshold), # type: ignore[index] + ops.map(lambda result: result[0]), # type: ignore[index] ) @staticmethod def with_edge_detection( frame_processor: "FrameProcessor", - ) -> Callable[[Observable], Observable]: + ) -> Callable[[Observable], Observable]: # type: ignore[type-arg] return lambda source: source.pipe( - ops.map(lambda frame: frame_processor.edge_detection(frame)) + ops.map(lambda frame: frame_processor.edge_detection(frame)) # type: ignore[no-untyped-call] ) @staticmethod def with_optical_flow( frame_processor: "FrameProcessor", - ) -> Callable[[Observable], Observable]: + ) -> Callable[[Observable], Observable]: # type: ignore[type-arg] return lambda source: source.pipe( ops.scan( - lambda acc, frame: frame_processor.compute_optical_flow( - acc, frame, compute_relevancy=False + lambda acc, frame: frame_processor.compute_optical_flow( # type: ignore[arg-type, return-value] + acc, frame, compute_relevancy=False # type: ignore[arg-type] ), (None, None, None), ), - ops.map(lambda result: result[1]), # Extract flow component + ops.map(lambda result: result[1]), # type: ignore[index] # Extract flow component ops.filter(lambda flow: flow is not None), ops.map(frame_processor.visualize_flow), ) @staticmethod def with_zmq_socket( - socket: zmq.Socket, scheduler: Any | None = None - ) -> Callable[[Observable], Observable]: - def send_frame(frame, socket) -> None: + socket: zmq.Socket, scheduler: Any | None = None # type: ignore[type-arg] + ) -> Callable[[Observable], Observable]: # type: ignore[type-arg] + def send_frame(frame, socket) -> None: # type: ignore[no-untyped-def] _, img_encoded = cv2.imencode(".jpg", frame) socket.send(img_encoded.tobytes()) # print(f"Frame received: {frame.shape}") @@ -234,7 +234,7 @@ def send_frame(frame, socket) -> None: ) @staticmethod - def encode_image() -> Callable[[Observable], Observable]: + def encode_image() -> Callable[[Observable], Observable]: # type: ignore[type-arg] """ Operator to encode an image to JPEG format and convert it to a Base64 string. @@ -243,8 +243,8 @@ def encode_image() -> Callable[[Observable], Observable]: of tuples containing the Base64 string of the encoded image and its dimensions. """ - def _operator(source: Observable) -> Observable: - def _encode_image(image: np.ndarray) -> tuple[str, tuple[int, int]]: + def _operator(source: Observable) -> Observable: # type: ignore[type-arg] + def _encode_image(image: np.ndarray) -> tuple[str, tuple[int, int]]: # type: ignore[type-arg] try: width, height = image.shape[:2] _, buffer = cv2.imencode(".jpg", image) @@ -268,15 +268,15 @@ def _encode_image(image: np.ndarray) -> tuple[str, tuple[int, int]]: class Operators: @staticmethod - def exhaust_lock(process_item): + def exhaust_lock(process_item): # type: ignore[no-untyped-def] """ For each incoming item, call `process_item(item)` to get an Observable. - If we're busy processing the previous one, skip new items. - Use a lock to ensure concurrency safety across threads. """ - def _exhaust_lock(source: Observable) -> Observable: - def _subscribe(observer, scheduler=None): + def _exhaust_lock(source: Observable) -> Observable: # type: ignore[type-arg] + def _subscribe(observer, scheduler=None): # type: ignore[no-untyped-def] in_flight = False lock = Lock() upstream_done = False @@ -290,7 +290,7 @@ def dispose_all() -> None: if active_inner_disp: active_inner_disp.dispose() - def on_next(value) -> None: + def on_next(value) -> None: # type: ignore[no-untyped-def] nonlocal in_flight, active_inner_disp lock.acquire() try: @@ -310,10 +310,10 @@ def on_next(value) -> None: observer.on_error(ex) return - def inner_on_next(ivalue) -> None: + def inner_on_next(ivalue) -> None: # type: ignore[no-untyped-def] observer.on_next(ivalue) - def inner_on_error(err) -> None: + def inner_on_error(err) -> None: # type: ignore[no-untyped-def] nonlocal in_flight with lock: in_flight = False @@ -335,7 +335,7 @@ def inner_on_completed() -> None: scheduler=scheduler, ) - def on_error(err) -> None: + def on_error(err) -> None: # type: ignore[no-untyped-def] dispose_all() observer.on_error(err) @@ -357,15 +357,15 @@ def on_completed() -> None: return _exhaust_lock @staticmethod - def exhaust_lock_per_instance(process_item, lock: Lock): + def exhaust_lock_per_instance(process_item, lock: Lock): # type: ignore[no-untyped-def] """ - For each item from upstream, call process_item(item) -> Observable. - If a frame arrives while one is "in flight", discard it. - 'lock' ensures we safely check/modify the 'in_flight' state in a multithreaded environment. """ - def _exhaust_lock(source: Observable) -> Observable: - def _subscribe(observer, scheduler=None): + def _exhaust_lock(source: Observable) -> Observable: # type: ignore[type-arg] + def _subscribe(observer, scheduler=None): # type: ignore[no-untyped-def] in_flight = False upstream_done = False @@ -378,7 +378,7 @@ def dispose_all() -> None: if active_inner_disp: active_inner_disp.dispose() - def on_next(value) -> None: + def on_next(value) -> None: # type: ignore[no-untyped-def] nonlocal in_flight, active_inner_disp with lock: # If not busy, claim the slot @@ -397,10 +397,10 @@ def on_next(value) -> None: observer.on_error(ex) return - def inner_on_next(ivalue) -> None: + def inner_on_next(ivalue) -> None: # type: ignore[no-untyped-def] observer.on_next(ivalue) - def inner_on_error(err) -> None: + def inner_on_error(err) -> None: # type: ignore[no-untyped-def] nonlocal in_flight with lock: in_flight = False @@ -424,7 +424,7 @@ def inner_on_completed() -> None: scheduler=scheduler, ) - def on_error(e) -> None: + def on_error(e) -> None: # type: ignore[no-untyped-def] dispose_all() observer.on_error(e) @@ -450,12 +450,12 @@ def on_completed() -> None: return _exhaust_lock @staticmethod - def exhaust_map(project): - def _exhaust_map(source: Observable): - def subscribe(observer, scheduler=None): + def exhaust_map(project): # type: ignore[no-untyped-def] + def _exhaust_map(source: Observable): # type: ignore[no-untyped-def, type-arg] + def subscribe(observer, scheduler=None): # type: ignore[no-untyped-def] is_processing = False - def on_next(item) -> None: + def on_next(item) -> None: # type: ignore[no-untyped-def] nonlocal is_processing if not is_processing: is_processing = True @@ -490,10 +490,10 @@ def set_not_processing() -> None: return _exhaust_map @staticmethod - def with_lock(lock: Lock): - def operator(source: Observable): - def subscribe(observer, scheduler=None): - def on_next(item) -> None: + def with_lock(lock: Lock): # type: ignore[no-untyped-def] + def operator(source: Observable): # type: ignore[no-untyped-def, type-arg] + def subscribe(observer, scheduler=None): # type: ignore[no-untyped-def] + def on_next(item) -> None: # type: ignore[no-untyped-def] if not lock.locked(): # Check if the lock is free if lock.acquire(blocking=False): # Non-blocking acquire try: @@ -506,7 +506,7 @@ def on_next(item) -> None: else: print("\033[34mLock busy, skipping item.\033[0m") - def on_error(error) -> None: + def on_error(error) -> None: # type: ignore[no-untyped-def] observer.on_error(error) def on_completed() -> None: @@ -524,10 +524,10 @@ def on_completed() -> None: return operator @staticmethod - def with_lock_check(lock: Lock): # Renamed for clarity - def operator(source: Observable): - def subscribe(observer, scheduler=None): - def on_next(item) -> None: + def with_lock_check(lock: Lock): # type: ignore[no-untyped-def] # Renamed for clarity + def operator(source: Observable): # type: ignore[no-untyped-def, type-arg] + def subscribe(observer, scheduler=None): # type: ignore[no-untyped-def] + def on_next(item) -> None: # type: ignore[no-untyped-def] if not lock.locked(): # Check if the lock is held WITHOUT acquiring print(f"\033[32mLock is free, processing item: {item}\033[0m") observer.on_next(item) @@ -535,7 +535,7 @@ def on_next(item) -> None: print(f"\033[34mLock is busy, skipping item: {item}\033[0m") # observer.on_completed() - def on_error(error) -> None: + def on_error(error) -> None: # type: ignore[no-untyped-def] observer.on_error(error) def on_completed() -> None: @@ -564,11 +564,11 @@ class PrintColor(Enum): RESET = "\033[0m" @staticmethod - def print_emission( + def print_emission( # type: ignore[no-untyped-def] id: str, dev_name: str = "NA", - counts: dict | None = None, - color: "Operators.PrintColor" = None, + counts: dict | None = None, # type: ignore[type-arg] + color: "Operators.PrintColor" = None, # type: ignore[assignment] enabled: bool = True, ): """ @@ -591,9 +591,9 @@ def print_emission( if color is None: color = Operators.PrintColor.RED - def _operator(source: Observable) -> Observable: - def _subscribe(observer: Observer, scheduler=None): - def on_next(value) -> None: + def _operator(source: Observable) -> Observable: # type: ignore[type-arg] + def _subscribe(observer: Observer, scheduler=None): # type: ignore[no-untyped-def, type-arg] + def on_next(value) -> None: # type: ignore[no-untyped-def] if counts is not None: # Initialize count if necessary if id not in counts: @@ -619,6 +619,6 @@ def on_next(value) -> None: scheduler=scheduler, ) - return create(_subscribe) + return create(_subscribe) # type: ignore[arg-type] return _operator diff --git a/dimos/stream/video_provider.py b/dimos/stream/video_provider.py index 0b7e815ae2..408c9a7947 100644 --- a/dimos/stream/video_provider.py +++ b/dimos/stream/video_provider.py @@ -73,7 +73,7 @@ def __init__( self.disposables = CompositeDisposable() @abstractmethod - def capture_video_as_observable(self, fps: int = 30) -> Observable: + def capture_video_as_observable(self, fps: int = 30) -> Observable: # type: ignore[type-arg] """Create an observable from video capture. Args: @@ -135,7 +135,7 @@ def _initialize_capture(self) -> None: logger.info("Released previous capture") # Attempt to open new capture - self.cap = cv2.VideoCapture(self.video_source) + self.cap = cv2.VideoCapture(self.video_source) # type: ignore[assignment] if self.cap is None or not self.cap.isOpened(): error_msg = f"Failed to open video source: {self.video_source}" logger.error(error_msg) @@ -143,7 +143,7 @@ def _initialize_capture(self) -> None: logger.info(f"Opened new capture: {self.video_source}") - def capture_video_as_observable(self, realtime: bool = True, fps: int = 30) -> Observable: + def capture_video_as_observable(self, realtime: bool = True, fps: int = 30) -> Observable: # type: ignore[override, type-arg] """Creates an observable from video capture. Creates an observable that emits frames at specified FPS or the video's @@ -162,14 +162,14 @@ def capture_video_as_observable(self, realtime: bool = True, fps: int = 30) -> O VideoFrameError: If frames cannot be read properly. """ - def emit_frames(observer, scheduler) -> None: + def emit_frames(observer, scheduler) -> None: # type: ignore[no-untyped-def] try: self._initialize_capture() # Determine the FPS to use based on configuration and availability local_fps: float = fps if realtime: - native_fps: float = self.cap.get(cv2.CAP_PROP_FPS) + native_fps: float = self.cap.get(cv2.CAP_PROP_FPS) # type: ignore[attr-defined] if native_fps > 0: local_fps = native_fps else: @@ -178,16 +178,16 @@ def emit_frames(observer, scheduler) -> None: frame_interval: float = 1.0 / local_fps frame_time: float = time.monotonic() - while self.cap.isOpened(): + while self.cap.isOpened(): # type: ignore[attr-defined] # Thread-safe access to video capture with self.lock: - ret, frame = self.cap.read() + ret, frame = self.cap.read() # type: ignore[attr-defined] if not ret: # Loop video when we reach the end logger.warning("End of video reached, restarting playback") with self.lock: - self.cap.set(cv2.CAP_PROP_POS_FRAMES, 0) + self.cap.set(cv2.CAP_PROP_POS_FRAMES, 0) # type: ignore[attr-defined] continue # Control frame rate to match target FPS @@ -215,7 +215,7 @@ def emit_frames(observer, scheduler) -> None: logger.info("Capture released") observer.on_completed() - return rx.create(emit_frames).pipe( + return rx.create(emit_frames).pipe( # type: ignore[arg-type] ops.subscribe_on(self.pool_scheduler), ops.observe_on(self.pool_scheduler), ops.share(), # Share the stream among multiple subscribers diff --git a/dimos/types/manipulation.py b/dimos/types/manipulation.py index 0df62362a4..7693b2d01e 100644 --- a/dimos/types/manipulation.py +++ b/dimos/types/manipulation.py @@ -24,7 +24,7 @@ from dimos.types.vector import Vector if TYPE_CHECKING: - import open3d as o3d + import open3d as o3d # type: ignore[import-untyped] class ConstraintType(Enum): @@ -47,7 +47,7 @@ class AbstractConstraint(ABC): class TranslationConstraint(AbstractConstraint): """Constraint parameters for translational movement along a single axis.""" - translation_axis: Literal["x", "y", "z"] = None # Axis to translate along + translation_axis: Literal["x", "y", "z"] = None # type: ignore[assignment] # Axis to translate along reference_point: Vector | None = None bounds_min: Vector | None = None # For bounded translation bounds_max: Vector | None = None # For bounded translation @@ -58,7 +58,7 @@ class TranslationConstraint(AbstractConstraint): class RotationConstraint(AbstractConstraint): """Constraint parameters for rotational movement around a single axis.""" - rotation_axis: Literal["roll", "pitch", "yaw"] = None # Axis to rotate around + rotation_axis: Literal["roll", "pitch", "yaw"] = None # type: ignore[assignment] # Axis to rotate around start_angle: Vector | None = None # Angle values applied to the specified rotation axis end_angle: Vector | None = None # Angle values applied to the specified rotation axis pivot_point: Vector | None = None # Point of rotation @@ -85,7 +85,7 @@ class ObjectData(TypedDict, total=False): class_id: int # Class ID from the detector label: str # Semantic label (e.g., 'cup', 'table') movement_tolerance: float # (0.0 = immovable, 1.0 = freely movable) - segmentation_mask: np.ndarray # Binary mask of the object's pixels + segmentation_mask: np.ndarray # type: ignore[type-arg] # Binary mask of the object's pixels # 3D pose and dimensions position: dict[str, float] | Vector # 3D position {x, y, z} or Vector @@ -94,8 +94,8 @@ class ObjectData(TypedDict, total=False): # Point cloud data point_cloud: "o3d.geometry.PointCloud" # Open3D point cloud object - point_cloud_numpy: np.ndarray # Nx6 array of XYZRGB points - color: np.ndarray # RGB color for visualization [R, G, B] + point_cloud_numpy: np.ndarray # type: ignore[type-arg] # Nx6 array of XYZRGB points + color: np.ndarray # type: ignore[type-arg] # RGB color for visualization [R, G, B] class ManipulationMetadata(TypedDict, total=False): @@ -130,7 +130,7 @@ class ManipulationTask: target_point: tuple[float, float] | None = ( None # (X,Y) point in pixel-space of the point to manipulate on target object ) - metadata: ManipulationMetadata = field(default_factory=dict) + metadata: ManipulationMetadata = field(default_factory=dict) # type: ignore[assignment] timestamp: float = field(default_factory=time.time) task_id: str = "" result: dict[str, Any] | None = None # Any result data from the task execution diff --git a/dimos/types/robot_location.py b/dimos/types/robot_location.py index 59a780daf5..cce37af61e 100644 --- a/dimos/types/robot_location.py +++ b/dimos/types/robot_location.py @@ -54,13 +54,13 @@ def __post_init__(self) -> None: if len(self.position) == 2: self.position = (self.position[0], self.position[1], 0.0) else: - self.position = tuple(float(x) for x in self.position) + self.position = tuple(float(x) for x in self.position) # type: ignore[assignment] # Ensure rotation is a tuple of 3 floats if len(self.rotation) == 1: self.rotation = (0.0, 0.0, self.rotation[0]) else: - self.rotation = tuple(float(x) for x in self.rotation) + self.rotation = tuple(float(x) for x in self.rotation) # type: ignore[assignment] def to_vector_metadata(self) -> dict[str, Any]: """ diff --git a/dimos/types/ros_polyfill.py b/dimos/types/ros_polyfill.py index c8919caec3..747a32dc5d 100644 --- a/dimos/types/ros_polyfill.py +++ b/dimos/types/ros_polyfill.py @@ -13,18 +13,18 @@ # limitations under the License. try: - from geometry_msgs.msg import Vector3 + from geometry_msgs.msg import Vector3 # type: ignore[attr-defined] except ImportError: - from dimos.msgs.geometry_msgs import Vector3 # type: ignore[import] + from dimos.msgs.geometry_msgs import Vector3 try: - from geometry_msgs.msg import Point, Pose, Quaternion, Twist - from nav_msgs.msg import OccupancyGrid, Odometry - from std_msgs.msg import Header + from geometry_msgs.msg import Point, Pose, Quaternion, Twist # type: ignore[attr-defined] + from nav_msgs.msg import OccupancyGrid, Odometry # type: ignore[attr-defined] + from std_msgs.msg import Header # type: ignore[attr-defined] except ImportError: - from dimos_lcm.geometry_msgs import Point, Pose, Quaternion, Twist - from dimos_lcm.nav_msgs import OccupancyGrid, Odometry - from dimos_lcm.std_msgs import Header + from dimos_lcm.geometry_msgs import Point, Pose, Quaternion, Twist # type: ignore[import-untyped, no-redef] + from dimos_lcm.nav_msgs import OccupancyGrid, Odometry # type: ignore[import-untyped, no-redef] + from dimos_lcm.std_msgs import Header # type: ignore[import-untyped, no-redef] __all__ = [ "Header", diff --git a/dimos/types/sample.py b/dimos/types/sample.py index fdb29cf174..0ad38e6192 100644 --- a/dimos/types/sample.py +++ b/dimos/types/sample.py @@ -21,11 +21,11 @@ from pathlib import Path from typing import Annotated, Any, Literal, Union, get_origin -from datasets import Dataset -from gymnasium import spaces -from jsonref import replace_refs -from mbodied.data.utils import to_features -from mbodied.utils.import_utils import smart_import +from datasets import Dataset # type: ignore[import-not-found] +from gymnasium import spaces # type: ignore[import-not-found] +from jsonref import replace_refs # type: ignore[import-not-found] +from mbodied.data.utils import to_features # type: ignore[import-not-found] +from mbodied.utils.import_utils import smart_import # type: ignore[import-not-found] import numpy as np from pydantic import BaseModel, ConfigDict, ValidationError from pydantic.fields import FieldInfo @@ -74,7 +74,7 @@ class Sample(BaseModel): __doc__ = "A base model class for serializing, recording, and manipulating arbitray data." - model_config: ConfigDict = ConfigDict( + model_config: ConfigDict = ConfigDict( # type: ignore[misc] use_enum_values=False, from_attributes=True, validate_assignment=False, @@ -82,7 +82,7 @@ class Sample(BaseModel): arbitrary_types_allowed=True, ) - def __init__(self, datum=None, **data) -> None: + def __init__(self, datum=None, **data) -> None: # type: ignore[no-untyped-def] """Accepts an arbitrary datum as well as keyword arguments.""" if datum is not None: if isinstance(datum, Sample): @@ -101,7 +101,7 @@ def __str__(self) -> str: """Return a string representation of the Sample instance.""" return f"{self.__class__.__name__}({', '.join([f'{k}={v}' for k, v in self.dict().items() if v is not None])})" - def dict(self, exclude_none: bool = True, exclude: set[str] | None = None) -> dict[str, Any]: + def dict(self, exclude_none: bool = True, exclude: set[str] | None = None) -> dict[str, Any]: # type: ignore[override] """Return the Sample object as a dictionary with None values excluded. Args: @@ -114,7 +114,7 @@ def dict(self, exclude_none: bool = True, exclude: set[str] | None = None) -> di return self.model_dump(exclude_none=exclude_none, exclude=exclude) @classmethod - def unflatten(cls, one_d_array_or_dict, schema=None) -> "Sample": + def unflatten(cls, one_d_array_or_dict, schema=None) -> "Sample": # type: ignore[no-untyped-def] """Unflatten a one-dimensional array or dictionary into a Sample instance. If a dictionary is provided, its keys are ignored. @@ -143,7 +143,7 @@ def unflatten(cls, one_d_array_or_dict, schema=None) -> "Sample": else: flat_data = list(one_d_array_or_dict) - def unflatten_recursive(schema_part, index: int = 0): + def unflatten_recursive(schema_part, index: int = 0): # type: ignore[no-untyped-def] if schema_part["type"] == "object": result = {} for prop, prop_schema in schema_part["properties"].items(): @@ -166,10 +166,10 @@ def flatten( self, output_type: Flattenable = "dict", non_numerical: Literal["ignore", "forbid", "allow"] = "allow", - ) -> builtins.dict[str, Any] | np.ndarray | torch.Tensor | list: - accumulator = {} if output_type == "dict" else [] + ) -> builtins.dict[str, Any] | np.ndarray | torch.Tensor | list: # type: ignore[type-arg] + accumulator = {} if output_type == "dict" else [] # type: ignore[var-annotated] - def flatten_recursive(obj, path: str = "") -> None: + def flatten_recursive(obj, path: str = "") -> None: # type: ignore[no-untyped-def] if isinstance(obj, Sample): for k, v in obj.dict().items(): flatten_recursive(v, path + k + "/") @@ -183,20 +183,20 @@ def flatten_recursive(obj, path: str = "") -> None: flat_list = obj.flatten().tolist() if output_type == "dict": # Convert to list for dict storage - accumulator[path[:-1]] = flat_list + accumulator[path[:-1]] = flat_list # type: ignore[index] else: - accumulator.extend(flat_list) + accumulator.extend(flat_list) # type: ignore[attr-defined] else: if non_numerical == "ignore" and not isinstance(obj, int | float | bool): return final_key = path[:-1] # Remove trailing slash if output_type == "dict": - accumulator[final_key] = obj + accumulator[final_key] = obj # type: ignore[index] else: - accumulator.append(obj) + accumulator.append(obj) # type: ignore[attr-defined] flatten_recursive(self) - accumulator = accumulator.values() if output_type == "dict" else accumulator + accumulator = accumulator.values() if output_type == "dict" else accumulator # type: ignore[attr-defined] if non_numerical == "forbid" and any( not isinstance(v, int | float | bool) for v in accumulator ): @@ -205,11 +205,11 @@ def flatten_recursive(obj, path: str = "") -> None: return np.array(accumulator) if output_type == "pt": torch = smart_import("torch") - return torch.tensor(accumulator) - return accumulator + return torch.tensor(accumulator) # type: ignore[no-any-return] + return accumulator # type: ignore[return-value] @staticmethod - def obj_to_schema(value: Any) -> builtins.dict: + def obj_to_schema(value: Any) -> builtins.dict: # type: ignore[type-arg] """Generates a simplified JSON schema from a dictionary. Args: @@ -238,8 +238,8 @@ def obj_to_schema(value: Any) -> builtins.dict: return {} def schema( - self, resolve_refs: bool = True, include_descriptions: bool = False - ) -> builtins.dict: + self, resolve_refs: bool = True, include_descriptions: bool = False # type: ignore[override] + ) -> builtins.dict: # type: ignore[type-arg] """Returns a simplified json schema. Removing additionalProperties, @@ -315,8 +315,8 @@ def to(self, container: Any) -> Any: Returns: Any: The converted container. """ - if isinstance(container, Sample) and not issubclass(container, Sample): - return container(**self.dict()) + if isinstance(container, Sample) and not issubclass(container, Sample): # type: ignore[arg-type] + return container(**self.dict()) # type: ignore[operator] if isinstance(container, type) and issubclass(container, Sample): return container.unflatten(self.flatten()) @@ -354,7 +354,7 @@ def space_for( cls, value: Any, max_text_length: int = 1000, - info: Annotated = None, + info: Annotated = None, # type: ignore[valid-type] ) -> spaces.Space: """Default Gym space generation for a given value. @@ -412,7 +412,7 @@ def space_for( def init_from(cls, d: Any, pack: bool = False) -> "Sample": if isinstance(d, spaces.Space): return cls.from_space(d) - if isinstance(d, Union[Sequence, np.ndarray]): + if isinstance(d, Union[Sequence, np.ndarray]): # type: ignore[arg-type] if pack: return cls.pack_from(d) return cls.unflatten(d) @@ -431,7 +431,7 @@ def init_from(cls, d: Any, pack: bool = False) -> "Sample": @classmethod def from_flat_dict( - cls, flat_dict: builtins.dict[str, Any], schema: builtins.dict | None = None + cls, flat_dict: builtins.dict[str, Any], schema: builtins.dict | None = None # type: ignore[type-arg] ) -> "Sample": """Initialize a Sample instance from a flattened dictionary.""" """ @@ -445,7 +445,7 @@ def from_flat_dict( dict: The reconstructed JSON object. """ schema = schema or replace_refs(cls.model_json_schema()) - reconstructed = {} + reconstructed = {} # type: ignore[var-annotated] for flat_key, value in flat_dict.items(): keys = flat_key.split(".") @@ -456,7 +456,7 @@ def from_flat_dict( current = current[key] current[keys[-1]] = value - return reconstructed + return reconstructed # type: ignore[return-value] @classmethod def from_space(cls, space: spaces.Space) -> "Sample": @@ -467,11 +467,11 @@ def from_space(cls, space: spaces.Space) -> "Sample": if hasattr(sampled, "__len__") and not isinstance(sampled, str): sampled = np.asarray(sampled) if len(sampled.shape) > 0 and isinstance(sampled[0], dict | Sample): - return cls.pack_from(sampled) + return cls.pack_from(sampled) # type: ignore[arg-type] return cls(sampled) @classmethod - def pack_from(cls, samples: list[Union["Sample", builtins.dict]]) -> "Sample": + def pack_from(cls, samples: list[Union["Sample", builtins.dict]]) -> "Sample": # type: ignore[type-arg] """Pack a list of samples into a single sample with lists for attributes. Args: @@ -491,7 +491,7 @@ def pack_from(cls, samples: list[Union["Sample", builtins.dict]]) -> "Sample": else: attributes = ["item" + str(i) for i in range(len(samples))] - aggregated = {attr: [] for attr in attributes} + aggregated = {attr: [] for attr in attributes} # type: ignore[var-annotated] for sample in samples: for attr in attributes: # Handle both Sample instances and dictionaries @@ -501,9 +501,9 @@ def pack_from(cls, samples: list[Union["Sample", builtins.dict]]) -> "Sample": aggregated[attr].append(getattr(sample, attr, None)) return cls(**aggregated) - def unpack(self, to_dicts: bool = False) -> list[Union["Sample", builtins.dict]]: + def unpack(self, to_dicts: bool = False) -> list[Union["Sample", builtins.dict]]: # type: ignore[type-arg] """Unpack the packed Sample object into a list of Sample objects or dictionaries.""" - attributes = list(self.model_extra.keys()) + list(self.model_fields.keys()) + attributes = list(self.model_extra.keys()) + list(self.model_fields.keys()) # type: ignore[union-attr] attributes = [attr for attr in attributes if getattr(self, attr) is not None] if not attributes or getattr(self, attributes[0]) is None: return [] @@ -544,13 +544,13 @@ def default_sample( def model_field_info(self, key: str) -> FieldInfo: """Get the FieldInfo for a given attribute key.""" if self.model_extra and self.model_extra.get(key) is not None: - info = FieldInfo(metadata=self.model_extra[key]) + info = FieldInfo(metadata=self.model_extra[key]) # type: ignore[call-arg] if self.model_fields.get(key) is not None: - info = FieldInfo(metadata=self.model_fields[key]) + info = FieldInfo(metadata=self.model_fields[key]) # type: ignore[call-arg] if info and hasattr(info, "annotation"): - return info.annotation - return None + return info.annotation # type: ignore[return-value] + return None # type: ignore[return-value] def space(self) -> spaces.Dict: """Return the corresponding Gym space for the Sample instance based on its instance attributes. Omits None values. diff --git a/dimos/types/timestamped.py b/dimos/types/timestamped.py index 0045c73ef4..0d0db75749 100644 --- a/dimos/types/timestamped.py +++ b/dimos/types/timestamped.py @@ -16,13 +16,13 @@ from datetime import datetime, timezone from typing import Generic, TypeVar, Union -from dimos_lcm.builtin_interfaces import Time as ROSTime +from dimos_lcm.builtin_interfaces import Time as ROSTime # type: ignore[import-untyped] from reactivex import create from reactivex.disposable import CompositeDisposable # from dimos_lcm.std_msgs import Time as ROSTime from reactivex.observable import Observable -from sortedcontainers import SortedKeyList +from sortedcontainers import SortedKeyList # type: ignore[import-untyped] from dimos.types.weaklist import WeakList from dimos.utils.logging_config import setup_logger @@ -49,14 +49,14 @@ def to_timestamp(ts: TimeLike) -> float: if isinstance(ts, int | float): return float(ts) if isinstance(ts, dict) and "sec" in ts and "nanosec" in ts: - return ts["sec"] + ts["nanosec"] / 1e9 + return ts["sec"] + ts["nanosec"] / 1e9 # type: ignore[no-any-return] # Check for ROS Time-like objects by attributes if hasattr(ts, "sec") and (hasattr(ts, "nanosec") or hasattr(ts, "nsec")): # Handle both std_msgs.Time (nsec) and builtin_interfaces.Time (nanosec) if hasattr(ts, "nanosec"): - return ts.sec + ts.nanosec / 1e9 + return ts.sec + ts.nanosec / 1e9 # type: ignore[no-any-return] else: # has nsec - return ts.sec + ts.nsec / 1e9 + return ts.sec + ts.nsec / 1e9 # type: ignore[no-any-return] raise TypeError("unsupported timestamp type") @@ -78,7 +78,7 @@ def to_human_readable(ts: float) -> str: return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(ts)) -def to_datetime(ts: TimeLike, tz=None) -> datetime: +def to_datetime(ts: TimeLike, tz=None) -> datetime: # type: ignore[no-untyped-def] if isinstance(ts, datetime): if ts.tzinfo is None: # Assume UTC for naive datetime @@ -137,7 +137,7 @@ def find_closest(self, timestamp: float, tolerance: float | None = None) -> T | # Check exact match if idx < len(self._items) and self._items[idx].ts == timestamp: - return self._items[idx] + return self._items[idx] # type: ignore[no-any-return] # Find candidates: item before and after candidates = [] @@ -161,7 +161,7 @@ def find_closest(self, timestamp: float, tolerance: float | None = None) -> T | if tolerance is not None and closest_distance > tolerance: return None - return self._items[closest_idx] + return self._items[closest_idx] # type: ignore[no-any-return] def find_before(self, timestamp: float) -> T | None: """Find the last item before the given timestamp.""" @@ -183,7 +183,7 @@ def duration(self) -> float: """Get the duration of the collection in seconds.""" if len(self._items) < 2: return 0.0 - return self._items[-1].ts - self._items[0].ts + return self._items[-1].ts - self._items[0].ts # type: ignore[no-any-return] def time_range(self) -> tuple[float, float] | None: """Get the time range (start, end) of the collection.""" @@ -210,11 +210,11 @@ def end_ts(self) -> float | None: def __len__(self) -> int: return len(self._items) - def __iter__(self) -> Iterator: + def __iter__(self) -> Iterator: # type: ignore[type-arg] return iter(self._items) def __getitem__(self, idx: int) -> T: - return self._items[idx] + return self._items[idx] # type: ignore[no-any-return] PRIMARY = TypeVar("PRIMARY", bound=Timestamped) @@ -287,7 +287,7 @@ def is_complete(self) -> bool: def get_tuple(self) -> tuple[PRIMARY, ...]: """Get the result tuple for emission.""" - return (self.primary, *self.matches) + return (self.primary, *self.matches) # type: ignore[arg-type] def align_timestamped( @@ -311,14 +311,14 @@ def align_timestamped( secondary observable, or None if no match within tolerance. """ - def subscribe(observer, scheduler=None): + def subscribe(observer, scheduler=None): # type: ignore[no-untyped-def] # Create a timed buffer collection for each secondary observable secondary_collections: list[TimestampedBufferCollection[SECONDARY]] = [ TimestampedBufferCollection(buffer_size) for _ in secondary_observables ] # WeakLists to track subscribers to each secondary observable - secondary_stakeholders = defaultdict(WeakList) + secondary_stakeholders = defaultdict(WeakList) # type: ignore[var-annotated] # Buffer for unmatched MatchContainers - automatically expires old items primary_buffer: TimestampedBufferCollection[MatchContainer[PRIMARY, SECONDARY]] = ( @@ -332,7 +332,7 @@ def has_secondary_progressed_past(secondary_ts: float, primary_ts: float) -> boo """Check if secondary stream has progressed past the primary + tolerance.""" return secondary_ts > primary_ts + match_tolerance - def remove_stakeholder(stakeholder: MatchContainer) -> None: + def remove_stakeholder(stakeholder: MatchContainer) -> None: # type: ignore[type-arg] """Remove a stakeholder from all tracking structures.""" primary_buffer.remove(stakeholder) for weak_list in secondary_stakeholders.values(): @@ -365,7 +365,7 @@ def on_secondary(i: int, secondary_item: SECONDARY) -> None: for i, secondary_obs in enumerate(secondary_observables): secondary_subs.append( secondary_obs.subscribe( - lambda x, idx=i: on_secondary(idx, x), on_error=observer.on_error + lambda x, idx=i: on_secondary(idx, x), on_error=observer.on_error # type: ignore[misc] ) ) @@ -376,7 +376,7 @@ def on_primary(primary_item: PRIMARY) -> None: for i, collection in enumerate(secondary_collections): closest = collection.find_closest(primary_item.ts, tolerance=match_tolerance) if closest is not None: - matches[i] = closest + matches[i] = closest # type: ignore[call-overload] else: # Check if this secondary stream has already progressed past this primary if collection.end_ts is not None and has_secondary_progressed_past( @@ -392,8 +392,8 @@ def on_primary(primary_item: PRIMARY) -> None: observer.on_next(result) else: logger.debug(f"Deferred match attempt {primary_item.ts}") - match_container = MatchContainer(primary_item, matches) - primary_buffer.add(match_container) + match_container = MatchContainer(primary_item, matches) # type: ignore[type-var] + primary_buffer.add(match_container) # type: ignore[arg-type] for i, match in enumerate(matches): if match is None: diff --git a/dimos/types/vector.py b/dimos/types/vector.py index 161084fc2c..4cdf429ffa 100644 --- a/dimos/types/vector.py +++ b/dimos/types/vector.py @@ -23,7 +23,7 @@ T = TypeVar("T", bound="Vector") # Vector-like types that can be converted to/from Vector -VectorLike = Union[Sequence[int | float], Vector3, "Vector", np.ndarray] +VectorLike = Union[Sequence[int | float], Vector3, "Vector", np.ndarray] # type: ignore[type-arg] class Vector: @@ -42,7 +42,7 @@ def __init__(self, *args: VectorLike) -> None: self._data = np.array(args[0], dtype=float) elif len(args) == 1: - self._data = np.array([args[0].x, args[0].y, args[0].z], dtype=float) + self._data = np.array([args[0].x, args[0].y, args[0].z], dtype=float) # type: ignore[union-attr] else: self._data = np.array(args, dtype=float) @@ -77,11 +77,11 @@ def dim(self) -> int: return len(self._data) @property - def data(self) -> np.ndarray: + def data(self) -> np.ndarray: # type: ignore[type-arg] """Get the underlying numpy array.""" return self._data - def __getitem__(self, idx: int): + def __getitem__(self, idx: int): # type: ignore[no-untyped-def] return self._data[idx] def __repr__(self) -> str: @@ -91,7 +91,7 @@ def __str__(self) -> str: if self.dim < 2: return self.__repr__() - def getArrow(): + def getArrow(): # type: ignore[no-untyped-def] repr = ["←", "↖", "↑", "↗", "→", "↘", "↓", "↙"] if self.x == 0 and self.y == 0: @@ -104,13 +104,13 @@ def getArrow(): # Get directional arrow symbol return repr[dir_index] - return f"{getArrow()} Vector {self.__repr__()}" + return f"{getArrow()} Vector {self.__repr__()}" # type: ignore[no-untyped-call] - def serialize(self) -> builtins.tuple: + def serialize(self) -> builtins.tuple: # type: ignore[type-arg] """Serialize the vector to a tuple.""" - return {"type": "vector", "c": self._data.tolist()} + return {"type": "vector", "c": self._data.tolist()} # type: ignore[return-value] - def __eq__(self, other) -> bool: + def __eq__(self, other) -> bool: # type: ignore[no-untyped-def] """Check if two vectors are equal using numpy's allclose for floating point comparison.""" if not isinstance(other, Vector): return False @@ -229,7 +229,7 @@ def project(self: T, onto: VectorLike) -> T: # this is here to test ros_observable_topic # doesn't happen irl afaik that we want a vector from ros message @classmethod - def from_msg(cls: type[T], msg) -> T: + def from_msg(cls: type[T], msg) -> T: # type: ignore[no-untyped-def] return cls(*msg) @classmethod @@ -266,13 +266,13 @@ def unit_z(cls: type[T], dim: int = 3) -> T: def to_list(self) -> list[float]: """Convert the vector to a list.""" - return self._data.tolist() + return self._data.tolist() # type: ignore[no-any-return] def to_tuple(self) -> builtins.tuple[float, ...]: """Convert the vector to a tuple.""" return tuple(self._data) - def to_numpy(self) -> np.ndarray: + def to_numpy(self) -> np.ndarray: # type: ignore[type-arg] """Convert the vector to a numpy array.""" return self._data @@ -296,7 +296,7 @@ def __bool__(self) -> bool: return not self.is_zero() -def to_numpy(value: VectorLike) -> np.ndarray: +def to_numpy(value: VectorLike) -> np.ndarray: # type: ignore[type-arg] """Convert a vector-compatible value to a numpy array. Args: @@ -361,13 +361,13 @@ def to_list(value: VectorLike) -> list[float]: List of floats """ if isinstance(value, Vector): - return value.data.tolist() + return value.data.tolist() # type: ignore[no-any-return] elif isinstance(value, np.ndarray): - return value.tolist() + return value.tolist() # type: ignore[no-any-return] elif isinstance(value, list): return value else: - return list(value) + return list(value) # type: ignore[arg-type] # Helper functions to check dimensionality @@ -383,7 +383,7 @@ def is_2d(value: VectorLike) -> bool: if isinstance(value, Vector3): return False elif isinstance(value, Vector): - return len(value) == 2 + return len(value) == 2 # type: ignore[arg-type] elif isinstance(value, np.ndarray): return value.shape[-1] == 2 or value.size == 2 else: @@ -400,7 +400,7 @@ def is_3d(value: VectorLike) -> bool: True if the value is 3D """ if isinstance(value, Vector): - return len(value) == 3 + return len(value) == 3 # type: ignore[arg-type] elif isinstance(value, Vector3): return True elif isinstance(value, np.ndarray): @@ -422,7 +422,7 @@ def x(value: VectorLike) -> float: if isinstance(value, Vector): return value.x elif isinstance(value, Vector3): - return value.x + return value.x # type: ignore[no-any-return] else: return float(to_numpy(value)[0]) @@ -439,7 +439,7 @@ def y(value: VectorLike) -> float: if isinstance(value, Vector): return value.y elif isinstance(value, Vector3): - return value.y + return value.y # type: ignore[no-any-return] else: arr = to_numpy(value) return float(arr[1]) if len(arr) > 1 else 0.0 @@ -457,7 +457,7 @@ def z(value: VectorLike) -> float: if isinstance(value, Vector): return value.z elif isinstance(value, Vector3): - return value.z + return value.z # type: ignore[no-any-return] else: arr = to_numpy(value) return float(arr[2]) if len(arr) > 2 else 0.0 diff --git a/dimos/types/weaklist.py b/dimos/types/weaklist.py index e09b36157c..d3dd3d2c34 100644 --- a/dimos/types/weaklist.py +++ b/dimos/types/weaklist.py @@ -27,12 +27,12 @@ class WeakList: """ def __init__(self) -> None: - self._refs = [] + self._refs = [] # type: ignore[var-annotated] def append(self, obj: Any) -> None: """Add an object to the list (stored as weak reference).""" - def _cleanup(ref) -> None: + def _cleanup(ref) -> None: # type: ignore[no-untyped-def] try: self._refs.remove(ref) except ValueError: diff --git a/dimos/utils/actor_registry.py b/dimos/utils/actor_registry.py index 9cd589bed2..7fbdca4569 100644 --- a/dimos/utils/actor_registry.py +++ b/dimos/utils/actor_registry.py @@ -67,15 +67,15 @@ def clear() -> None: pass @staticmethod - def _read_from_shm(shm) -> dict[str, str]: + def _read_from_shm(shm) -> dict[str, str]: # type: ignore[no-untyped-def] """Read JSON data from shared memory.""" raw = bytes(shm.buf[:]).rstrip(b"\x00") if not raw: return {} - return json.loads(raw.decode("utf-8")) + return json.loads(raw.decode("utf-8")) # type: ignore[no-any-return] @staticmethod - def _write_to_shm(shm, data: dict[str, str]): + def _write_to_shm(shm, data: dict[str, str]): # type: ignore[no-untyped-def] """Write JSON data to shared memory.""" json_bytes = json.dumps(data).encode("utf-8") if len(json_bytes) > ActorRegistry.SHM_SIZE: diff --git a/dimos/utils/cli/agentspy/agentspy.py b/dimos/utils/cli/agentspy/agentspy.py index 84f68c10af..b825d9fa3d 100644 --- a/dimos/utils/cli/agentspy/agentspy.py +++ b/dimos/utils/cli/agentspy/agentspy.py @@ -58,7 +58,7 @@ def __init__(self, topic: str = "/agent", max_messages: int = 1000) -> None: self.messages: deque[MessageEntry] = deque(maxlen=max_messages) self.transport = PickleLCM() self.transport.start() - self.callbacks: list[callable] = [] + self.callbacks: list[callable] = [] # type: ignore[valid-type] pass def start(self) -> None: @@ -79,11 +79,11 @@ def _handle_message(self, msg: Any, topic: str) -> None: # Notify callbacks for callback in self.callbacks: - callback(entry) + callback(entry) # type: ignore[misc] else: pass - def subscribe(self, callback: callable) -> None: + def subscribe(self, callback: callable) -> None: # type: ignore[valid-type] """Subscribe to new messages.""" self.callbacks.append(callback) @@ -130,12 +130,12 @@ def format_message_content(msg: AnyMessage) -> str: return f"{content}\n[Tool Calls: {', '.join(tool_info)}]" elif tool_info: return f"[Tool Calls: {', '.join(tool_info)}]" - return content + return content # type: ignore[return-value] else: return str(msg.content) if hasattr(msg, "content") else str(msg) -class AgentSpyApp(App): +class AgentSpyApp(App): # type: ignore[type-arg] """TUI application for monitoring agent messages.""" CSS_PATH = theme.CSS_PATH @@ -165,7 +165,7 @@ class AgentSpyApp(App): Binding("ctrl+c", "quit", show=False), ] - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(*args, **kwargs) self.monitor = AgentMessageMonitor() self.message_log: RichLog | None = None @@ -225,7 +225,7 @@ def main() -> None: if len(sys.argv) > 1 and sys.argv[1] == "web": import os - from textual_serve.server import Server + from textual_serve.server import Server # type: ignore[import-not-found] server = Server(f"python {os.path.abspath(__file__)}") server.serve() diff --git a/dimos/utils/cli/agentspy/demo_agentspy.py b/dimos/utils/cli/agentspy/demo_agentspy.py index 100f22522d..d0d32602c9 100755 --- a/dimos/utils/cli/agentspy/demo_agentspy.py +++ b/dimos/utils/cli/agentspy/demo_agentspy.py @@ -24,7 +24,7 @@ ToolMessage, ) -from dimos.protocol.pubsub import lcm +from dimos.protocol.pubsub import lcm # type: ignore[attr-defined] from dimos.protocol.pubsub.lcmpubsub import PickleLCM diff --git a/dimos/utils/cli/boxglove/boxglove.py b/dimos/utils/cli/boxglove/boxglove.py index 1e0e09a277..fa7be453c8 100644 --- a/dimos/utils/cli/boxglove/boxglove.py +++ b/dimos/utils/cli/boxglove/boxglove.py @@ -52,7 +52,7 @@ full = alphabet[0] # Full block -class OccupancyGridApp(App): +class OccupancyGridApp(App): # type: ignore[type-arg] """A Textual app for visualizing OccupancyGrid data in real-time.""" CSS = """ @@ -90,7 +90,7 @@ class OccupancyGridApp(App): ("ctrl+c", "quit", "Quit"), ] - def __init__(self, connection: Connection, *args, **kwargs) -> None: + def __init__(self, connection: Connection, *args, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(*args, **kwargs) self.connection = connection self.subscription: Disposable | None = None @@ -117,7 +117,7 @@ def on_grid(grid: OccupancyGrid) -> None: def on_error(error: Exception) -> None: self.notify(f"Error: {error}", severity="error") - self.subscription = self.connection().subscribe(on_next=on_grid, on_error=on_error) + self.subscription = self.connection().subscribe(on_next=on_grid, on_error=on_error) # type: ignore[assignment] async def on_unmount(self) -> None: """Clean up subscription when app closes.""" @@ -134,14 +134,14 @@ def watch_grid_data(self, grid: OccupancyGrid | None) -> None: # Render the grid as ASCII art grid_text = self.render_grid(grid) - self.grid_display.update(grid_text) + self.grid_display.update(grid_text) # type: ignore[union-attr] - def on_resize(self, event) -> None: + def on_resize(self, event) -> None: # type: ignore[no-untyped-def] """Handle terminal resize events.""" if self.cached_grid: # Re-render with new terminal dimensions grid_text = self.render_grid(self.cached_grid) - self.grid_display.update(grid_text) + self.grid_display.update(grid_text) # type: ignore[union-attr] def render_grid(self, grid: OccupancyGrid) -> Text: """Render the OccupancyGrid as colored ASCII art, scaled to fit terminal.""" @@ -181,7 +181,7 @@ def render_grid(self, grid: OccupancyGrid) -> Text: actual_scale_y = grid.height / render_height if render_height > 0 else 1 # Function to get value with fractional scaling - def get_cell_value(grid_data: np.ndarray, x: int, y: int) -> int: + def get_cell_value(grid_data: np.ndarray, x: int, y: int) -> int: # type: ignore[type-arg] # Use fractional coordinates for smoother scaling y_center = int((y + 0.5) * actual_scale_y) x_center = int((x + 0.5) * actual_scale_x) @@ -192,17 +192,17 @@ def get_cell_value(grid_data: np.ndarray, x: int, y: int) -> int: # For now, just sample the center point # Could do area averaging for smoother results - return grid_data[y_center, x_center] + return grid_data[y_center, x_center] # type: ignore[no-any-return] # Helper function to check if a cell is an obstacle - def is_obstacle(grid_data: np.ndarray, x: int, y: int) -> bool: + def is_obstacle(grid_data: np.ndarray, x: int, y: int) -> bool: # type: ignore[type-arg] if x < 0 or x >= render_width or y < 0 or y >= render_height: return False value = get_cell_value(grid_data, x, y) return value > 90 # Consider cells with >90% probability as obstacles # Character and color mapping with intelligent obstacle rendering - def get_cell_char_and_style(grid_data: np.ndarray, x: int, y: int) -> tuple[str, str]: + def get_cell_char_and_style(grid_data: np.ndarray, x: int, y: int) -> tuple[str, str]: # type: ignore[type-arg] value = get_cell_value(grid_data, x, y) norm_value = min(value, 100) / 100.0 @@ -255,9 +255,9 @@ def get_cell_char_and_style(grid_data: np.ndarray, x: int, y: int) -> tuple[str, # No neighbors - isolated obstacle symbol = full + full - return symbol, None + return symbol, None # type: ignore[return-value] else: - return " ", None + return " ", None # type: ignore[return-value] # Render the scaled grid row by row (flip Y axis for proper display) for y in range(render_height - 1, -1, -1): @@ -277,9 +277,9 @@ def main() -> None: # app = OccupancyGridApp(core.LCMTransport("/global_costmap", OccupancyGrid).observable) app = OccupancyGridApp( - lambda: core.LCMTransport("/lidar", LidarMessage) + lambda: core.LCMTransport("/lidar", LidarMessage) # type: ignore[no-untyped-call] .observable() - .pipe(ops.map(lambda msg: msg.costmap())) + .pipe(ops.map(lambda msg: msg.costmap())) # type: ignore[attr-defined] ) app.run() import time diff --git a/dimos/utils/cli/boxglove/connection.py b/dimos/utils/cli/boxglove/connection.py index 5d3b3f8806..167dccd519 100644 --- a/dimos/utils/cli/boxglove/connection.py +++ b/dimos/utils/cli/boxglove/connection.py @@ -22,7 +22,7 @@ from dimos.msgs.nav_msgs import OccupancyGrid from dimos.msgs.sensor_msgs import PointCloud2 -from dimos.protocol.pubsub import lcm +from dimos.protocol.pubsub import lcm # type: ignore[attr-defined] from dimos.robot.unitree_webrtc.type.lidar import LidarMessage from dimos.robot.unitree_webrtc.type.map import Map from dimos.utils.data import get_data @@ -33,11 +33,11 @@ def live_connection() -> Observable[OccupancyGrid]: - def subscribe(observer, scheduler=None): + def subscribe(observer, scheduler=None): # type: ignore[no-untyped-def] lcm.autoconf() l = lcm.LCM() - def on_message(grid: OccupancyGrid, _) -> None: + def on_message(grid: OccupancyGrid, _) -> None: # type: ignore[no-untyped-def] observer.on_next(grid) l.subscribe(lcm.Topic("/global_costmap", OccupancyGrid), on_message) @@ -57,7 +57,7 @@ def recorded_connection() -> Observable[OccupancyGrid]: return backpressure( lidar_store.stream(speed=1).pipe( ops.map(mapper.add_frame), - ops.map(lambda _: mapper.costmap().inflate(0.1).gradient()), + ops.map(lambda _: mapper.costmap().inflate(0.1).gradient()), # type: ignore[attr-defined] ) ) @@ -68,4 +68,4 @@ def single_message() -> Observable[OccupancyGrid]: pointcloud = PointCloud2.lcm_decode(pickle.load(f)) mapper = Map() mapper.add_frame(pointcloud) - return rx.just(mapper.costmap()) + return rx.just(mapper.costmap()) # type: ignore[attr-defined] diff --git a/dimos/utils/cli/foxglove_bridge/run_foxglove_bridge.py b/dimos/utils/cli/foxglove_bridge/run_foxglove_bridge.py index 8244d16d39..3fad571c6e 100644 --- a/dimos/utils/cli/foxglove_bridge/run_foxglove_bridge.py +++ b/dimos/utils/cli/foxglove_bridge/run_foxglove_bridge.py @@ -21,8 +21,8 @@ import os import threading -import dimos_lcm -from dimos_lcm.foxglove_bridge import FoxgloveBridge +import dimos_lcm # type: ignore[import-untyped] +from dimos_lcm.foxglove_bridge import FoxgloveBridge # type: ignore[import-untyped] dimos_lcm_path = os.path.dirname(os.path.abspath(dimos_lcm.__file__)) print(f"Using dimos_lcm from: {dimos_lcm_path}") diff --git a/dimos/utils/cli/human/humancli.py b/dimos/utils/cli/human/humancli.py index 15fd82bf5b..14a2ee4d41 100644 --- a/dimos/utils/cli/human/humancli.py +++ b/dimos/utils/cli/human/humancli.py @@ -48,7 +48,7 @@ ) -class HumanCLIApp(App): +class HumanCLIApp(App): # type: ignore[type-arg] """IRC-like interface for interacting with DimOS agents.""" CSS_PATH = theme.CSS_PATH @@ -77,10 +77,10 @@ class HumanCLIApp(App): Binding("ctrl+l", "clear", "Clear chat"), ] - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(*args, **kwargs) - self.human_transport = pLCMTransport("/human_input") - self.agent_transport = pLCMTransport("/agent") + self.human_transport = pLCMTransport("/human_input") # type: ignore[var-annotated] + self.agent_transport = pLCMTransport("/agent") # type: ignore[var-annotated] self.chat_log: RichLog | None = None self.input_widget: Input | None = None self._subscription_thread: threading.Thread | None = None @@ -103,16 +103,16 @@ def on_mount(self) -> None: self.console.push_theme(JSON_THEME) # Set custom highlighter for RichLog - self.chat_log.highlighter = JSONHighlighter() + self.chat_log.highlighter = JSONHighlighter() # type: ignore[union-attr] # Start subscription thread self._subscription_thread = threading.Thread(target=self._subscribe_to_agent, daemon=True) self._subscription_thread.start() # Focus on input - self.input_widget.focus() + self.input_widget.focus() # type: ignore[union-attr] - self.chat_log.write(f"[{theme.ACCENT}]{theme.ascii_logo}[/{theme.ACCENT}]") + self.chat_log.write(f"[{theme.ACCENT}]{theme.ascii_logo}[/{theme.ACCENT}]") # type: ignore[union-attr] # Welcome message self._add_system_message("Connected to DimOS Agent Interface") @@ -124,7 +124,7 @@ def on_unmount(self) -> None: def _subscribe_to_agent(self) -> None: """Subscribe to agent messages in a separate thread.""" - def receive_msg(msg) -> None: + def receive_msg(msg) -> None: # type: ignore[no-untyped-def] if not self._running: return @@ -175,8 +175,8 @@ def receive_msg(msg) -> None: def _format_tool_call(self, tool_call: ToolCall) -> str: """Format a tool call for display.""" f = tool_call.get("function", {}) - name = f.get("name", "unknown") - return f"▶ {name}({f.get('arguments', '')})" + name = f.get("name", "unknown") # type: ignore[attr-defined] + return f"▶ {name}({f.get('arguments', '')})" # type: ignore[attr-defined] def _add_message(self, timestamp: str, sender: str, content: str, color: str) -> None: """Add a message to the chat log.""" @@ -200,7 +200,7 @@ def _add_message(self, timestamp: str, sender: str, content: str, color: str) -> indent = " " * 19 # Spaces to align with the content after the separator # Get the width of the chat area (accounting for borders and padding) - width = self.chat_log.size.width - 4 if self.chat_log.size else 76 + width = self.chat_log.size.width - 4 if self.chat_log.size else 76 # type: ignore[union-attr] # Calculate the available width for text (subtract prefix length) text_width = max(width - 20, 40) # Minimum 40 chars for text @@ -216,12 +216,12 @@ def _add_message(self, timestamp: str, sender: str, content: str, color: str) -> line, width=text_width, initial_indent="", subsequent_indent="" ) if wrapped: - self.chat_log.write(prefix + f"[{color}]{wrapped[0]}[/{color}]") + self.chat_log.write(prefix + f"[{color}]{wrapped[0]}[/{color}]") # type: ignore[union-attr] for wrapped_line in wrapped[1:]: - self.chat_log.write(indent + f"│ [{color}]{wrapped_line}[/{color}]") + self.chat_log.write(indent + f"│ [{color}]{wrapped_line}[/{color}]") # type: ignore[union-attr] else: # Empty line - self.chat_log.write(prefix) + self.chat_log.write(prefix) # type: ignore[union-attr] else: # Subsequent lines from explicit newlines wrapped = textwrap.wrap( @@ -229,10 +229,10 @@ def _add_message(self, timestamp: str, sender: str, content: str, color: str) -> ) if wrapped: for wrapped_line in wrapped: - self.chat_log.write(indent + f"│ [{color}]{wrapped_line}[/{color}]") + self.chat_log.write(indent + f"│ [{color}]{wrapped_line}[/{color}]") # type: ignore[union-attr] else: # Empty line - self.chat_log.write(indent + "│") + self.chat_log.write(indent + "│") # type: ignore[union-attr] def _add_system_message(self, content: str) -> None: """Add a system message to the chat.""" @@ -252,7 +252,7 @@ def on_input_submitted(self, event: Input.Submitted) -> None: return # Clear input - self.input_widget.value = "" + self.input_widget.value = "" # type: ignore[union-attr] # Check for commands if message.lower() in ["/exit", "/quit"]: @@ -277,9 +277,9 @@ def on_input_submitted(self, event: Input.Submitted) -> None: def action_clear(self) -> None: """Clear the chat log.""" - self.chat_log.clear() + self.chat_log.clear() # type: ignore[union-attr] - def action_quit(self) -> None: + def action_quit(self) -> None: # type: ignore[override] """Quit the application.""" self._running = False self.exit() @@ -293,7 +293,7 @@ def main() -> None: # Support for textual-serve web mode import os - from textual_serve.server import Server + from textual_serve.server import Server # type: ignore[import-not-found] server = Server(f"python {os.path.abspath(__file__)}") server.serve() diff --git a/dimos/utils/cli/human/humanclianim.py b/dimos/utils/cli/human/humanclianim.py index 8b6aae059e..d2cdc98113 100644 --- a/dimos/utils/cli/human/humanclianim.py +++ b/dimos/utils/cli/human/humanclianim.py @@ -19,7 +19,7 @@ import threading import time -from terminaltexteffects import Color +from terminaltexteffects import Color # type: ignore[attr-defined] from dimos.utils.cli import theme @@ -43,7 +43,7 @@ def import_cli_in_background() -> None: _import_complete.set() -def get_effect_config(effect_name: str): +def get_effect_config(effect_name: str): # type: ignore[no-untyped-def] """Get hardcoded configuration for a specific effect""" # Hardcoded configs for each effect global_config = { @@ -76,7 +76,7 @@ def get_effect_config(effect_name: str): "highlight": {"highlight_brightness": 3}, } - return {**configs.get(effect_name, {}), **global_config} + return {**configs.get(effect_name, {}), **global_config} # type: ignore[dict-item] def run_banner_animation() -> None: @@ -136,11 +136,11 @@ def run_banner_animation() -> None: # Create and run the effect with config effect = EffectClass(ascii_art) for key, value in effect_config.items(): - setattr(effect.effect_config, key, value) + setattr(effect.effect_config, key, value) # type: ignore[attr-defined] # Run the animation - terminal.print() handles all screen management - with effect.terminal_output() as terminal: - for frame in effect: + with effect.terminal_output() as terminal: # type: ignore[attr-defined] + for frame in effect: # type: ignore[attr-defined] terminal.print(frame) # Brief pause to see the final frame diff --git a/dimos/utils/cli/lcmspy/lcmspy.py b/dimos/utils/cli/lcmspy/lcmspy.py index 42f811ffbc..4253905da9 100755 --- a/dimos/utils/cli/lcmspy/lcmspy.py +++ b/dimos/utils/cli/lcmspy/lcmspy.py @@ -48,7 +48,7 @@ class Topic: def __init__(self, name: str, history_window: float = 60.0) -> None: self.name = name # Store (timestamp, data_size) tuples for statistics - self.message_history = deque() + self.message_history = deque() # type: ignore[var-annotated] self.history_window = history_window # Total traffic accumulator (doesn't get cleaned up) self.total_traffic_bytes = 0 @@ -68,7 +68,7 @@ def _cleanup_old_messages(self, max_age: float | None = None) -> None: ): self.message_history.popleft() - def _get_messages_in_window(self, time_window: float): + def _get_messages_in_window(self, time_window: float): # type: ignore[no-untyped-def] """Get messages within the specified time window""" current_time = time.time() cutoff_time = current_time - time_window @@ -88,7 +88,7 @@ def kbps(self, time_window: float) -> float: return 0.0 total_bytes = sum(size for _, size in messages) total_kbytes = total_bytes / 1000 # Convert bytes to kB - return total_kbytes / time_window + return total_kbytes / time_window # type: ignore[no-any-return] def kbps_hr(self, time_window: float, round_to: int = 2) -> tuple[float, BandwidthUnit]: """Return human-readable bandwidth with appropriate units""" @@ -103,7 +103,7 @@ def size(self, time_window: float) -> float: if not messages: return 0.0 total_size = sum(size for _, size in messages) - return total_size / len(messages) + return total_size / len(messages) # type: ignore[no-any-return] def total_traffic(self) -> int: """Return total traffic passed in bytes since the beginning""" @@ -129,36 +129,36 @@ class LCMSpy(LCMService, Topic): graph_log_window: float = 1.0 topic_class: type[Topic] = Topic - def __init__(self, **kwargs) -> None: + def __init__(self, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(**kwargs) - Topic.__init__(self, name="total", history_window=self.config.topic_history_window) - self.topic = {} + Topic.__init__(self, name="total", history_window=self.config.topic_history_window) # type: ignore[attr-defined] + self.topic = {} # type: ignore[assignment] self.l = lcm.LCM(self.config.url) if self.config.url else lcm.LCM() def start(self) -> None: super().start() - self.l.subscribe(".*", self.msg) + self.l.subscribe(".*", self.msg) # type: ignore[union-attr] def stop(self) -> None: """Stop the LCM spy and clean up resources""" super().stop() - def msg(self, topic, data) -> None: + def msg(self, topic, data) -> None: # type: ignore[no-untyped-def, override] Topic.msg(self, data) - if topic not in self.topic: + if topic not in self.topic: # type: ignore[operator] print(self.config) - self.topic[topic] = self.topic_class( - topic, history_window=self.config.topic_history_window + self.topic[topic] = self.topic_class( # type: ignore[assignment, call-arg] + topic, history_window=self.config.topic_history_window # type: ignore[attr-defined] ) - self.topic[topic].msg(data) + self.topic[topic].msg(data) # type: ignore[attr-defined, type-arg] class GraphTopic(Topic): - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(*args, **kwargs) - self.freq_history = deque(maxlen=20) - self.bandwidth_history = deque(maxlen=20) + self.freq_history = deque(maxlen=20) # type: ignore[var-annotated] + self.bandwidth_history = deque(maxlen=20) # type: ignore[var-annotated] def update_graphs(self, step_window: float = 1.0) -> None: """Update historical data for graphing""" @@ -180,9 +180,9 @@ class GraphLCMSpy(LCMSpy, GraphTopic): graph_log_stop_event: threading.Event = threading.Event() topic_class: type[Topic] = GraphTopic - def __init__(self, **kwargs) -> None: + def __init__(self, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(**kwargs) - GraphTopic.__init__(self, name="total", history_window=self.config.topic_history_window) + GraphTopic.__init__(self, name="total", history_window=self.config.topic_history_window) # type: ignore[attr-defined] def start(self) -> None: super().start() @@ -191,10 +191,10 @@ def start(self) -> None: def graph_log(self) -> None: while not self.graph_log_stop_event.is_set(): - self.update_graphs(self.config.graph_log_window) # Update global history - for topic in self.topic.values(): - topic.update_graphs(self.config.graph_log_window) - time.sleep(self.config.graph_log_window) + self.update_graphs(self.config.graph_log_window) # type: ignore[attr-defined] # Update global history + for topic in self.topic.values(): # type: ignore[call-arg] + topic.update_graphs(self.config.graph_log_window) # type: ignore[attr-defined] + time.sleep(self.config.graph_log_window) # type: ignore[attr-defined] def stop(self) -> None: """Stop the graph logging and LCM spy""" diff --git a/dimos/utils/cli/lcmspy/run_lcmspy.py b/dimos/utils/cli/lcmspy/run_lcmspy.py index 2e96156852..fdd47be0a0 100644 --- a/dimos/utils/cli/lcmspy/run_lcmspy.py +++ b/dimos/utils/cli/lcmspy/run_lcmspy.py @@ -48,7 +48,7 @@ def topic_text(topic_name: str) -> Text: return Text(topic_name, style=theme.BRIGHT_WHITE) -class LCMSpyApp(App): +class LCMSpyApp(App): # type: ignore[type-arg] """A real-time CLI dashboard for LCM traffic statistics using Textual.""" CSS_PATH = "../dimos.tcss" @@ -78,13 +78,13 @@ class LCMSpyApp(App): ("ctrl+c", "quit"), ] - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(*args, **kwargs) self.spy = GraphLCMSpy(autoconf=True, graph_log_window=0.5) - self.table: DataTable | None = None + self.table: DataTable | None = None # type: ignore[type-arg] def compose(self) -> ComposeResult: - self.table = DataTable(zebra_stripes=False, cursor_type=None) + self.table = DataTable(zebra_stripes=False, cursor_type=None) # type: ignore[arg-type] self.table.add_column("Topic") self.table.add_column("Freq (Hz)") self.table.add_column("Bandwidth") @@ -99,9 +99,9 @@ async def on_unmount(self) -> None: self.spy.stop() def refresh_table(self) -> None: - topics: list[SpyTopic] = list(self.spy.topic.values()) + topics: list[SpyTopic] = list(self.spy.topic.values()) # type: ignore[arg-type, call-arg] topics.sort(key=lambda t: t.total_traffic(), reverse=True) - self.table.clear(columns=False) + self.table.clear(columns=False) # type: ignore[union-attr] for t in topics: freq = t.freq(5.0) @@ -109,7 +109,7 @@ def refresh_table(self) -> None: bw_val, bw_unit = t.kbps_hr(5.0) total_val, total_unit = t.total_traffic_hr() - self.table.add_row( + self.table.add_row( # type: ignore[union-attr] topic_text(t.name), Text(f"{freq:.1f}", style=gradient(10, freq)), Text(f"{bw_val} {bw_unit.value}/s", style=gradient(1024 * 3, kbps)), @@ -123,7 +123,7 @@ def main() -> None: if len(sys.argv) > 1 and sys.argv[1] == "web": import os - from textual_serve.server import Server + from textual_serve.server import Server # type: ignore[import-not-found] server = Server(f"python {os.path.abspath(__file__)}") server.serve() diff --git a/dimos/utils/cli/skillspy/skillspy.py b/dimos/utils/cli/skillspy/skillspy.py index 769478b00e..7c5d914671 100644 --- a/dimos/utils/cli/skillspy/skillspy.py +++ b/dimos/utils/cli/skillspy/skillspy.py @@ -29,7 +29,7 @@ if TYPE_CHECKING: from collections.abc import Callable - from dimos.protocol.skill.comms import SkillMsg + from dimos.protocol.skill.comms import SkillMsg # type: ignore[attr-defined] class AgentSpy: @@ -58,7 +58,7 @@ def stop(self) -> None: time.sleep(0.2) self.agent_interface.stop() - def _handle_message(self, msg: SkillMsg) -> None: + def _handle_message(self, msg: SkillMsg) -> None: # type: ignore[type-arg] """Handle incoming skill messages.""" if not self._running: return @@ -111,7 +111,7 @@ def format_duration(duration: float) -> str: return f"{duration / 3600:.1f}h" -class AgentSpyApp(App): +class AgentSpyApp(App): # type: ignore[type-arg] """A real-time CLI dashboard for agent skill monitoring using Textual.""" CSS_PATH = theme.CSS_PATH @@ -140,14 +140,14 @@ class AgentSpyApp(App): Binding("ctrl+c", "quit", "Quit", show=False), ] - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def] super().__init__(*args, **kwargs) self.spy = AgentSpy() - self.table: DataTable | None = None + self.table: DataTable | None = None # type: ignore[type-arg] self.skill_history: list[tuple[str, SkillState, float]] = [] # (call_id, state, start_time) def compose(self) -> ComposeResult: - self.table = DataTable(zebra_stripes=False, cursor_type=None) + self.table = DataTable(zebra_stripes=False, cursor_type=None) # type: ignore[arg-type] self.table.add_column("Call ID") self.table.add_column("Skill Name") self.table.add_column("State") @@ -268,7 +268,7 @@ def main() -> None: if len(sys.argv) > 1 and sys.argv[1] == "web": import os - from textual_serve.server import Server + from textual_serve.server import Server # type: ignore[import-not-found] server = Server(f"python {os.path.abspath(__file__)}") server.serve() diff --git a/dimos/utils/decorators/accumulators.py b/dimos/utils/decorators/accumulators.py index 7672ff7033..0f155c5c6b 100644 --- a/dimos/utils/decorators/accumulators.py +++ b/dimos/utils/decorators/accumulators.py @@ -23,12 +23,12 @@ class Accumulator(ABC, Generic[T]): """Base class for accumulating messages between rate-limited calls.""" @abstractmethod - def add(self, *args, **kwargs) -> None: + def add(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def] """Add args and kwargs to the accumulator.""" pass @abstractmethod - def get(self) -> tuple[tuple, dict] | None: + def get(self) -> tuple[tuple, dict] | None: # type: ignore[type-arg] """Get the accumulated args and kwargs and reset the accumulator.""" pass @@ -42,14 +42,14 @@ class LatestAccumulator(Accumulator[T]): """Simple accumulator that remembers only the latest args and kwargs.""" def __init__(self) -> None: - self._latest: tuple[tuple, dict] | None = None + self._latest: tuple[tuple, dict] | None = None # type: ignore[type-arg] self._lock = threading.Lock() - def add(self, *args, **kwargs) -> None: + def add(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def] with self._lock: self._latest = (args, kwargs) - def get(self) -> tuple[tuple, dict] | None: + def get(self) -> tuple[tuple, dict] | None: # type: ignore[type-arg] with self._lock: result = self._latest self._latest = None @@ -70,10 +70,10 @@ class RollingAverageAccumulator(Accumulator[T]): def __init__(self) -> None: self._sum: float = 0.0 self._count: int = 0 - self._latest_kwargs: dict = {} + self._latest_kwargs: dict = {} # type: ignore[type-arg] self._lock = threading.Lock() - def add(self, *args, **kwargs) -> None: + def add(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def] if not args: raise ValueError("RollingAverageAccumulator requires at least one argument") @@ -86,7 +86,7 @@ def add(self, *args, **kwargs) -> None: except (TypeError, ValueError): raise TypeError(f"First argument must be numeric, got {type(args[0])}") - def get(self) -> tuple[tuple, dict] | None: + def get(self) -> tuple[tuple, dict] | None: # type: ignore[type-arg] with self._lock: if self._count == 0: return None diff --git a/dimos/utils/decorators/decorators.py b/dimos/utils/decorators/decorators.py index 4511aea309..f41b21c598 100644 --- a/dimos/utils/decorators/decorators.py +++ b/dimos/utils/decorators/decorators.py @@ -20,7 +20,7 @@ from .accumulators import Accumulator, LatestAccumulator -def limit(max_freq: float, accumulator: Accumulator | None = None): +def limit(max_freq: float, accumulator: Accumulator | None = None): # type: ignore[no-untyped-def, type-arg] """ Decorator that limits function call frequency. @@ -43,7 +43,7 @@ def limit(max_freq: float, accumulator: Accumulator | None = None): if accumulator is None: accumulator = LatestAccumulator() - def decorator(func: Callable) -> Callable: + def decorator(func: Callable) -> Callable: # type: ignore[type-arg] last_call_time = 0.0 lock = threading.Lock() timer: threading.Timer | None = None @@ -52,13 +52,13 @@ def execute_accumulated() -> None: nonlocal last_call_time, timer with lock: if len(accumulator): - acc_args, acc_kwargs = accumulator.get() + acc_args, acc_kwargs = accumulator.get() # type: ignore[misc] last_call_time = time.time() timer = None func(*acc_args, **acc_kwargs) @wraps(func) - def wrapper(*args, **kwargs): + def wrapper(*args, **kwargs): # type: ignore[no-untyped-def] nonlocal last_call_time, timer current_time = time.time() @@ -77,7 +77,7 @@ def wrapper(*args, **kwargs): # if we have accumulated data, we get a compound value if len(accumulator): accumulator.add(*args, **kwargs) - acc_args, acc_kwargs = accumulator.get() # accumulator resets here + acc_args, acc_kwargs = accumulator.get() # type: ignore[misc] # accumulator resets here return func(*acc_args, **acc_kwargs) # No accumulated data, normal call @@ -102,7 +102,7 @@ def wrapper(*args, **kwargs): return decorator -def simple_mcache(method: Callable) -> Callable: +def simple_mcache(method: Callable) -> Callable: # type: ignore[type-arg] """ Decorator to cache the result of a method call on the instance. @@ -124,7 +124,7 @@ def simple_mcache(method: Callable) -> Callable: lock_name = f"_lock_{method.__name__}" @wraps(method) - def getter(self): + def getter(self): # type: ignore[no-untyped-def] # Get or create the lock for this instance if not hasattr(self, lock_name): # This is a one-time operation, race condition here is acceptable @@ -145,7 +145,7 @@ def getter(self): return getter -def retry(max_retries: int = 3, on_exception: type[Exception] = Exception, delay: float = 0.0): +def retry(max_retries: int = 3, on_exception: type[Exception] = Exception, delay: float = 0.0): # type: ignore[no-untyped-def] """ Decorator that retries a function call if it raises an exception. @@ -173,9 +173,9 @@ def risky_operation(): if delay < 0: raise ValueError("delay must be non-negative") - def decorator(func: Callable) -> Callable: + def decorator(func: Callable) -> Callable: # type: ignore[type-arg] @wraps(func) - def wrapper(*args, **kwargs): + def wrapper(*args, **kwargs): # type: ignore[no-untyped-def] last_exception = None for attempt in range(max_retries + 1): diff --git a/dimos/utils/demo_image_encoding.py b/dimos/utils/demo_image_encoding.py index 00df5c1a62..cecf49aef4 100644 --- a/dimos/utils/demo_image_encoding.py +++ b/dimos/utils/demo_image_encoding.py @@ -40,7 +40,7 @@ class EmitterModule(Module): - image: Out[Image] = None + image: Out[Image] = None # type: ignore[assignment] _thread: threading.Thread | None = None _stop_event: threading.Event | None = None @@ -53,24 +53,24 @@ def start(self) -> None: def stop(self) -> None: if self._thread: - self._stop_event.set() + self._stop_event.set() # type: ignore[union-attr] self._thread.join(timeout=2) super().stop() def _publish_image(self) -> None: open_file = open("/tmp/emitter-times", "w") - while not self._stop_event.is_set(): + while not self._stop_event.is_set(): # type: ignore[union-attr] start = time.time() data = random_image(1280, 720) total = time.time() - start print("took", total) open_file.write(str(time.time()) + "\n") - self.image.publish(Image(data=data)) + self.image.publish(Image(data=data)) # type: ignore[no-untyped-call] open_file.close() class ReceiverModule(Module): - image: In[Image] = None + image: In[Image] = None # type: ignore[assignment] _open_file = None @@ -80,11 +80,11 @@ def start(self) -> None: self._open_file = open("/tmp/receiver-times", "w") def stop(self) -> None: - self._open_file.close() + self._open_file.close() # type: ignore[union-attr] super().stop() def _on_image(self, image: Image) -> None: - self._open_file.write(str(time.time()) + "\n") + self._open_file.write(str(time.time()) + "\n") # type: ignore[union-attr] print("image") @@ -120,7 +120,7 @@ def main() -> None: pass finally: foxglove_bridge.stop() - dimos.close() + dimos.close() # type: ignore[attr-defined] if __name__ == "__main__": diff --git a/dimos/utils/extract_frames.py b/dimos/utils/extract_frames.py index d57b0641cd..65c2b5fbaf 100644 --- a/dimos/utils/extract_frames.py +++ b/dimos/utils/extract_frames.py @@ -18,7 +18,7 @@ import cv2 -def extract_frames(video_path, output_dir, frame_rate) -> None: +def extract_frames(video_path, output_dir, frame_rate) -> None: # type: ignore[no-untyped-def] """ Extract frames from a video file at a specified frame rate. diff --git a/dimos/utils/generic.py b/dimos/utils/generic.py index adbb18988f..ec5a4b7612 100644 --- a/dimos/utils/generic.py +++ b/dimos/utils/generic.py @@ -74,5 +74,5 @@ def short_id(from_string: str | None = None) -> str: class classproperty(property): - def __get__(self, obj, cls): - return self.fget(cls) + def __get__(self, obj, cls): # type: ignore[no-untyped-def, override] + return self.fget(cls) # type: ignore[misc] diff --git a/dimos/utils/gpu_utils.py b/dimos/utils/gpu_utils.py index e0a1a23734..9e10e5e3b2 100644 --- a/dimos/utils/gpu_utils.py +++ b/dimos/utils/gpu_utils.py @@ -13,9 +13,9 @@ # limitations under the License. -def is_cuda_available(): +def is_cuda_available(): # type: ignore[no-untyped-def] try: - import pycuda.driver as cuda + import pycuda.driver as cuda # type: ignore[import-not-found] cuda.init() return cuda.Device.count() > 0 diff --git a/dimos/utils/llm_utils.py b/dimos/utils/llm_utils.py index 124169e794..a52aa0d582 100644 --- a/dimos/utils/llm_utils.py +++ b/dimos/utils/llm_utils.py @@ -16,7 +16,7 @@ import re -def extract_json(response: str) -> dict | list: +def extract_json(response: str) -> dict | list: # type: ignore[type-arg] """Extract JSON from potentially messy LLM response. Tries multiple strategies: @@ -35,7 +35,7 @@ def extract_json(response: str) -> dict | list: """ # First try to parse the whole response as JSON try: - return json.loads(response) + return json.loads(response) # type: ignore[no-any-return] except json.JSONDecodeError: pass @@ -64,7 +64,7 @@ def extract_json(response: str) -> dict | list: matches = re.findall(object_pattern, response, re.DOTALL) for match in matches: try: - return json.loads(match) + return json.loads(match) # type: ignore[no-any-return] except json.JSONDecodeError: continue diff --git a/dimos/utils/monitoring.py b/dimos/utils/monitoring.py index 17415781b5..1a6f119851 100644 --- a/dimos/utils/monitoring.py +++ b/dimos/utils/monitoring.py @@ -35,7 +35,7 @@ logger = setup_logger(__file__) -def print_data_table(data) -> None: +def print_data_table(data) -> None: # type: ignore[no-untyped-def] headers = [ "cpu_percent", "active_percent", @@ -85,9 +85,9 @@ def print_data_table(data) -> None: class UtilizationThread(threading.Thread): _module: "UtilizationModule" _stop_event: threading.Event - _monitors: dict + _monitors: dict # type: ignore[type-arg] - def __init__(self, module) -> None: + def __init__(self, module) -> None: # type: ignore[no-untyped-def] super().__init__(daemon=True) self._module = module self._stop_event = threading.Event() @@ -95,8 +95,8 @@ def __init__(self, module) -> None: def run(self) -> None: while not self._stop_event.is_set(): - workers = self._module.client.scheduler_info()["workers"] - pids = {pid: None for pid in get_worker_pids()} + workers = self._module.client.scheduler_info()["workers"] # type: ignore[union-attr] + pids = {pid: None for pid in get_worker_pids()} # type: ignore[no-untyped-call] for worker, info in workers.items(): pid = get_pid_by_port(worker.rsplit(":", 1)[-1]) if pid is None: @@ -129,7 +129,7 @@ def stop(self) -> None: monitor.stop() monitor.join(timeout=2) - def _fix_missing_ids(self, data) -> None: + def _fix_missing_ids(self, data) -> None: # type: ignore[no-untyped-def] """ Some worker IDs are None. But if we order the workers by PID and all non-None ids are in order, then we can deduce that the None ones are the @@ -153,7 +153,7 @@ def __init__(self) -> None: logger.info("Set `MEASURE_GIL_UTILIZATION=true` to print GIL utilization.") return - if not _can_use_py_spy(): + if not _can_use_py_spy(): # type: ignore[no-untyped-call] logger.warning( "Cannot start UtilizationModule because in order to run py-spy without " "being root you need to enable this:\n" @@ -190,7 +190,7 @@ def stop(self) -> None: __all__ = ["UtilizationModule", "utilization"] -def _can_use_py_spy(): +def _can_use_py_spy(): # type: ignore[no-untyped-def] try: with open("/proc/sys/kernel/yama/ptrace_scope") as f: value = f.read().strip() @@ -212,7 +212,7 @@ def get_pid_by_port(port: int) -> int | None: return None -def get_worker_pids(): +def get_worker_pids(): # type: ignore[no-untyped-def] pids = [] for pid in os.listdir("/proc"): if not pid.isdigit(): @@ -240,7 +240,7 @@ def __init__(self, pid: int) -> None: self._stop_event = threading.Event() self._lock = threading.Lock() - def run(self): + def run(self): # type: ignore[no-untyped-def] command = ["py-spy", "top", "--pid", str(self.pid), "--rate", "100"] process = None try: @@ -252,7 +252,7 @@ def run(self): bufsize=1, # Line-buffered output ) - for line in iter(process.stdout.readline, ""): + for line in iter(process.stdout.readline, ""): # type: ignore[union-attr] if self._stop_event.is_set(): break @@ -289,7 +289,7 @@ def run(self): process.wait(timeout=1) self._stop_event.set() - def get_values(self): + def get_values(self): # type: ignore[no-untyped-def] with self._lock: return self._latest_values diff --git a/dimos/utils/reactive.py b/dimos/utils/reactive.py index f7885d3129..30acd8a241 100644 --- a/dimos/utils/reactive.py +++ b/dimos/utils/reactive.py @@ -21,7 +21,7 @@ from reactivex.disposable import Disposable from reactivex.observable import Observable from reactivex.scheduler import ThreadPoolScheduler -from rxpy_backpressure import BackPressure +from rxpy_backpressure import BackPressure # type: ignore[import-untyped] from dimos.utils.threadpool import get_scheduler @@ -46,7 +46,7 @@ def backpressure( ) # per-subscriber factory - def per_sub(): + def per_sub(): # type: ignore[no-untyped-def] # Move processing to thread pool base = core.pipe(ops.observe_on(scheduler)) @@ -54,19 +54,19 @@ def per_sub(): if not drop_unprocessed: return base - def _subscribe(observer, sch=None): + def _subscribe(observer, sch=None): # type: ignore[no-untyped-def] return base.subscribe(BackPressure.LATEST(observer), scheduler=sch) return rx.create(_subscribe) # each `.subscribe()` call gets its own async backpressure chain - return rx.defer(lambda *_: per_sub()) + return rx.defer(lambda *_: per_sub()) # type: ignore[no-untyped-call] class LatestReader(Generic[T]): """A callable object that returns the latest value from an observable.""" - def __init__(self, initial_value: T, subscription, connection=None) -> None: + def __init__(self, initial_value: T, subscription, connection=None) -> None: # type: ignore[no-untyped-def] self._value = initial_value self._subscription = subscription self._connection = connection @@ -83,16 +83,16 @@ def dispose(self) -> None: def getter_ondemand(observable: Observable[T], timeout: float | None = 30.0) -> T: - def getter(): + def getter(): # type: ignore[no-untyped-def] result = [] error = [] event = threading.Event() - def on_next(value) -> None: + def on_next(value) -> None: # type: ignore[no-untyped-def] result.append(value) event.set() - def on_error(e) -> None: + def on_error(e) -> None: # type: ignore[no-untyped-def] error.append(e) event.set() @@ -121,10 +121,10 @@ def on_completed() -> None: finally: subscription.dispose() - return getter + return getter # type: ignore[return-value] -T = TypeVar("T") +T = TypeVar("T") # type: ignore[misc] def getter_streaming( @@ -171,10 +171,10 @@ def _dispose() -> None: sub.dispose() reader.dispose = _dispose # type: ignore[attr-defined] - return reader + return reader # type: ignore[return-value] -T = TypeVar("T") +T = TypeVar("T") # type: ignore[misc] CB = Callable[[T], Any] @@ -182,7 +182,7 @@ def callback_to_observable( start: Callable[[CB[T]], Any], stop: Callable[[CB[T]], Any], ) -> Observable[T]: - def _subscribe(observer, _scheduler=None): + def _subscribe(observer, _scheduler=None): # type: ignore[no-untyped-def] def _on_msg(value: T) -> None: observer.on_next(value) @@ -192,15 +192,15 @@ def _on_msg(value: T) -> None: return rx.create(_subscribe) -def spy(name: str): - def spyfun(x): +def spy(name: str): # type: ignore[no-untyped-def] + def spyfun(x): # type: ignore[no-untyped-def] print(f"SPY {name}:", x) return x return ops.map(spyfun) -def quality_barrier(quality_func: Callable[[T], float], target_frequency: float): +def quality_barrier(quality_func: Callable[[T], float], target_frequency: float): # type: ignore[no-untyped-def] """ RxPY pipe operator that selects the highest quality item within each time window. @@ -219,10 +219,10 @@ def _quality_barrier(source: Observable[T]) -> Observable[T]: ops.window_with_time(window_duration, window_duration), # For each window, find the highest quality item ops.flat_map( - lambda window: window.pipe( + lambda window: window.pipe( # type: ignore[attr-defined] ops.to_list(), - ops.map(lambda items: max(items, key=quality_func) if items else None), - ops.filter(lambda x: x is not None), + ops.map(lambda items: max(items, key=quality_func) if items else None), # type: ignore[call-overload] + ops.filter(lambda x: x is not None), # type: ignore[arg-type] ) ), ) diff --git a/dimos/utils/simple_controller.py b/dimos/utils/simple_controller.py index dd92ae0c55..2cd4f455d4 100644 --- a/dimos/utils/simple_controller.py +++ b/dimos/utils/simple_controller.py @@ -15,7 +15,7 @@ import math -def normalize_angle(angle: float): +def normalize_angle(angle: float): # type: ignore[no-untyped-def] """Normalize angle to the range [-pi, pi].""" return math.atan2(math.sin(angle), math.cos(angle)) @@ -24,7 +24,7 @@ def normalize_angle(angle: float): # PID Controller Class # ---------------------------- class PIDController: - def __init__( + def __init__( # type: ignore[no-untyped-def] self, kp, ki: float = 0.0, @@ -59,7 +59,7 @@ def __init__( self.prev_error = 0.0 self.inverse_output = inverse_output - def update(self, error, dt): + def update(self, error, dt): # type: ignore[no-untyped-def] """Compute the PID output with anti-windup, output deadband compensation and output saturation.""" # Update integral term with windup protection. self.integral += error * dt @@ -78,7 +78,7 @@ def update(self, error, dt): output = self.kp * error + self.ki * self.integral + self.kd * derivative # Apply deadband compensation to the output - output = self._apply_output_deadband_compensation(output) + output = self._apply_output_deadband_compensation(output) # type: ignore[no-untyped-call] # Apply output limits if specified. if self.max_output is not None: @@ -91,7 +91,7 @@ def update(self, error, dt): return -output return output - def _apply_output_deadband_compensation(self, output): + def _apply_output_deadband_compensation(self, output): # type: ignore[no-untyped-def] """ Apply deadband compensation to the output. @@ -110,7 +110,7 @@ def _apply_output_deadband_compensation(self, output): else: return output - def _apply_deadband_compensation(self, error): + def _apply_deadband_compensation(self, error): # type: ignore[no-untyped-def] """ Apply deadband compensation to the error. @@ -124,7 +124,7 @@ def _apply_deadband_compensation(self, error): # Visual Servoing Controller Class # ---------------------------- class VisualServoingController: - def __init__(self, distance_pid_params, angle_pid_params) -> None: + def __init__(self, distance_pid_params, angle_pid_params) -> None: # type: ignore[no-untyped-def] """ Initialize the visual servoing controller using enhanced PID controllers. @@ -136,7 +136,7 @@ def __init__(self, distance_pid_params, angle_pid_params) -> None: self.angle_pid = PIDController(*angle_pid_params) self.prev_measured_angle = 0.0 # Used for angular feed-forward damping - def compute_control( + def compute_control( # type: ignore[no-untyped-def] self, measured_distance, measured_angle, desired_distance, desired_angle, dt ): """ @@ -157,8 +157,8 @@ def compute_control( error_angle = normalize_angle(measured_angle - desired_angle) # Get raw PID outputs. - forward_command_raw = self.distance_pid.update(error_distance, dt) - angular_command_raw = self.angle_pid.update(error_angle, dt) + forward_command_raw = self.distance_pid.update(error_distance, dt) # type: ignore[no-untyped-call] + angular_command_raw = self.angle_pid.update(error_angle, dt) # type: ignore[no-untyped-call] # print("forward: {} angular: {}".format(forward_command_raw, angular_command_raw)) diff --git a/dimos/utils/testing.py b/dimos/utils/testing.py index 5e3725bc81..bf6eaaa8be 100644 --- a/dimos/utils/testing.py +++ b/dimos/utils/testing.py @@ -74,14 +74,14 @@ def first(self) -> T | Any | None: @functools.cached_property def files(self) -> list[Path]: - def extract_number(filepath): + def extract_number(filepath): # type: ignore[no-untyped-def] """Extract last digits before .pickle extension""" basename = os.path.basename(filepath) match = re.search(r"(\d+)\.pickle$", basename) return int(match.group(1)) if match else 0 return sorted( - glob.glob(os.path.join(self.root_dir, "*")), + glob.glob(os.path.join(self.root_dir, "*")), # type: ignore[arg-type] key=extract_number, ) @@ -136,19 +136,19 @@ def __init__(self, name: str, autocast: Callable[[T], Any] | None = None) -> Non def consume_stream(self, observable: Observable[T | Any]) -> None: """Consume an observable stream of sensor data without saving.""" - return observable.subscribe(self.save_one) + return observable.subscribe(self.save_one) # type: ignore[arg-type, return-value] def save_stream(self, observable: Observable[T | Any]) -> Observable[int]: """Save an observable stream of sensor data to pickle files.""" return observable.pipe(ops.map(lambda frame: self.save_one(frame))) - def save(self, *frames) -> int: + def save(self, *frames) -> int: # type: ignore[no-untyped-def] """Save one or more frames to pickle files.""" for frame in frames: self.save_one(frame) return self.cnt - def save_one(self, frame) -> int: + def save_one(self, frame) -> int: # type: ignore[no-untyped-def] """Save a single frame to a pickle file.""" file_name = f"{self.cnt:03d}.pickle" full_path = self.root_dir / file_name @@ -254,7 +254,7 @@ def first_timestamp(self) -> float | None: return None def iterate(self, loop: bool = False) -> Iterator[T | Any]: - return (x[1] for x in super().iterate(loop=loop)) + return (x[1] for x in super().iterate(loop=loop)) # type: ignore[index] def iterate_ts( self, @@ -270,14 +270,14 @@ def iterate_ts( return if seek is not None: - from_timestamp = first_ts + seek + from_timestamp = first_ts + seek # type: ignore[operator] end_timestamp = None if duration is not None: - end_timestamp = (from_timestamp if from_timestamp else first_ts) + duration + end_timestamp = (from_timestamp if from_timestamp else first_ts) + duration # type: ignore[operator] while True: - for ts, data in super().iterate(): + for ts, data in super().iterate(): # type: ignore[misc] if from_timestamp is None or ts >= from_timestamp: if end_timestamp is not None and ts >= end_timestamp: break @@ -285,7 +285,7 @@ def iterate_ts( if not loop: break - def stream( + def stream( # type: ignore[override] self, speed: float = 1.0, seek: float | None = None, @@ -293,7 +293,7 @@ def stream( from_timestamp: float | None = None, loop: bool = False, ) -> Observable[T | Any]: - def _subscribe(observer, scheduler=None): + def _subscribe(observer, scheduler=None): # type: ignore[no-untyped-def] from reactivex.disposable import CompositeDisposable, Disposable scheduler = scheduler or TimeoutScheduler() @@ -325,7 +325,7 @@ def _subscribe(observer, scheduler=None): observer.on_completed() return disp - def schedule_emission(message) -> None: + def schedule_emission(message) -> None: # type: ignore[no-untyped-def] nonlocal next_message, is_disposed if is_disposed: diff --git a/dimos/utils/transform_utils.py b/dimos/utils/transform_utils.py index 21421b4390..c108bc34b9 100644 --- a/dimos/utils/transform_utils.py +++ b/dimos/utils/transform_utils.py @@ -21,10 +21,10 @@ def normalize_angle(angle: float) -> float: """Normalize angle to [-pi, pi] range""" - return np.arctan2(np.sin(angle), np.cos(angle)) + return np.arctan2(np.sin(angle), np.cos(angle)) # type: ignore[no-any-return] -def pose_to_matrix(pose: Pose) -> np.ndarray: +def pose_to_matrix(pose: Pose) -> np.ndarray: # type: ignore[type-arg] """ Convert pose to 4x4 homogeneous transform matrix. @@ -57,7 +57,7 @@ def pose_to_matrix(pose: Pose) -> np.ndarray: return T -def matrix_to_pose(T: np.ndarray) -> Pose: +def matrix_to_pose(T: np.ndarray) -> Pose: # type: ignore[type-arg] """ Convert 4x4 transformation matrix to Pose object. @@ -80,7 +80,7 @@ def matrix_to_pose(T: np.ndarray) -> Pose: return Pose(pos, orientation) -def apply_transform(pose: Pose, transform: np.ndarray | Transform) -> Pose: +def apply_transform(pose: Pose, transform: np.ndarray | Transform) -> Pose: # type: ignore[type-arg] """ Apply a transformation matrix to a pose. @@ -202,7 +202,7 @@ def robot_to_optical_frame(pose: Pose) -> Pose: ) -def yaw_towards_point(position: Vector3, target_point: Vector3 = None) -> float: +def yaw_towards_point(position: Vector3, target_point: Vector3 = None) -> float: # type: ignore[assignment] """ Calculate yaw angle from target point to position (away from target). This is commonly used for object orientation in grasping applications. @@ -219,10 +219,10 @@ def yaw_towards_point(position: Vector3, target_point: Vector3 = None) -> float: target_point = Vector3(0.0, 0.0, 0.0) direction_x = position.x - target_point.x direction_y = position.y - target_point.y - return np.arctan2(direction_y, direction_x) + return np.arctan2(direction_y, direction_x) # type: ignore[no-any-return] -def create_transform_from_6dof(translation: Vector3, euler_angles: Vector3) -> np.ndarray: +def create_transform_from_6dof(translation: Vector3, euler_angles: Vector3) -> np.ndarray: # type: ignore[type-arg] """ Create a 4x4 transformation matrix from 6DOF parameters. @@ -247,7 +247,7 @@ def create_transform_from_6dof(translation: Vector3, euler_angles: Vector3) -> n return T -def invert_transform(T: np.ndarray) -> np.ndarray: +def invert_transform(T: np.ndarray) -> np.ndarray: # type: ignore[type-arg] """ Invert a 4x4 transformation matrix efficiently. @@ -271,7 +271,7 @@ def invert_transform(T: np.ndarray) -> np.ndarray: return T_inv -def compose_transforms(*transforms: np.ndarray) -> np.ndarray: +def compose_transforms(*transforms: np.ndarray) -> np.ndarray: # type: ignore[type-arg] """ Compose multiple transformation matrices. @@ -345,7 +345,7 @@ def get_distance(pose1: Pose | Vector3, pose2: Pose | Vector3) -> float: dy = pose1.y - pose2.y dz = pose1.z - pose2.z - return np.linalg.norm(np.array([dx, dy, dz])) + return np.linalg.norm(np.array([dx, dy, dz])) # type: ignore[return-value] def offset_distance( diff --git a/dimos/web/dimos_interface/api/server.py b/dimos/web/dimos_interface/api/server.py index bddb01495e..e205022b3b 100644 --- a/dimos/web/dimos_interface/api/server.py +++ b/dimos/web/dimos_interface/api/server.py @@ -39,12 +39,12 @@ from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import HTMLResponse, JSONResponse, StreamingResponse from fastapi.templating import Jinja2Templates -import ffmpeg +import ffmpeg # type: ignore[import-untyped] import numpy as np import reactivex as rx from reactivex import operators as ops from reactivex.disposable import SingleAssignmentDisposable -import soundfile as sf +import soundfile as sf # type: ignore[import-untyped] from sse_starlette.sse import EventSourceResponse import uvicorn @@ -55,7 +55,7 @@ class FastAPIServer(EdgeIO): - def __init__( + def __init__( # type: ignore[no-untyped-def] self, dev_name: str = "FastAPI Server", edge_type: str = "Bidirectional", @@ -86,17 +86,17 @@ def __init__( self.streams = streams self.active_streams = {} self.stream_locks = {key: Lock() for key in self.streams} - self.stream_queues = {} - self.stream_disposables = {} + self.stream_queues = {} # type: ignore[var-annotated] + self.stream_disposables = {} # type: ignore[var-annotated] # Initialize text streams self.text_streams = text_streams or {} - self.text_queues = {} + self.text_queues = {} # type: ignore[var-annotated] self.text_disposables = {} - self.text_clients = set() + self.text_clients = set() # type: ignore[var-annotated] # Create a Subject for text queries - self.query_subject = rx.subject.Subject() + self.query_subject = rx.subject.Subject() # type: ignore[var-annotated] self.query_stream = self.query_subject.pipe(ops.share()) self.audio_subject = audio_subject @@ -122,15 +122,15 @@ def __init__( self.setup_routes() print("FastAPIServer initialization complete") # Debug print - def process_frame_fastapi(self, frame): + def process_frame_fastapi(self, frame): # type: ignore[no-untyped-def] """Convert frame to JPEG format for streaming.""" _, buffer = cv2.imencode(".jpg", frame) return buffer.tobytes() - def stream_generator(self, key): + def stream_generator(self, key): # type: ignore[no-untyped-def] """Generate frames for a given video stream.""" - def generate(): + def generate(): # type: ignore[no-untyped-def] if key not in self.stream_queues: self.stream_queues[key] = Queue(maxsize=10) @@ -175,17 +175,17 @@ def generate(): return generate - def create_video_feed_route(self, key): + def create_video_feed_route(self, key): # type: ignore[no-untyped-def] """Create a video feed route for a specific stream.""" - async def video_feed(): + async def video_feed(): # type: ignore[no-untyped-def] return StreamingResponse( - self.stream_generator(key)(), media_type="multipart/x-mixed-replace; boundary=frame" + self.stream_generator(key)(), media_type="multipart/x-mixed-replace; boundary=frame" # type: ignore[no-untyped-call] ) return video_feed - async def text_stream_generator(self, key): + async def text_stream_generator(self, key): # type: ignore[no-untyped-def] """Generate SSE events for text stream.""" client_id = id(object()) self.text_clients.add(client_id) @@ -210,7 +210,7 @@ async def text_stream_generator(self, key): self.text_clients.remove(client_id) @staticmethod - def _decode_audio(raw: bytes) -> tuple[np.ndarray, int]: + def _decode_audio(raw: bytes) -> tuple[np.ndarray, int]: # type: ignore[type-arg] """Convert the webm/opus blob sent by the browser into mono 16-kHz PCM.""" try: # Use ffmpeg to convert to 16-kHz mono 16-bit PCM WAV in memory @@ -234,23 +234,23 @@ def _decode_audio(raw: bytes) -> tuple[np.ndarray, int]: return np.array(audio), sr except Exception as exc: print(f"ffmpeg decoding failed: {exc}") - return None, None + return None, None # type: ignore[return-value] def setup_routes(self) -> None: """Set up FastAPI routes.""" @self.app.get("/streams") - async def get_streams(): + async def get_streams(): # type: ignore[no-untyped-def] """Get list of available video streams""" return {"streams": list(self.streams.keys())} @self.app.get("/text_streams") - async def get_text_streams(): + async def get_text_streams(): # type: ignore[no-untyped-def] """Get list of available text streams""" return {"streams": list(self.text_streams.keys())} @self.app.get("/", response_class=HTMLResponse) - async def index(request: Request): + async def index(request: Request): # type: ignore[no-untyped-def] stream_keys = list(self.streams.keys()) text_stream_keys = list(self.text_streams.keys()) return self.templates.TemplateResponse( @@ -264,7 +264,7 @@ async def index(request: Request): ) @self.app.post("/submit_query") - async def submit_query(query: str = Form(...)): + async def submit_query(query: str = Form(...)): # type: ignore[no-untyped-def] # Using Form directly as a dependency ensures proper form handling try: if query: @@ -280,7 +280,7 @@ async def submit_query(query: str = Form(...)): ) @self.app.post("/upload_audio") - async def upload_audio(file: UploadFile = File(...)): + async def upload_audio(file: UploadFile = File(...)): # type: ignore[no-untyped-def] """Handle audio upload from the browser.""" if self.audio_subject is None: return JSONResponse( @@ -314,12 +314,12 @@ async def upload_audio(file: UploadFile = File(...)): # Unitree API endpoints @self.app.get("/unitree/status") - async def unitree_status(): + async def unitree_status(): # type: ignore[no-untyped-def] """Check the status of the Unitree API server""" return JSONResponse({"status": "online", "service": "unitree"}) @self.app.post("/unitree/command") - async def unitree_command(request: Request): + async def unitree_command(request: Request): # type: ignore[no-untyped-def] """Process commands sent from the terminal frontend""" try: data = await request.json() @@ -343,13 +343,13 @@ async def unitree_command(request: Request): ) @self.app.get("/text_stream/{key}") - async def text_stream(key: str): + async def text_stream(key: str): # type: ignore[no-untyped-def] if key not in self.text_streams: raise HTTPException(status_code=404, detail=f"Text stream '{key}' not found") - return EventSourceResponse(self.text_stream_generator(key)) + return EventSourceResponse(self.text_stream_generator(key)) # type: ignore[no-untyped-call] for key in self.streams: - self.app.get(f"/video_feed/{key}")(self.create_video_feed_route(key)) + self.app.get(f"/video_feed/{key}")(self.create_video_feed_route(key)) # type: ignore[no-untyped-call] def run(self) -> None: """Run the FastAPI server.""" diff --git a/dimos/web/fastapi_server.py b/dimos/web/fastapi_server.py index 6c8a85344a..bc4f3ae223 100644 --- a/dimos/web/fastapi_server.py +++ b/dimos/web/fastapi_server.py @@ -44,7 +44,7 @@ class FastAPIServer(EdgeIO): - def __init__( + def __init__( # type: ignore[no-untyped-def] self, dev_name: str = "FastAPI Server", edge_type: str = "Bidirectional", @@ -62,17 +62,17 @@ def __init__( self.streams = streams self.active_streams = {} self.stream_locks = {key: Lock() for key in self.streams} - self.stream_queues = {} - self.stream_disposables = {} + self.stream_queues = {} # type: ignore[var-annotated] + self.stream_disposables = {} # type: ignore[var-annotated] # Initialize text streams self.text_streams = text_streams or {} - self.text_queues = {} + self.text_queues = {} # type: ignore[var-annotated] self.text_disposables = {} - self.text_clients = set() + self.text_clients = set() # type: ignore[var-annotated] # Create a Subject for text queries - self.query_subject = rx.subject.Subject() + self.query_subject = rx.subject.Subject() # type: ignore[var-annotated] self.query_stream = self.query_subject.pipe(ops.share()) for key in self.streams: @@ -95,15 +95,15 @@ def __init__( self.setup_routes() - def process_frame_fastapi(self, frame): + def process_frame_fastapi(self, frame): # type: ignore[no-untyped-def] """Convert frame to JPEG format for streaming.""" _, buffer = cv2.imencode(".jpg", frame) return buffer.tobytes() - def stream_generator(self, key): + def stream_generator(self, key): # type: ignore[no-untyped-def] """Generate frames for a given video stream.""" - def generate(): + def generate(): # type: ignore[no-untyped-def] if key not in self.stream_queues: self.stream_queues[key] = Queue(maxsize=10) @@ -148,17 +148,17 @@ def generate(): return generate - def create_video_feed_route(self, key): + def create_video_feed_route(self, key): # type: ignore[no-untyped-def] """Create a video feed route for a specific stream.""" - async def video_feed(): + async def video_feed(): # type: ignore[no-untyped-def] return StreamingResponse( - self.stream_generator(key)(), media_type="multipart/x-mixed-replace; boundary=frame" + self.stream_generator(key)(), media_type="multipart/x-mixed-replace; boundary=frame" # type: ignore[no-untyped-call] ) return video_feed - async def text_stream_generator(self, key): + async def text_stream_generator(self, key): # type: ignore[no-untyped-def] """Generate SSE events for text stream.""" client_id = id(object()) self.text_clients.add(client_id) @@ -181,7 +181,7 @@ def setup_routes(self) -> None: """Set up FastAPI routes.""" @self.app.get("/", response_class=HTMLResponse) - async def index(request: Request): + async def index(request: Request): # type: ignore[no-untyped-def] stream_keys = list(self.streams.keys()) text_stream_keys = list(self.text_streams.keys()) return self.templates.TemplateResponse( @@ -194,7 +194,7 @@ async def index(request: Request): ) @self.app.post("/submit_query") - async def submit_query(query: str = Form(...)): + async def submit_query(query: str = Form(...)): # type: ignore[no-untyped-def] # Using Form directly as a dependency ensures proper form handling try: if query: @@ -210,13 +210,13 @@ async def submit_query(query: str = Form(...)): ) @self.app.get("/text_stream/{key}") - async def text_stream(key: str): + async def text_stream(key: str): # type: ignore[no-untyped-def] if key not in self.text_streams: raise HTTPException(status_code=404, detail=f"Text stream '{key}' not found") - return EventSourceResponse(self.text_stream_generator(key)) + return EventSourceResponse(self.text_stream_generator(key)) # type: ignore[no-untyped-call] for key in self.streams: - self.app.get(f"/video_feed/{key}")(self.create_video_feed_route(key)) + self.app.get(f"/video_feed/{key}")(self.create_video_feed_route(key)) # type: ignore[no-untyped-call] def run(self) -> None: """Run the FastAPI server.""" diff --git a/dimos/web/flask_server.py b/dimos/web/flask_server.py index b0cf6fc143..94435629d8 100644 --- a/dimos/web/flask_server.py +++ b/dimos/web/flask_server.py @@ -23,7 +23,7 @@ class FlaskServer(EdgeIO): - def __init__( + def __init__( # type: ignore[no-untyped-def] self, dev_name: str = "Flask Server", edge_type: str = "Bidirectional", @@ -46,21 +46,21 @@ def __init__( self.setup_routes() - def process_frame_flask(self, frame): + def process_frame_flask(self, frame): # type: ignore[no-untyped-def] """Convert frame to JPEG format for streaming.""" _, buffer = cv2.imencode(".jpg", frame) return buffer.tobytes() def setup_routes(self) -> None: @self.app.route("/") - def index(): + def index(): # type: ignore[no-untyped-def] stream_keys = list(self.streams.keys()) # Get the keys from the streams dictionary return render_template("index_flask.html", stream_keys=stream_keys) # Function to create a streaming response - def stream_generator(key): - def generate(): - frame_queue = Queue() + def stream_generator(key): # type: ignore[no-untyped-def] + def generate(): # type: ignore[no-untyped-def] + frame_queue = Queue() # type: ignore[var-annotated] disposable = SingleAssignmentDisposable() # Subscribe to the shared, ref-counted stream @@ -82,10 +82,10 @@ def generate(): return generate - def make_response_generator(key): - def response_generator(): + def make_response_generator(key): # type: ignore[no-untyped-def] + def response_generator(): # type: ignore[no-untyped-def] return Response( - stream_generator(key)(), mimetype="multipart/x-mixed-replace; boundary=frame" + stream_generator(key)(), mimetype="multipart/x-mixed-replace; boundary=frame" # type: ignore[no-untyped-call] ) return response_generator @@ -94,7 +94,7 @@ def response_generator(): for key in self.streams: endpoint = f"video_feed_{key}" self.app.add_url_rule( - f"/video_feed/{key}", endpoint, view_func=make_response_generator(key) + f"/video_feed/{key}", endpoint, view_func=make_response_generator(key) # type: ignore[no-untyped-call] ) def run(self, host: str = "0.0.0.0", port: int = 5555, threaded: bool = True) -> None: diff --git a/dimos/web/robot_web_interface.py b/dimos/web/robot_web_interface.py index 0dc7636ac9..6c78c36292 100644 --- a/dimos/web/robot_web_interface.py +++ b/dimos/web/robot_web_interface.py @@ -23,7 +23,7 @@ class RobotWebInterface(FastAPIServer): """Wrapper class for the dimos-interface FastAPI server.""" - def __init__(self, port: int = 5555, text_streams=None, audio_subject=None, **streams) -> None: + def __init__(self, port: int = 5555, text_streams=None, audio_subject=None, **streams) -> None: # type: ignore[no-untyped-def] super().__init__( dev_name="Robot Web Interface", edge_type="Bidirectional", diff --git a/dimos/web/websocket_vis/costmap_viz.py b/dimos/web/websocket_vis/costmap_viz.py index ec2088b3b8..fcc98e67ac 100644 --- a/dimos/web/websocket_vis/costmap_viz.py +++ b/dimos/web/websocket_vis/costmap_viz.py @@ -30,7 +30,7 @@ def __init__(self, occupancy_grid: OccupancyGrid | None = None) -> None: self.occupancy_grid = occupancy_grid @property - def data(self) -> np.ndarray | None: + def data(self) -> np.ndarray | None: # type: ignore[type-arg] """Get the costmap data as a numpy array.""" if self.occupancy_grid: return self.occupancy_grid.grid @@ -58,7 +58,7 @@ def resolution(self) -> float: return 1.0 @property - def origin(self): + def origin(self): # type: ignore[no-untyped-def] """Get the origin pose of the costmap.""" if self.occupancy_grid: return self.occupancy_grid.origin diff --git a/dimos/web/websocket_vis/optimized_costmap.py b/dimos/web/websocket_vis/optimized_costmap.py index 03307ff2c0..501eaf0e35 100644 --- a/dimos/web/websocket_vis/optimized_costmap.py +++ b/dimos/web/websocket_vis/optimized_costmap.py @@ -30,12 +30,12 @@ class OptimizedCostmapEncoder: def __init__(self, chunk_size: int = 64) -> None: self.chunk_size = chunk_size - self.last_full_grid: np.ndarray | None = None + self.last_full_grid: np.ndarray | None = None # type: ignore[type-arg] self.last_full_sent_time: float = 0 # Track when last full update was sent self.chunk_hashes: dict[tuple[int, int], str] = {} self.full_update_interval = 3.0 # Send full update every 3 seconds - def encode_costmap(self, grid: np.ndarray, force_full: bool = False) -> dict[str, Any]: + def encode_costmap(self, grid: np.ndarray, force_full: bool = False) -> dict[str, Any]: # type: ignore[type-arg] """Encode a costmap grid with optimizations. Args: @@ -60,7 +60,7 @@ def encode_costmap(self, grid: np.ndarray, force_full: bool = False) -> dict[str else: return self._encode_delta(grid, current_time) - def _encode_full(self, grid: np.ndarray, current_time: float) -> dict[str, Any]: + def _encode_full(self, grid: np.ndarray, current_time: float) -> dict[str, Any]: # type: ignore[type-arg] height, width = grid.shape # Convert to uint8 for better compression (costmap values are -1 to 100) @@ -89,7 +89,7 @@ def _encode_full(self, grid: np.ndarray, current_time: float) -> dict[str, Any]: "data": encoded, } - def _encode_delta(self, grid: np.ndarray, current_time: float) -> dict[str, Any]: + def _encode_delta(self, grid: np.ndarray, current_time: float) -> dict[str, Any]: # type: ignore[type-arg] height, width = grid.shape changed_chunks = [] @@ -146,7 +146,7 @@ def _encode_delta(self, grid: np.ndarray, current_time: float) -> dict[str, Any] "chunks": changed_chunks, } - def _update_chunk_hashes(self, grid: np.ndarray) -> None: + def _update_chunk_hashes(self, grid: np.ndarray) -> None: # type: ignore[type-arg] """Update all chunk hashes for the grid.""" self.chunk_hashes.clear() height, width = grid.shape diff --git a/dimos/web/websocket_vis/path_history.py b/dimos/web/websocket_vis/path_history.py index f60031bc51..baa0d63904 100644 --- a/dimos/web/websocket_vis/path_history.py +++ b/dimos/web/websocket_vis/path_history.py @@ -23,7 +23,7 @@ class PathHistory: """A simple container for storing a history of positions for visualization.""" - def __init__(self, points: list[Vector3 | tuple | list] | None = None) -> None: + def __init__(self, points: list[Vector3 | tuple | list] | None = None) -> None: # type: ignore[type-arg] """Initialize with optional list of points.""" self.points: list[Vector3] = [] if points: @@ -33,7 +33,7 @@ def __init__(self, points: list[Vector3 | tuple | list] | None = None) -> None: else: self.points.append(Vector3(*p)) - def ipush(self, point: Vector3 | tuple | list) -> "PathHistory": + def ipush(self, point: Vector3 | tuple | list) -> "PathHistory": # type: ignore[type-arg] """Add a point to the history (in-place) and return self.""" if isinstance(point, Vector3): self.points.append(point) diff --git a/dimos/web/websocket_vis/websocket_vis_module.py b/dimos/web/websocket_vis/websocket_vis_module.py index 91e0428f33..9926c0cd0b 100644 --- a/dimos/web/websocket_vis/websocket_vis_module.py +++ b/dimos/web/websocket_vis/websocket_vis_module.py @@ -23,9 +23,9 @@ import time from typing import Any -from dimos_lcm.std_msgs import Bool +from dimos_lcm.std_msgs import Bool # type: ignore[import-untyped] from reactivex.disposable import Disposable -import socketio +import socketio # type: ignore[import-untyped] from starlette.applications import Starlette from starlette.responses import HTMLResponse from starlette.routing import Route @@ -62,20 +62,20 @@ class WebsocketVisModule(Module): """ # LCM inputs - odom: In[PoseStamped] = None - gps_location: In[LatLon] = None - path: In[Path] = None - global_costmap: In[OccupancyGrid] = None + odom: In[PoseStamped] = None # type: ignore[assignment] + gps_location: In[LatLon] = None # type: ignore[assignment] + path: In[Path] = None # type: ignore[assignment] + global_costmap: In[OccupancyGrid] = None # type: ignore[assignment] # LCM outputs - goal_request: Out[PoseStamped] = None - gps_goal: Out[LatLon] = None - explore_cmd: Out[Bool] = None - stop_explore_cmd: Out[Bool] = None - cmd_vel: Out[Twist] = None - movecmd_stamped: Out[TwistStamped] = None - - def __init__(self, port: int = 7779, **kwargs) -> None: + goal_request: Out[PoseStamped] = None # type: ignore[assignment] + gps_goal: Out[LatLon] = None # type: ignore[assignment] + explore_cmd: Out[Bool] = None # type: ignore[assignment] + stop_explore_cmd: Out[Bool] = None # type: ignore[assignment] + cmd_vel: Out[Twist] = None # type: ignore[assignment] + movecmd_stamped: Out[TwistStamped] = None # type: ignore[assignment] + + def __init__(self, port: int = 7779, **kwargs) -> None: # type: ignore[no-untyped-def] """Initialize the WebSocket visualization module. Args: @@ -91,7 +91,7 @@ def __init__(self, port: int = 7779, **kwargs) -> None: self._broadcast_thread = None self._uvicorn_server: uvicorn.Server | None = None - self.vis_state = {} + self.vis_state = {} # type: ignore[var-annotated] self.state_lock = threading.Lock() self.costmap_encoder = OptimizedCostmapEncoder(chunk_size=64) @@ -100,17 +100,17 @@ def __init__(self, port: int = 7779, **kwargs) -> None: def _start_broadcast_loop(self) -> None: def websocket_vis_loop() -> None: - self._broadcast_loop = asyncio.new_event_loop() + self._broadcast_loop = asyncio.new_event_loop() # type: ignore[assignment] asyncio.set_event_loop(self._broadcast_loop) try: - self._broadcast_loop.run_forever() + self._broadcast_loop.run_forever() # type: ignore[attr-defined] except Exception as e: logger.error(f"Broadcast loop error: {e}") finally: - self._broadcast_loop.close() + self._broadcast_loop.close() # type: ignore[attr-defined] - self._broadcast_thread = threading.Thread(target=websocket_vis_loop, daemon=True) - self._broadcast_thread.start() + self._broadcast_thread = threading.Thread(target=websocket_vis_loop, daemon=True) # type: ignore[assignment] + self._broadcast_thread.start() # type: ignore[attr-defined] @rpc def start(self) -> None: @@ -177,7 +177,7 @@ def _create_server(self) -> None: # Create SocketIO server self.sio = socketio.AsyncServer(async_mode="asgi", cors_allowed_origins="*") - async def serve_index(request): + async def serve_index(request): # type: ignore[no-untyped-def] return HTMLResponse("Use the extension.") routes = [Route("/", serve_index)] @@ -186,43 +186,43 @@ async def serve_index(request): self.app = socketio.ASGIApp(self.sio, starlette_app) # Register SocketIO event handlers - @self.sio.event - async def connect(sid, environ) -> None: + @self.sio.event # type: ignore[misc] + async def connect(sid, environ) -> None: # type: ignore[no-untyped-def] with self.state_lock: current_state = dict(self.vis_state) # Force full costmap update on new connection self.costmap_encoder.last_full_grid = None - await self.sio.emit("full_state", current_state, room=sid) + await self.sio.emit("full_state", current_state, room=sid) # type: ignore[union-attr] - @self.sio.event - async def click(sid, position) -> None: + @self.sio.event # type: ignore[misc] + async def click(sid, position) -> None: # type: ignore[no-untyped-def] goal = PoseStamped( position=(position[0], position[1], 0), orientation=(0, 0, 0, 1), # Default orientation frame_id="world", ) - self.goal_request.publish(goal) + self.goal_request.publish(goal) # type: ignore[no-untyped-call] logger.info(f"Click goal published: ({goal.position.x:.2f}, {goal.position.y:.2f})") - @self.sio.event - async def gps_goal(sid, goal) -> None: + @self.sio.event # type: ignore[misc] + async def gps_goal(sid, goal) -> None: # type: ignore[no-untyped-def] logger.info(f"Set GPS goal: {goal}") - self.gps_goal.publish(LatLon(lat=goal["lat"], lon=goal["lon"])) + self.gps_goal.publish(LatLon(lat=goal["lat"], lon=goal["lon"])) # type: ignore[no-untyped-call] - @self.sio.event - async def start_explore(sid) -> None: + @self.sio.event # type: ignore[misc] + async def start_explore(sid) -> None: # type: ignore[no-untyped-def] logger.info("Starting exploration") - self.explore_cmd.publish(Bool(data=True)) + self.explore_cmd.publish(Bool(data=True)) # type: ignore[no-untyped-call] - @self.sio.event - async def stop_explore(sid) -> None: + @self.sio.event # type: ignore[misc] + async def stop_explore(sid) -> None: # type: ignore[no-untyped-def] logger.info("Stopping exploration") - self.stop_explore_cmd.publish(Bool(data=True)) + self.stop_explore_cmd.publish(Bool(data=True)) # type: ignore[no-untyped-call] - @self.sio.event - async def move_command(sid, data) -> None: + @self.sio.event # type: ignore[misc] + async def move_command(sid, data) -> None: # type: ignore[no-untyped-def] # Publish Twist if transport is configured if self.cmd_vel and self.cmd_vel.transport: twist = Twist( @@ -231,7 +231,7 @@ async def move_command(sid, data) -> None: data["angular"]["x"], data["angular"]["y"], data["angular"]["z"] ), ) - self.cmd_vel.publish(twist) + self.cmd_vel.publish(twist) # type: ignore[no-untyped-call] # Publish TwistStamped if transport is configured if self.movecmd_stamped and self.movecmd_stamped.transport: @@ -243,11 +243,11 @@ async def move_command(sid, data) -> None: data["angular"]["x"], data["angular"]["y"], data["angular"]["z"] ), ) - self.movecmd_stamped.publish(twist_stamped) + self.movecmd_stamped.publish(twist_stamped) # type: ignore[no-untyped-call] def _run_uvicorn_server(self) -> None: config = uvicorn.Config( - self.app, + self.app, # type: ignore[arg-type] host="0.0.0.0", port=self.port, log_level="error", # Reduce verbosity diff --git a/mypy_strict.ini b/mypy_strict.ini deleted file mode 100644 index ed49020e9b..0000000000 --- a/mypy_strict.ini +++ /dev/null @@ -1,30 +0,0 @@ -[mypy] -python_version = 3.10 -strict = True -exclude = ^dimos/models/Detic(/|$)|.*/test_.|.*/conftest.py* - -# Enable all optional error checks individually (redundant with strict=True, but explicit) -warn_unused_configs = True -warn_unused_ignores = True -warn_redundant_casts = True -warn_no_return = True -warn_return_any = True -warn_unreachable = True -disallow_untyped_calls = True -disallow_untyped_defs = True -disallow_incomplete_defs = True -disallow_untyped_decorators = True -disallow_any_generics = True -no_implicit_optional = True -check_untyped_defs = True -strict_optional = True -ignore_missing_imports = False -show_error_context = True -show_column_numbers = True -pretty = True -color_output = True -error_summary = True - -# Performance and caching -incremental = True -cache_dir = .mypy_cache_strict diff --git a/pyproject.toml b/pyproject.toml index 4f2d866ffa..eee88f14a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -263,13 +263,10 @@ combine-as-imports = true force-sort-within-sections = true [tool.mypy] -# mypy doesn't understand plum @dispatch decorator -# so we gave up on this check globally -disable_error_code = ["no-redef", "import-untyped", "import-not-found"] -files = [ - "dimos/msgs/**/*.py", - "dimos/protocol/**/*.py" -] +python_version = "3.12" +incremental = true +strict = true +exclude = "^dimos/models/Detic(/|$)|.*/test_.|.*/conftest.py*" [tool.pytest.ini_options] testpaths = ["dimos"] From 17c3d2ccb2ef872efd0eeb13adc386c3b0fc5b15 Mon Sep 17 00:00:00 2001 From: paul-nechifor <1262969+paul-nechifor@users.noreply.github.com> Date: Tue, 25 Nov 2025 03:25:24 +0000 Subject: [PATCH 2/3] CI code cleanup --- dimos/agents/agent.py | 13 +++++++--- dimos/agents/memory/image_embedding.py | 6 ++++- dimos/agents/memory/spatial_vector_db.py | 6 ++++- dimos/agents2/agent.py | 3 ++- dimos/agents2/skills/osm.py | 3 ++- dimos/agents2/spec.py | 3 ++- dimos/core/transport.py | 3 ++- dimos/manipulation/manip_aio_pipeline.py | 8 +++++-- dimos/manipulation/manip_aio_processer.py | 22 +++++++++++++---- .../visual_servoing/detection3d.py | 5 +++- dimos/manipulation/visual_servoing/utils.py | 4 +++- dimos/mapping/osm/current_location_map.py | 5 +++- dimos/msgs/foxglove_msgs/ImageAnnotations.py | 4 +++- dimos/msgs/geometry_msgs/Pose.py | 11 +++++++-- .../msgs/geometry_msgs/PoseWithCovariance.py | 12 +++++++--- .../PoseWithCovarianceStamped.py | 8 +++++-- dimos/msgs/geometry_msgs/Twist.py | 5 +++- .../msgs/geometry_msgs/TwistWithCovariance.py | 8 +++++-- .../TwistWithCovarianceStamped.py | 8 +++++-- dimos/msgs/nav_msgs/OccupancyGrid.py | 5 +++- dimos/msgs/nav_msgs/__init__.py | 6 ++++- dimos/msgs/sensor_msgs/CameraInfo.py | 5 +++- dimos/msgs/sensor_msgs/Image.py | 5 +++- dimos/msgs/sensor_msgs/PointCloud2.py | 10 ++++++-- .../sensor_msgs/image_impls/AbstractImage.py | 4 +++- .../msgs/sensor_msgs/image_impls/CudaImage.py | 24 ++++++++++++++----- dimos/msgs/std_msgs/Header.py | 4 +++- dimos/msgs/tf2_msgs/TFMessage.py | 4 +++- dimos/msgs/vision_msgs/BoundingBox2DArray.py | 4 +++- dimos/msgs/vision_msgs/BoundingBox3DArray.py | 4 +++- dimos/msgs/vision_msgs/Detection2DArray.py | 4 +++- dimos/msgs/vision_msgs/Detection3DArray.py | 4 +++- .../local_planner/holonomic_local_planner.py | 4 +++- dimos/navigation/rosnav.py | 5 +++- dimos/perception/common/utils.py | 17 +++++++++---- dimos/perception/detection/detectors/detic.py | 4 +++- dimos/perception/detection/module2D.py | 4 +++- dimos/perception/detection/module3D.py | 4 +++- dimos/perception/detection/moduleDB.py | 4 +++- .../detection/reid/embedding_id_system.py | 4 +++- .../detection/type/detection2d/base.py | 5 +++- .../detection/type/detection2d/person.py | 5 +++- .../detection/type/detection3d/pointcloud.py | 13 ++++++++-- .../grasp_generation/grasp_generation.py | 8 +++++-- dimos/perception/grasp_generation/utils.py | 3 ++- dimos/perception/object_detection_stream.py | 4 +++- dimos/perception/object_tracker_3d.py | 5 +++- dimos/perception/pointcloud/cuboid_fit.py | 12 +++++++--- .../pointcloud/pointcloud_filtering.py | 20 ++++++++++++---- dimos/perception/spatial_perception.py | 3 ++- dimos/robot/foxglove_bridge.py | 4 +++- dimos/robot/ros_control.py | 4 +++- dimos/robot/ros_transform.py | 4 +++- dimos/robot/unitree/connection/connection.py | 6 ++++- dimos/robot/unitree_webrtc/connection.py | 6 ++++- dimos/robot/unitree_webrtc/modular/detect.py | 9 +++++-- dimos/robot/unitree_webrtc/unitree_g1.py | 10 ++++++-- dimos/skills/manipulation/pick_and_place.py | 8 +++++-- .../manipulation/rotation_constraint_skill.py | 4 +++- dimos/stream/audio/base.py | 6 ++++- dimos/stream/frame_processor.py | 5 +++- dimos/stream/video_operators.py | 7 ++++-- dimos/types/ros_polyfill.py | 7 +++++- dimos/types/sample.py | 8 +++++-- dimos/types/timestamped.py | 3 ++- dimos/utils/cli/lcmspy/lcmspy.py | 3 ++- dimos/web/dimos_interface/api/server.py | 3 ++- dimos/web/fastapi_server.py | 3 ++- dimos/web/flask_server.py | 7 ++++-- 69 files changed, 347 insertions(+), 104 deletions(-) diff --git a/dimos/agents/agent.py b/dimos/agents/agent.py index f5bb6d49ee..428ee78b61 100644 --- a/dimos/agents/agent.py +++ b/dimos/agents/agent.py @@ -369,7 +369,10 @@ def _tooling_callback(message, messages, response_message, skill_library: SkillL if response_message.tool_calls is not None: return _tooling_callback( - response_message, messages, response_message, self.skill_library # type: ignore[attr-defined] + response_message, + messages, + response_message, + self.skill_library, # type: ignore[attr-defined] ) return None @@ -472,7 +475,9 @@ def _log_response_to_file(self, response, output_dir: str | None = None) -> None logger.info(f"LLM Response [{self.dev_name}]: {response}") def subscribe_to_image_processing( # type: ignore[no-untyped-def] - self, frame_observable: Observable, query_extractor=None # type: ignore[type-arg] + self, + frame_observable: Observable, + query_extractor=None, # type: ignore[type-arg] ) -> Disposable: """Subscribes to a stream of video frames for processing. @@ -672,7 +677,9 @@ def run_observable_query(self, query_text: str, **kwargs) -> Observable: # type """ return create( lambda observer, _: self._observable_query( - observer, incoming_query=query_text, **kwargs # type: ignore[arg-type] + observer, + incoming_query=query_text, + **kwargs, # type: ignore[arg-type] ) ) diff --git a/dimos/agents/memory/image_embedding.py b/dimos/agents/memory/image_embedding.py index c06bb30989..1c2464f9b0 100644 --- a/dimos/agents/memory/image_embedding.py +++ b/dimos/agents/memory/image_embedding.py @@ -64,7 +64,11 @@ def _initialize_model(self): # type: ignore[no-untyped-def] try: import onnxruntime as ort # type: ignore[import-untyped] import torch - from transformers import AutoFeatureExtractor, AutoModel, CLIPProcessor # type: ignore[import-untyped] + from transformers import ( # type: ignore[import-untyped] + AutoFeatureExtractor, + AutoModel, + CLIPProcessor, + ) if self.model_name == "clip": model_id = get_data("models_clip") / "model.onnx" diff --git a/dimos/agents/memory/spatial_vector_db.py b/dimos/agents/memory/spatial_vector_db.py index 46c1f04464..c270d93863 100644 --- a/dimos/agents/memory/spatial_vector_db.py +++ b/dimos/agents/memory/spatial_vector_db.py @@ -109,7 +109,11 @@ def __init__( # type: ignore[no-untyped-def] ) def add_image_vector( - self, vector_id: str, image: np.ndarray, embedding: np.ndarray, metadata: dict[str, Any] # type: ignore[type-arg] + self, + vector_id: str, + image: np.ndarray, + embedding: np.ndarray, + metadata: dict[str, Any], # type: ignore[type-arg] ) -> None: """ Add an image with its embedding and metadata to the vector database. diff --git a/dimos/agents2/agent.py b/dimos/agents2/agent.py index 840a220ef7..956783e63a 100644 --- a/dimos/agents2/agent.py +++ b/dimos/agents2/agent.py @@ -314,7 +314,8 @@ def _get_state() -> str: self.state_messages = snapshot_msgs.get("state_msgs", []) # type: ignore[attr-defined] self.append_history( - *snapshot_msgs.get("tool_msgs", []), *snapshot_msgs.get("history_msgs", []) # type: ignore[attr-defined] + *snapshot_msgs.get("tool_msgs", []), + *snapshot_msgs.get("history_msgs", []), # type: ignore[attr-defined] ) except Exception as e: diff --git a/dimos/agents2/skills/osm.py b/dimos/agents2/skills/osm.py index f2a088a2ab..e2a1a689fe 100644 --- a/dimos/agents2/skills/osm.py +++ b/dimos/agents2/skills/osm.py @@ -62,7 +62,8 @@ def street_map_query(self, query_sentence: str) -> str: self._current_location_map.update_position(self._latest_location) # type: ignore[arg-type] location = self._current_location_map.query_for_one_position_and_context( - query_sentence, self._latest_location # type: ignore[arg-type] + query_sentence, + self._latest_location, # type: ignore[arg-type] ) if not location: return "Could not find anything." diff --git a/dimos/agents2/spec.py b/dimos/agents2/spec.py index b604c7c366..e20d622610 100644 --- a/dimos/agents2/spec.py +++ b/dimos/agents2/spec.py @@ -200,7 +200,8 @@ def __str__(self) -> str: ) else: table.add_row( - Text("Agent", style="magenta"), Text(message.content, style="magenta") # type: ignore[arg-type] + Text("Agent", style="magenta"), + Text(message.content, style="magenta"), # type: ignore[arg-type] ) for tool_call in message.tool_calls: diff --git a/dimos/core/transport.py b/dimos/core/transport.py index b64645001b..f46346d5c7 100644 --- a/dimos/core/transport.py +++ b/dimos/core/transport.py @@ -223,7 +223,8 @@ def dask_register_subscriber(self, remoteInput: RemoteIn[T]) -> None: def subscribe(self, callback: Callable[[T], None], selfstream: In[T]) -> None: # type: ignore[override] if not self._started: selfstream.connection.owner.dask_register_subscriber( # type: ignore[union-attr] - selfstream.connection.name, selfstream # type: ignore[union-attr] + selfstream.connection.name, + selfstream, # type: ignore[union-attr] ).result() self._started = True self.subscribers.append(callback) diff --git a/dimos/manipulation/manip_aio_pipeline.py b/dimos/manipulation/manip_aio_pipeline.py index 4a10147fba..a741eed0c0 100644 --- a/dimos/manipulation/manip_aio_pipeline.py +++ b/dimos/manipulation/manip_aio_pipeline.py @@ -28,7 +28,9 @@ import websockets from dimos.perception.common.utils import colorize_depth -from dimos.perception.detection2d.detic_2d_det import Detic2DDetector # type: ignore[import-untyped] +from dimos.perception.detection2d.detic_2d_det import ( + Detic2DDetector, # type: ignore[import-untyped] +) from dimos.perception.grasp_generation.utils import draw_grasps_on_image from dimos.perception.object_detection_stream import ObjectDetectionStream from dimos.perception.pointcloud.pointcloud_filtering import PointcloudFiltering @@ -317,7 +319,9 @@ def run_loop() -> None: time.sleep(0.01) async def _send_grasp_request( - self, points: np.ndarray, colors: np.ndarray | None # type: ignore[type-arg] + self, + points: np.ndarray, + colors: np.ndarray | None, # type: ignore[type-arg] ) -> list[dict] | None: # type: ignore[type-arg] """Send grasp request to Dimensional Grasp server.""" try: diff --git a/dimos/manipulation/manip_aio_processer.py b/dimos/manipulation/manip_aio_processer.py index 4b39d87233..905e71a15e 100644 --- a/dimos/manipulation/manip_aio_processer.py +++ b/dimos/manipulation/manip_aio_processer.py @@ -27,7 +27,9 @@ combine_object_data, detection_results_to_object_data, ) -from dimos.perception.detection2d.detic_2d_det import Detic2DDetector # type: ignore[import-untyped] +from dimos.perception.detection2d.detic_2d_det import ( + Detic2DDetector, # type: ignore[import-untyped] +) from dimos.perception.grasp_generation.grasp_generation import HostedGraspGenerator from dimos.perception.grasp_generation.utils import create_grasp_overlay from dimos.perception.pointcloud.pointcloud_filtering import PointcloudFiltering @@ -119,7 +121,10 @@ def __init__( ) def process_frame( - self, rgb_image: np.ndarray, depth_image: np.ndarray, generate_grasps: bool | None = None # type: ignore[type-arg] + self, + rgb_image: np.ndarray, + depth_image: np.ndarray, + generate_grasps: bool | None = None, # type: ignore[type-arg] ) -> dict[str, Any]: """ Process a single RGB-D frame through the complete pipeline. @@ -191,7 +196,9 @@ def process_frame( # Combine all objects using intelligent duplicate removal all_objects = combine_object_data( - detected_objects, segmentation_filtered_objects, overlap_threshold=0.8 # type: ignore[arg-type] + detected_objects, + segmentation_filtered_objects, + overlap_threshold=0.8, # type: ignore[arg-type] ) # Get full point cloud @@ -329,12 +336,17 @@ def run_object_detection(self, rgb_image: np.ndarray) -> dict[str, Any]: # type return {"objects": [], "viz_frame": rgb_image.copy()} def run_pointcloud_filtering( - self, rgb_image: np.ndarray, depth_image: np.ndarray, objects: list[dict] # type: ignore[type-arg] + self, + rgb_image: np.ndarray, + depth_image: np.ndarray, + objects: list[dict], # type: ignore[type-arg] ) -> list[dict]: # type: ignore[type-arg] """Run point cloud filtering on detected objects.""" try: filtered_objects = self.pointcloud_filter.process_images( - rgb_image, depth_image, objects # type: ignore[arg-type] + rgb_image, + depth_image, + objects, # type: ignore[arg-type] ) return filtered_objects if filtered_objects else [] # type: ignore[return-value] except Exception as e: diff --git a/dimos/manipulation/visual_servoing/detection3d.py b/dimos/manipulation/visual_servoing/detection3d.py index d564dee3b1..982e28ef31 100644 --- a/dimos/manipulation/visual_servoing/detection3d.py +++ b/dimos/manipulation/visual_servoing/detection3d.py @@ -91,7 +91,10 @@ def __init__( ) def process_frame( - self, rgb_image: np.ndarray, depth_image: np.ndarray, transform: np.ndarray | None = None # type: ignore[type-arg] + self, + rgb_image: np.ndarray, + depth_image: np.ndarray, + transform: np.ndarray | None = None, # type: ignore[type-arg] ) -> tuple[Detection3DArray, Detection2DArray]: """ Process a single RGB-D frame to extract 3D object detections. diff --git a/dimos/manipulation/visual_servoing/utils.py b/dimos/manipulation/visual_servoing/utils.py index 2edca60e44..2de8d11c6d 100644 --- a/dimos/manipulation/visual_servoing/utils.py +++ b/dimos/manipulation/visual_servoing/utils.py @@ -440,7 +440,9 @@ def parse_zed_pose(zed_pose_data: dict[str, Any]) -> Pose | None: def estimate_object_depth( - depth_image: np.ndarray, segmentation_mask: np.ndarray | None, bbox: list[float] # type: ignore[type-arg] + depth_image: np.ndarray, + segmentation_mask: np.ndarray | None, + bbox: list[float], # type: ignore[type-arg] ) -> float: """ Estimate object depth dimension using segmentation mask and depth data. diff --git a/dimos/mapping/osm/current_location_map.py b/dimos/mapping/osm/current_location_map.py index 168350d5b3..deca8a70aa 100644 --- a/dimos/mapping/osm/current_location_map.py +++ b/dimos/mapping/osm/current_location_map.py @@ -46,7 +46,10 @@ def query_for_one_position_and_context( self, query: str, robot_position: LatLon ) -> tuple[LatLon, str] | None: return query_for_one_position_and_context( - self._vl_model, self._get_current_map(), query, robot_position # type: ignore[no-untyped-call] + self._vl_model, + self._get_current_map(), + query, + robot_position, # type: ignore[no-untyped-call] ) def _get_current_map(self): # type: ignore[no-untyped-def] diff --git a/dimos/msgs/foxglove_msgs/ImageAnnotations.py b/dimos/msgs/foxglove_msgs/ImageAnnotations.py index 6ee5f1228d..5375eea93b 100644 --- a/dimos/msgs/foxglove_msgs/ImageAnnotations.py +++ b/dimos/msgs/foxglove_msgs/ImageAnnotations.py @@ -12,7 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from dimos_lcm.foxglove_msgs.ImageAnnotations import ImageAnnotations as FoxgloveImageAnnotations # type: ignore[import-untyped] +from dimos_lcm.foxglove_msgs.ImageAnnotations import ( + ImageAnnotations as FoxgloveImageAnnotations, # type: ignore[import-untyped] +) class ImageAnnotations(FoxgloveImageAnnotations): # type: ignore[misc] diff --git a/dimos/msgs/geometry_msgs/Pose.py b/dimos/msgs/geometry_msgs/Pose.py index 6eb9b3aff1..a6e428cf25 100644 --- a/dimos/msgs/geometry_msgs/Pose.py +++ b/dimos/msgs/geometry_msgs/Pose.py @@ -16,10 +16,17 @@ from typing import TypeAlias -from dimos_lcm.geometry_msgs import Pose as LCMPose, Transform as LCMTransform # type: ignore[import-untyped] +from dimos_lcm.geometry_msgs import ( # type: ignore[import-untyped] + Pose as LCMPose, + Transform as LCMTransform, +) try: - from geometry_msgs.msg import Point as ROSPoint, Pose as ROSPose, Quaternion as ROSQuaternion # type: ignore[attr-defined] + from geometry_msgs.msg import ( # type: ignore[attr-defined] + Point as ROSPoint, + Pose as ROSPose, + Quaternion as ROSQuaternion, + ) except ImportError: ROSPose = None # type: ignore[assignment, misc] ROSPoint = None # type: ignore[assignment, misc] diff --git a/dimos/msgs/geometry_msgs/PoseWithCovariance.py b/dimos/msgs/geometry_msgs/PoseWithCovariance.py index dc2302986b..f6ef67a6c9 100644 --- a/dimos/msgs/geometry_msgs/PoseWithCovariance.py +++ b/dimos/msgs/geometry_msgs/PoseWithCovariance.py @@ -16,12 +16,16 @@ from typing import TYPE_CHECKING, TypeAlias -from dimos_lcm.geometry_msgs import PoseWithCovariance as LCMPoseWithCovariance # type: ignore[import-untyped] +from dimos_lcm.geometry_msgs import ( + PoseWithCovariance as LCMPoseWithCovariance, # type: ignore[import-untyped] +) import numpy as np from plum import dispatch try: - from geometry_msgs.msg import PoseWithCovariance as ROSPoseWithCovariance # type: ignore[attr-defined] + from geometry_msgs.msg import ( + PoseWithCovariance as ROSPoseWithCovariance, # type: ignore[attr-defined] + ) except ImportError: ROSPoseWithCovariance = None # type: ignore[assignment, misc] @@ -51,7 +55,9 @@ def __init__(self) -> None: @dispatch # type: ignore[no-redef] def __init__( - self, pose: Pose | PoseConvertable, covariance: list[float] | np.ndarray | None = None # type: ignore[type-arg] + self, + pose: Pose | PoseConvertable, + covariance: list[float] | np.ndarray | None = None, # type: ignore[type-arg] ) -> None: """Initialize with pose and optional covariance.""" self.pose = Pose(pose) if not isinstance(pose, Pose) else pose diff --git a/dimos/msgs/geometry_msgs/PoseWithCovarianceStamped.py b/dimos/msgs/geometry_msgs/PoseWithCovarianceStamped.py index 1174057be7..eb9dd526b6 100644 --- a/dimos/msgs/geometry_msgs/PoseWithCovarianceStamped.py +++ b/dimos/msgs/geometry_msgs/PoseWithCovarianceStamped.py @@ -17,12 +17,16 @@ import time from typing import TypeAlias -from dimos_lcm.geometry_msgs import PoseWithCovarianceStamped as LCMPoseWithCovarianceStamped # type: ignore[import-untyped] +from dimos_lcm.geometry_msgs import ( + PoseWithCovarianceStamped as LCMPoseWithCovarianceStamped, # type: ignore[import-untyped] +) import numpy as np from plum import dispatch try: - from geometry_msgs.msg import PoseWithCovarianceStamped as ROSPoseWithCovarianceStamped # type: ignore[attr-defined] + from geometry_msgs.msg import ( + PoseWithCovarianceStamped as ROSPoseWithCovarianceStamped, # type: ignore[attr-defined] + ) except ImportError: ROSPoseWithCovarianceStamped = None # type: ignore[assignment, misc] diff --git a/dimos/msgs/geometry_msgs/Twist.py b/dimos/msgs/geometry_msgs/Twist.py index 98a2421f7d..8dea77dac5 100644 --- a/dimos/msgs/geometry_msgs/Twist.py +++ b/dimos/msgs/geometry_msgs/Twist.py @@ -18,7 +18,10 @@ from plum import dispatch try: - from geometry_msgs.msg import Twist as ROSTwist, Vector3 as ROSVector3 # type: ignore[attr-defined] + from geometry_msgs.msg import ( # type: ignore[attr-defined] + Twist as ROSTwist, + Vector3 as ROSVector3, + ) except ImportError: ROSTwist = None # type: ignore[assignment, misc] ROSVector3 = None # type: ignore[assignment, misc] diff --git a/dimos/msgs/geometry_msgs/TwistWithCovariance.py b/dimos/msgs/geometry_msgs/TwistWithCovariance.py index 67caf297fd..28e40934ae 100644 --- a/dimos/msgs/geometry_msgs/TwistWithCovariance.py +++ b/dimos/msgs/geometry_msgs/TwistWithCovariance.py @@ -16,12 +16,16 @@ from typing import TypeAlias -from dimos_lcm.geometry_msgs import TwistWithCovariance as LCMTwistWithCovariance # type: ignore[import-untyped] +from dimos_lcm.geometry_msgs import ( + TwistWithCovariance as LCMTwistWithCovariance, # type: ignore[import-untyped] +) import numpy as np from plum import dispatch try: - from geometry_msgs.msg import TwistWithCovariance as ROSTwistWithCovariance # type: ignore[attr-defined] + from geometry_msgs.msg import ( + TwistWithCovariance as ROSTwistWithCovariance, # type: ignore[attr-defined] + ) except ImportError: ROSTwistWithCovariance = None # type: ignore[assignment, misc] diff --git a/dimos/msgs/geometry_msgs/TwistWithCovarianceStamped.py b/dimos/msgs/geometry_msgs/TwistWithCovarianceStamped.py index a4b212bdcf..83c0914e23 100644 --- a/dimos/msgs/geometry_msgs/TwistWithCovarianceStamped.py +++ b/dimos/msgs/geometry_msgs/TwistWithCovarianceStamped.py @@ -17,12 +17,16 @@ import time from typing import TypeAlias -from dimos_lcm.geometry_msgs import TwistWithCovarianceStamped as LCMTwistWithCovarianceStamped # type: ignore[import-untyped] +from dimos_lcm.geometry_msgs import ( + TwistWithCovarianceStamped as LCMTwistWithCovarianceStamped, # type: ignore[import-untyped] +) import numpy as np from plum import dispatch try: - from geometry_msgs.msg import TwistWithCovarianceStamped as ROSTwistWithCovarianceStamped # type: ignore[attr-defined] + from geometry_msgs.msg import ( + TwistWithCovarianceStamped as ROSTwistWithCovarianceStamped, # type: ignore[attr-defined] + ) except ImportError: ROSTwistWithCovarianceStamped = None # type: ignore[assignment, misc] diff --git a/dimos/msgs/nav_msgs/OccupancyGrid.py b/dimos/msgs/nav_msgs/OccupancyGrid.py index 145cd74398..068422a7f2 100644 --- a/dimos/msgs/nav_msgs/OccupancyGrid.py +++ b/dimos/msgs/nav_msgs/OccupancyGrid.py @@ -18,7 +18,10 @@ import time from typing import TYPE_CHECKING, BinaryIO -from dimos_lcm.nav_msgs import MapMetaData, OccupancyGrid as LCMOccupancyGrid # type: ignore[import-untyped] +from dimos_lcm.nav_msgs import ( # type: ignore[import-untyped] + MapMetaData, + OccupancyGrid as LCMOccupancyGrid, +) from dimos_lcm.std_msgs import Time as LCMTime # type: ignore[import-untyped] import numpy as np from scipy import ndimage diff --git a/dimos/msgs/nav_msgs/__init__.py b/dimos/msgs/nav_msgs/__init__.py index 54350b3f59..9d099068ad 100644 --- a/dimos/msgs/nav_msgs/__init__.py +++ b/dimos/msgs/nav_msgs/__init__.py @@ -1,4 +1,8 @@ -from dimos.msgs.nav_msgs.OccupancyGrid import CostValues, MapMetaData, OccupancyGrid # type: ignore[attr-defined] +from dimos.msgs.nav_msgs.OccupancyGrid import ( # type: ignore[attr-defined] + CostValues, + MapMetaData, + OccupancyGrid, +) from dimos.msgs.nav_msgs.Odometry import Odometry from dimos.msgs.nav_msgs.Path import Path diff --git a/dimos/msgs/sensor_msgs/CameraInfo.py b/dimos/msgs/sensor_msgs/CameraInfo.py index fe638195bb..866ee896fd 100644 --- a/dimos/msgs/sensor_msgs/CameraInfo.py +++ b/dimos/msgs/sensor_msgs/CameraInfo.py @@ -23,7 +23,10 @@ # Import ROS types try: - from sensor_msgs.msg import CameraInfo as ROSCameraInfo, RegionOfInterest as ROSRegionOfInterest # type: ignore[attr-defined] + from sensor_msgs.msg import ( # type: ignore[attr-defined] + CameraInfo as ROSCameraInfo, + RegionOfInterest as ROSRegionOfInterest, + ) from std_msgs.msg import Header as ROSHeader # type: ignore[attr-defined] ROS_AVAILABLE = True diff --git a/dimos/msgs/sensor_msgs/Image.py b/dimos/msgs/sensor_msgs/Image.py index e7ddb6c751..3ac9660042 100644 --- a/dimos/msgs/sensor_msgs/Image.py +++ b/dimos/msgs/sensor_msgs/Image.py @@ -177,7 +177,10 @@ def from_file( # type: ignore[no-untyped-def] @classmethod def from_opencv( # type: ignore[no-untyped-def] - cls, cv_image: np.ndarray, format: ImageFormat = ImageFormat.BGR, **kwargs # type: ignore[type-arg] + cls, + cv_image: np.ndarray, + format: ImageFormat = ImageFormat.BGR, + **kwargs, # type: ignore[type-arg] ) -> Image: """Construct from an OpenCV image (NumPy array).""" return cls( diff --git a/dimos/msgs/sensor_msgs/PointCloud2.py b/dimos/msgs/sensor_msgs/PointCloud2.py index 20e3a70c49..a0df184c6f 100644 --- a/dimos/msgs/sensor_msgs/PointCloud2.py +++ b/dimos/msgs/sensor_msgs/PointCloud2.py @@ -30,7 +30,10 @@ # Import ROS types try: - from sensor_msgs.msg import PointCloud2 as ROSPointCloud2, PointField as ROSPointField # type: ignore[attr-defined] + from sensor_msgs.msg import ( # type: ignore[attr-defined] + PointCloud2 as ROSPointCloud2, + PointField as ROSPointField, + ) from std_msgs.msg import Header as ROSHeader # type: ignore[attr-defined] ROS_AVAILABLE = True @@ -56,7 +59,10 @@ def __init__( @classmethod def from_numpy( - cls, points: np.ndarray, frame_id: str = "world", timestamp: float | None = None # type: ignore[type-arg] + cls, + points: np.ndarray, + frame_id: str = "world", + timestamp: float | None = None, # type: ignore[type-arg] ) -> PointCloud2: """Create PointCloud2 from numpy array of shape (N, 3). diff --git a/dimos/msgs/sensor_msgs/image_impls/AbstractImage.py b/dimos/msgs/sensor_msgs/image_impls/AbstractImage.py index 10d8133402..1d707c12a1 100644 --- a/dimos/msgs/sensor_msgs/image_impls/AbstractImage.py +++ b/dimos/msgs/sensor_msgs/image_impls/AbstractImage.py @@ -203,7 +203,9 @@ def to_base64(self, quality: int = 80) -> str: NVIMGCODEC_LAST_USED = False bgr = self.to_bgr() success, buffer = cv2.imencode( - ".jpg", _to_cpu(bgr.data), [int(cv2.IMWRITE_JPEG_QUALITY), int(quality)] # type: ignore[no-untyped-call] + ".jpg", + _to_cpu(bgr.data), + [int(cv2.IMWRITE_JPEG_QUALITY), int(quality)], # type: ignore[no-untyped-call] ) if not success: raise ValueError("Failed to encode image as JPEG") diff --git a/dimos/msgs/sensor_msgs/image_impls/CudaImage.py b/dimos/msgs/sensor_msgs/image_impls/CudaImage.py index 44c0851ec8..5b33c97120 100644 --- a/dimos/msgs/sensor_msgs/image_impls/CudaImage.py +++ b/dimos/msgs/sensor_msgs/image_impls/CudaImage.py @@ -414,8 +414,7 @@ def _rodrigues(x, inverse: bool = False): # type: ignore[no-untyped-def] """Unified Rodrigues transform (vector<->matrix) for NumPy/CuPy arrays.""" if cp is not None and ( - isinstance(x, cp.ndarray) - or getattr(x, "__cuda_array_interface__", None) is not None + isinstance(x, cp.ndarray) or getattr(x, "__cuda_array_interface__", None) is not None ): xp = cp else: @@ -579,7 +578,10 @@ def to_rgb(self) -> CudaImage: return self.copy() # type: ignore if self.format == ImageFormat.BGRA: return CudaImage( - _bgra_to_rgba_cuda(self.data), ImageFormat.RGBA, self.frame_id, self.ts # type: ignore[no-untyped-call] + _bgra_to_rgba_cuda(self.data), + ImageFormat.RGBA, + self.frame_id, + self.ts, # type: ignore[no-untyped-call] ) if self.format == ImageFormat.GRAY: return CudaImage(_gray_to_rgb_cuda(self.data), ImageFormat.RGB, self.frame_id, self.ts) # type: ignore[no-untyped-call] @@ -595,7 +597,10 @@ def to_bgr(self) -> CudaImage: return CudaImage(_rgb_to_bgr_cuda(self.data), ImageFormat.BGR, self.frame_id, self.ts) # type: ignore[no-untyped-call] if self.format == ImageFormat.RGBA: return CudaImage( - _rgba_to_bgra_cuda(self.data)[..., :3], ImageFormat.BGR, self.frame_id, self.ts # type: ignore[no-untyped-call] + _rgba_to_bgra_cuda(self.data)[..., :3], + ImageFormat.BGR, + self.frame_id, + self.ts, # type: ignore[no-untyped-call] ) if self.format == ImageFormat.BGRA: return CudaImage(self.data[..., :3], ImageFormat.BGR, self.frame_id, self.ts) # type: ignore[index] @@ -609,7 +614,10 @@ def to_bgr(self) -> CudaImage: if self.format in (ImageFormat.GRAY16, ImageFormat.DEPTH16): gray8 = (self.data.astype(cp.float32) / 256.0).clip(0, 255).astype(cp.uint8) # type: ignore return CudaImage( - _rgb_to_bgr_cuda(_gray_to_rgb_cuda(gray8)), ImageFormat.BGR, self.frame_id, self.ts # type: ignore[no-untyped-call] + _rgb_to_bgr_cuda(_gray_to_rgb_cuda(gray8)), + ImageFormat.BGR, + self.frame_id, + self.ts, # type: ignore[no-untyped-call] ) return self.copy() # type: ignore @@ -771,7 +779,11 @@ def solve_pnp_batch( else: raise ValueError("dist_coeffs must be 1D or batched 2D") ok, rvec, tvec = cv2.solvePnP( - obj[b], img[b], K_b, dist_b, flags=cv2.SOLVEPNP_ITERATIVE # type: ignore[arg-type] + obj[b], + img[b], + K_b, + dist_b, + flags=cv2.SOLVEPNP_ITERATIVE, # type: ignore[arg-type] ) if not ok: raise RuntimeError(f"cv2.solvePnP failed for batch index {b}") diff --git a/dimos/msgs/std_msgs/Header.py b/dimos/msgs/std_msgs/Header.py index 48c52c9e01..e8135bdd07 100644 --- a/dimos/msgs/std_msgs/Header.py +++ b/dimos/msgs/std_msgs/Header.py @@ -22,7 +22,9 @@ # Import the actual LCM header type that's returned from decoding try: - from lcm_msgs.std_msgs.Header import Header as DecodedLCMHeader # type: ignore[import-not-found] + from lcm_msgs.std_msgs.Header import ( + Header as DecodedLCMHeader, # type: ignore[import-not-found] + ) except ImportError: DecodedLCMHeader = None diff --git a/dimos/msgs/tf2_msgs/TFMessage.py b/dimos/msgs/tf2_msgs/TFMessage.py index a3b7f3e664..7a66f60459 100644 --- a/dimos/msgs/tf2_msgs/TFMessage.py +++ b/dimos/msgs/tf2_msgs/TFMessage.py @@ -32,7 +32,9 @@ from dimos_lcm.tf2_msgs import TFMessage as LCMTFMessage # type: ignore[import-untyped] try: - from geometry_msgs.msg import TransformStamped as ROSTransformStamped # type: ignore[attr-defined] + from geometry_msgs.msg import ( + TransformStamped as ROSTransformStamped, # type: ignore[attr-defined] + ) from tf2_msgs.msg import TFMessage as ROSTFMessage # type: ignore[attr-defined] except ImportError: ROSTFMessage = None # type: ignore[assignment, misc] diff --git a/dimos/msgs/vision_msgs/BoundingBox2DArray.py b/dimos/msgs/vision_msgs/BoundingBox2DArray.py index 6cfd2b6cfd..23067fe5e2 100644 --- a/dimos/msgs/vision_msgs/BoundingBox2DArray.py +++ b/dimos/msgs/vision_msgs/BoundingBox2DArray.py @@ -12,7 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from dimos_lcm.vision_msgs.BoundingBox2DArray import BoundingBox2DArray as LCMBoundingBox2DArray # type: ignore[import-untyped] +from dimos_lcm.vision_msgs.BoundingBox2DArray import ( + BoundingBox2DArray as LCMBoundingBox2DArray, # type: ignore[import-untyped] +) class BoundingBox2DArray(LCMBoundingBox2DArray): # type: ignore[misc] diff --git a/dimos/msgs/vision_msgs/BoundingBox3DArray.py b/dimos/msgs/vision_msgs/BoundingBox3DArray.py index 32ce36c8df..cd6bd2dd79 100644 --- a/dimos/msgs/vision_msgs/BoundingBox3DArray.py +++ b/dimos/msgs/vision_msgs/BoundingBox3DArray.py @@ -12,7 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from dimos_lcm.vision_msgs.BoundingBox3DArray import BoundingBox3DArray as LCMBoundingBox3DArray # type: ignore[import-untyped] +from dimos_lcm.vision_msgs.BoundingBox3DArray import ( + BoundingBox3DArray as LCMBoundingBox3DArray, # type: ignore[import-untyped] +) class BoundingBox3DArray(LCMBoundingBox3DArray): # type: ignore[misc] diff --git a/dimos/msgs/vision_msgs/Detection2DArray.py b/dimos/msgs/vision_msgs/Detection2DArray.py index f66e743409..d8be591872 100644 --- a/dimos/msgs/vision_msgs/Detection2DArray.py +++ b/dimos/msgs/vision_msgs/Detection2DArray.py @@ -11,7 +11,9 @@ # 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 dimos_lcm.vision_msgs.Detection2DArray import Detection2DArray as LCMDetection2DArray # type: ignore[import-untyped] +from dimos_lcm.vision_msgs.Detection2DArray import ( + Detection2DArray as LCMDetection2DArray, # type: ignore[import-untyped] +) from dimos.types.timestamped import to_timestamp diff --git a/dimos/msgs/vision_msgs/Detection3DArray.py b/dimos/msgs/vision_msgs/Detection3DArray.py index 9a195005b4..a82c929e50 100644 --- a/dimos/msgs/vision_msgs/Detection3DArray.py +++ b/dimos/msgs/vision_msgs/Detection3DArray.py @@ -12,7 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from dimos_lcm.vision_msgs.Detection3DArray import Detection3DArray as LCMDetection3DArray # type: ignore[import-untyped] +from dimos_lcm.vision_msgs.Detection3DArray import ( + Detection3DArray as LCMDetection3DArray, # type: ignore[import-untyped] +) class Detection3DArray(LCMDetection3DArray): # type: ignore[misc] diff --git a/dimos/navigation/local_planner/holonomic_local_planner.py b/dimos/navigation/local_planner/holonomic_local_planner.py index 4b16a3c3a0..b91ddfb11d 100644 --- a/dimos/navigation/local_planner/holonomic_local_planner.py +++ b/dimos/navigation/local_planner/holonomic_local_planner.py @@ -222,7 +222,9 @@ def _compute_obstacle_repulsion(self, pose: np.ndarray, costmap: np.ndarray) -> return v_rep def _find_closest_point_on_path( - self, pose: np.ndarray, path: np.ndarray # type: ignore[type-arg] + self, + pose: np.ndarray, + path: np.ndarray, # type: ignore[type-arg] ) -> tuple[int, np.ndarray]: # type: ignore[type-arg] """ Find the closest point on the path to current pose. diff --git a/dimos/navigation/rosnav.py b/dimos/navigation/rosnav.py index b60e7abbd7..8793d16c89 100644 --- a/dimos/navigation/rosnav.py +++ b/dimos/navigation/rosnav.py @@ -34,7 +34,10 @@ from rclpy.node import Node from reactivex import operators as ops from reactivex.subject import Subject -from sensor_msgs.msg import Joy as ROSJoy, PointCloud2 as ROSPointCloud2 # type: ignore[attr-defined] +from sensor_msgs.msg import ( # type: ignore[attr-defined] + Joy as ROSJoy, + PointCloud2 as ROSPointCloud2, +) from std_msgs.msg import Bool as ROSBool, Int8 as ROSInt8 # type: ignore[attr-defined] from tf2_msgs.msg import TFMessage as ROSTFMessage # type: ignore[attr-defined] diff --git a/dimos/perception/common/utils.py b/dimos/perception/common/utils.py index 6a246f15e5..c54ac6b352 100644 --- a/dimos/perception/common/utils.py +++ b/dimos/perception/common/utils.py @@ -16,7 +16,11 @@ import cv2 from dimos_lcm.sensor_msgs import CameraInfo # type: ignore[import-untyped] -from dimos_lcm.vision_msgs import BoundingBox2D, Detection2D, Detection3D # type: ignore[import-untyped] +from dimos_lcm.vision_msgs import ( # type: ignore[import-untyped] + BoundingBox2D, + Detection2D, + Detection3D, +) import numpy as np import torch import yaml @@ -309,7 +313,8 @@ def project_3d_points_to_2d_cuda( def project_3d_points_to_2d_cpu( - points_3d: np.ndarray, camera_intrinsics: list[float] | np.ndarray # type: ignore[type-arg] + points_3d: np.ndarray, + camera_intrinsics: list[float] | np.ndarray, # type: ignore[type-arg] ) -> np.ndarray: # type: ignore[type-arg] pts = np.asarray(points_3d, dtype=np.float64) valid_mask = pts[:, 2] > 0 @@ -441,7 +446,9 @@ def project_2d_points_to_3d( def colorize_depth( - depth_img: Union[np.ndarray, "cp.ndarray"], max_depth: float = 5.0, overlay_stats: bool = True # type: ignore[type-arg] + depth_img: Union[np.ndarray, "cp.ndarray"], + max_depth: float = 5.0, + overlay_stats: bool = True, # type: ignore[type-arg] ) -> Union[np.ndarray, "cp.ndarray"] | None: # type: ignore[type-arg] """ Normalize and colorize depth image using COLORMAP_JET with optional statistics overlay. @@ -478,7 +485,9 @@ def colorize_depth( max_depth_actual = float(np.max(valid_depths)) h, w = depth_rgb_np.shape[:2] center_y, center_x = h // 2, w // 2 - center_region = _to_numpy(depth)[ # type: ignore[no-untyped-call] + center_region = _to_numpy( + depth + )[ # type: ignore[no-untyped-call] max(0, center_y - 2) : min(h, center_y + 3), max(0, center_x - 2) : min(w, center_x + 3) ] center_mask = np.isfinite(center_region) & (center_region > 0) diff --git a/dimos/perception/detection/detectors/detic.py b/dimos/perception/detection/detectors/detic.py index 4ad8c39af9..dd012f6864 100644 --- a/dimos/perception/detection/detectors/detic.py +++ b/dimos/perception/detection/detectors/detic.py @@ -181,7 +181,9 @@ def __init__( # type: ignore[no-untyped-def] # Import Detic modules from centernet.config import add_centernet_config # type: ignore[import-not-found] from detic.config import add_detic_config # type: ignore[import-not-found] - from detic.modeling.text.text_encoder import build_text_encoder # type: ignore[import-not-found] + from detic.modeling.text.text_encoder import ( + build_text_encoder, # type: ignore[import-not-found] + ) from detic.modeling.utils import reset_cls_test # type: ignore[import-not-found] # Keep reference to these functions for later use diff --git a/dimos/perception/detection/module2D.py b/dimos/perception/detection/module2D.py index e0ccb9b658..100bc6ee0b 100644 --- a/dimos/perception/detection/module2D.py +++ b/dimos/perception/detection/module2D.py @@ -112,7 +112,9 @@ def track(self, detections: ImageDetections2D) -> None: # Active detection - compute real position detection = detections.detections[index] position_3d = self.pixel_to_3d( # type: ignore[attr-defined] - detection.center_bbox, self.config.camera_info, assumed_depth=1.0 # type: ignore[attr-defined] + detection.center_bbox, + self.config.camera_info, + assumed_depth=1.0, # type: ignore[attr-defined] ) else: # No detection at this index - publish zero transform diff --git a/dimos/perception/detection/module3D.py b/dimos/perception/detection/module3D.py index 2bb3037542..8245a716c7 100644 --- a/dimos/perception/detection/module3D.py +++ b/dimos/perception/detection/module3D.py @@ -13,7 +13,9 @@ # limitations under the License. -from dimos_lcm.foxglove_msgs.ImageAnnotations import ImageAnnotations # type: ignore[import-untyped] +from dimos_lcm.foxglove_msgs.ImageAnnotations import ( + ImageAnnotations, # type: ignore[import-untyped] +) from lcm_msgs.foxglove_msgs import SceneUpdate # type: ignore[import-not-found] from reactivex import operators as ops from reactivex.observable import Observable diff --git a/dimos/perception/detection/moduleDB.py b/dimos/perception/detection/moduleDB.py index 5bc8c87318..c679f06fef 100644 --- a/dimos/perception/detection/moduleDB.py +++ b/dimos/perception/detection/moduleDB.py @@ -17,7 +17,9 @@ import time from typing import Any -from dimos_lcm.foxglove_msgs.ImageAnnotations import ImageAnnotations # type: ignore[import-untyped] +from dimos_lcm.foxglove_msgs.ImageAnnotations import ( + ImageAnnotations, # type: ignore[import-untyped] +) from lcm_msgs.foxglove_msgs import SceneUpdate # type: ignore[import-not-found] from reactivex.observable import Observable diff --git a/dimos/perception/detection/reid/embedding_id_system.py b/dimos/perception/detection/reid/embedding_id_system.py index 69bbed584d..169747fc58 100644 --- a/dimos/perception/detection/reid/embedding_id_system.py +++ b/dimos/perception/detection/reid/embedding_id_system.py @@ -129,7 +129,9 @@ def update_embedding(self, track_id: int, new_embedding: Embedding) -> None: embeddings.pop(0) # Remove oldest def _compute_group_similarity( - self, query_embeddings: list[np.ndarray], candidate_embeddings: list[np.ndarray] # type: ignore[type-arg] + self, + query_embeddings: list[np.ndarray], + candidate_embeddings: list[np.ndarray], # type: ignore[type-arg] ) -> float: """Compute similarity between two groups of embeddings. diff --git a/dimos/perception/detection/type/detection2d/base.py b/dimos/perception/detection/type/detection2d/base.py index da356985d0..5e09663bac 100644 --- a/dimos/perception/detection/type/detection2d/base.py +++ b/dimos/perception/detection/type/detection2d/base.py @@ -15,7 +15,10 @@ from abc import abstractmethod from collections.abc import Callable -from dimos_lcm.foxglove_msgs.ImageAnnotations import PointsAnnotation, TextAnnotation # type: ignore[import-untyped] +from dimos_lcm.foxglove_msgs.ImageAnnotations import ( # type: ignore[import-untyped] + PointsAnnotation, + TextAnnotation, +) from dimos_lcm.vision_msgs import Detection2D as ROSDetection2D # type: ignore[import-untyped] from dimos.msgs.foxglove_msgs import ImageAnnotations diff --git a/dimos/perception/detection/type/detection2d/person.py b/dimos/perception/detection/type/detection2d/person.py index 7822c48129..ec5da289ef 100644 --- a/dimos/perception/detection/type/detection2d/person.py +++ b/dimos/perception/detection/type/detection2d/person.py @@ -17,7 +17,10 @@ # Import for type checking only to avoid circular imports from typing import TYPE_CHECKING -from dimos_lcm.foxglove_msgs.ImageAnnotations import PointsAnnotation, TextAnnotation # type: ignore[import-untyped] +from dimos_lcm.foxglove_msgs.ImageAnnotations import ( # type: ignore[import-untyped] + PointsAnnotation, + TextAnnotation, +) from dimos_lcm.foxglove_msgs.Point2 import Point2 # type: ignore[import-untyped] import numpy as np diff --git a/dimos/perception/detection/type/detection3d/pointcloud.py b/dimos/perception/detection/type/detection3d/pointcloud.py index 032a78b774..285a7590c5 100644 --- a/dimos/perception/detection/type/detection3d/pointcloud.py +++ b/dimos/perception/detection/type/detection3d/pointcloud.py @@ -19,8 +19,17 @@ from typing import TYPE_CHECKING, Any from lcm_msgs.builtin_interfaces import Duration # type: ignore[import-not-found] -from lcm_msgs.foxglove_msgs import CubePrimitive, SceneEntity, TextPrimitive # type: ignore[import-not-found] -from lcm_msgs.geometry_msgs import Point, Pose, Quaternion, Vector3 as LCMVector3 # type: ignore[import-not-found] +from lcm_msgs.foxglove_msgs import ( # type: ignore[import-not-found] + CubePrimitive, + SceneEntity, + TextPrimitive, +) +from lcm_msgs.geometry_msgs import ( # type: ignore[import-not-found] + Point, + Pose, + Quaternion, + Vector3 as LCMVector3, +) import numpy as np from dimos.msgs.foxglove_msgs.Color import Color diff --git a/dimos/perception/grasp_generation/grasp_generation.py b/dimos/perception/grasp_generation/grasp_generation.py index 91256356f0..84a470b813 100644 --- a/dimos/perception/grasp_generation/grasp_generation.py +++ b/dimos/perception/grasp_generation/grasp_generation.py @@ -112,7 +112,9 @@ def generate_grasps_from_objects( return [] def _send_grasp_request_sync( - self, points: np.ndarray, colors: np.ndarray | None # type: ignore[type-arg] + self, + points: np.ndarray, + colors: np.ndarray | None, # type: ignore[type-arg] ) -> list[dict] | None: # type: ignore[type-arg] """Send synchronous grasp request to grasp server.""" @@ -148,7 +150,9 @@ def _send_grasp_request_sync( return None async def _async_grasp_request( - self, points: np.ndarray, colors: np.ndarray # type: ignore[type-arg] + self, + points: np.ndarray, + colors: np.ndarray, # type: ignore[type-arg] ) -> list[dict] | None: # type: ignore[type-arg] """Async grasp request helper.""" import json diff --git a/dimos/perception/grasp_generation/utils.py b/dimos/perception/grasp_generation/utils.py index ea30c4911d..73e859a93f 100644 --- a/dimos/perception/grasp_generation/utils.py +++ b/dimos/perception/grasp_generation/utils.py @@ -146,7 +146,8 @@ def create_gripper_geometry( def create_all_gripper_geometries( - grasp_list: list[dict], max_grasps: int = -1 # type: ignore[type-arg] + grasp_list: list[dict], + max_grasps: int = -1, # type: ignore[type-arg] ) -> list[o3d.geometry.TriangleMesh]: """ Create gripper geometries for multiple grasps. diff --git a/dimos/perception/object_detection_stream.py b/dimos/perception/object_detection_stream.py index a72824fdf0..358dd3e0fc 100644 --- a/dimos/perception/object_detection_stream.py +++ b/dimos/perception/object_detection_stream.py @@ -19,7 +19,9 @@ from dimos.perception.detection2d.yolo_2d_det import Yolo2DDetector # type: ignore[import-untyped] try: - from dimos.perception.detection2d.detic_2d_det import Detic2DDetector # type: ignore[import-untyped] + from dimos.perception.detection2d.detic_2d_det import ( + Detic2DDetector, # type: ignore[import-untyped] + ) DETIC_AVAILABLE = True except (ModuleNotFoundError, ImportError): diff --git a/dimos/perception/object_tracker_3d.py b/dimos/perception/object_tracker_3d.py index e19d1c55e3..1f22c9b5d7 100644 --- a/dimos/perception/object_tracker_3d.py +++ b/dimos/perception/object_tracker_3d.py @@ -15,7 +15,10 @@ # Import LCM messages from dimos_lcm.sensor_msgs import CameraInfo # type: ignore[import-untyped] -from dimos_lcm.vision_msgs import Detection3D, ObjectHypothesisWithPose # type: ignore[import-untyped] +from dimos_lcm.vision_msgs import ( # type: ignore[import-untyped] + Detection3D, + ObjectHypothesisWithPose, +) import numpy as np from dimos.core import In, Out, rpc diff --git a/dimos/perception/pointcloud/cuboid_fit.py b/dimos/perception/pointcloud/cuboid_fit.py index 1e3f451507..2e7f517620 100644 --- a/dimos/perception/pointcloud/cuboid_fit.py +++ b/dimos/perception/pointcloud/cuboid_fit.py @@ -19,7 +19,8 @@ def fit_cuboid( - points: np.ndarray | o3d.geometry.PointCloud, method: str = "minimal" # type: ignore[type-arg] + points: np.ndarray | o3d.geometry.PointCloud, + method: str = "minimal", # type: ignore[type-arg] ) -> dict | None: # type: ignore[type-arg] """ Fit a cuboid to a point cloud using Open3D's built-in methods. @@ -118,7 +119,10 @@ def fit_cuboid_simple(points: np.ndarray | o3d.geometry.PointCloud) -> dict | No def _compute_fitting_error( - points: np.ndarray, center: np.ndarray, dimensions: np.ndarray, rotation: np.ndarray # type: ignore[type-arg] + points: np.ndarray, + center: np.ndarray, + dimensions: np.ndarray, + rotation: np.ndarray, # type: ignore[type-arg] ) -> float: """ Compute fitting error as mean squared distance from points to cuboid surface. @@ -154,7 +158,9 @@ def _compute_fitting_error( def get_cuboid_corners( - center: np.ndarray, dimensions: np.ndarray, rotation: np.ndarray # type: ignore[type-arg] + center: np.ndarray, + dimensions: np.ndarray, + rotation: np.ndarray, # type: ignore[type-arg] ) -> np.ndarray: # type: ignore[type-arg] """ Get the 8 corners of a cuboid. diff --git a/dimos/perception/pointcloud/pointcloud_filtering.py b/dimos/perception/pointcloud/pointcloud_filtering.py index 2d189ce0c1..31c211ac29 100644 --- a/dimos/perception/pointcloud/pointcloud_filtering.py +++ b/dimos/perception/pointcloud/pointcloud_filtering.py @@ -114,7 +114,10 @@ def generate_color_from_id(self, object_id: int) -> np.ndarray: # type: ignore[ return color def _validate_inputs( # type: ignore[no-untyped-def] - self, color_img: np.ndarray, depth_img: np.ndarray, objects: list[ObjectData] # type: ignore[type-arg] + self, + color_img: np.ndarray, + depth_img: np.ndarray, + objects: list[ObjectData], # type: ignore[type-arg] ): """Validate input parameters.""" if color_img.shape[:2] != depth_img.shape: @@ -147,7 +150,9 @@ def _prepare_masks(self, masks: list[np.ndarray], target_shape: tuple) -> list[n return processed_masks def _apply_color_mask( - self, pcd: o3d.geometry.PointCloud, rgb_color: np.ndarray # type: ignore[type-arg] + self, + pcd: o3d.geometry.PointCloud, + rgb_color: np.ndarray, # type: ignore[type-arg] ) -> o3d.geometry.PointCloud: """Apply weighted color mask to point cloud.""" if len(np.asarray(pcd.colors)) > 0: @@ -193,7 +198,10 @@ def get_full_point_cloud(self) -> o3d.geometry.PointCloud: return self._apply_subsampling(self.full_pcd) def process_images( - self, color_img: np.ndarray, depth_img: np.ndarray, objects: list[ObjectData] # type: ignore[type-arg] + self, + color_img: np.ndarray, + depth_img: np.ndarray, + objects: list[ObjectData], # type: ignore[type-arg] ) -> list[ObjectData]: """ Process color and depth images with object detection results to create filtered point clouds. @@ -267,7 +275,11 @@ def process_images( # Create point clouds efficiently self.full_pcd, masked_pcds = create_point_cloud_and_extract_masks( - color_img, depth_img, processed_masks, self.depth_camera_matrix, depth_scale=1.0 # type: ignore[arg-type] + color_img, + depth_img, + processed_masks, + self.depth_camera_matrix, + depth_scale=1.0, # type: ignore[arg-type] ) # Process each object and update ObjectData diff --git a/dimos/perception/spatial_perception.py b/dimos/perception/spatial_perception.py index 15425edc03..bb6e76d2d3 100644 --- a/dimos/perception/spatial_perception.py +++ b/dimos/perception/spatial_perception.py @@ -149,7 +149,8 @@ def __init__( try: logger.info(f"Loading existing visual memory from {visual_memory_path}...") self._visual_memory = VisualMemory.load( - visual_memory_path, output_dir=output_dir # type: ignore[arg-type] + visual_memory_path, + output_dir=output_dir, # type: ignore[arg-type] ) logger.info(f"Loaded {self._visual_memory.count()} images from previous runs") except Exception as e: diff --git a/dimos/robot/foxglove_bridge.py b/dimos/robot/foxglove_bridge.py index 063383db80..0a37322df7 100644 --- a/dimos/robot/foxglove_bridge.py +++ b/dimos/robot/foxglove_bridge.py @@ -17,7 +17,9 @@ import threading # this is missing, I'm just trying to import lcm_foxglove_bridge.py from dimos_lcm -from dimos_lcm.foxglove_bridge import FoxgloveBridge as LCMFoxgloveBridge # type: ignore[import-untyped] +from dimos_lcm.foxglove_bridge import ( + FoxgloveBridge as LCMFoxgloveBridge, # type: ignore[import-untyped] +) from dimos.core import DimosCluster, Module, rpc diff --git a/dimos/robot/ros_control.py b/dimos/robot/ros_control.py index 2a4660c328..e857615a31 100644 --- a/dimos/robot/ros_control.py +++ b/dimos/robot/ros_control.py @@ -239,7 +239,9 @@ def __init__( if webrtc_msg_type: self._webrtc_pub = self._node.create_publisher( - webrtc_msg_type, webrtc_topic, qos_profile=command_qos # type: ignore[arg-type] + webrtc_msg_type, + webrtc_topic, + qos_profile=command_qos, # type: ignore[arg-type] ) # Initialize command queue diff --git a/dimos/robot/ros_transform.py b/dimos/robot/ros_transform.py index aa67b3c288..e1bcb880a0 100644 --- a/dimos/robot/ros_transform.py +++ b/dimos/robot/ros_transform.py @@ -127,7 +127,9 @@ def transform_point( # type: ignore[no-untyped-def] # Return as Vector type if len(point) > 2: # type: ignore[arg-type] return Vector( - transformed_ps.point.x, transformed_ps.point.y, transformed_ps.point.z # type: ignore[union-attr] + transformed_ps.point.x, + transformed_ps.point.y, + transformed_ps.point.z, # type: ignore[union-attr] ) else: return Vector(transformed_ps.point.x, transformed_ps.point.y) # type: ignore[union-attr] diff --git a/dimos/robot/unitree/connection/connection.py b/dimos/robot/unitree/connection/connection.py index 352309afc1..1a3f9d7d96 100644 --- a/dimos/robot/unitree/connection/connection.py +++ b/dimos/robot/unitree/connection/connection.py @@ -20,7 +20,11 @@ from typing import TypeAlias from aiortc import MediaStreamTrack # type: ignore[import-untyped] -from go2_webrtc_driver.constants import RTC_TOPIC, SPORT_CMD, VUI_COLOR # type: ignore[import-untyped] +from go2_webrtc_driver.constants import ( # type: ignore[import-untyped] + RTC_TOPIC, + SPORT_CMD, + VUI_COLOR, +) from go2_webrtc_driver.webrtc_driver import ( # type: ignore[import-untyped] Go2WebRTCConnection, WebRTCConnectionMethod, diff --git a/dimos/robot/unitree_webrtc/connection.py b/dimos/robot/unitree_webrtc/connection.py index c23c65d76b..d77244910d 100644 --- a/dimos/robot/unitree_webrtc/connection.py +++ b/dimos/robot/unitree_webrtc/connection.py @@ -20,7 +20,11 @@ from typing import Literal, TypeAlias from aiortc import MediaStreamTrack # type: ignore[import-untyped] -from go2_webrtc_driver.constants import RTC_TOPIC, SPORT_CMD, VUI_COLOR # type: ignore[import-untyped] +from go2_webrtc_driver.constants import ( # type: ignore[import-untyped] + RTC_TOPIC, + SPORT_CMD, + VUI_COLOR, +) from go2_webrtc_driver.webrtc_driver import ( # type: ignore[import-untyped] Go2WebRTCConnection, WebRTCConnectionMethod, diff --git a/dimos/robot/unitree_webrtc/modular/detect.py b/dimos/robot/unitree_webrtc/modular/detect.py index b6e263ae15..4abdc7ed71 100644 --- a/dimos/robot/unitree_webrtc/modular/detect.py +++ b/dimos/robot/unitree_webrtc/modular/detect.py @@ -108,7 +108,9 @@ def broadcast( # type: ignore[no-untyped-def] detections, annotations, ) -> None: - from dimos_lcm.foxglove_msgs.ImageAnnotations import ImageAnnotations # type: ignore[import-untyped] + from dimos_lcm.foxglove_msgs.ImageAnnotations import ( + ImageAnnotations, # type: ignore[import-untyped] + ) from dimos.core import LCMTransport from dimos.msgs.geometry_msgs import PoseStamped @@ -135,7 +137,10 @@ def broadcast( # type: ignore[no-untyped-def] def process_data(): # type: ignore[no-untyped-def] from dimos.msgs.sensor_msgs import Image - from dimos.perception.detection.module2D import Detection2DModule, build_imageannotations # type: ignore[attr-defined] + from dimos.perception.detection.module2D import ( # type: ignore[attr-defined] + Detection2DModule, + build_imageannotations, + ) from dimos.robot.unitree_webrtc.type.lidar import LidarMessage from dimos.robot.unitree_webrtc.type.odometry import Odometry from dimos.utils.data import get_data diff --git a/dimos/robot/unitree_webrtc/unitree_g1.py b/dimos/robot/unitree_webrtc/unitree_g1.py index 0610d8b7f9..293fe64c63 100644 --- a/dimos/robot/unitree_webrtc/unitree_g1.py +++ b/dimos/robot/unitree_webrtc/unitree_g1.py @@ -23,10 +23,16 @@ import time from dimos_lcm.foxglove_msgs import SceneUpdate # type: ignore[import-untyped] -from geometry_msgs.msg import PoseStamped as ROSPoseStamped, TwistStamped as ROSTwistStamped # type: ignore[attr-defined] +from geometry_msgs.msg import ( # type: ignore[attr-defined] + PoseStamped as ROSPoseStamped, + TwistStamped as ROSTwistStamped, +) from nav_msgs.msg import Odometry as ROSOdometry # type: ignore[attr-defined] from reactivex.disposable import Disposable -from sensor_msgs.msg import Joy as ROSJoy, PointCloud2 as ROSPointCloud2 # type: ignore[attr-defined] +from sensor_msgs.msg import ( # type: ignore[attr-defined] + Joy as ROSJoy, + PointCloud2 as ROSPointCloud2, +) from tf2_msgs.msg import TFMessage as ROSTFMessage # type: ignore[attr-defined] from dimos import core diff --git a/dimos/skills/manipulation/pick_and_place.py b/dimos/skills/manipulation/pick_and_place.py index 386bd80395..93323c6b4a 100644 --- a/dimos/skills/manipulation/pick_and_place.py +++ b/dimos/skills/manipulation/pick_and_place.py @@ -237,7 +237,8 @@ def _get_camera_frame(self) -> np.ndarray | None: # type: ignore[type-arg] return None def _query_pick_and_place_points( - self, frame: np.ndarray # type: ignore[type-arg] + self, + frame: np.ndarray, # type: ignore[type-arg] ) -> tuple[tuple[int, int], tuple[int, int]] | None: """ Query Qwen to get both pick and place points in a single query. @@ -270,7 +271,10 @@ def _query_pick_and_place_points( return None def _query_single_point( - self, frame: np.ndarray, query: str, point_type: str # type: ignore[type-arg] + self, + frame: np.ndarray, + query: str, + point_type: str, # type: ignore[type-arg] ) -> tuple[int, int] | None: """ Query Qwen to get a single point location. diff --git a/dimos/skills/manipulation/rotation_constraint_skill.py b/dimos/skills/manipulation/rotation_constraint_skill.py index 90672e7632..52d01d52cb 100644 --- a/dimos/skills/manipulation/rotation_constraint_skill.py +++ b/dimos/skills/manipulation/rotation_constraint_skill.py @@ -89,7 +89,9 @@ def __call__(self) -> RotationConstraint: secondary_pivot_vector = None if self.secondary_pivot_point: secondary_pivot_vector = Vector( - self.secondary_pivot_point[0], self.secondary_pivot_point[1], 0.0 # type: ignore[arg-type] + self.secondary_pivot_point[0], + self.secondary_pivot_point[1], + 0.0, # type: ignore[arg-type] ) constraint = RotationConstraint( diff --git a/dimos/stream/audio/base.py b/dimos/stream/audio/base.py index 178cc8bc0b..cdefcbc705 100644 --- a/dimos/stream/audio/base.py +++ b/dimos/stream/audio/base.py @@ -60,7 +60,11 @@ class AudioEvent: """Class to represent an audio frame event with metadata.""" def __init__( - self, data: np.ndarray, sample_rate: int, timestamp: float, channels: int = 1 # type: ignore[type-arg] + self, + data: np.ndarray, + sample_rate: int, + timestamp: float, + channels: int = 1, # type: ignore[type-arg] ) -> None: """ Initialize an AudioEvent. diff --git a/dimos/stream/frame_processor.py b/dimos/stream/frame_processor.py index 3d53685c4c..84f1d0e578 100644 --- a/dimos/stream/frame_processor.py +++ b/dimos/stream/frame_processor.py @@ -262,7 +262,10 @@ def process_stream_optical_flow_with_relevancy(self, frame_stream: Observable) - ) def process_stream_with_jpeg_export( - self, frame_stream: Observable, suffix: str = "", loop: bool = False # type: ignore[type-arg] + self, + frame_stream: Observable, + suffix: str = "", + loop: bool = False, # type: ignore[type-arg] ) -> Observable: # type: ignore[type-arg] """Processes stream by saving frames as JPEGs while passing them through. diff --git a/dimos/stream/video_operators.py b/dimos/stream/video_operators.py index 221cf04ae9..3b5a8ec75b 100644 --- a/dimos/stream/video_operators.py +++ b/dimos/stream/video_operators.py @@ -204,7 +204,9 @@ def with_optical_flow( return lambda source: source.pipe( ops.scan( lambda acc, frame: frame_processor.compute_optical_flow( # type: ignore[arg-type, return-value] - acc, frame, compute_relevancy=False # type: ignore[arg-type] + acc, + frame, + compute_relevancy=False, # type: ignore[arg-type] ), (None, None, None), ), @@ -215,7 +217,8 @@ def with_optical_flow( @staticmethod def with_zmq_socket( - socket: zmq.Socket, scheduler: Any | None = None # type: ignore[type-arg] + socket: zmq.Socket, + scheduler: Any | None = None, # type: ignore[type-arg] ) -> Callable[[Observable], Observable]: # type: ignore[type-arg] def send_frame(frame, socket) -> None: # type: ignore[no-untyped-def] _, img_encoded = cv2.imencode(".jpg", frame) diff --git a/dimos/types/ros_polyfill.py b/dimos/types/ros_polyfill.py index 747a32dc5d..200964dcc6 100644 --- a/dimos/types/ros_polyfill.py +++ b/dimos/types/ros_polyfill.py @@ -22,7 +22,12 @@ from nav_msgs.msg import OccupancyGrid, Odometry # type: ignore[attr-defined] from std_msgs.msg import Header # type: ignore[attr-defined] except ImportError: - from dimos_lcm.geometry_msgs import Point, Pose, Quaternion, Twist # type: ignore[import-untyped, no-redef] + from dimos_lcm.geometry_msgs import ( # type: ignore[import-untyped, no-redef] + Point, + Pose, + Quaternion, + Twist, + ) from dimos_lcm.nav_msgs import OccupancyGrid, Odometry # type: ignore[import-untyped, no-redef] from dimos_lcm.std_msgs import Header # type: ignore[import-untyped, no-redef] diff --git a/dimos/types/sample.py b/dimos/types/sample.py index 0ad38e6192..7a1b707eda 100644 --- a/dimos/types/sample.py +++ b/dimos/types/sample.py @@ -238,7 +238,9 @@ def obj_to_schema(value: Any) -> builtins.dict: # type: ignore[type-arg] return {} def schema( - self, resolve_refs: bool = True, include_descriptions: bool = False # type: ignore[override] + self, + resolve_refs: bool = True, + include_descriptions: bool = False, # type: ignore[override] ) -> builtins.dict: # type: ignore[type-arg] """Returns a simplified json schema. @@ -431,7 +433,9 @@ def init_from(cls, d: Any, pack: bool = False) -> "Sample": @classmethod def from_flat_dict( - cls, flat_dict: builtins.dict[str, Any], schema: builtins.dict | None = None # type: ignore[type-arg] + cls, + flat_dict: builtins.dict[str, Any], + schema: builtins.dict | None = None, # type: ignore[type-arg] ) -> "Sample": """Initialize a Sample instance from a flattened dictionary.""" """ diff --git a/dimos/types/timestamped.py b/dimos/types/timestamped.py index 0d0db75749..16785c3e0d 100644 --- a/dimos/types/timestamped.py +++ b/dimos/types/timestamped.py @@ -365,7 +365,8 @@ def on_secondary(i: int, secondary_item: SECONDARY) -> None: for i, secondary_obs in enumerate(secondary_observables): secondary_subs.append( secondary_obs.subscribe( - lambda x, idx=i: on_secondary(idx, x), on_error=observer.on_error # type: ignore[misc] + lambda x, idx=i: on_secondary(idx, x), + on_error=observer.on_error, # type: ignore[misc] ) ) diff --git a/dimos/utils/cli/lcmspy/lcmspy.py b/dimos/utils/cli/lcmspy/lcmspy.py index 4253905da9..ade00a9c0a 100755 --- a/dimos/utils/cli/lcmspy/lcmspy.py +++ b/dimos/utils/cli/lcmspy/lcmspy.py @@ -149,7 +149,8 @@ def msg(self, topic, data) -> None: # type: ignore[no-untyped-def, override] if topic not in self.topic: # type: ignore[operator] print(self.config) self.topic[topic] = self.topic_class( # type: ignore[assignment, call-arg] - topic, history_window=self.config.topic_history_window # type: ignore[attr-defined] + topic, + history_window=self.config.topic_history_window, # type: ignore[attr-defined] ) self.topic[topic].msg(data) # type: ignore[attr-defined, type-arg] diff --git a/dimos/web/dimos_interface/api/server.py b/dimos/web/dimos_interface/api/server.py index e205022b3b..4345ce3ba4 100644 --- a/dimos/web/dimos_interface/api/server.py +++ b/dimos/web/dimos_interface/api/server.py @@ -180,7 +180,8 @@ def create_video_feed_route(self, key): # type: ignore[no-untyped-def] async def video_feed(): # type: ignore[no-untyped-def] return StreamingResponse( - self.stream_generator(key)(), media_type="multipart/x-mixed-replace; boundary=frame" # type: ignore[no-untyped-call] + self.stream_generator(key)(), + media_type="multipart/x-mixed-replace; boundary=frame", # type: ignore[no-untyped-call] ) return video_feed diff --git a/dimos/web/fastapi_server.py b/dimos/web/fastapi_server.py index bc4f3ae223..cf326312ea 100644 --- a/dimos/web/fastapi_server.py +++ b/dimos/web/fastapi_server.py @@ -153,7 +153,8 @@ def create_video_feed_route(self, key): # type: ignore[no-untyped-def] async def video_feed(): # type: ignore[no-untyped-def] return StreamingResponse( - self.stream_generator(key)(), media_type="multipart/x-mixed-replace; boundary=frame" # type: ignore[no-untyped-call] + self.stream_generator(key)(), + media_type="multipart/x-mixed-replace; boundary=frame", # type: ignore[no-untyped-call] ) return video_feed diff --git a/dimos/web/flask_server.py b/dimos/web/flask_server.py index 94435629d8..ac922f4120 100644 --- a/dimos/web/flask_server.py +++ b/dimos/web/flask_server.py @@ -85,7 +85,8 @@ def generate(): # type: ignore[no-untyped-def] def make_response_generator(key): # type: ignore[no-untyped-def] def response_generator(): # type: ignore[no-untyped-def] return Response( - stream_generator(key)(), mimetype="multipart/x-mixed-replace; boundary=frame" # type: ignore[no-untyped-call] + stream_generator(key)(), + mimetype="multipart/x-mixed-replace; boundary=frame", # type: ignore[no-untyped-call] ) return response_generator @@ -94,7 +95,9 @@ def response_generator(): # type: ignore[no-untyped-def] for key in self.streams: endpoint = f"video_feed_{key}" self.app.add_url_rule( - f"/video_feed/{key}", endpoint, view_func=make_response_generator(key) # type: ignore[no-untyped-call] + f"/video_feed/{key}", + endpoint, + view_func=make_response_generator(key), # type: ignore[no-untyped-call] ) def run(self, host: str = "0.0.0.0", port: int = 5555, threaded: bool = True) -> None: From 346f8a49a09337288f4c155064a5b3b34911d926 Mon Sep 17 00:00:00 2001 From: Paul Nechifor Date: Tue, 25 Nov 2025 05:29:50 +0200 Subject: [PATCH 3/3] fix rewrite --- dimos/agents/agent.py | 8 ++++---- dimos/agents/memory/spatial_vector_db.py | 6 +++--- dimos/agents2/agent.py | 2 +- dimos/core/transport.py | 4 ++-- dimos/manipulation/manip_aio_pipeline.py | 6 +++--- dimos/manipulation/manip_aio_processer.py | 20 +++++++++---------- .../visual_servoing/detection3d.py | 4 ++-- dimos/manipulation/visual_servoing/utils.py | 6 +++--- dimos/mapping/osm/current_location_map.py | 4 ++-- dimos/msgs/foxglove_msgs/ImageAnnotations.py | 4 ++-- .../msgs/geometry_msgs/PoseWithCovariance.py | 8 ++++---- .../PoseWithCovarianceStamped.py | 8 ++++---- .../msgs/geometry_msgs/TwistWithCovariance.py | 8 ++++---- .../TwistWithCovarianceStamped.py | 8 ++++---- dimos/msgs/sensor_msgs/Image.py | 4 ++-- dimos/msgs/sensor_msgs/PointCloud2.py | 4 ++-- .../sensor_msgs/image_impls/AbstractImage.py | 4 ++-- .../msgs/sensor_msgs/image_impls/CudaImage.py | 16 +++++++-------- dimos/msgs/std_msgs/Header.py | 4 ++-- dimos/msgs/tf2_msgs/TFMessage.py | 4 ++-- dimos/msgs/vision_msgs/BoundingBox2DArray.py | 4 ++-- dimos/msgs/vision_msgs/BoundingBox3DArray.py | 4 ++-- dimos/msgs/vision_msgs/Detection2DArray.py | 4 ++-- dimos/msgs/vision_msgs/Detection3DArray.py | 4 ++-- .../local_planner/holonomic_local_planner.py | 2 +- dimos/perception/common/utils.py | 6 +++--- dimos/perception/detection/detectors/detic.py | 4 ++-- dimos/perception/detection/module2D.py | 4 ++-- dimos/perception/detection/module3D.py | 4 ++-- dimos/perception/detection/moduleDB.py | 4 ++-- .../detection/reid/embedding_id_system.py | 2 +- .../grasp_generation/grasp_generation.py | 4 ++-- dimos/perception/grasp_generation/utils.py | 4 ++-- dimos/perception/object_detection_stream.py | 4 ++-- dimos/perception/pointcloud/cuboid_fit.py | 14 ++++++------- .../pointcloud/pointcloud_filtering.py | 16 +++++++-------- dimos/perception/spatial_perception.py | 4 ++-- dimos/robot/foxglove_bridge.py | 4 ++-- dimos/robot/ros_control.py | 4 ++-- dimos/robot/ros_transform.py | 4 ++-- dimos/robot/unitree_webrtc/modular/detect.py | 4 ++-- dimos/skills/manipulation/pick_and_place.py | 4 ++-- .../manipulation/rotation_constraint_skill.py | 4 ++-- dimos/stream/audio/base.py | 4 ++-- dimos/stream/frame_processor.py | 4 ++-- dimos/stream/video_operators.py | 10 +++++----- dimos/types/timestamped.py | 4 ++-- dimos/web/dimos_interface/api/server.py | 4 ++-- dimos/web/fastapi_server.py | 4 ++-- dimos/web/flask_server.py | 4 ++-- 50 files changed, 139 insertions(+), 139 deletions(-) diff --git a/dimos/agents/agent.py b/dimos/agents/agent.py index 428ee78b61..65b4509bfb 100644 --- a/dimos/agents/agent.py +++ b/dimos/agents/agent.py @@ -476,8 +476,8 @@ def _log_response_to_file(self, response, output_dir: str | None = None) -> None def subscribe_to_image_processing( # type: ignore[no-untyped-def] self, - frame_observable: Observable, - query_extractor=None, # type: ignore[type-arg] + frame_observable: Observable, # type: ignore[type-arg] + query_extractor=None, ) -> Disposable: """Subscribes to a stream of video frames for processing. @@ -677,9 +677,9 @@ def run_observable_query(self, query_text: str, **kwargs) -> Observable: # type """ return create( lambda observer, _: self._observable_query( - observer, + observer, # type: ignore[arg-type] incoming_query=query_text, - **kwargs, # type: ignore[arg-type] + **kwargs, ) ) diff --git a/dimos/agents/memory/spatial_vector_db.py b/dimos/agents/memory/spatial_vector_db.py index c270d93863..5b1a29741f 100644 --- a/dimos/agents/memory/spatial_vector_db.py +++ b/dimos/agents/memory/spatial_vector_db.py @@ -111,9 +111,9 @@ def __init__( # type: ignore[no-untyped-def] def add_image_vector( self, vector_id: str, - image: np.ndarray, - embedding: np.ndarray, - metadata: dict[str, Any], # type: ignore[type-arg] + image: np.ndarray, # type: ignore[type-arg] + embedding: np.ndarray, # type: ignore[type-arg] + metadata: dict[str, Any], ) -> None: """ Add an image with its embedding and metadata to the vector database. diff --git a/dimos/agents2/agent.py b/dimos/agents2/agent.py index 956783e63a..d902dba3da 100644 --- a/dimos/agents2/agent.py +++ b/dimos/agents2/agent.py @@ -314,7 +314,7 @@ def _get_state() -> str: self.state_messages = snapshot_msgs.get("state_msgs", []) # type: ignore[attr-defined] self.append_history( - *snapshot_msgs.get("tool_msgs", []), + *snapshot_msgs.get("tool_msgs", []), # type: ignore[attr-defined] *snapshot_msgs.get("history_msgs", []), # type: ignore[attr-defined] ) diff --git a/dimos/core/transport.py b/dimos/core/transport.py index f46346d5c7..c087d33dd0 100644 --- a/dimos/core/transport.py +++ b/dimos/core/transport.py @@ -223,8 +223,8 @@ def dask_register_subscriber(self, remoteInput: RemoteIn[T]) -> None: def subscribe(self, callback: Callable[[T], None], selfstream: In[T]) -> None: # type: ignore[override] if not self._started: selfstream.connection.owner.dask_register_subscriber( # type: ignore[union-attr] - selfstream.connection.name, - selfstream, # type: ignore[union-attr] + selfstream.connection.name, # type: ignore[union-attr] + selfstream, ).result() self._started = True self.subscribers.append(callback) diff --git a/dimos/manipulation/manip_aio_pipeline.py b/dimos/manipulation/manip_aio_pipeline.py index a741eed0c0..b351b84ec9 100644 --- a/dimos/manipulation/manip_aio_pipeline.py +++ b/dimos/manipulation/manip_aio_pipeline.py @@ -28,8 +28,8 @@ import websockets from dimos.perception.common.utils import colorize_depth -from dimos.perception.detection2d.detic_2d_det import ( - Detic2DDetector, # type: ignore[import-untyped] +from dimos.perception.detection2d.detic_2d_det import ( # type: ignore[import-untyped] + Detic2DDetector, ) from dimos.perception.grasp_generation.utils import draw_grasps_on_image from dimos.perception.object_detection_stream import ObjectDetectionStream @@ -320,7 +320,7 @@ def run_loop() -> None: async def _send_grasp_request( self, - points: np.ndarray, + points: np.ndarray, # type: ignore[type-arg] colors: np.ndarray | None, # type: ignore[type-arg] ) -> list[dict] | None: # type: ignore[type-arg] """Send grasp request to Dimensional Grasp server.""" diff --git a/dimos/manipulation/manip_aio_processer.py b/dimos/manipulation/manip_aio_processer.py index 905e71a15e..56be0860da 100644 --- a/dimos/manipulation/manip_aio_processer.py +++ b/dimos/manipulation/manip_aio_processer.py @@ -27,8 +27,8 @@ combine_object_data, detection_results_to_object_data, ) -from dimos.perception.detection2d.detic_2d_det import ( - Detic2DDetector, # type: ignore[import-untyped] +from dimos.perception.detection2d.detic_2d_det import ( # type: ignore[import-untyped] + Detic2DDetector, ) from dimos.perception.grasp_generation.grasp_generation import HostedGraspGenerator from dimos.perception.grasp_generation.utils import create_grasp_overlay @@ -122,9 +122,9 @@ def __init__( def process_frame( self, - rgb_image: np.ndarray, - depth_image: np.ndarray, - generate_grasps: bool | None = None, # type: ignore[type-arg] + rgb_image: np.ndarray, # type: ignore[type-arg] + depth_image: np.ndarray, # type: ignore[type-arg] + generate_grasps: bool | None = None, ) -> dict[str, Any]: """ Process a single RGB-D frame through the complete pipeline. @@ -196,9 +196,9 @@ def process_frame( # Combine all objects using intelligent duplicate removal all_objects = combine_object_data( - detected_objects, - segmentation_filtered_objects, - overlap_threshold=0.8, # type: ignore[arg-type] + detected_objects, # type: ignore[arg-type] + segmentation_filtered_objects, # type: ignore[arg-type] + overlap_threshold=0.8, ) # Get full point cloud @@ -337,8 +337,8 @@ def run_object_detection(self, rgb_image: np.ndarray) -> dict[str, Any]: # type def run_pointcloud_filtering( self, - rgb_image: np.ndarray, - depth_image: np.ndarray, + rgb_image: np.ndarray, # type: ignore[type-arg] + depth_image: np.ndarray, # type: ignore[type-arg] objects: list[dict], # type: ignore[type-arg] ) -> list[dict]: # type: ignore[type-arg] """Run point cloud filtering on detected objects.""" diff --git a/dimos/manipulation/visual_servoing/detection3d.py b/dimos/manipulation/visual_servoing/detection3d.py index 982e28ef31..f758317c03 100644 --- a/dimos/manipulation/visual_servoing/detection3d.py +++ b/dimos/manipulation/visual_servoing/detection3d.py @@ -92,8 +92,8 @@ def __init__( def process_frame( self, - rgb_image: np.ndarray, - depth_image: np.ndarray, + rgb_image: np.ndarray, # type: ignore[type-arg] + depth_image: np.ndarray, # type: ignore[type-arg] transform: np.ndarray | None = None, # type: ignore[type-arg] ) -> tuple[Detection3DArray, Detection2DArray]: """ diff --git a/dimos/manipulation/visual_servoing/utils.py b/dimos/manipulation/visual_servoing/utils.py index 2de8d11c6d..ef3df2ffeb 100644 --- a/dimos/manipulation/visual_servoing/utils.py +++ b/dimos/manipulation/visual_servoing/utils.py @@ -440,9 +440,9 @@ def parse_zed_pose(zed_pose_data: dict[str, Any]) -> Pose | None: def estimate_object_depth( - depth_image: np.ndarray, - segmentation_mask: np.ndarray | None, - bbox: list[float], # type: ignore[type-arg] + depth_image: np.ndarray, # type: ignore[type-arg] + segmentation_mask: np.ndarray | None, # type: ignore[type-arg] + bbox: list[float], ) -> float: """ Estimate object depth dimension using segmentation mask and depth data. diff --git a/dimos/mapping/osm/current_location_map.py b/dimos/mapping/osm/current_location_map.py index deca8a70aa..749972e94b 100644 --- a/dimos/mapping/osm/current_location_map.py +++ b/dimos/mapping/osm/current_location_map.py @@ -47,9 +47,9 @@ def query_for_one_position_and_context( ) -> tuple[LatLon, str] | None: return query_for_one_position_and_context( self._vl_model, - self._get_current_map(), + self._get_current_map(), # type: ignore[no-untyped-call] query, - robot_position, # type: ignore[no-untyped-call] + robot_position, ) def _get_current_map(self): # type: ignore[no-untyped-def] diff --git a/dimos/msgs/foxglove_msgs/ImageAnnotations.py b/dimos/msgs/foxglove_msgs/ImageAnnotations.py index 5375eea93b..3625429b89 100644 --- a/dimos/msgs/foxglove_msgs/ImageAnnotations.py +++ b/dimos/msgs/foxglove_msgs/ImageAnnotations.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from dimos_lcm.foxglove_msgs.ImageAnnotations import ( - ImageAnnotations as FoxgloveImageAnnotations, # type: ignore[import-untyped] +from dimos_lcm.foxglove_msgs.ImageAnnotations import ( # type: ignore[import-untyped] + ImageAnnotations as FoxgloveImageAnnotations, ) diff --git a/dimos/msgs/geometry_msgs/PoseWithCovariance.py b/dimos/msgs/geometry_msgs/PoseWithCovariance.py index f6ef67a6c9..5348efdc13 100644 --- a/dimos/msgs/geometry_msgs/PoseWithCovariance.py +++ b/dimos/msgs/geometry_msgs/PoseWithCovariance.py @@ -16,15 +16,15 @@ from typing import TYPE_CHECKING, TypeAlias -from dimos_lcm.geometry_msgs import ( - PoseWithCovariance as LCMPoseWithCovariance, # type: ignore[import-untyped] +from dimos_lcm.geometry_msgs import ( # type: ignore[import-untyped] + PoseWithCovariance as LCMPoseWithCovariance, ) import numpy as np from plum import dispatch try: - from geometry_msgs.msg import ( - PoseWithCovariance as ROSPoseWithCovariance, # type: ignore[attr-defined] + from geometry_msgs.msg import ( # type: ignore[attr-defined] + PoseWithCovariance as ROSPoseWithCovariance, ) except ImportError: ROSPoseWithCovariance = None # type: ignore[assignment, misc] diff --git a/dimos/msgs/geometry_msgs/PoseWithCovarianceStamped.py b/dimos/msgs/geometry_msgs/PoseWithCovarianceStamped.py index eb9dd526b6..a79088007d 100644 --- a/dimos/msgs/geometry_msgs/PoseWithCovarianceStamped.py +++ b/dimos/msgs/geometry_msgs/PoseWithCovarianceStamped.py @@ -17,15 +17,15 @@ import time from typing import TypeAlias -from dimos_lcm.geometry_msgs import ( - PoseWithCovarianceStamped as LCMPoseWithCovarianceStamped, # type: ignore[import-untyped] +from dimos_lcm.geometry_msgs import ( # type: ignore[import-untyped] + PoseWithCovarianceStamped as LCMPoseWithCovarianceStamped, ) import numpy as np from plum import dispatch try: - from geometry_msgs.msg import ( - PoseWithCovarianceStamped as ROSPoseWithCovarianceStamped, # type: ignore[attr-defined] + from geometry_msgs.msg import ( # type: ignore[attr-defined] + PoseWithCovarianceStamped as ROSPoseWithCovarianceStamped, ) except ImportError: ROSPoseWithCovarianceStamped = None # type: ignore[assignment, misc] diff --git a/dimos/msgs/geometry_msgs/TwistWithCovariance.py b/dimos/msgs/geometry_msgs/TwistWithCovariance.py index 28e40934ae..ceeb3a0193 100644 --- a/dimos/msgs/geometry_msgs/TwistWithCovariance.py +++ b/dimos/msgs/geometry_msgs/TwistWithCovariance.py @@ -16,15 +16,15 @@ from typing import TypeAlias -from dimos_lcm.geometry_msgs import ( - TwistWithCovariance as LCMTwistWithCovariance, # type: ignore[import-untyped] +from dimos_lcm.geometry_msgs import ( # type: ignore[import-untyped] + TwistWithCovariance as LCMTwistWithCovariance, ) import numpy as np from plum import dispatch try: - from geometry_msgs.msg import ( - TwistWithCovariance as ROSTwistWithCovariance, # type: ignore[attr-defined] + from geometry_msgs.msg import ( # type: ignore[attr-defined] + TwistWithCovariance as ROSTwistWithCovariance, ) except ImportError: ROSTwistWithCovariance = None # type: ignore[assignment, misc] diff --git a/dimos/msgs/geometry_msgs/TwistWithCovarianceStamped.py b/dimos/msgs/geometry_msgs/TwistWithCovarianceStamped.py index 83c0914e23..256dca79e3 100644 --- a/dimos/msgs/geometry_msgs/TwistWithCovarianceStamped.py +++ b/dimos/msgs/geometry_msgs/TwistWithCovarianceStamped.py @@ -17,15 +17,15 @@ import time from typing import TypeAlias -from dimos_lcm.geometry_msgs import ( - TwistWithCovarianceStamped as LCMTwistWithCovarianceStamped, # type: ignore[import-untyped] +from dimos_lcm.geometry_msgs import ( # type: ignore[import-untyped] + TwistWithCovarianceStamped as LCMTwistWithCovarianceStamped, ) import numpy as np from plum import dispatch try: - from geometry_msgs.msg import ( - TwistWithCovarianceStamped as ROSTwistWithCovarianceStamped, # type: ignore[attr-defined] + from geometry_msgs.msg import ( # type: ignore[attr-defined] + TwistWithCovarianceStamped as ROSTwistWithCovarianceStamped, ) except ImportError: ROSTwistWithCovarianceStamped = None # type: ignore[assignment, misc] diff --git a/dimos/msgs/sensor_msgs/Image.py b/dimos/msgs/sensor_msgs/Image.py index 3ac9660042..7f26f88a71 100644 --- a/dimos/msgs/sensor_msgs/Image.py +++ b/dimos/msgs/sensor_msgs/Image.py @@ -178,9 +178,9 @@ def from_file( # type: ignore[no-untyped-def] @classmethod def from_opencv( # type: ignore[no-untyped-def] cls, - cv_image: np.ndarray, + cv_image: np.ndarray, # type: ignore[type-arg] format: ImageFormat = ImageFormat.BGR, - **kwargs, # type: ignore[type-arg] + **kwargs, ) -> Image: """Construct from an OpenCV image (NumPy array).""" return cls( diff --git a/dimos/msgs/sensor_msgs/PointCloud2.py b/dimos/msgs/sensor_msgs/PointCloud2.py index a0df184c6f..a7db0ca24b 100644 --- a/dimos/msgs/sensor_msgs/PointCloud2.py +++ b/dimos/msgs/sensor_msgs/PointCloud2.py @@ -60,9 +60,9 @@ def __init__( @classmethod def from_numpy( cls, - points: np.ndarray, + points: np.ndarray, # type: ignore[type-arg] frame_id: str = "world", - timestamp: float | None = None, # type: ignore[type-arg] + timestamp: float | None = None, ) -> PointCloud2: """Create PointCloud2 from numpy array of shape (N, 3). diff --git a/dimos/msgs/sensor_msgs/image_impls/AbstractImage.py b/dimos/msgs/sensor_msgs/image_impls/AbstractImage.py index 1d707c12a1..f885e465a2 100644 --- a/dimos/msgs/sensor_msgs/image_impls/AbstractImage.py +++ b/dimos/msgs/sensor_msgs/image_impls/AbstractImage.py @@ -204,8 +204,8 @@ def to_base64(self, quality: int = 80) -> str: bgr = self.to_bgr() success, buffer = cv2.imencode( ".jpg", - _to_cpu(bgr.data), - [int(cv2.IMWRITE_JPEG_QUALITY), int(quality)], # type: ignore[no-untyped-call] + _to_cpu(bgr.data), # type: ignore[no-untyped-call] + [int(cv2.IMWRITE_JPEG_QUALITY), int(quality)], ) if not success: raise ValueError("Failed to encode image as JPEG") diff --git a/dimos/msgs/sensor_msgs/image_impls/CudaImage.py b/dimos/msgs/sensor_msgs/image_impls/CudaImage.py index 5b33c97120..facb595ef1 100644 --- a/dimos/msgs/sensor_msgs/image_impls/CudaImage.py +++ b/dimos/msgs/sensor_msgs/image_impls/CudaImage.py @@ -578,10 +578,10 @@ def to_rgb(self) -> CudaImage: return self.copy() # type: ignore if self.format == ImageFormat.BGRA: return CudaImage( - _bgra_to_rgba_cuda(self.data), + _bgra_to_rgba_cuda(self.data), # type: ignore[no-untyped-call] ImageFormat.RGBA, self.frame_id, - self.ts, # type: ignore[no-untyped-call] + self.ts, ) if self.format == ImageFormat.GRAY: return CudaImage(_gray_to_rgb_cuda(self.data), ImageFormat.RGB, self.frame_id, self.ts) # type: ignore[no-untyped-call] @@ -597,10 +597,10 @@ def to_bgr(self) -> CudaImage: return CudaImage(_rgb_to_bgr_cuda(self.data), ImageFormat.BGR, self.frame_id, self.ts) # type: ignore[no-untyped-call] if self.format == ImageFormat.RGBA: return CudaImage( - _rgba_to_bgra_cuda(self.data)[..., :3], + _rgba_to_bgra_cuda(self.data)[..., :3], # type: ignore[no-untyped-call] ImageFormat.BGR, self.frame_id, - self.ts, # type: ignore[no-untyped-call] + self.ts, ) if self.format == ImageFormat.BGRA: return CudaImage(self.data[..., :3], ImageFormat.BGR, self.frame_id, self.ts) # type: ignore[index] @@ -614,10 +614,10 @@ def to_bgr(self) -> CudaImage: if self.format in (ImageFormat.GRAY16, ImageFormat.DEPTH16): gray8 = (self.data.astype(cp.float32) / 256.0).clip(0, 255).astype(cp.uint8) # type: ignore return CudaImage( - _rgb_to_bgr_cuda(_gray_to_rgb_cuda(gray8)), + _rgb_to_bgr_cuda(_gray_to_rgb_cuda(gray8)), # type: ignore[no-untyped-call] ImageFormat.BGR, self.frame_id, - self.ts, # type: ignore[no-untyped-call] + self.ts, ) return self.copy() # type: ignore @@ -782,8 +782,8 @@ def solve_pnp_batch( obj[b], img[b], K_b, - dist_b, - flags=cv2.SOLVEPNP_ITERATIVE, # type: ignore[arg-type] + dist_b, # type: ignore[arg-type] + flags=cv2.SOLVEPNP_ITERATIVE, ) if not ok: raise RuntimeError(f"cv2.solvePnP failed for batch index {b}") diff --git a/dimos/msgs/std_msgs/Header.py b/dimos/msgs/std_msgs/Header.py index e8135bdd07..39af66f0c8 100644 --- a/dimos/msgs/std_msgs/Header.py +++ b/dimos/msgs/std_msgs/Header.py @@ -22,8 +22,8 @@ # Import the actual LCM header type that's returned from decoding try: - from lcm_msgs.std_msgs.Header import ( - Header as DecodedLCMHeader, # type: ignore[import-not-found] + from lcm_msgs.std_msgs.Header import ( # type: ignore[import-not-found] + Header as DecodedLCMHeader, ) except ImportError: DecodedLCMHeader = None diff --git a/dimos/msgs/tf2_msgs/TFMessage.py b/dimos/msgs/tf2_msgs/TFMessage.py index 7a66f60459..732ecf3872 100644 --- a/dimos/msgs/tf2_msgs/TFMessage.py +++ b/dimos/msgs/tf2_msgs/TFMessage.py @@ -32,8 +32,8 @@ from dimos_lcm.tf2_msgs import TFMessage as LCMTFMessage # type: ignore[import-untyped] try: - from geometry_msgs.msg import ( - TransformStamped as ROSTransformStamped, # type: ignore[attr-defined] + from geometry_msgs.msg import ( # type: ignore[attr-defined] + TransformStamped as ROSTransformStamped, ) from tf2_msgs.msg import TFMessage as ROSTFMessage # type: ignore[attr-defined] except ImportError: diff --git a/dimos/msgs/vision_msgs/BoundingBox2DArray.py b/dimos/msgs/vision_msgs/BoundingBox2DArray.py index 23067fe5e2..f45edc3703 100644 --- a/dimos/msgs/vision_msgs/BoundingBox2DArray.py +++ b/dimos/msgs/vision_msgs/BoundingBox2DArray.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from dimos_lcm.vision_msgs.BoundingBox2DArray import ( - BoundingBox2DArray as LCMBoundingBox2DArray, # type: ignore[import-untyped] +from dimos_lcm.vision_msgs.BoundingBox2DArray import ( # type: ignore[import-untyped] + BoundingBox2DArray as LCMBoundingBox2DArray, ) diff --git a/dimos/msgs/vision_msgs/BoundingBox3DArray.py b/dimos/msgs/vision_msgs/BoundingBox3DArray.py index cd6bd2dd79..f1bb31ffed 100644 --- a/dimos/msgs/vision_msgs/BoundingBox3DArray.py +++ b/dimos/msgs/vision_msgs/BoundingBox3DArray.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from dimos_lcm.vision_msgs.BoundingBox3DArray import ( - BoundingBox3DArray as LCMBoundingBox3DArray, # type: ignore[import-untyped] +from dimos_lcm.vision_msgs.BoundingBox3DArray import ( # type: ignore[import-untyped] + BoundingBox3DArray as LCMBoundingBox3DArray, ) diff --git a/dimos/msgs/vision_msgs/Detection2DArray.py b/dimos/msgs/vision_msgs/Detection2DArray.py index d8be591872..98c2b3e8dc 100644 --- a/dimos/msgs/vision_msgs/Detection2DArray.py +++ b/dimos/msgs/vision_msgs/Detection2DArray.py @@ -11,8 +11,8 @@ # 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 dimos_lcm.vision_msgs.Detection2DArray import ( - Detection2DArray as LCMDetection2DArray, # type: ignore[import-untyped] +from dimos_lcm.vision_msgs.Detection2DArray import ( # type: ignore[import-untyped] + Detection2DArray as LCMDetection2DArray, ) from dimos.types.timestamped import to_timestamp diff --git a/dimos/msgs/vision_msgs/Detection3DArray.py b/dimos/msgs/vision_msgs/Detection3DArray.py index a82c929e50..e1da455e26 100644 --- a/dimos/msgs/vision_msgs/Detection3DArray.py +++ b/dimos/msgs/vision_msgs/Detection3DArray.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from dimos_lcm.vision_msgs.Detection3DArray import ( - Detection3DArray as LCMDetection3DArray, # type: ignore[import-untyped] +from dimos_lcm.vision_msgs.Detection3DArray import ( # type: ignore[import-untyped] + Detection3DArray as LCMDetection3DArray, ) diff --git a/dimos/navigation/local_planner/holonomic_local_planner.py b/dimos/navigation/local_planner/holonomic_local_planner.py index b91ddfb11d..72d068e05b 100644 --- a/dimos/navigation/local_planner/holonomic_local_planner.py +++ b/dimos/navigation/local_planner/holonomic_local_planner.py @@ -223,7 +223,7 @@ def _compute_obstacle_repulsion(self, pose: np.ndarray, costmap: np.ndarray) -> def _find_closest_point_on_path( self, - pose: np.ndarray, + pose: np.ndarray, # type: ignore[type-arg] path: np.ndarray, # type: ignore[type-arg] ) -> tuple[int, np.ndarray]: # type: ignore[type-arg] """ diff --git a/dimos/perception/common/utils.py b/dimos/perception/common/utils.py index c54ac6b352..2fdd441855 100644 --- a/dimos/perception/common/utils.py +++ b/dimos/perception/common/utils.py @@ -313,7 +313,7 @@ def project_3d_points_to_2d_cuda( def project_3d_points_to_2d_cpu( - points_3d: np.ndarray, + points_3d: np.ndarray, # type: ignore[type-arg] camera_intrinsics: list[float] | np.ndarray, # type: ignore[type-arg] ) -> np.ndarray: # type: ignore[type-arg] pts = np.asarray(points_3d, dtype=np.float64) @@ -446,9 +446,9 @@ def project_2d_points_to_3d( def colorize_depth( - depth_img: Union[np.ndarray, "cp.ndarray"], + depth_img: Union[np.ndarray, "cp.ndarray"], # type: ignore[type-arg] max_depth: float = 5.0, - overlay_stats: bool = True, # type: ignore[type-arg] + overlay_stats: bool = True, ) -> Union[np.ndarray, "cp.ndarray"] | None: # type: ignore[type-arg] """ Normalize and colorize depth image using COLORMAP_JET with optional statistics overlay. diff --git a/dimos/perception/detection/detectors/detic.py b/dimos/perception/detection/detectors/detic.py index dd012f6864..e36856ac0d 100644 --- a/dimos/perception/detection/detectors/detic.py +++ b/dimos/perception/detection/detectors/detic.py @@ -181,8 +181,8 @@ def __init__( # type: ignore[no-untyped-def] # Import Detic modules from centernet.config import add_centernet_config # type: ignore[import-not-found] from detic.config import add_detic_config # type: ignore[import-not-found] - from detic.modeling.text.text_encoder import ( - build_text_encoder, # type: ignore[import-not-found] + from detic.modeling.text.text_encoder import ( # type: ignore[import-not-found] + build_text_encoder, ) from detic.modeling.utils import reset_cls_test # type: ignore[import-not-found] diff --git a/dimos/perception/detection/module2D.py b/dimos/perception/detection/module2D.py index 100bc6ee0b..bb6d7017c0 100644 --- a/dimos/perception/detection/module2D.py +++ b/dimos/perception/detection/module2D.py @@ -112,9 +112,9 @@ def track(self, detections: ImageDetections2D) -> None: # Active detection - compute real position detection = detections.detections[index] position_3d = self.pixel_to_3d( # type: ignore[attr-defined] - detection.center_bbox, + detection.center_bbox, # type: ignore[attr-defined] self.config.camera_info, - assumed_depth=1.0, # type: ignore[attr-defined] + assumed_depth=1.0, ) else: # No detection at this index - publish zero transform diff --git a/dimos/perception/detection/module3D.py b/dimos/perception/detection/module3D.py index 8245a716c7..e2dd0b0af3 100644 --- a/dimos/perception/detection/module3D.py +++ b/dimos/perception/detection/module3D.py @@ -13,8 +13,8 @@ # limitations under the License. -from dimos_lcm.foxglove_msgs.ImageAnnotations import ( - ImageAnnotations, # type: ignore[import-untyped] +from dimos_lcm.foxglove_msgs.ImageAnnotations import ( # type: ignore[import-untyped] + ImageAnnotations, ) from lcm_msgs.foxglove_msgs import SceneUpdate # type: ignore[import-not-found] from reactivex import operators as ops diff --git a/dimos/perception/detection/moduleDB.py b/dimos/perception/detection/moduleDB.py index c679f06fef..b1e67bcbe7 100644 --- a/dimos/perception/detection/moduleDB.py +++ b/dimos/perception/detection/moduleDB.py @@ -17,8 +17,8 @@ import time from typing import Any -from dimos_lcm.foxglove_msgs.ImageAnnotations import ( - ImageAnnotations, # type: ignore[import-untyped] +from dimos_lcm.foxglove_msgs.ImageAnnotations import ( # type: ignore[import-untyped] + ImageAnnotations, ) from lcm_msgs.foxglove_msgs import SceneUpdate # type: ignore[import-not-found] from reactivex.observable import Observable diff --git a/dimos/perception/detection/reid/embedding_id_system.py b/dimos/perception/detection/reid/embedding_id_system.py index 169747fc58..0e41d428fd 100644 --- a/dimos/perception/detection/reid/embedding_id_system.py +++ b/dimos/perception/detection/reid/embedding_id_system.py @@ -130,7 +130,7 @@ def update_embedding(self, track_id: int, new_embedding: Embedding) -> None: def _compute_group_similarity( self, - query_embeddings: list[np.ndarray], + query_embeddings: list[np.ndarray], # type: ignore[type-arg] candidate_embeddings: list[np.ndarray], # type: ignore[type-arg] ) -> float: """Compute similarity between two groups of embeddings. diff --git a/dimos/perception/grasp_generation/grasp_generation.py b/dimos/perception/grasp_generation/grasp_generation.py index 84a470b813..b6f0cee936 100644 --- a/dimos/perception/grasp_generation/grasp_generation.py +++ b/dimos/perception/grasp_generation/grasp_generation.py @@ -113,7 +113,7 @@ def generate_grasps_from_objects( def _send_grasp_request_sync( self, - points: np.ndarray, + points: np.ndarray, # type: ignore[type-arg] colors: np.ndarray | None, # type: ignore[type-arg] ) -> list[dict] | None: # type: ignore[type-arg] """Send synchronous grasp request to grasp server.""" @@ -151,7 +151,7 @@ def _send_grasp_request_sync( async def _async_grasp_request( self, - points: np.ndarray, + points: np.ndarray, # type: ignore[type-arg] colors: np.ndarray, # type: ignore[type-arg] ) -> list[dict] | None: # type: ignore[type-arg] """Async grasp request helper.""" diff --git a/dimos/perception/grasp_generation/utils.py b/dimos/perception/grasp_generation/utils.py index 73e859a93f..9632479360 100644 --- a/dimos/perception/grasp_generation/utils.py +++ b/dimos/perception/grasp_generation/utils.py @@ -146,8 +146,8 @@ def create_gripper_geometry( def create_all_gripper_geometries( - grasp_list: list[dict], - max_grasps: int = -1, # type: ignore[type-arg] + grasp_list: list[dict], # type: ignore[type-arg] + max_grasps: int = -1, ) -> list[o3d.geometry.TriangleMesh]: """ Create gripper geometries for multiple grasps. diff --git a/dimos/perception/object_detection_stream.py b/dimos/perception/object_detection_stream.py index 358dd3e0fc..0f15d75f60 100644 --- a/dimos/perception/object_detection_stream.py +++ b/dimos/perception/object_detection_stream.py @@ -19,8 +19,8 @@ from dimos.perception.detection2d.yolo_2d_det import Yolo2DDetector # type: ignore[import-untyped] try: - from dimos.perception.detection2d.detic_2d_det import ( - Detic2DDetector, # type: ignore[import-untyped] + from dimos.perception.detection2d.detic_2d_det import ( # type: ignore[import-untyped] + Detic2DDetector, ) DETIC_AVAILABLE = True diff --git a/dimos/perception/pointcloud/cuboid_fit.py b/dimos/perception/pointcloud/cuboid_fit.py index 2e7f517620..f0d096bdfa 100644 --- a/dimos/perception/pointcloud/cuboid_fit.py +++ b/dimos/perception/pointcloud/cuboid_fit.py @@ -19,8 +19,8 @@ def fit_cuboid( - points: np.ndarray | o3d.geometry.PointCloud, - method: str = "minimal", # type: ignore[type-arg] + points: np.ndarray | o3d.geometry.PointCloud, # type: ignore[type-arg] + method: str = "minimal", ) -> dict | None: # type: ignore[type-arg] """ Fit a cuboid to a point cloud using Open3D's built-in methods. @@ -119,9 +119,9 @@ def fit_cuboid_simple(points: np.ndarray | o3d.geometry.PointCloud) -> dict | No def _compute_fitting_error( - points: np.ndarray, - center: np.ndarray, - dimensions: np.ndarray, + points: np.ndarray, # type: ignore[type-arg] + center: np.ndarray, # type: ignore[type-arg] + dimensions: np.ndarray, # type: ignore[type-arg] rotation: np.ndarray, # type: ignore[type-arg] ) -> float: """ @@ -158,8 +158,8 @@ def _compute_fitting_error( def get_cuboid_corners( - center: np.ndarray, - dimensions: np.ndarray, + center: np.ndarray, # type: ignore[type-arg] + dimensions: np.ndarray, # type: ignore[type-arg] rotation: np.ndarray, # type: ignore[type-arg] ) -> np.ndarray: # type: ignore[type-arg] """ diff --git a/dimos/perception/pointcloud/pointcloud_filtering.py b/dimos/perception/pointcloud/pointcloud_filtering.py index 31c211ac29..6f15749910 100644 --- a/dimos/perception/pointcloud/pointcloud_filtering.py +++ b/dimos/perception/pointcloud/pointcloud_filtering.py @@ -115,9 +115,9 @@ def generate_color_from_id(self, object_id: int) -> np.ndarray: # type: ignore[ def _validate_inputs( # type: ignore[no-untyped-def] self, - color_img: np.ndarray, - depth_img: np.ndarray, - objects: list[ObjectData], # type: ignore[type-arg] + color_img: np.ndarray, # type: ignore[type-arg] + depth_img: np.ndarray, # type: ignore[type-arg] + objects: list[ObjectData], ): """Validate input parameters.""" if color_img.shape[:2] != depth_img.shape: @@ -199,9 +199,9 @@ def get_full_point_cloud(self) -> o3d.geometry.PointCloud: def process_images( self, - color_img: np.ndarray, - depth_img: np.ndarray, - objects: list[ObjectData], # type: ignore[type-arg] + color_img: np.ndarray, # type: ignore[type-arg] + depth_img: np.ndarray, # type: ignore[type-arg] + objects: list[ObjectData], ) -> list[ObjectData]: """ Process color and depth images with object detection results to create filtered point clouds. @@ -278,8 +278,8 @@ def process_images( color_img, depth_img, processed_masks, - self.depth_camera_matrix, - depth_scale=1.0, # type: ignore[arg-type] + self.depth_camera_matrix, # type: ignore[arg-type] + depth_scale=1.0, ) # Process each object and update ObjectData diff --git a/dimos/perception/spatial_perception.py b/dimos/perception/spatial_perception.py index bb6e76d2d3..5f2f4ef31f 100644 --- a/dimos/perception/spatial_perception.py +++ b/dimos/perception/spatial_perception.py @@ -149,8 +149,8 @@ def __init__( try: logger.info(f"Loading existing visual memory from {visual_memory_path}...") self._visual_memory = VisualMemory.load( - visual_memory_path, - output_dir=output_dir, # type: ignore[arg-type] + visual_memory_path, # type: ignore[arg-type] + output_dir=output_dir, ) logger.info(f"Loaded {self._visual_memory.count()} images from previous runs") except Exception as e: diff --git a/dimos/robot/foxglove_bridge.py b/dimos/robot/foxglove_bridge.py index 0a37322df7..4b925abdfa 100644 --- a/dimos/robot/foxglove_bridge.py +++ b/dimos/robot/foxglove_bridge.py @@ -17,8 +17,8 @@ import threading # this is missing, I'm just trying to import lcm_foxglove_bridge.py from dimos_lcm -from dimos_lcm.foxglove_bridge import ( - FoxgloveBridge as LCMFoxgloveBridge, # type: ignore[import-untyped] +from dimos_lcm.foxglove_bridge import ( # type: ignore[import-untyped] + FoxgloveBridge as LCMFoxgloveBridge, ) from dimos.core import DimosCluster, Module, rpc diff --git a/dimos/robot/ros_control.py b/dimos/robot/ros_control.py index e857615a31..8c41620780 100644 --- a/dimos/robot/ros_control.py +++ b/dimos/robot/ros_control.py @@ -240,8 +240,8 @@ def __init__( if webrtc_msg_type: self._webrtc_pub = self._node.create_publisher( webrtc_msg_type, - webrtc_topic, - qos_profile=command_qos, # type: ignore[arg-type] + webrtc_topic, # type: ignore[arg-type] + qos_profile=command_qos, ) # Initialize command queue diff --git a/dimos/robot/ros_transform.py b/dimos/robot/ros_transform.py index e1bcb880a0..7ca041ec1b 100644 --- a/dimos/robot/ros_transform.py +++ b/dimos/robot/ros_transform.py @@ -127,8 +127,8 @@ def transform_point( # type: ignore[no-untyped-def] # Return as Vector type if len(point) > 2: # type: ignore[arg-type] return Vector( - transformed_ps.point.x, - transformed_ps.point.y, + transformed_ps.point.x, # type: ignore[union-attr] + transformed_ps.point.y, # type: ignore[union-attr] transformed_ps.point.z, # type: ignore[union-attr] ) else: diff --git a/dimos/robot/unitree_webrtc/modular/detect.py b/dimos/robot/unitree_webrtc/modular/detect.py index 4abdc7ed71..f0b453a809 100644 --- a/dimos/robot/unitree_webrtc/modular/detect.py +++ b/dimos/robot/unitree_webrtc/modular/detect.py @@ -108,8 +108,8 @@ def broadcast( # type: ignore[no-untyped-def] detections, annotations, ) -> None: - from dimos_lcm.foxglove_msgs.ImageAnnotations import ( - ImageAnnotations, # type: ignore[import-untyped] + from dimos_lcm.foxglove_msgs.ImageAnnotations import ( # type: ignore[import-untyped] + ImageAnnotations, ) from dimos.core import LCMTransport diff --git a/dimos/skills/manipulation/pick_and_place.py b/dimos/skills/manipulation/pick_and_place.py index 93323c6b4a..3b7dd808c6 100644 --- a/dimos/skills/manipulation/pick_and_place.py +++ b/dimos/skills/manipulation/pick_and_place.py @@ -272,9 +272,9 @@ def _query_pick_and_place_points( def _query_single_point( self, - frame: np.ndarray, + frame: np.ndarray, # type: ignore[type-arg] query: str, - point_type: str, # type: ignore[type-arg] + point_type: str, ) -> tuple[int, int] | None: """ Query Qwen to get a single point location. diff --git a/dimos/skills/manipulation/rotation_constraint_skill.py b/dimos/skills/manipulation/rotation_constraint_skill.py index 52d01d52cb..e7291d658d 100644 --- a/dimos/skills/manipulation/rotation_constraint_skill.py +++ b/dimos/skills/manipulation/rotation_constraint_skill.py @@ -89,8 +89,8 @@ def __call__(self) -> RotationConstraint: secondary_pivot_vector = None if self.secondary_pivot_point: secondary_pivot_vector = Vector( - self.secondary_pivot_point[0], - self.secondary_pivot_point[1], + self.secondary_pivot_point[0], # type: ignore[arg-type] + self.secondary_pivot_point[1], # type: ignore[arg-type] 0.0, # type: ignore[arg-type] ) diff --git a/dimos/stream/audio/base.py b/dimos/stream/audio/base.py index cdefcbc705..8b6d278bfb 100644 --- a/dimos/stream/audio/base.py +++ b/dimos/stream/audio/base.py @@ -61,10 +61,10 @@ class AudioEvent: def __init__( self, - data: np.ndarray, + data: np.ndarray, # type: ignore[type-arg] sample_rate: int, timestamp: float, - channels: int = 1, # type: ignore[type-arg] + channels: int = 1, ) -> None: """ Initialize an AudioEvent. diff --git a/dimos/stream/frame_processor.py b/dimos/stream/frame_processor.py index 84f1d0e578..fb8198d7b4 100644 --- a/dimos/stream/frame_processor.py +++ b/dimos/stream/frame_processor.py @@ -263,9 +263,9 @@ def process_stream_optical_flow_with_relevancy(self, frame_stream: Observable) - def process_stream_with_jpeg_export( self, - frame_stream: Observable, + frame_stream: Observable, # type: ignore[type-arg] suffix: str = "", - loop: bool = False, # type: ignore[type-arg] + loop: bool = False, ) -> Observable: # type: ignore[type-arg] """Processes stream by saving frames as JPEGs while passing them through. diff --git a/dimos/stream/video_operators.py b/dimos/stream/video_operators.py index 3b5a8ec75b..769993bf9e 100644 --- a/dimos/stream/video_operators.py +++ b/dimos/stream/video_operators.py @@ -204,9 +204,9 @@ def with_optical_flow( return lambda source: source.pipe( ops.scan( lambda acc, frame: frame_processor.compute_optical_flow( # type: ignore[arg-type, return-value] - acc, - frame, - compute_relevancy=False, # type: ignore[arg-type] + acc, # type: ignore[arg-type] + frame, # type: ignore[arg-type] + compute_relevancy=False, ), (None, None, None), ), @@ -217,8 +217,8 @@ def with_optical_flow( @staticmethod def with_zmq_socket( - socket: zmq.Socket, - scheduler: Any | None = None, # type: ignore[type-arg] + socket: zmq.Socket, # type: ignore[type-arg] + scheduler: Any | None = None, ) -> Callable[[Observable], Observable]: # type: ignore[type-arg] def send_frame(frame, socket) -> None: # type: ignore[no-untyped-def] _, img_encoded = cv2.imencode(".jpg", frame) diff --git a/dimos/types/timestamped.py b/dimos/types/timestamped.py index 16785c3e0d..8dfd5299d5 100644 --- a/dimos/types/timestamped.py +++ b/dimos/types/timestamped.py @@ -365,8 +365,8 @@ def on_secondary(i: int, secondary_item: SECONDARY) -> None: for i, secondary_obs in enumerate(secondary_observables): secondary_subs.append( secondary_obs.subscribe( - lambda x, idx=i: on_secondary(idx, x), - on_error=observer.on_error, # type: ignore[misc] + lambda x, idx=i: on_secondary(idx, x), # type: ignore[misc] + on_error=observer.on_error, ) ) diff --git a/dimos/web/dimos_interface/api/server.py b/dimos/web/dimos_interface/api/server.py index 4345ce3ba4..5db477dbca 100644 --- a/dimos/web/dimos_interface/api/server.py +++ b/dimos/web/dimos_interface/api/server.py @@ -180,8 +180,8 @@ def create_video_feed_route(self, key): # type: ignore[no-untyped-def] async def video_feed(): # type: ignore[no-untyped-def] return StreamingResponse( - self.stream_generator(key)(), - media_type="multipart/x-mixed-replace; boundary=frame", # type: ignore[no-untyped-call] + self.stream_generator(key)(), # type: ignore[no-untyped-call] + media_type="multipart/x-mixed-replace; boundary=frame", ) return video_feed diff --git a/dimos/web/fastapi_server.py b/dimos/web/fastapi_server.py index cf326312ea..e649f44f03 100644 --- a/dimos/web/fastapi_server.py +++ b/dimos/web/fastapi_server.py @@ -153,8 +153,8 @@ def create_video_feed_route(self, key): # type: ignore[no-untyped-def] async def video_feed(): # type: ignore[no-untyped-def] return StreamingResponse( - self.stream_generator(key)(), - media_type="multipart/x-mixed-replace; boundary=frame", # type: ignore[no-untyped-call] + self.stream_generator(key)(), # type: ignore[no-untyped-call] + media_type="multipart/x-mixed-replace; boundary=frame", ) return video_feed diff --git a/dimos/web/flask_server.py b/dimos/web/flask_server.py index ac922f4120..180ca01891 100644 --- a/dimos/web/flask_server.py +++ b/dimos/web/flask_server.py @@ -85,8 +85,8 @@ def generate(): # type: ignore[no-untyped-def] def make_response_generator(key): # type: ignore[no-untyped-def] def response_generator(): # type: ignore[no-untyped-def] return Response( - stream_generator(key)(), - mimetype="multipart/x-mixed-replace; boundary=frame", # type: ignore[no-untyped-call] + stream_generator(key)(), # type: ignore[no-untyped-call] + mimetype="multipart/x-mixed-replace; boundary=frame", ) return response_generator